組成
構(gòu)建一般的ListView
包含三部分:
- ListView布局(控件)
- MainActivity(承載Activity或Fragment等)
- Adapter適配器
Adapter
Adapter
是適配器孽糖,一個通過泛型來指定要適配的數(shù)據(jù)類型砚哗,并在構(gòu)造函數(shù)中將要適配的數(shù)據(jù)傳入髓窜⌒荆可以簡單理解為數(shù)據(jù)綁定砸烦,并將其顯示在ListView
上,可以理解類似于MVP
中的Presenter
。其作為View
和“Model
數(shù)據(jù)”之間的橋梁胀溺,ListView
會在Adapter
中得到需要的數(shù)據(jù),并加載出來皆看。而ListView只是一種列表的表現(xiàn)形式仓坞,將獲取的數(shù)據(jù)以列表的方式進行展現(xiàn)。
通常我們會選用繼承ArrayAdapter
進行數(shù)組數(shù)據(jù)的展示腰吟,或者繼承BaseAdapter
進行自定義Adapter
无埃。
ArrayAdapter
以簡單、便捷的繼承ArrayAdapter
展開毛雇,快速構(gòu)建一個ListView實例嫉称。
- 根據(jù)上述組成的三步,首先是
ListView
布局的添加灵疮。我們先構(gòu)建一個id為listview_test
的ListView
织阅。
<?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="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listview_test"/>
</LinearLayout>
- 緊接著是繼承
ArrayAdapter
的MyAdapter
適配器構(gòu)建,首先創(chuàng)建一個User
實體類:
class User(val name:String) {}
再創(chuàng)建MyAdapter
震捣。重寫getView
使得每個子項滾動到可視窗口界面時會被調(diào)用荔棉;getItem
獲取當(dāng)前位置的User
實例
class MyAdapter(mActivity: Activity, val resourceId: Int,val myList:List<User>): ArrayAdapter<User>(mActivity,resourceId,myList) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = LayoutInflater.from(context).inflate(resourceId,parent,false)
//綁定布局中user_name
val userName:TextView = view.findViewById(R.id.user_name)
//獲取當(dāng)前位置的user實例
val user= getItem(position)
if(user != null){
userName.text =user.name
}
return view
}
}
- 最后是
MainActivity
,Java中通過setAdapter
往ListView
中傳入Adapter
蒿赢,Kotlin中通過listview.adapter
傳入润樱。
class MainActivity : AppCompatActivity() {
private val user = ArrayList<User>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//初始化測試數(shù)據(jù)
initUser()
val adapter=MyAdapter(this, R.layout.user_list_item_layout,user)
//往listview中傳入adapter
listview_test.adapter =adapter
}
private fun initUser(){
for(i in 0..10){
user.add(User("用戶test"+i))
}
}
}
針對Item的點擊監(jiān)聽示例代碼如下:
fun listviewtest(){
val adapter=MyAdapter(this, R.layout.user_list_item_layout,user)
listview_test.adapter =adapter
listview_test.setOnItemClickListener{parent,view,position,id->
var userTest = user[position]
userTest.name = "zhangkai"
adapter.notifyDataSetChanged()
}
}
通過user[position]
獲取被點擊的Item
,并修改其Item.name
羡棵,并通過adapter.notifyDataSetChanged()
通知ListView
發(fā)生變化了壹若。(成果圖和Demo代碼中未包含監(jiān)聽部分)
成果圖
性能優(yōu)化
在上述方法中,getView每次調(diào)用都會將布局重新加載一遍皂冰,造成效率低店展。因此使用getView中的convertView參數(shù)對之前加載過的布局進行緩存,從而優(yōu)化性能灼擂。
class MyAdapter(mActivity: Activity, val resourceId: Int,val myList:List<User>): ArrayAdapter<User>(mActivity,resourceId,myList) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var view:View
//使用convertView進行緩存
if( convertView==null){
view = LayoutInflater.from(context).inflate(resourceId,parent,false)
}else{
view = convertView
}
// val view = LayoutInflater.from(context).inflate(resourceId,parent,false)
//綁定布局中user_name
val userName:TextView = view.findViewById(R.id.user_name)
//獲取當(dāng)前位置的user實例
val user= getItem(position)
if(user != null){
userName.text =user.name
}
return view
}
}
當(dāng)前解決了每次getView
重新加載布局的問題壁查,但是getView
中View
依舊會每次通過findViewById()
獲取控件實例,可以引入ViewHolder
進行優(yōu)化剔应。
class MyAdapter(mActivity: Activity, val resourceId: Int,val myList:List<User>): ArrayAdapter<User>(mActivity,resourceId,myList) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var view:View
val myViewHolder: MyViewHolder
//使用convertView進行緩存
if( convertView==null){
view = LayoutInflater.from(context).inflate(resourceId,parent,false)
//綁定布局中user_name
val userName:TextView = view.findViewById(R.id.user_name)
myViewHolder = MyViewHolder(userName)
view.tag = myViewHolder
}else{
view = convertView
myViewHolder = view.tag as MyViewHolder
}
//獲取當(dāng)前位置的user實例
val user= getItem(position)
if(user != null){
myViewHolder.userName.text = user.name
}
return view
}
//新增MyViewHolder對控件進行緩存
class MyViewHolder(val userName:TextView)
}
當(dāng)convertView
為null
時睡腿,調(diào)用View.setTag()
將控件實例存到myViewHolder
中,convertView
不為null
時通過getTag()
把已有的控件myViewHolder
取出來峻贮。(Kotlin語法席怪,get\set
可以直接view.tag
代替)
Demo代碼
GitHub:alicechenzhu/ListViewTest