写在前头
  这是一篇笔记,试图挽救哞哞学一点忘一点的小脑袋瓜!
  参考资料:《第一行代码》
  (2020.12.26更新:目前学到第9章,复习到第6章,笔记就先暂停更新啦)

ListView控件

简单用法

  1. 修改activity_main,添加ListView控件
  2. 修改MainActivity如下:
    class MainActivity : AppCompatActivity() {

    private val data = listOf("Apple", "Pear")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //使用ArrayAdapter实现类,内置布局list_item_1作为子项布局
        val adapter = ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, data)
        listView.adapter = adapter
    }
}

自定义布局

  1. 定义实体类(Contacts)
    class Contacts(val name: String,
            val phone: String, val imageId: Int) 
  1. 在layout目录下新建自定义布局
    <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="80dp">

    <ImageView
        android:id="@+id/contactImage"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="22dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/line"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:srcCompat="@tools:sample/avatars" />

    <TextView
        android:id="@+id/contactName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="2dp"
        android:layout_marginTop="29dp"
        android:layout_marginBottom="12dp"
        android:text="姓名"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toTopOf="@+id/line"
        app:layout_constraintStart_toStartOf="@+id/line"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/line"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginEnd="73dp"
        android:layout_marginBottom="16dp"
        android:background="#00BCD4"
        android:text=" "
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/contactImage"
        app:layout_constraintTop_toBottomOf="@+id/contactName" />
</androidx.constraintlayout.widget.ConstraintLayout>

布局效果图

自定义适配器Adapter

class ContactAdapter(activity: Activity, resourceId: Int, data: List<Contacts>) :
    ArrayAdapter<Contacts>(activity, resourceId, data) {

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val view = LayoutInflater.from(context).inflate(R.layout.contact_item, parent, false)
        val contactImage: ImageView = view.findViewById(R.id.contactImage)
        val contactName: TextView = view.findViewById(R.id.contactName)
        val contact = getItem(position)
        contact?.let {
            contactImage.setImageResource(it.imageId)
            contactName.text = it.name
        }
        return view
    }
}

  

使用自定义的listView适配器

  修改MainActivity的代码:

class MainActivity : AppCompatActivity() {

    private val contactsList = ArrayList<Contacts>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initContacts()
        val adapter = ContactAdapter(this,R.layout.contact_item,contactsList)
        listView.adapter = adapter
    }

    fun initContacts(){
        repeat(5){
            contactsList?.apply {
                add(Contacts("WangMoumou","10086",0))
                add(Contacts("WangMoumou","10086",0))
                add(Contacts("WangMoumou","10086",0))
            }
        }
    }
}

  

子项点击事件

  在MainActivity中使用以下代码:

   listView.setOnItemClickListener { parent, view, position, id -> 
            val contacts = contactsList[position]
            //业务逻辑...
        }

  

RecyclerView控件

更强大常用的滚动控件

添加依赖

  在项目build.gradle中的dependencies添加RecyclerView库,确保所有Android系统版本都可以使用。

dependencies {
    ...
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    ...
}

  

添加recyclerView控件布局和子项目布局

  和listView的操作一致

  

自定义适配器Adapter

class ContactAdapter(val contactsList: List<Contacts>) :
    RecyclerView.Adapter<ContactAdapter.ContactHolder>() {
    //一个操控Contact View的ViewHolder类
    inner class ContactHolder(view: View) : RecyclerView.ViewHolder(view) {
        val contactImage: ImageView = view.findViewById(R.id.contactImage)
        val contactName: TextView = view.findViewById(R.id.contactName)
    }

    //引入子项布局对应的view,创建holder
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactHolder {
        val view: View = LayoutInflater.from(parent.context)
            .inflate(R.layout.contact_item, parent, false)
        return ContactHolder(view)
    }

    override fun getItemCount(): Int {
        return contactsList.size
    }
    
    //设置view的值
    override fun onBindViewHolder(holder: ContactHolder, position: Int) {
        val contact: Contacts = contactsList[position]
        holder.contactImage.setImageResource(contact.imageId)
        holder.contactName.text = contact.name
    }
}

  

使用recyclerView

  修改MainActivity的代码:

class MainActivity : AppCompatActivity() {

    private val contactsList = ArrayList<Contacts>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initContacts()

