写在前头
这是一篇笔记,试图挽救哞哞学一点忘一点的小脑袋瓜!
参考资料:《第一行代码》
(2020.12.26更新:目前学到第9章,复习到第6章,笔记就先暂停更新啦)
ListView控件
简单用法
- 修改activity_main,添加ListView控件
- 修改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
}
}
自定义布局
- 定义实体类(Contacts)
class Contacts(val name: String,
val phone: String, val imageId: Int)
- 在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)
广播机制
- 标准广播:完全异步执行,广播发出后,所有BroadcastReceiver几乎同时接收到该广播消息,无法被截断
- 有序广播:同步执行,同一时刻只有一个Receiver会收到该广播消息,优先级高的Receiver可截断广播
接收系统广播
接收广播需要新建一个类继承BroadcastReceiver类,实现onReceive方法,有动态注册和静态注册两种方式;
动态注册
可以自由注册和注销BroadcastReceiver,但需要在程序启动后才能接收广播;以下是监听系统时间变化的案例:
- 新建一个类继承BR,实现onReceive方法
class TimeChangeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context, "Time has changed", Toast.LENGTH_SHORT).show()
}
}
- 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)
}
}
静态注册
程序在未启动的情况下依旧可以接收广播
- 创建BR
class BootCompleteReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show()
}
}
- 在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)
发送有序广播
- 修改发送代码为:第二个参数为权限相关字符串
sendOrderedBroadcast(intent,null)
- 设置优先级:在Manifest文件中
<receiver>
<intent-filter android:priority="100">
...
</intent-filter>
</receiver>
- 截断广播:
override fun onReceive(context: Context, intent: Intent) {
...
abortBroadcast()
}
未完待续...
2021年11月10日更:已弃坑客户端
Q.E.D.