天天看点

Kotlin学习记录(三)—— 子线程获取数据,实现简单ListView

接上篇:Kotlin的变量、属性、类、构造函数、继承、方法

上一篇简单介绍了Kotlin的一些基础构成,当然还有像对象声明、操作符等等都未涉及到,这些会在以后用到的过程中进行详细说明。

项目中ListView列表出现的频率是很高的,我们就以实现一个简单ListView为目标,介绍一下在子线程中获取数据等问题。

首先在layout中新增个listview:

<ListView
    	    android:id="@+id/listview"
    	    android:layout_width="match_parent"
    	    android:layout_height="match_parent">
	</ListView>      

在activity中绑定View,之前说了,可以通过 " import kotlinx.android.synthetic.main.activity_main.*" 的方式获取对应layout中的view,可以省略findViewById的代码。那这里我们使用findviewById的方式绑定view:

val listview:ListView=findViewById(R.id.listview) as ListView      

可以看到Java中通过“()”完成类型转换,在这里使用了“as”关键字。

创建adapter的布局list_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns: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,效果图:

Kotlin学习记录(三)—— 子线程获取数据,实现简单ListView

正常开发的情况下,数据的来源肯定不是这样的,而我们从服务器端获取数据网络请求都是在子线程中进行的,关于Android线程的问题,这里就不做解释了(这是基础常识

Kotlin学习记录(三)—— 子线程获取数据,实现简单ListView

)。因为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基本能够满足我们大部分的需求了。

快快尝试一下吧!

Kotlin学习记录(三)—— 子线程获取数据,实现简单ListView

Kotlin学习记录(四)—— 常用集合的使用