        //配置布局管理器!
        val layoutManager = LinearLayoutManager(this)
        recyclerView.layoutManager = layoutManager
        //配置适配器
        val adapter = ContactAdapter(contactsList)
        recyclerView.adapter = adapter
    }

    fun initContacts(){
        repeat(10){
            contactsList?.apply {
                add(Contacts("WangMoumou","10086",R.drawable.avatar))
            }
        }
    }
}

  

设置点击事件:

  不同于ListView,RecyclerView是在适配器类中创建ViewHolder时,交给View执行点击操作:

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactHolder {
        val view: View = LayoutInflater.from(parent.context)
            .inflate(R.layout.contact_item, parent, false)

        val viewHolder = ContactHolder(view)
        //触碰事件设置
        viewHolder.itemView.setOnTouchListener { v, event ->
            when(event.action){
                MotionEvent.ACTION_DOWN -> {
                    viewHolder.itemView.setBackgroundColor(Color.rgb(200,200,200))
                }
                MotionEvent.ACTION_MOVE ->{
                    viewHolder.itemView.setBackgroundColor(Color.rgb(255,255,255))
                }
                MotionEvent.ACTION_UP -> {
                    viewHolder.itemView.setBackgroundColor(Color.rgb(255,255,255))
                }
            }
            false
        }
        //点击事件设置
        viewHolder.itemView.setOnClickListener {
            val position = viewHolder.adapterPosition
            val contact = contactsList[position]
            //业务逻辑
            Toast.makeText(parent.context,"Test+${contact.name}",Toast.LENGTH_SHORT).show()
        }
        return viewHolder
    }

  

网格布局

修改子项布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/contactImage"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp" />

    <TextView
        android:id="@+id/contactName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:text="姓名"
        android:textSize="16sp"
        android:textStyle="bold" />

</LinearLayout>

修改MainActivity:

val layoutManager = GridLayoutManager(this,4,GridLayoutManager.VERTICAL,false)

广播机制

  1. 标准广播:完全异步执行,广播发出后,所有BroadcastReceiver几乎同时接收到该广播消息,无法被截断
  1. 有序广播:同步执行,同一时刻只有一个Receiver会收到该广播消息,优先级高的Receiver可截断广播

接收系统广播

   接收广播需要新建一个类继承BroadcastReceiver类,实现onReceive方法,有动态注册和静态注册两种方式;

动态注册

   可以自由注册和注销BroadcastReceiver,但需要在程序启动后才能接收广播;以下是监听系统时间变化的案例:

  1. 新建一个类继承BR,实现onReceive方法
class TimeChangeReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            Toast.makeText(context, "Time has changed", Toast.LENGTH_SHORT).show()
        }
}
  1. MainActivity:在intentFilter中添加监听广播对应的action,并完成Receiver的注册与注销:
class MainActivity : AppCompatActivity() {

    lateinit var timeChangeReceiver: TimeChangeReceiver
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val intentFilter = IntentFilter()
        intentFilter.addAction("android.intent.action.TIME_TICK")
        timeChangeReceiver = TimeChangeReceiver()
        registerReceiver(timeChangeReceiver, intentFilter)
    }
    
    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(timeChangeReceiver)
    }
}

静态注册

程序在未启动的情况下依旧可以接收广播
  1. 创建BR
class BootCompleteReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show()
    }
}
  1. 在AndroidManifest中注册Receiver:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="top.scauwlt.broadcasttest">
    //申请权限
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application>
        ...
        //注册receiver
        <receiver
            android:name=".BootCompleteReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        ...
</manifest>

发送自定义广播

发送标准广播

  发送广播可以用Intent发送:

setPackage可以将广播设置为显性广播,能被静态注册的receiver接收到

val intent = Intent("top.scauwlt.broadcasttest.MY_BROADCAST")
    intent.setPackage(packageName)
    sendBroadcast(intent)

  

发送有序广播

  1. 修改发送代码为:第二个参数为权限相关字符串
    sendOrderedBroadcast(intent,null)
  2. 设置优先级:在Manifest文件中
<receiver>
   <intent-filter android:priority="100">
       ...
   </intent-filter>
</receiver>
  1. 截断广播:
override fun onReceive(context: Context, intent: Intent) {
        ...
        abortBroadcast()
    }

未完待续...

2021年11月10日更:已弃坑客户端

Q.E.D.


记录 • 分享 • 日常