Android笔记(二)
文章目录
1. 基础UI
1.1 常用控件
关于单位:
- dp:一般用于固定值的单位,可以保证在不同分辨率显示效果大致相同
- sp:用于文字大小的单位,保证程序中文字大小可以随系统文字尺寸变化
注:下面每个控件后的属性只代表《第三行代码》中在该控件小节中学习到的,控件属性一般都能应用在大部分的控件中
TextView:
-
android:gravity
属性指定文字对齐方式(gravity:重力),可选值top、bottom、start、end、center等,通过|
来指定多个值
Button:……
EditText:
-
android:hint
属性指定一段提示性的文本 -
android:maxLines
属性指定EditText最大行数
ImageView:
-
android:src
属性指定所需资源,一般图片资源存储在drawable-xxx目录中,一般主流的手机屏幕分辨率是xxhdpi的 - 可以通过imageView.setImageResource()方法动态更改图片
ProgressBar:(progress:进步)
-
android:visibility
属性指定控件的可见性,可选值visible 默认,可见、invisible “透明”、gone 完全不可见,也可以通过控件的setVisibility()方法动态更改可见性,可传入参数View.VISIBLE、View.INVISIBLE、View.GONE -
style
属性设置为水平进度条(?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,用于放置控件和布局的容器
引入布局:
- 自定义一个xml布局
- 然后在主布局中通过
<include>
标签的layout
属性引入布局
创建自定义控件:
使引入的布局中的一些控件可以响应事件
例:自定义导航栏
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
}
...
}
相关知识:
创建可调整大小的位图(9-Patch 文件) - Android 开发者