Android笔记(二)
文章目录
1. 基础UI
1.1 常用控件
关于单位:
注:下面每个控件后的属性只代表《第三行代码》中在该控件小节中学习到的,控件属性一般都能应用在大部分的控件中
TextView:
Button:……
EditText:
ImageView:
android:src
属性指定所需资源,一般图片资源存储在drawable-xxx目录中,一般主流的手机屏幕分辨率是xxhdpi的- 可以通过imageView.setimageResource()方法动态更改图片
ProgressBar:(progress:进步)
android:visibility
属性指定控件的可见性,可选值visible 默认,可见、invisible “透明”、gone 完全不可见,也可以通过控件的setVisibility()方法动态更改可见性,可传入参数View.VISIBLE、View.INVISIBLE、View.GONEstyle
属性设置为水平进度条(?android:attr/progressBarStyleHorizontal),默认圆形进度条android:max
属性设置进度条最大值
class MainActivity : AppCompatActivity(), View.OnClickListener {
...
override fun onClick(v: View?) {
when(v?.id) {
R.id.button -> {
progressBar.progress = progressBar.progress + 10
}
}
}
}
AlertDialog:
class MainActivity : AppCompatActivity(), View.OnClickListener {
...
override fun onClick(v: View?) {
when(v?.id) {
R.id.button -> {
AlertDialog.Builder(this).apply {
setTitle(This is Dialog)
// setView(R.layout.dialog) 给弹窗设置自定义的布局
setMessage("Something important.")
// positive:积极的 negative:消极的,负
setPositiveButton("OK") { dialog, which -> }
setNegativeButton("Canel") { dialog, which -> }
show()
}
}
}
}
}
1.2 基本布局
LinearLayout线性布局:
android:orientation
属性指定排列方向,可选值vertical(垂直)、horizontal(水平)android:layout_gravity
属性用于指定控件在布局中的对齐方式,可选值与gravity差不多android:weight
属性根据比例来指定控件大小,由这个属性决定的方向属性规范设为0dp
RelativeLayout相对布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:Layout_height="match_parent">
<!-- align:对齐 -->
<Button
android:id="@+id/buttonl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="Button l" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="Button 2" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Button 3" />
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="Button 4" />
<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="Button 5" />
</RelativeLayout>
<!-- 上述代码是相对于父布局进行定位的,也可以相对于控件进行定位 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button3"
android:Layout width="wrap content"
android:layout_height="wrap_content"
android:Layout_centerInParent="true"
android:text="Button 3" />
<Button
android:id="@+id/buttonl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/button3"
android:layout_toLeftof="@id/button3"
android:text="Button 1" />
<Button
android:id="@+id/button2"
android:layout width="wrap content"
android:layout_height="wrap_content"
android:Layout_above="@id/button3"
android:Layout_toRightof="@id/button3"
android:texte"Button 2" />
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:Layout_below="@id/button3"
android:Layout_toLeftof="@id/button3"
android:text="Button 4" />
<Button
android:id="@+id/button5"
android:layout_width="wrap content"
android:layout_height="wrap_content"
android:Layout_beLow="@id/button3"
android:Layout_toRightof="@id/button3"
android:text="Button 5" />
</RelativeLayout>
FrameLayout帧布局:
所有控件默认摆放在布局左上角,主要在用到Fragment的时候使用
1.3 自定义控件
控件继承关系:
View基础UI组件,在屏幕上绘制一块矩形区域,并能响应这块区域的各种事件
ViewGroup可以包含多个子View和子ViewGroup,用于放置控件和布局的容器
引入布局:
创建自定义控件:
使引入的布局中的一些控件可以响应事件
例:自定义导航栏
class TitleLayout(context: Context, attrs: AttributeSet) :
LinearLayout(context, attrs) {
// attribute:属性
init {
LayoutInflater.from(context).inflate(R.layout.title, this)
titleBack.setonClickListener {
val activity = context as Activity
activity.finish()
}
}
}
Kotlin小知识——as关键字用于强制类型转换
<!-- activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:Layout_height="match_parent">
<com.example.uicustomviews.TitleLayout
android:Layout_width="match_parent"
android:Layout_height="wrap_content" /
</LinearLayout>
相关知识:
从inflate方法开始,搞懂LayoutInflater的inflate过程(上) - 知乎
2. ListView和RecyclerView
2.1 ListView基本使用
简单使用:……
定制界面:
定制一个界面,一个ImageView显示水果图片和一个TextView显示水果名称
// 定义实体类,作为ListView适配器的适配类型
class Fruit(val name: String, val imageId: Int)
// 自定义适配器
class FruitAdapter(activity: Activity, val resourceId: Int, data: List<Fruit>) :
ArrayAdapter<Fruit>(activity, resourceId, data) {
// getView()方法在每个子项被滚动到屏幕内的时候会被调用
override fun getView(position: Int, convertView: View?, parent: ViewGoup): View {
val view = LayoutInflater.from(context).inflate(resourced, parent. false)
val fruitimage: ImageView = view.findViewById(R.id.fruitimage)
val fruitName: TextView = view.findViewById(R.id.fruitName)
val fruit = getItem(position) // 获取当前像的Fruit实例
if (fruit != null) {
fruitimage.setimageResource(fruit.imageId)
fruitimage.text = fruit.name
}
return view
}
}
然后在Activity中设置这个自定义的适配器就行
提升运行效率:
效率低:自定义适配器的getView()方法每次都将布局重新加载一遍,ListView快速滚动的时候,会成为性能的瓶颈
解决:缓存之前的一些资源
class FruitAdapter(activity: Activity, val resourceId: Int, data: List<Fruit>) :
ArrayAdapter<Fruit>(activity, resourceId, data) {
// ViewHolder内部类对控件实例进行缓存
inner class ViewHolder(val fruitimage: ImageView, val fruitName: TextView)
override fun getView(position: Int, convertView: View?, parent: ViewGoup): View {
// convertView用于将之前加载好的布局进行缓存(convert:转换)
val view: View
val viewHodler: ViewHolder
if (convertView == null) {
val view = LayoutInflater.from(context).inflate(resourced, parent. false)
val fruitimage: ImageView = view.findViewById(R.id.fruitimage)
val fruitName: TextView = view.findViewById(R.id.fruitName)
viewHolder = ViewHolder(fruitimage, fruitName)
view.tag = ViewHolder
} else {
view = convertView
viewHodler = view.tag as ViewHolder
}
val fruit = getItem(position)
if (fruit != null) {
viewHodler.fruitimage.setimageResource(fruit.imageId)
viewHodler.fruitimage.text = fruit.name
}
return view
}
}
ListView点击事件:
listView.setonItemClickListener { parent, view, position, id ->
val fruit = fruitList[position]
Toast.makeText(this, fruit.name, Toast.LENGTH_SHORT).show()
}
Kotlin小知识——Lambda表达式没有用到的参数可以使用下划线替代,但相对位置不能改变
2. RecyclerView
ListView性能差,不能实现数据横向滚动,RecyclerView优化了这些问题
基本用法:
使用RecyclerView需要在build.grale文件的dependencies闭包中添加implementation 'androidx.recyclerview:recyclerview:1.0.0'
// 新建一个FruitAdapter类继承自RecyclerView.Adapter
class FruitAdpater(val fruistList: List<Fruit>) :
RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
// view是子项最外层布局
val fruitimage: ImageView = view.findViewById(R.id.fruitimage)
val fruitName: TextView = view.findViewById(R.id.fruitName)
}
// 用于创建ViewHolder实例
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHodler {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fruit_item, parent, false)
return ViewHolder(view)
}
// 用于对子项的数据进行赋值
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val fruit = fruitList[position]
holder.fruitimage.setimageResource(fruit.imageId)
holder.fruitName.text = fruit.name
}
override fun getItemCount() = fruitList.size
}
// 使用RecyclerView
class MainActivity : AppCompatActivity() {
private val fruitList = ArrayList<Fruit>()
override fun onCreate(savedInstanceState: Bundle?) {
...
initFruit()
// layoutManager用于指定RecyclerView布局方式
val layoutManager = linearlayoutmanager(this)
recycleView.layoutManager = layoutManager
val adapter = FruitAdapter(fruitList)
recyclerView.adapter = adapter
}
...
}
横向滚动:
通过linearlayoutmanager的方法setorientaton()设置值为linearlayoutmanager.HORIZONTAL
也就是上述代码中增加一行layoutManager.orientaton = linearlayoutmanager.HORIZONTAL
ListView的布局排列时自身管理的,而RecyclerView是交给了LayoutManager。LayoutManager指定了一套可扩展的布局排列接口,子类只要按照接口规范实现就能指定各种不同排列方式的布局,提供了linearlayoutmanager、GridLayoutManager(网格布局)和StaggeredGridLayoutManager(瀑布流布局)
瀑布流布局:
class MainActivity : AppCompatActivity() {
private val fruitList = ArrayList<Fruit>()
override fun onCreate(savedInstancestate: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initFruits() // 初始化水果数据
// 第一个参数:指定布局列数
// 第二个参数:VERTICAL表示让布局纵向排列
val layoutManager = StaggeredGridLayoutManager(3,
StaggeredGridLayoutManager.VERTICAL)
recyclerview.layoutManager = layoutManager
val adapter = FruitAdapter(fruitList)
recyclerview.adapter = adapter
}
}
RecyclerView点击事件:
与ListView不同,RecyclerView没有类似setonItemClickListener()这样的方法,而是需要给子项具体的View去注册点击事件,可能会复杂一点,但这样的好处可以去处理布局中具体的View,而ListVi额外要实现的话会有点麻烦
class FruitAdapter(val fruitList:List<Fruit>) :
RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
...
override fun onCreateviewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fruit item, parent, false)
val viewHolder = ViewHolder(view)
// 给子项注册点击事件
viewHolder.itemview.setonCLickListener {
val position = viewHoLder.adapterPosition
val fruit = fruitList[position]
Toast.makeText(parent.context, "you clicked view ${fruit.name}",Toast.LENGTH_SHORT).show()
}
// 给子项中的图片设置点击事件
viewHolder.fruitimage.setonCLickListener {
val position = viewHolder.adapterPosition
val fruit = fruitList[position]
Toast.makeText(parent.context, "you clicked image ${fruit.name}",Toast.LENGTH_SHORT).show()
}
return viewHolder
}
...
}
相关知识: