Kotlin开发第三天,UI开发

完整代码Gitee地址:kotlin-demo: 15天Kotlin学习计划

第三天学习内容代码:Chapter3

目录

知识点1:公共标题栏

知识点2:自定义标题栏控件

知识点3:RecyclerView

①标准写法     

②使用框架

知识点4:编写精美的聊天对话界面

①标准写法-多布局

②使用框架-多布局


知识点1:公共标题栏

         市场上应用的界面顶部有一个标题栏,标题栏上会有一到两个按钮可用于返回或其他操作(iPhone没有专门的返回键)。虽然Android系统已经给每个Activity提供了标题栏功能,但样式有很大局限性,我们自定义一个标题栏,新建item_title布局。 

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardBackgroundColor="@color/purple_500"
    app:cardElevation="2dp"
    app:cardMaxElevation="0dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="56dp">

        <LinearLayout
            android:id="@+id/lly_back"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:padding="15dp"
            android:orientation="horizontal">
            <ImageView
                android:layout_width="25dp"
                android:layout_height="25dp"
                android:src="@mipmap/img_return_withe"/>
        </LinearLayout>

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/white"
            android:layout_centerVertical="true"
            android:layout_centerHorizontal="true"
            android:textSize="18sp"
            android:textStyle="bold"
            android:text="标题"/>

        <TextView
            android:visibility="invisible"
            android:id="@+id/tv_right_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/white"
            android:layout_centerVertical="true"
            android:layout_alignParentRight="true"
            android:textSize="16sp"
            android:padding="12dp"
            android:text="确定"/>

    </RelativeLayout>
</androidx.cardview.widget.CardView>

在activity_learn3布局中引用:

<include layout="@layout/item_title"/>

运行效果如下:

        可以看到,我们在LinearLayout中分别加入了两个TextView和一个LinearLayout包裹ImageView,左边的LinearLayout可用于返回,右边的TextView可用于编辑,中间的TextView则可以显示一段标题文本。

知识点2:自定义标题栏控件

        引入布局的技巧确实解决了重复编写布局代码的问题,但是如果布局中有一些控件要求能够响应事件,我们还是需要在每个Activity中为这些控件单独编写一次事件注册的代码。比如标题栏中的返回按钮,其实不管是在哪一个Activity中,这个按钮的功能都是相同的,即销毁当前Activity。而如果在每一个Activity中都需要重新注册一遍返回按钮的点击事件,无疑会增加很多重复代码,这种情况最好是使用自定义控件的方式来解决。

        新建TitleLayout继承自LinearLayout,代码如下:

class TitleLayout(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
    //init结构体中需要对标题栏布局进行动态加载
    init {
        val view = LayoutInflater.from(context).inflate(R.layout.item_title, this)
        //为标题栏中的按钮注册点击事件
        val llyBack: LinearLayout = view.findViewById(R.id.lly_back)
        llyBack.setOnClickListener {
            val activity = context as Activity
            activity.finish()
        }
    }
}

        现在自定义控件已经创建好了,接下来我们需要在布局文件中添加这个自定义控件,修改activity_learn3.xml中的代码,如下所示:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!-- <include layout="@layout/item_title"/> -->

    <com.example.kotlin_demo.TitleLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

重新运行程序,和使用引入布局方式的效果是一样的。

知识点3:RecyclerView

        ListView由于强大的功能,在过去的Android开发当中可以说是贡献卓越,直到今天仍然还有不计其数的程序在使用ListView。不过ListView并不是完美无缺的,比如如果不使用一些技巧来提升它的运行效率,那么ListView的性能就会非常差。还有,ListView的扩展性也不够好,它只能实现数据纵向滚动的效果,如果我们想实现横向滚动的话,ListView是做不到的。

        它可以说是一个增强版的ListView,不仅可以轻松实现和ListView同样的效果,还优化了ListView存在的各种不足之处。目前Android官方更加推荐使用RecyclerView:

新建item_rcy_cont作为RecyclerView每项Item局部:

<?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="wrap_content"
    android:orientation="horizontal"
    android:padding="10dp">

    <ImageView
        android:id="@+id/iv_herd"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_marginStart="5dp"
        android:layout_marginLeft="5dp"
        tools:srcCompat="@tools:sample/avatars" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:gravity="center"
            android:textSize="15sp"
            android:text="姓名:" />

        <TextView
            android:id="@+id/tv_phone"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:gravity="center"
            android:textSize="15sp"
            android:text="电话:" />
    </LinearLayout>

</LinearLayout>

新建实体类UserBean,作为数据存储映射实体:

class UserBean(val herd: Int, val name: String, val phone: String)

修改activity_learn3.xml中的代码,如下所示:

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

    <com.example.kotlin_demo.TitleLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:listitem="@layout/item_rcy_cont"/>
</LinearLayout>

预览效果如下:

①标准写法     

        接下来需要为RecyclerView准备一个适配器,新建UserAdapter类,让这个适配器继承自RecyclerView.Adapter,并将泛型指定为UserAdapter.ViewHolder。其中,ViewHolder是我们在UserAdapter中定义的一个内部类,代码如下所示:

//让这个适配器继承自RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder。
class UserAdapter(private val userList: List<UserBean>) :
    RecyclerView.Adapter<UserAdapter.ViewHolder>() {

    //内部类绑定控件
    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val nameText: TextView = view.findViewById(R.id.tv_name)
        val phoneText: TextView = view.findViewById(R.id.tv_phone)
        val ivHerd: ImageView = view.findViewById(R.id.iv_herd)
    }

    //创建布局
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view =
            LayoutInflater.from(parent.context).inflate(R.layout.item_rcy_cont, parent, false)
        return ViewHolder(view)
    }

    //展示布局数量
    override fun getItemCount(): Int {
        return userList.size
    }

    //给控件赋值与样式
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.nameText.text = userList[position].name
        holder.phoneText.text = userList[position].phone
        holder.ivHerd.setImageResource(userList[position].herd)
    }
}

        虽然看上去好像多了好几个方法,但其实它比ListView的适配器要更容易理解。适配器准备好了之后,我们就可以开始使用RecyclerView了,修改Learn3Activity中的代码,如下所示:

class Learn3Activity : BaseActivity() {

    private val userList = ArrayList<UserBean>()
    private lateinit var mAdapter: UserNewAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_learn3)
        //3、RecyclerView控件
        recyclerView()
    }

    private fun recyclerView() {
        //初始化用户数据
        initUser()
        val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
        //①标准写法,使用适配器
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = UserAdapter(userList)
    }

    private fun initUser() {
        repeat(2) {
            userList.add(UserBean(R.mipmap.image_nv,"迪丽不热", "17321341289"))
            userList.add(UserBean(R.mipmap.image_nan,"杀手不冷", "17377621412"))
            userList.add(UserBean(R.mipmap.image_nv,"赵思露", "19987878221"))
            userList.add(UserBean(R.mipmap.image_nv,"井川里予", "13612344637"))
            userList.add(UserBean(R.mipmap.image_nan,"阿斯顿", "13635465678"))
            userList.add(UserBean(R.mipmap.image_nan,"没啥用科技", "13801940921"))
            userList.add(UserBean(R.mipmap.image_nv,"阿瑟东", "16622348923"))
        }
    }
}

        这里使用了initUser()方法,用于初始化所有的用户数据。接着在onCreate()方法中先创建了一个LinearLayoutManager对象,并将它设置到RecyclerView当中。LayoutManager用于指定RecyclerView的布局方式,这里使用的LinearLayoutManager是线性布局的意思,可以实现和ListView类似的效果。接下来我们创建了UserAdapter的实例,并将水果数据传入UserAdapter的构造函数中,最后调用RecyclerView的setAdapter()方法来完成适配器设置,这样RecyclerView和数据之间的关联就建立完成了。现在运行一下程序,如下所示。

        当然这只是RecyclerView的基本用法而已,还有更多用法,比如实现横向滚动和瀑布流布局,这个也并不复杂,与Java写法类似。

②使用框架

        BaseRecyclerViewAdapterHelper是一个比较成熟的框架,代码书写简洁很大提升开发效率,接下来我们用Kotlin写法使用它,在app目录下的build.gradle添加依赖:

/* 灵活的RecyclerView框架 */
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'

创建适配器UserNewAdapter:

class UserNewAdapter :
    BaseQuickAdapter<UserBean, BaseViewHolder>(R.layout.item_rcy_cont) {
    override fun convert(holder: BaseViewHolder, item: UserBean) {
        //获取控件ID
        val icon = holder.getView<ImageView>(R.id.iv_herd)
        //展示图片
        icon.setImageResource(item.herd)
        //直接设置文本内容
        holder.setText(R.id.tv_name, item.name)
        holder.setText(R.id.tv_phone, item.phone)
    }
}

        适配器准备好了之后,我们就可以开始使用RecyclerView了,修改Learn3Activity中的代码,如下所示:

private fun recyclerView() {
    //初始化用户数据
    initUser()
    val recyclerView: RecyclerView = findViewById(R.id.recyclerView)

    //②使用框架,BaseRecyclerViewAdapterHelper
    mAdapter = UserNewAdapter()
    recyclerView.layoutManager = LinearLayoutManager(this)
    recyclerView.adapter = mAdapter
    mAdapter.addData(userList)//添加数据
}

 总体来看框架写法更简单,运行效果与标准写法一样。

知识点4:编写精美的聊天对话界面

        下面我们来编写一个聊天界面,需要用到.9图片,如何制作这里不做讲解,图片资源可去顶部gitee获取。我们在主界面中放置了一个RecyclerView用于显示聊天的消息内容,定义消息的实体类,新建MsgBean,代码如下所示:

//content表示消息的内容,type表示消息的类型
class MsgBean(val content: String, val itemType: Int) {
    companion object {
        //定义常量的关键字是const
        //表示这是一条收到的消息
        const val TYPE_LEFT = 0
        //表示这是一条发出的消息
        const val TYPE_RIGHT = 1
    }
}

        MsgBean类中只有两个字段:content表示消息的内容,type表示消息的类型。其中消息类型有两个值可选:TYPE_LEFT 表示这是一条收到的消息,TYPE_RIGHT表示这是一条发出的消息。定义常量的关键字是const,注意只有在单例类、companion object或顶层方法中才可以使用const关键字。

新建子项布局,左边显示item_msg_left.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="10dp"
    android:background="#e8e8e8">

    <ImageView
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_marginLeft="5dp"
        android:layout_marginStart="5dp"
        android:src="@mipmap/image_nv"/>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:layout_marginStart="5dp"
        android:gravity="center"
        android:background="@drawable/item_msg_left_selector">

        <TextView
            android:id="@+id/tv_left"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxWidth="240dp"
            android:layout_marginLeft="12dp"
            android:layout_marginRight="10dp"
            android:text="您好!"
            android:textSize="15sp"/>

    </LinearLayout>

</LinearLayout>

预览效果:

新建子项布局,右边显示item_msg_right.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="10dp"
    android:background="#e8e8e8">

    <LinearLayout
        android:layout_toLeftOf="@+id/iv_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="5dp"
        android:gravity="center"
        android:background="@drawable/item_msg_right_selector">

        <TextView
            android:id="@+id/tv_right"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxWidth="240dp"
            android:layout_marginLeft="12dp"
            android:layout_marginRight="12dp"
            android:text="您好!"
            android:textSize="15sp"/>

    </LinearLayout>

    <ImageView
        android:id="@+id/iv_right"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_marginRight="5dp"
        android:layout_alignParentRight="true"
        android:src="@mipmap/image_nan" />


</RelativeLayout>

预览效果:

①标准写法-多布局

接下来需要创建RecyclerView的适配器类,新建类MsgAdapter,代码如下所示:

class MsgAdapter(private val msgList: List<MsgBean>) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    inner class LeftViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val leftMsg: TextView = view.findViewById(R.id.tv_left)
    }

    inner class RightViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val rightMsg: TextView = view.findViewById(R.id.tv_right)
    }

    override fun getItemViewType(position: Int): Int {
        return msgList[position].itemType
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):     
        RecyclerView.ViewHolder {
            return when (viewType) {
                MsgBean.TYPE_LEFT ->
                    LeftViewHolder(
                        LayoutInflater.from(parent.context)
                            .inflate(R.layout.item_msg_left, parent, false)
                    )
                MsgBean.TYPE_RIGHT ->
                    RightViewHolder(
                        LayoutInflater.from(parent.context)
                            .inflate(R.layout.item_msg_right, parent, false)
                    )
                else -> throw IllegalArgumentException()
        }
    }

    override fun getItemCount(): Int {
        return msgList.size
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val msg = msgList[position]
        when (holder) {
            is LeftViewHolder -> holder.leftMsg.text = msg.content
            is RightViewHolder -> holder.rightMsg.text = msg.content
            else -> throw IllegalArgumentException()
        }
    }

}

        上述代码中用到了一个新的知识点:根据不同的viewType创建不同的界面。首先我们定义了LeftViewHolder和RightViewHolder这两个ViewHolder,分别用于缓存msg_left_item.xml和msg_right_item.xml布局中的控件。然后要重写getItemViewType()方法,并在这个方法中返回当前position对应的消息类型。

最后修改Learn3Activity中的代码:

class Learn3Activity : BaseActivity() {

    private val msgList = ArrayList<MsgBean>()

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

        //4、编写精美的聊天对话界面
        val tvTitle: TextView = findViewById(R.id.tv_title)
        tvTitle.text = "聊天室"
        recyclerNewView()
    }

    private fun initMsg() {
        repeat(1) {
            msgList.add(MsgBean("在吗?还记得我吗?",0))
            msgList.add(MsgBean("当然,初中坐我前排的班花,那时候我还扯过你的肩带",1))
            msgList.add(MsgBean("有啥事吗?",1))
            msgList.add(MsgBean("能借我200块钱吗",0))
            msgList.add(MsgBean("其实从初中开始我就一直暗恋你,没好意思跟你说,我们在一起吧",1))
            msgList.add(MsgBean("别吧🤦我们都多久没联系了,都不了解对方,也不太熟悉😳‍",0))
            msgList.add(MsgBean("那你还好意思找我借钱?‍",1))
            msgList.add(MsgBean("?‍",0))
        }
    }

    private fun recyclerNewView() {
        initMsg()
        val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
        //①标准写法,使用适配器
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = MsgAdapter(msgList)
    }

}

        我们先在initMsg()方法中初始化了几条数据用于在RecyclerView中显示,接下来按照标准的方式构建RecyclerView,给它指定一个LayoutManager和一个适配器。运行程序之后,你将会看到非常美观的聊天界面:

②使用框架-多布局

        布局不变,MsgBean实体类继承MultiItemEntity ,类型必须命名为itemType,并设置关键字override,修改如下:

class MsgBean(val content: String,override val itemType: Int) : MultiItemEntity {
    companion object {
        //定义常量的关键字是const
        //表示这是一条收到的消息
        const val TYPE_LEFT = 0
        //表示这是一条发出的消息
        const val TYPE_RIGHT = 1
    }
}

        创建多布局的适配器,新建类MsgNewAdapter,继承BaseMultiItemQuickAdapter,代码如下所示:

class MsgNewAdapter(data: MutableList<MsgBean>?) :
    BaseMultiItemQuickAdapter<MsgBean, BaseViewHolder>(data) {

    init {
        //必须绑定type和layout的关系
        addItemType(MsgBean.TYPE_LEFT, R.layout.item_msg_left)
        addItemType(MsgBean.TYPE_RIGHT, R.layout.item_msg_right)
    }

    override fun convert(holder: BaseViewHolder, item: MsgBean) {
        holder ?: return
        item ?: return
        when (holder.itemViewType) {
            MsgBean.TYPE_LEFT -> holder.setText(R.id.tv_left, item.content)
            MsgBean.TYPE_RIGHT -> holder.setText(R.id.tv_right, item.content)
            else -> throw IllegalArgumentException()
        }
    }
}

 最后修改Learn3Activity中的代码:

private fun recyclerNewView() {
    initMsg()
    val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
    //②使用框架,BaseRecyclerViewAdapterHelper
    msgAdapter = MsgNewAdapter(msgList)
    recyclerView.layoutManager = LinearLayoutManager(this)
    recyclerView.adapter = msgAdapter
}

运行结果与标准写法一致。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

peacejay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值