Android笔记(二)

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 TextView ImageView ViewGroup EditText Button LinearLayout RelativeLayout ...

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>

相关知识:

AlertDialog(对话框)详解 - 菜鸟教程

RelativeLayout(相对布局)- 菜鸟教程

从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
    }
    ...
}

相关知识:

Adapter基础讲解 - 菜鸟教程

创建可调整大小的位图(9-Patch 文件) - Android 开发者

上一篇:Android 自定义View之随机数验证码(仿写鸿洋),写给即将正在找工作的Android攻城狮


下一篇:JavaException的使用