完整代码Gitee地址:kotlin-demo: 15天Kotlin学习计划
第三天学习内容代码:Chapter3
目录
知识点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,"迪丽不热", "")) userList.add(UserBean(R.mipmap.image_nan,"杀手不冷", "")) userList.add(UserBean(R.mipmap.image_nv,"赵思露", "")) userList.add(UserBean(R.mipmap.image_nv,"井川里予", "")) userList.add(UserBean(R.mipmap.image_nan,"阿斯顿", "")) userList.add(UserBean(R.mipmap.image_nan,"没啥用科技", "")) userList.add(UserBean(R.mipmap.image_nv,"阿瑟东", "")) } } }
这里使用了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 }
运行结果与标准写法一致。
到此这篇kotlin程序开发入门精要_kotlin web开发的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/kotlinkf/1107.html