Kotlin學習記錄(三)—— 子執行緒獲取資料,實現簡單ListView
上一篇簡單介紹了Kotlin的一些基礎構成,當然還有像物件宣告、操作符等等都未涉及到,這些會在以後用到的過程中進行詳細說明。
專案中ListView列表出現的頻率是很高的,我們就以實現一個簡單ListView為目標,介紹一下在子執行緒中獲取資料等問題。
首先在layout中新增個listview:
<ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView>在activity中繫結View,之前說了,可以通過 "import
val listview:ListView=findViewById(R.id.listview) as ListView可以看到Java中通過“()”完成型別轉換,在這裡使用了“as”關鍵字。
建立adapter的佈局list_item.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="100dp" android:background="#ffffff" android:id="@+id/ll_item"> <TextView android:id="@+id/textView" android:layout_width="match_parent"android:layout_height="50dp" android:layout_weight="1" android:layout_gravity="center" android:gravity="center" android:text="TextView" /> </LinearLayout>
新建adapter類ListViewAdapter.kt
class ListViewAdapter ( val datas: List<String>,val context: Context): BaseAdapter(){ override fun getCount(): Int { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } override fun getItem(p0: Int): Any { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } override fun getItemId(p0: Int): Long { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } }
可以看到ListViewAdapter類定義了需要傳入datas和context兩個函式。一個數據和一個上下文。然後我們隊這個adapter進行的一定的修改:
class ListViewAdapter ( val datas: List<String>,val context: Context): BaseAdapter(){ override fun getCount(): Int=datas.size ?: datas.size ?: 0 //註釋一 override fun getItem(p0: Int): Any = datas.get(p0) override fun getItemId(p0: Int): Long = 0 override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { var viewHodler:ViewHodler?=null //註釋二 var view:View if(convertView==null){ view= View.inflate(context,R.layout.list_item,null) viewHodler=ViewHodler(view) view.tag=viewHodler }else{ view=convertView viewHodler=view.tag as ViewHodler } if(getItem(position) is String){ //給textview賦值 viewHodler.textView.text=getItem(position).toString() //隔行變色,閒著啥事 if((position+1)%2!=0)viewHodler.ll_item.setBackgroundColor(context.resources.getColor(R.color.f2f2f2)) } return view } class ViewHodler(var view: View){ var textView: TextView=view.findViewById(R.id.textView) as TextView var ll_item: LinearLayout=view.findViewById(R.id.ll_item) as LinearLayout } }
註釋一:getCount()方法大家都知道,是adapter獲取item數量的,通過這個數量決定adapter繪製的view個數。這個寫法的話,在Kotlin裡如果一個方法的返回值可以直接通過計算獲得,可以使用等號,不需要括號,這個在前邊的文章裡有講過。然後是"datas.size ?: datas.size ?: 0",這句就相當於Java裡的"datas.size ? datas.size : 0",這樣應該就明白了,?:前邊的是判斷條件,為true在取datas.size,為false則取0。
註釋二:這裡的?又是幹啥的呢,“?”這是kotlin的一個基本概念,之前有提到,kotlin是空安全的,那麼在kotlin中,型別系統將可空型別和非空型別進行了區分,例如:String為不可空型別,String?為可空型別,如果將不可空型別賦值null將會編譯不通過。
var str1:String=null //錯誤 var str2:String="123131" //正確 var str3:String?=null //正確如果此時操作str3,比如獲取其長度str3.length也是編譯不通過的,操作可空型別時,需要對其進行判斷,否則編譯不通過:
var length1:Int=str3.length //錯誤 var length2:Int=str3!!.length //正確
這時候是不是覺得好煩啊,我要知道這個東西是空的,肯定都改了,哈哈,自然不是這樣的,我們可以通過下邊的方式解決這種可空型別的引用:
var length3:Int=str3?.length
通過?.呼叫length,如果str3為空則返回null,否則就返回str3的長度。這樣是不是就方便了,也避免了空指標的問題。所以對於可空型別的操作在呼叫前,需要先檢查,因為可能為空,使用?.的形式呼叫,如果為空在返回null,而使用!!.的形式呼叫,如果為空則會丟擲空指標異常。這就是kotlin的空安全的一種體現。
好咯,回到我們的Listview,上邊的adapter除了剛才這兩個地方,其他的程式碼相信大家只要是寫過adapter的都能看得懂。那麼既然是list列表,肯定是要有資料的吧,這裡我們就建一個Request的類來模擬伺服器端的資料,Request裡有個run的方法返回了一個List<String>:
public class Request{ public fun run():List<String> { val items= listOf<String>( "瓦洛蘭", "德瑪西亞", "班德爾城", "諾克薩斯", "祖安", "皮爾特沃夫", "艾歐尼亞", "李青", "阿利斯塔", "希維爾", "潘森", "伊澤瑞爾", "雷克頓", "古拉加斯", "奧利安娜", "崔斯塔娜", "泰達米爾", "馬爾扎哈", "卡西奧佩婭", "艾尼維亞" ) return items } }
然後我們回到Activity裡,給listview繫結adapter:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val listview:ListView=findViewById(R.id.listview) as ListView //繫結listview val request:Request= Request() //宣告新增資料的物件, var list:List<String>?=request.run() listview.adapter=ListViewAdapter(list!!,this) //繫結adapter }listview.adapter就不說了,相當於Java裡的:setAdapter,前邊的文章裡有介紹。那麼執行APP,效果圖:
正常開發的情況下,資料的來源肯定不是這樣的,而我們從伺服器端獲取資料網路請求都是在子執行緒中進行的,關於Android執行緒的問題,這裡就不做解釋了(這是基礎常識)。因為kotlin強大的互操作性,之前適用的Java的第三方網路框架都是可以使用的。這裡資料和例子都比較簡單,就不使用第三方了,只來介紹下kotlin裡的執行緒簡單使用。大家比較熟悉的是Java裡的AsyncTasks,在kotlin中,Anko庫(Anko是JetBrains開發的一個強大的庫,包含了很多非常有幫助的函式和屬性來避免讓你寫很多的模板程式碼)提供了簡單的DSL來處理非同步任務:
import android.app.Activity import android.os.Bundle import kotlinx.android.synthetic.main.activity_main.* import org.jetbrains.anko.async import org.jetbrains.anko.uiThread class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) initData() } fun initData(){ async() { //可以在此進行網路請求獲取資料 //我們還使用Request模擬 val request:Request= Request() var list:List<String>?=request.run() uiThread { listview.adapter=ListViewAdapter(list!!,this@MainActivity) } } } }
我們通過async函式,在其他執行緒裡執行了網路請求的程式碼,然後通過uiThread的方式回到主執行緒給listview綁定了adapter。和Java比有個好處就是如果使用AsyncTasks時,當執行到postExecute的時候Activity被銷燬了,那麼就會崩潰了,而uiThread不會,如果activity.isFinishing()返回true,uiThread是不會執行的。async基本能夠滿足我們大部分的需求了。
快快嘗試一下吧!