1 2 3 4

4. UI 开发

4. UI 开发

1. 常用控件

1. TextView

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
    <TextView 
        android:id="@+id/textView" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:gravity="center" 
        android:textColor="#00ff00" 
        android:textSize="24sp" 
        android:text="This is TextView"/> 
</LinearLayout> 
  • layout_width
    • match_parent:匹配父布局
    • wrap_content:包裹内容
    • 50 dp:与屏幕密度无关的尺寸单位
  • gravity:对齐方式
    • 用 | 指定多个值
    • top
    • bottom
    • start
    • end
    • center

2. Button

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
    ... 
    <Button 
        android:id="@+id/button" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:textAllCaps="false"
        android:text="Button" /> 
</LinearLayout
  • textAllCaps:表示是否改为大写

  • 使用函数式API的方式来注册监听器,也可以使用实现接口的方式来进行注册

3. EditText

  • 应用场景:发短信、发微博、聊QQ等
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
    ... 
    <EditText 
        android:id="@+id/editText" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:hint="Type something here" 
        android:maxLines="2" 
        /> 
</LinearLayout>
  • hint:提示性的文本

  • maxLines:超过 n 行文本就会向上滚动,EditText 则不会再继续拉伸

  • 应用:如通过点击按钮获取 EditText 中输入的内容

override fun onClick(v: View?) { 
    when (v?.id) { 
        R.id.button -> { 
            val inputText = editText.text.toString() 
            Toast.makeText(this, inputText, Toast.LENGTH_SHORT).show() 
        } 
    } 
} 

4. ImageView

  • 资源

    • 图片通常放在以 drawable 开头的目录下的,并且要带上具体的分辨率
      • 即 res/drawable-xxhdpi 中
      • 【这里的xx就是xx还是代表某个数值?】
  • xml 代码

    • <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
          android:orientation="vertical"
          android:layout_width="match_parent" 
          android:layout_height="match_parent"> 
          ... 
          <ImageView 
              android:id="@+id/imageView" 
              android:layout_width="wrap_content" 
              android:layout_height="wrap_content" 
              android:src="@drawable/img_1" 
              /> 
      </LinearLayout> 
      
    • src:指定图片

      • 【能自动识别对应dpi吗】
  • 动态修改图片

    • override fun onClick(v: View?) { 
          when (v?.id) { 
              R.id.button -> { 			// 点到 button 则替换 Res
                  imageView.setImageResource(R.drawable.img_2) 
              } 
          } 
      } 
      

5. ProgressBar

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
 
    ... 
 
    <ProgressBar 
        android:id="@+id/progressBar" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        style="?android:attr/progressBarStyleHorizontal" 
        android:max="100" 
        /> 
 
</LinearLayout> 
  • style:风格

    • 默认为圆圈

      • 可以设置可见性属性

        • VISIBLE:可见
        • INVISIBLE:不可见
        • GONE:不可见且不占用屏幕空间
      • // 切换可见性
        override fun onClick(v: View?) { 
            when (v?.id) { 
                R.id.button -> { 
                    if (progressBar.visibility == View.VISIBLE) { 
                        progressBar.visibility = View.GONE 
                    } else { 
                        progressBar.visibility = View.VISIBLE 
                    } 
                } 
            } 
        } 
        
    • progressBarStyleHorizontal 为进度条

      • 此时可以设置最大值,实现进度条进度

      • override fun onClick(v: View?) { 
            when (v?.id) { 
                R.id.button -> { 
                    progressBar.progress = progressBar.progress + 10 
                } 
            } 
        } 
        

6. AlertDialog

  • 在当前界面弹出一个对话框
    • 这个对话框是置顶于所有界面元素之上的,能够屏蔽其他控件的交互能力
    • 因 AlertDialog 一般用于提示一些非常重要的内容或者警告信息。
override fun onClick(v: View?) { 
    when (v?.id) { 
        R.id.button -> { 
            AlertDialog.Builder(this).apply { 
                setTitle("This is Dialog") 
                setMessage("Something important.") 
                setCancelable(false) 
                setPositiveButton("OK") { dialog, which -> 
                } 
                setNegativeButton("Cancel") { dialog, which -> 
                } 
                show() 
            } 
        } 
    } 
} 

2. 布局

场景 推荐layout 理由
通用复杂界面 ConstraintLayout 扁平、高性能、易维护
简单横向/纵向排列(<4 个 View) LinearLayout 代码最少,够用
Fragment 容器/简单叠加 FrameLayout 轻量、语义清晰
长列表 RecyclerView 复用机制、内存安全
复杂滚动交互 CoordinatorLayout 类似 Fragment,Behavior 机制
严格二维网格 GridLayout 行列语义明确
  • 布局的内部除了放置控件外,也可以放置布局

1. LinearLayout

1. 排列方向 orientation

  • 水平:android:orientation="horizontal"
  • 垂直:android:orientation="vertical"
    • 注意
      • 如果 Layoutorientation水平,那么 Viewlayout_width不能match_parent
        • 因为这样一个控件就填满了
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
    <Button 
        android:id="@+id/button1" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="Button 1" /> 
    <Button 
        android:id="@+id/button2" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="Button 2" /> 
    <Button 
        android:id="@+id/button3" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="Button 3" /> 
</LinearLayout>

2. layout_gravity

  • 注意
    • 如果 Layoutorientation水平,那么 Viewlayout_gravity 只能是垂直方向的选择
    • 此时若干个控件仍会在水平方向线性排列,而高度根据具体的 layout_gravity 设置
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="horizontal" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
    <Button 
        android:id="@+id/button1" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="top" 
        android:text="Button 1" /> 
    <Button 
        android:id="@+id/button2" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="center_vertical" 
        android:text="Button 2" /> 
    <Button 
        android:id="@+id/button3" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="bottom" 
        android:text="Button 3" /> 
</LinearLayout> 

3. layout_weight

  • 此时 layout_width 指定为 0 就行
  • layout_weight 会在排除掉固定宽度后,剩下所有控件根据权重占据大小
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="horizontal" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
    <EditText 
        android:id="@+id/input_message" 
        android:layout_width="0dp" 
        android:layout_height="wrap_content" 
        android:layout_weight="1" 
        android:hint="Type something" 
        /> 
    <Button 
        android:id="@+id/send" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="Send" 
        /> 
</LinearLayout> 

2. RelativeLayout

  • 相对于父布局
  • layout_alignParentLeft
  • layout_alignParentTop
  • layout_alignParentRight
  • layout_alignParentBottom
  • layout_centerInParent
    • 相对于控件
    • 只能引用之前的控件
    • layout_above
    • layout_below
    • layout_toLeftOf
    • layout_toRightOf
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
    <Button 
        android:id="@+id/button1" 
		android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_alignParentLeft="true" 
        android:layout_alignParentTop="true" 
        android:text="Button 1" /> 
 
    <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/button1" 
        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:text="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> 

3. FrameLayout

  • 帧布局
    • 默认摆放在布局的左上角
    • 后定义的会覆盖之前定义的控件
    • 使用 layout_gravity 指定对齐方式
    • Fragment 场景使用的多一些
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
    <TextView 
        android:id="@+id/textView" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="left" 
        android:text="This is TextView" 
        /> 
    <Button 
        android:id="@+id/button" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="right" 
        android:text="Button" 
        /> 
</FrameLayout> 

3. 自定义控件

【控件继承图】

  • 引入布局:解决重复布局的问题
  • 自定义控件:解决重复逻辑的问题

1. 引入布局

  1. 在 layout 目录建立一个新的布局 title.xml
  2. 在 title.xml 中编写重复布局
  3. 在需要使用的 xml 中直接引用布局 <include layout="@layout/title" />
    • 引用和文件名对应

2. 创建自定义控件

  1. 场景:对于标题的返回按钮,每次按返回键,一定是销毁当前 Activity,因此有重复逻辑
  2. 新建 layout 类继承某个 layout
  3. 布局文件添加这个自定义控件
    • 需要指明控件的完整类名,包名在这里是不可以省略的
  4. 之后其他 Activity 可以直接使用这个控件来销毁 Activity
class TitleLayout(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { 
 
    init { 
        LayoutInflater.from(context).inflate(R.layout.title, this)  // 对标题栏布局进行动态加载
        // 构造出一个 LayoutInflater 对象, 调用 inflate 动态加载一个布局文件
        // 第一个参数是要加载的布局文件的id, 第二个参数是给加载好的布局再添加一个父布局
        // 【为什么父布局是本身】
        titleBack.setOnClickListener { 
            val activity = context as Activity 						// 强制转换
            activity.finish() 
        } 
        titleEdit.setOnClickListener { 
            Toast.makeText(context, "You clicked Edit button", Toast.LENGTH_SHORT).show() 
        } 
    } 
} 
<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>

4. ListView

  • 场景:有大量数据需要展示,比如查看QQ聊天记录,翻阅微博最新消息

1. 简单用法

    <ListView 
        android:id="@+id/listView" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" /> 
class MainActivity : AppCompatActivity() { 
 
    private val data = listOf("Apple", "Banana", "Orange", "Watermelon", 
        "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango", 
        "Apple", "Banana", "Orange", "Watermelon", "Pear", "Grape", 
        "Pineapple", "Strawberry", "Cherry", "Mango") 
 
    override fun onCreate(savedInstanceState: Bundle?) { 
        super.onCreate(savedInstanceState) 
        setContentView(R.layout.activity_main) 
        
        // 设置 listView 数据
        val adapter = ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data) // 通过适配器处理数据
        val listView: ListView = findViewById(R.id.listView)
        listView.adapter = adapter 
    } 
 
} 

2. 定制界面

  • 定义一个实体类,作为 ListView 适配器的适配类型
class Fruit(val name:String, val imageId: Int)  // imageId 表示水果对应图片的资源 id
  • 布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="60dp"> 
    <ImageView 
        android:id="@+id/fruitImage" 
        android:layout_width="40dp" 
        android:layout_height="40dp" 
        android:layout_gravity="center_vertical" 
        android:layout_marginLeft="10dp"/> 
    <TextView 
        android:id="@+id/fruitName" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="center_vertical" 
        android:layout_marginLeft="10dp" /> 
</LinearLayout>
  • layout_marginLeft:边距

  • 自定义适配器

class FruitAdapter(activity: Activity, val resourceId: Int, data: List<Fruit>) : 
        ArrayAdapter<Fruit>(activity, resourceId, data) { 
    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { 	// 每个子项滚动到屏幕内的时候会被调用
        val view = LayoutInflater.from(context).inflate(resourceId, 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) 		// 相当于 imageView.setImageResource(R.drawable.apple_pic)
            fruitName.text = fruit.name 					// 相当于 textView.text = "apple"
        } 
        return view 
    } 
} 
  • Main 代码
class MainActivity : AppCompatActivity() { 
 
    private val fruitList = ArrayList<Fruit>() 
 
    override fun onCreate(savedInstanceState: Bundle?) { 
        super.onCreate(savedInstanceState) 
        setContentView(R.layout.activity_main) 
        initFruits() // 初始化水果数据 
        val adapter = FruitAdapter(this, R.layout.fruit_item, fruitList) 
        listView.adapter = adapter 
    } 
 
    private fun initFruits() { 
        repeat(2) { 
            fruitList.add(Fruit("Apple", R.drawable.apple_pic)) 
            fruitList.add(Fruit("Banana", R.drawable.banana_pic)) 
            fruitList.add(Fruit("Orange", R.drawable.orange_pic)) 
            fruitList.add(Fruit("Watermelon", R.drawable.watermelon_pic)) 
            fruitList.add(Fruit("Pear", R.drawable.pear_pic)) 
            fruitList.add(Fruit("Grape", R.drawable.grape_pic)) 
            fruitList.add(Fruit("Pineapple", R.drawable.pineapple_pic)) 
            fruitList.add(Fruit("Strawberry", R.drawable.strawberry_pic)) 
            fruitList.add(Fruit("Cherry", R.drawable.cherry_pic)) 
            fruitList.add(Fruit("Mango", R.drawable.mango_pic)) 
        } 
    } 
}

3. 提升效率

  • 自定义适配器现在存在两个问题
    1. FruitAdapter 的 getView() 方法每次都会重新加载布局
      • 解决方法:getView() 的 convertView 参数缓存了布局
        • convertView 为 null 则加载
        • convertView 存在则重用
    2. 每次 getView() 会调用 findViewById() 方法来获取一次控件的实例
      • 使用内部类 ViewHolder 对 ImageView 和 TextView 的控件实例进行缓存
        • 当 convertView 为 null 的时候
          • 创建一个 ViewHolder 对象,并将控件的实例存放ViewHolder 里,然后调用 View 的 setTag() 方法,将 ViewHolder 对象存储View
        • 当 convertView 不为 null 的时候
          • 则调用 ViewgetTag() 方法,把 ViewHolder重新取出
class FruitAdapter(activity: Activity, val resourceId: Int, data: List<Fruit>) :
    ArrayAdapter<Fruit>(activity, resourceId, data) {
    inner class ViewHolder(val fruitImage: ImageView, val fruitName: TextView)
    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val view: View
        val viewHolder: ViewHolder
        if (convertView == null) {              // 不存在则新建布局
            view = LayoutInflater.from(context).inflate(resourceId, 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
            viewHolder = view.tag as ViewHolder
        }
        val fruit = getItem(position) // 获取当前项的Fruit实例
        if (fruit != null) {
            viewHolder.fruitImage.setImageResource(fruit.imageId)
            viewHolder.fruitName.text = fruit.name
        }
        return view
    }
}

4. 点击事件

listView.setOnItemClickListener { _, _, position, _ ->
	val fruit = fruitList[position]
	Toast.makeText(this, fruit.name, Toast.LENGTH_SHORT).show()
}

5. RecycleView

在 projectStructure 的 dependencies 中搜索 recyclerview
在 app/build.gradle 中添加代码 implementation libs.androidx.recyclerview

【细节和水平/瀑布流/网格布局】

0. 代码汇总

  • 修改文件

    • second_layout

      • <?xml version="1.0" encoding="utf-8"?>
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="?attr/colorPrimary"
                android:minHeight="?attr/actionBarSize"
                android:theme="?attr/actionBarTheme" />
        
            <Button
                android:id="@+id/button2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Back" />
        
            <Button
                android:id="@+id/button3"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Quit App" />
        
            <Button
                android:id="@+id/button4"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Go third" />
        
            <Button
                android:id="@+id/button5"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Go forth" />
        
        </LinearLayout>
        
    • MainActivity2

      • package com.example.helloworld
        
        import android.content.Context
        import android.content.Intent
        import android.os.Bundle
        import android.util.Log
        import android.widget.ArrayAdapter
        import android.widget.Button
        import android.widget.ListView
        import androidx.appcompat.app.AppCompatActivity
        import androidx.appcompat.widget.Toolbar
        
        
        class MainActivity2 : BaseActivity() {
        
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.second_layout)
        
                // toolbar 部分
                val toolbar: Toolbar = findViewById(R.id.toolbar2)
                setSupportActionBar(toolbar)
                supportActionBar?.setDisplayHomeAsUpEnabled(true)            // 设置返回按钮
                toolbar.setNavigationOnClickListener { finish() }             // 设置结束 act2
        
                ActivityCollector.addActivity(this)
        
                // 从前一个 Activity 接收数据
                val extraData1 = intent.getStringExtra("param1")
                val extraData2 = intent.getStringExtra("param2")
                Log.d("Activity2", "param1 is $extraData1, param2 is $extraData2")
        
        
                // 返回数据(但是未实现)
                val button2: Button = findViewById(R.id.button2)        // 通过 id 找到 view, 并指明类型为 button
                button2.setOnClickListener {
                    val intent = Intent().apply { putExtra("data_return", "Hello Activity1") }
                    setResult(RESULT_OK, intent)
                    finish()
                }
        
                // 关闭所有 Activity
                val button3: Button = findViewById(R.id.button3)
                button3.setOnClickListener {
                    ActivityCollector.finishAll()
                }
        
                val button4: Button = findViewById(R.id.button4)
                button4.setOnClickListener {
                    startNewActivity(this, MainActivity3::class.java)
                }
        
                val button5: Button = findViewById(R.id.button5)
                button5.setOnClickListener {
                    startNewActivity(this, MainActivity4::class.java)
                }
        
            }
        
        }
        
        
        
  • 增加文件

    • MainActivity3

      • package com.example.helloworld
        
        import android.app.Activity
        import android.content.Context
        import android.content.Intent
        import android.os.Bundle
        import android.util.Log
        import android.view.LayoutInflater
        import android.view.View
        import android.view.ViewGroup
        import android.widget.ArrayAdapter
        import android.widget.Button
        import android.widget.ImageView
        import android.widget.ListView
        import android.widget.TextView
        import android.widget.Toast
        import androidx.appcompat.app.AppCompatActivity
        import androidx.appcompat.widget.Toolbar
        
        
        class Fruit(val name:String, val imageId: Int)  // imageId 表示水果对应图片的资源 id
        
        class FruitAdapter(activity: Activity, val resourceId: Int, data: List<Fruit>) :
            ArrayAdapter<Fruit>(activity, resourceId, data) {
            inner class ViewHolder(val fruitImage: ImageView, val fruitName: TextView)
            override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
                val view: View
                val viewHolder: ViewHolder
                if (convertView == null) {              // 不存在则新建布局
                    view = LayoutInflater.from(context).inflate(resourceId, 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
                    viewHolder = view.tag as ViewHolder
                }
                val fruit = getItem(position) // 获取当前项的Fruit实例
                if (fruit != null) {
                    viewHolder.fruitImage.setImageResource(fruit.imageId)
                    viewHolder.fruitName.text = fruit.name
                }
                return view
            }
        }
        
        class MainActivity3 : BaseActivity() {
            private val fruitList = ArrayList<Fruit>()
            private val data = listOf("Apple", "Banana", "Orange", "Watermelon",
                "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango",
                "Apple", "Banana", "Orange", "Watermelon", "Pear", "Grape",
                "Pineapple", "Strawberry", "Cherry", "Mango")
        
        
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.third_layout)
                ActivityCollector.addActivity(this)
        
        
                // toolbar 部分
                val toolbar: Toolbar = findViewById(R.id.toolbar3)
                setSupportActionBar(toolbar)
                supportActionBar?.setDisplayHomeAsUpEnabled(true)
                toolbar.setNavigationOnClickListener { finish() }
        
                initFruits() // 初始化水果数据
                val adapter = FruitAdapter(this, R.layout.fruit_item, fruitList)    // 使用 fruit_item layout
                val listView: ListView = findViewById(R.id.listView)
                listView.adapter = adapter
                listView.setOnItemClickListener { _, _, position, _ ->
                    val fruit = fruitList[position]
                    Toast.makeText(this, fruit.name, Toast.LENGTH_SHORT).show()
                }
            }
        
            private fun initFruits() {
                val imageId = R.drawable.apple_pic
                repeat(3) {
                    data.forEach { fruitList.add(Fruit(it, imageId)) }
                }
            }
        }
        
        
        
    • MainActivity4.kt

      • package com.example.helloworld
        
        import android.app.Activity
        import android.content.Context
        import android.content.Intent
        import android.os.Bundle
        import android.util.Log
        import android.view.LayoutInflater
        import android.view.View
        import android.view.ViewGroup
        import android.widget.ArrayAdapter
        import android.widget.Button
        import android.widget.ImageView
        import android.widget.ListView
        import android.widget.TextView
        import android.widget.Toast
        import androidx.appcompat.app.AppCompatActivity
        import androidx.appcompat.widget.Toolbar
        import androidx.recyclerview.widget.LinearLayoutManager
        import androidx.recyclerview.widget.RecyclerView
        
        
        class FruitAdapter2(val fruitList: List<Fruit>) :
            RecyclerView.Adapter<FruitAdapter2.ViewHolder>() {
            inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
                val fruitImage: ImageView = view.findViewById(R.id.fruitImage)
                val fruitName: TextView = view.findViewById(R.id.fruitName)
            }
            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
                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
        }
        
        class MainActivity4 : BaseActivity() {
            private val fruitList = ArrayList<Fruit>()
            private val data = listOf("Apple", "Banana", "Orange", "Watermelon",
                "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango",
                "Apple", "Banana", "Orange", "Watermelon", "Pear", "Grape",
                "Pineapple", "Strawberry", "Cherry", "Mango")
        
        
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.forth_layout)
                ActivityCollector.addActivity(this)
        
        
                // toolbar 部分
                val toolbar: Toolbar = findViewById(R.id.toolbar4)
                setSupportActionBar(toolbar)
                supportActionBar?.setDisplayHomeAsUpEnabled(true)
                toolbar.setNavigationOnClickListener { finish() }
        
                initFruits() // 初始化水果数据
                val layoutManager = LinearLayoutManager(this)
                val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
                recyclerView.layoutManager = layoutManager
                val adapter = FruitAdapter2(fruitList)
                recyclerView.adapter = adapter
            }
        
            private fun initFruits() {
                val imageId = R.drawable.apple_pic
                repeat(3) {
                    data.forEach { fruitList.add(Fruit(it, imageId)) }
                }
            }
        }
        
        
        
    • third_layout.xml

      • <?xml version="1.0" encoding="utf-8"?>
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        
        
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar3"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="?attr/colorPrimary"
                android:minHeight="?attr/actionBarSize"
                android:theme="?attr/actionBarTheme" />
        
        
        
            <ListView
                android:id="@+id/listView"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </LinearLayout>
        
    • forth_layout.xml

      • <?xml version="1.0" encoding="utf-8"?>
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
        
        
        
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar4"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="?attr/colorPrimary"
                android:minHeight="?attr/actionBarSize"
                android:theme="?attr/actionBarTheme" />
        
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        
        
        </LinearLayout>
        
    • fruit_item.xml

      • <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="60dp">
            <ImageView
                android:id="@+id/fruitImage"
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="10dp"/>
            <TextView
                android:id="@+id/fruitName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="10dp" />
        </LinearLayout>
        
    • Util.kt

    • package com.example.helloworld
      
      import android.app.Activity
      import android.content.Context
      import android.content.Intent
      import android.os.Bundle
      import android.util.Log
      import android.view.View
      import android.widget.Toast
      import androidx.appcompat.app.AppCompatActivity
      import com.google.android.material.snackbar.Snackbar
      
      // 输出当前是哪一个 Activity
      open class BaseActivity : AppCompatActivity() {
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              Log.d("BaseActivity", "${javaClass.simpleName} is created")
          }
      
          override fun onStart() {
              super.onStart()
              Log.d("BaseActivity", "${javaClass.simpleName} is started")
          }
      
          override fun onResume() {
              super.onResume()
              Log.d("BaseActivity", "${javaClass.simpleName} is resumed")
          }
      }
      
      // Activity 管理
      object ActivityCollector {
          private val activities = ArrayList<Activity>()
          fun addActivity(activity: Activity){
              activities.add(activity)
          }
      
          fun removeActivity(activity: Activity){
              activities.remove(activity)
          }
      
          fun finishAll() {
              activities.forEach {
                  if(!it.isFinishing) {
                      it.finish()
                  }
              }
              activities.clear()
          }
      }
      
      fun startNewActivity(context: Context, targetActivity: Class<out AppCompatActivity>) {
          val intent = Intent(context, targetActivity)
          context.startActivity(intent)
      }
      
      // 调用方法 startNewActivity(this, ActivityClass::class.java)
      fun startNewActivity(context: Context, targetActivity: Class<out AppCompatActivity>, data1: String, data2: String) {
          val intent = Intent(context, targetActivity).apply {
            putExtra("param1", data1)
              putExtra("param2", data2)
          }
          context.startActivity(intent)
      }
      
      fun startNewActivityAndFinish(activity: Activity, targetActivity: Class<out AppCompatActivity>) {
          val intent = Intent(activity, targetActivity)
          activity.startActivity(intent)
          activity.finish()
      }
      
      // 调用方法 "This is Toast".showToast(context, Toast.LENGTH_LONG)
      fun String.showToast(context: Context, duration: Int = Toast.LENGTH_SHORT) {
          Toast.makeText(context, this, duration).show()
      }
      fun Int.showToast(context: Context, duration: Int = Toast.LENGTH_SHORT) {
          Toast.makeText(context, this, duration).show()
      }
      
      
      /*      调用方法
       *      view.showSnackBar("This is SnackBar", "Action") {
       *          //
       *      处理具体的逻辑
       *      }
       **/
      fun View.showSnackBar(text: String, actionText: String? = null,
                            duration: Int = Snackbar.LENGTH_SHORT, block: (() -> Unit)? = null) {
          val snackBar = Snackbar.make(this, text, duration)
          if (actionText != null && block != null) {
              snackBar.setAction(actionText) {
                  block()
              }
          }
          snackBar.show()
      }
      fun View.showSnackBar(resId: Int, actionResId: Int? = null,
                            duration: Int = Snackbar.LENGTH_SHORT, block: (() -> Unit)? = null) {
          val snackBar = Snackbar.make(this, resId, duration)
          if (actionResId != null && block != null) {
              snackBar.setAction(actionResId) {
                  block()
              }
          }
          snackBar.show()
      }
      
    • drawable/apple_pic.png

【实践和 kotlin 补充】

posted @ 2026-01-13 11:48  y丶innocence  阅读(7)  评论(0)    收藏  举报