• 【安卓】UI开发入门


    ❤️ Author: 老九
    ☕️ 个人博客:老九的CSDN博客
    🙏 个人名言:不可控之事 乐观面对
    😍 系列专栏:

    常用控件使用方法

    TextView

    • Testview的功能是显示一段文本信息
    <?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"
        android:orientation="vertical">
        <!--layout_width指明宽度,layout_height指明高度。三个值可供选择:match_parent(当前控件大小与父布局相同)、
        wrap_content(刚好包住)和固定值dp(与屏幕密度无关,不同分辨率下尽可能一致)-->
        <!--android:gravity指明文字对齐方式,可选值有top、bottom、top等。可以用|来同时指定多个值。譬如center等价于center_vertical|center_horizontal-->
        <!--android:textColor指明了文字颜色,android:textSize文字大小,文字大小以sp为单位-->
        <TextView
            android:id="@+id/text_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textColor="#00ff00"
            android:textSize="24sp"
            android:text="Hello World!" />
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Button

    <?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"
        android:orientation="vertical">
        .....
       <!--Android默认将英文字母转换成大写,android:textAllCaps设置为false保留指定原始文件内容-->
        <Button
            android:id="@+id/btn_click01"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAllCaps="false"
            android:text="Button" />
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 其次,点击事件可以采用函数式API进行事件相应,也可以通过实现接口的方式来进行注册
    package com.example.myapplication
     
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.view.View
    import kotlinx.android.synthetic.main.activity_main.*
     
    class MainActivity : AppCompatActivity(), View.OnClickListener {
     
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            //第一种调用方法:匿名内部类,利用Java单抽象方法接口特性,使用函数式API来写监听事件。
    //        btn_click01.setOnClickListener{
    //            //在此添加处理逻辑
    //        }
            //第二种调用方法:实现接口方法进行注册。让MainActivity实现了View.OnClickListener接口,并重写onClick方法
            //setOnClickListener将MainActivity实例传了进去
            btn_click01.setOnClickListener(this)
        }
        //重写的方法,很简单。
        override fun onClick(v: View?) {
            when (v?.id) {
                R.id.btn_click01 -> {
                    //在此添加逻辑
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    EditText

    • 用户在该控件中输入和编辑文本。应用场景如发微博,聊qq等
    <?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"
         .....
        <!--android:hint指定提示性的文本-->
        <!--android:maxLines指明最大行数为2行,若超过,文本向上滚动,EditText不会再拉伸。-->
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/edit_text01"
            android:maxLines="2"
            android:hint="type sth please"/>
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 点击按钮获取EditText里面的内容,代码如下:
        //重写的方法,很简单。
        override fun onClick(v: View?) {
            when (v?.id) {
                R.id.btn_click01 -> {
                    //点击按钮完成显式EditText文本内容的功能
                    //edit_text01.text通过语法糖调用了getText方法,编写代码直接调用实际方法即可,getText可以自动转换为text。
                    val inputText = edit_text01.text.toString()
                    Toast.makeText(this, inputText, Toast.LENGTH_SHORT).show()
                }
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ImageView

    • ImageView用于在界面显示图片,图片通常放在Drawable开头的目录,并且要附上具体的分辨率。目前主流屏幕分辨率是xxhdpi,所以在res目录再建一个drawable-xxhdpi目录,将事先准备好的照片复制到该目录
      <!--android:src为ImageView指定了一张图片,由于宽高未知,因此使用两个wrap_content使得其能显示出来-->
        <ImageView
            android:id="@+id/image_view01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/image_1" />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 动态更改inageview图片
     //通过代码动态更改ImageView图片
       R.id.btn_click02 ->{
              image_view01.setImageResource(R.drawable.image_2)
       }
    
    • 1
    • 2
    • 3
    • 4

    ProgressBar

    • PrograssBar是显示一个进度条,表示正在加载数据,控件默认都是可见的,我们需要使用android:visibility进行指定控件属性,有visible,invisible和gone,visible表示控件可见的,这个是默认值,invisible表示控件不可见,但是它仍然占据原来位置的大小;gone则表示控件不仅不可见,而且不再占用任何屏幕空间。我们可以通过代码来设置控件的可见性,使用setVisibility()方法,允许传入View.VISIBLE等等
       <!-- 默认是圆形样式,通过style属性将其变为水平进度条,修改其中的代码-->
        <!--android:max给进度条设置一个最大值,在代码中动态的更改进度条进度。-->
        <ProgressBar
            android:id="@+id/progress_bar01"
            style="?android:attr/progressBarStyleHorizontal"
            android:max="100"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
                //android:visibility有三个可选值:visiable(控件可见,默认)、invisible(空间不可见,仍占据原来未知)
                // 和gone(控件不仅不可见,也不再占用之前的屏幕),在使用setVisibility使用View.可选值
                R.id.btn_click03 -> {
                    //使用getVisibility判断prograssBar是否可见,如果可见隐藏,否则显示
                    if (progress_bar01.visibility == View.VISIBLE) {
                        //调用了setVisibility
                        progress_bar01.visibility = View.GONE
                    } else {
                        progress_bar01.visibility = View.VISIBLE
                    }
                }
                //进度条增加
                R.id.btn_click04 -> {
                    progress_bar01.progress += 10
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    AlertDialog

    • AlertDialog在当前界面显示一个置顶于所有界面之上的对话框,能够屏蔽掉其他控件的交互能力,因此AlertDialog一般用于提示一些非常重要的内容或者警告信息。
         R.id.btn_click05 -> {
                    //构建一个对话框并使用apply函数
                    AlertDialog.Builder(this).apply {
                        //消息标题
                        setTitle("This is Dialog")
                        //消息内容
                        setMessage("Sth important")
                        //是否可以使用back键返回对话框
                        setCancelable(false)
                        //确定按钮点击事件
                        setPositiveButton("OK") { dialog, which -> "删除吧" }
                        //取消按钮点击事件
                        setNegativeButton("Cancel") { dialog, which -> "保留吧" }
                        //对话框显示出来
                        show()
                    }
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    详解三种基本布局

    • 布局是防止很多控件的容器,布局内部可以放置布局或者控件,也可以通过多层布局嵌套完成复杂界面。

    LinearLayout

    • 线性布局,控件在线性方向上水平或者竖直排列,在这里使用android:orientation属性来指定。这里着重注意,android:gravity是文字控件的对齐方式,android:layout_gravity是控件的布局的对齐方式,android:layout_weight先将layout_weight所有值相加,再按照比例进行划分。
    <?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"
        android:orientation="vertical">
        <!--vertical竖直方向上布局,horiziontal水平布局,默认是水平布局。wrap_content刚好包含文字。-->
        <!--介绍一下android:gravity和android:layout_gravity,前者是文字在控件中的对齐方式,后者是指定控件在布局中的对齐方式-->
        <!--两者可选值差不多,但horizontal时,只有垂直方向上的对齐方式可以改变,换之亦然。每添加一个控件,水平上的长度会改变。-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="9"
            android:orientation="horizontal">
     
            <Button
                android:id="@+id/btn_01"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="top"
                android:text="Button 01" />
     
            <Button
                android:id="@+id/btn_02"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:text="Button 03" />
     
            <Button
                android:id="@+id/btn_03"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                android:text="Button 03" />
        </LinearLayout>
     
        <!--可能有人说为什么layout_width是0dp呢?这是因为使用android:layout_weight,0dp是比较规范的写法-->
        <!--android:layout_weight先将layout_weight所有值相加,再按照比例进行划分。-->
        <!--如果将后者的layout_width设置为wrap_content。前者layout_weight等于1,那么后面刚好包着,前面是屏幕剩余的所有空间-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:layout_gravity="bottom"
            android:orientation="horizontal">
     
            <EditText
                android:id="@+id/edit_text01"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="4"
                android:hint="Type sth please" />
     
            <Button
                android:id="@+id/btn_send"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Send" />
        </LinearLayout>
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    RelativeLayout

    • relativeLayout是相对定位的方式,可以让控件出现在布局中的任何位置。控件不仅可以相对于父布局进行定位,也可以相对于其他控件进行定位。
    <?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="match_parent">
        <!--Button 1与父布局左上角对齐,layout_alignParentLeft和layout_alignParentTop指定-->
        <Button
            android:id="@+id/btn_click_01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:text="Button 1" />
        <!--Button 2与父布局右上角对齐-->
        <Button
            android:id="@+id/btn_click_02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_alignParentRight="true"
            android:text="Button 2" />
     
        <!--Button 3居中显示-->
        <Button
            android:id="@+id/btn_click_03"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="Button 3" />
        <!--相对于控件进行定位,控件左上,需要注意的是:若一个控件引用另一个控件的id,那么该控件一定要定义在引用控件的后面-->
        <Button
            android:id="@+id/btn_click_06"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/btn_click_03"
            android:layout_toLeftOf="@id/btn_click_03"
            android:text="Button 6" />
        <!--相对于控件进行定位,控件右上-->
        <Button
            android:id="@+id/btn_click_07"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/btn_click_03"
            android:layout_toRightOf="@id/btn_click_03"
            android:text="Button 7" />
     
        <!--相对于控件进行定位,控件左下-->
        <Button
            android:id="@+id/btn_click_08"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/btn_click_03"
            android:layout_toLeftOf="@id/btn_click_03"
            android:text="Button 8" />
     
        <!--相对于控件进行定位,控件右下-->
        <Button
            android:id="@+id/btn_click_09"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/btn_click_03"
            android:layout_toRightOf="@id/btn_click_03"
            android:text="Button 9" />
        <!--Button 4与父布局左下角对齐-->
        <Button
            android:id="@+id/btn_click_04"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentBottom="true"
            android:text="Button 4" />
        <!--Button 5与父布局右下角对齐-->
        <Button
            android:id="@+id/btn_click_05"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_alignParentBottom="true"
            android:text="Button 5" />
    </RelativeLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    在这里插入图片描述

    FrameLayout

    • 帧布局,左右的控件都默认摆放在布局的左上角
    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
     
        <TextView
            android:id="@+id/text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:text="This is TextView" />
     
        <Button
            android:id="@+id/btn_001"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:text="Button" />
    </FrameLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    创建自定义控件

    • 所有的控件都是直接或者间接继承自View的,所有的布局直接或者间接继承自ViewGroup。View Group是特殊的View,包含很多子View和VIewGroup是用于放置控件和布局的容器。

    引入布局

    • 自定义一个标题栏并让所有Activity引用,防止代码大量重复,Layout目录下建立标题栏title.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="60dp"
        android:background="#ff00ff">
        <!--android:background表示用图片或者颜色填充背景-->
        <!--android:layout_margin用于指定控件在上下左右方向上的举例,当然也可以通过android:layout_marginLeft等指定-->
        <Button
            android:id="@+id/titleBack"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="left|center_vertical"
            android:layout_margin="5dp"
            android:background="#00ff00"
            android:text="Back"
            android:textColor="#fff" />
    
        <TextView
            android:id="@+id/title_text"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="Title text"
            android:textColor="#fff"
            android:textSize="24sp" />
    
        <Button
            android:id="@+id/titleEdit"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="right|center_vertical"
            android:layout_margin="5dp"
            android:background="#00ff00"
            android:text="Edit"
            android:textColor="#fff" />
    
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 然后修改Activity_main.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">
        
        <include layout="@layout/title" />
     
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 最后需要在MainActivity中隐藏系统自带的标题栏
    package com.example.myapplication
     
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
     
    class MainActivity : AppCompatActivity() {
     
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            //通过语法糖调用getSupportActionBar来获得ActionBar的实例,然后调用hide方法将标题隐藏
            //由于ActionBar可能为空,因此使用了?.操作符,目的是隐藏原始标题栏
            supportActionBar?.hide()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    创建自定义控件

    • 引入了布局确实解决了重复编写布局代码的问题,但是如果布局中有一些控件要求能够响应事件,我们还是需要在每个Activity中为这些控件单独编写一次事件注册的代码。
    • 新建TitleLayout继承自LInearLayout,让它称为我们自定义的标题栏控件。
    package com.example.myapplication
     
    import android.app.Activity
    import android.content.Context
    import android.util.AttributeSet
    import android.view.LayoutInflater
    import android.widget.LinearLayout
    import android.widget.Toast
    import kotlinx.android.synthetic.main.title.view.*
     
    //继承自LinearLayout使其成为我们的自定义控件。声明两个参数,在init结构体中对标题栏布局进行动态加载
    class TitleLayout(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
        init {
            //LayoutInflater的from方法可以构建出一个LayoutInflater对象,然后inflate方法动态加载一个布局文件
            //inflate加载两个参数,一个是布局文件ID,一个是给加载好的布局再添加一个父布局,在这里指定为TitleLayout,直接传入this。
            LayoutInflater.from(context).inflate(R.layout.title, this)
            titleBack.setOnClickListener {
                //context参数实际上是Activity实例,首先将其抓换为Activity类型,然后再行销毁。
    //            Kotlin的强制类型转换用as
                val activity = context as Activity
                activity.finish()
            }
            titleEdit.setOnClickListener {
                Toast.makeText(context, "You clicked the edit", Toast.LENGTH_SHORT).show()
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 在Activity_main.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">
     
        <!--    <include layout="@layout/title" />-->
        <com.example.myapplication.TitleLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ListView

    • ListView的简单用法
    • 先新建activity_main.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">
     
        <ListView
            android:id = "@+id/listView_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 修改MainActivity,构建适配器,并传入ListVIew中。这样可以通过滚动屏幕来看屏幕外的数据。
    package com.example.myapplication
     
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.widget.ArrayAdapter
    import kotlinx.android.synthetic.main.activity_main.*
     
    class MainActivity : AppCompatActivity() {
        //提供待展示数据,使用listOf初始化集合
        private val data = listOf(
            "Apple", "Banana", "Orange", "WaterMelon", "Pear", "Grape", "Cherry","Mango", "Apple", "Banana", "Orange", "WaterMelon", "Pear", "Grape", "Cherry", "Mango"
        )
     
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            //集合数据无法直接传递,需要借助适配器。比较好用的是ArrayAdapter,泛型指定为String
            //在构造方法中分别传入Activity实例、ListView子项布局的id以及数据源
            //simple_list_item_1为子项布局id,Android内置布局文件,里面只有TextView用于显示一段文本
            val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data)
            //最后利用setAdapter方法将构建好的适配器传递进去,这样ListView和数据之间的关联算是搞定了
            listView_main.adapter = adapter
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    定制LIstView的界面

    • ListView的每个子项中包含图片和文字,即自定义。新建实体类Fruit:
    package com.example.myapplication
    //Fruit类有两个字段,一个是水果名,一个是水果对应图片资源的ID
    class Fruit(val name: String, val imageid: Int)
    
    • 1
    • 2
    • 3
    • layout目录下新建fruit_item.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="60dp">
    <!--让两个玩意在垂直方向上居中显示-->
        <ImageView
            android:id="@+id/fruit_image"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="10dp" />
     
        <TextView
            android:id="@+id/fruit_Name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="10dp" />
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 新建自定义适配器FruitAdapter,继承自ArrayAdapter,范性指定为Fruit类
    package com.example.myapplication
     
    import android.app.Activity
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.ArrayAdapter
    import android.widget.ImageView
    import android.widget.TextView
    //定义一个主构造函数来将Activity实例、ListView子项布局id和数据源传递进来
    class FruitAdapter(activity: Activity, val resourceId: Int, data: List<Fruit>) :
        ArrayAdapter<Fruit>(activity, resourceId, data) {
        override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
            //首先使用LayoutInflater子项加载我们的布局,三个参数。
            // 最后一个false的意思是父布局声明的layout生效,不会为该View增加父布局。保准写法
            val view = LayoutInflater.from(context).inflate(resourceId, parent, false)
            //获取到ImageView和TextView的实例
            val fruitImage: ImageView = view.findViewById(R.id.fruit_image)
            val fruitName: TextView = view.findViewById(R.id.fruit_Name)
            //得到当前项的Fruit实例
            val fruit = getItem(position)
            //设置图片和文字
            if (fruit != null) {
                fruitImage.setImageResource(fruit.imageid)
                fruitName.text = fruit.name
            }
            //将布局返回
            return view
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 最后修改MainActivity里的代码。
    package com.example.myapplication
     
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.widget.ArrayAdapter
    import kotlinx.android.synthetic.main.activity_main.*
     
    class MainActivity : AppCompatActivity() {
        private val fruit_list = ArrayList<Fruit>()
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            initFruits()//初始化水果数据
            //创建Adapter对象,并将其作为适配器传递给ListView
            val adapter = FruitAdapter(this, R.layout.fruit_item, fruit_list)
            listView.adapter = adapter
        }
     
        fun initFruits() {
            //repeat函数将水果数据重复两遍
            repeat(2) {
                //构造方法中将水果名字和水果id传入,将创建好的对象添加到水果列表中。
                fruit_list.add(Fruit("Apple", R.drawable.apple_pic))
                fruit_list.add(Fruit("Banana", R.drawable.banana_pic))
                fruit_list.add(Fruit("Orange", R.drawable.orange_pic))
                fruit_list.add(Fruit("WaterMelon", R.drawable.watermelon_pic))
                fruit_list.add(Fruit("Pear", R.drawable.pear_pic))
                fruit_list.add(Fruit("PineApple", R.drawable.pineapple_pic))
                fruit_list.add(Fruit("StrawBerry", R.drawable.strawberry_pic))
                fruit_list.add(Fruit("Cherry", R.drawable.cherry_pic))
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    提升ListView的效率

    • ListView快速滑动时,性能成为了一个瓶颈,在这里我们接住了两种方案:getView的convertView参数和ViewHolder进行性能优化。
    • convertView用于将之前已经加载好的布局进行缓存,以便以后重用;如果converView为空,使用LayoutInfalter直接加载,如果不为空,直接对convertView进行重用。
    • 优化后的converView后,虽然已经不会加载重复布局,但仍然调用getView方法时调用view的findviewById获取控件实例,因此接住ViewHolder进行优化。
    • viewholder 的作用:1.获取控件2.将数据绑定在控件上
    package com.example.myapplication
     
    import android.app.Activity
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.ArrayAdapter
    import android.widget.ImageView
    import android.widget.TextView
     
    //定义一个主构造函数来将Activity实例、ListView子项布局id和数据源传递进来
    class FruitAdapter(activity: Activity, val resourceId: Int, data: List<Fruit>) :
        ArrayAdapter<Fruit>(activity, resourceId, data) {
        //innerclass来定义内部类
        inner class ViewHolder(val fruitImage: ImageView, val fruitName: TextView)
     
        override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
            //首先使用LayoutInflater子项加载我们的布局,三个参数。
            // 最后一个false的意思是父布局声明的layout生效,不会为该View增加父布局。保准写法
            val view: View
            val viewHolder: ViewHolder
            if (convertView == null) {
                view = LayoutInflater.from(context).inflate(resourceId, parent, false)
                //用于对ImageView和TextView的控件实例进行缓存
                val fruitImage: ImageView = view.findViewById(R.id.fruit_image)
                val fruitName: TextView = view.findViewById(R.id.fruit_Name)
                //创建ViewHolder对象并将控件实例放在ViewHolder里
                viewHolder = ViewHolder(fruitImage, fruitName)
                //使用View的setTag方法将ViewHolder存储在View中。
                view.tag = viewHolder
            } else {
                view = convertView
                //缓存对象不为空时,使用View的getTag方法将ViewHolder重新取出,这样所有控件的实例都存储于ViewHolder中了
                viewHolder = view.tag as ViewHolder
            }
     
            //得到当前项的Fruit实例
            val fruit = getItem(position)
            //设置图片和文字
            if (fruit != null) {
                viewHolder.fruitImage.setImageResource(fruit.imageid)
                viewHolder.fruitName.text = fruit.name
            }
            //将布局返回
            return view
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    ListView的点击事件

    • 使用setOnItemClickListener为listView注册一个监听器,当点击子项时,调用Lambda表达式,通过position确定哪一个子项。
    //        listView_main.setOnItemClickListener { parent, view, position, id ->
    //            val fruit = fruit_list[position]
    //            Toast.makeText(this, fruit.name, Toast.LENGTH_SHORT).show()
    //        }
            //没用到的参数可以用_来代替。因为是Java单抽象方法接口,所以可用函数式API的写法。onItemClick中接收四个参数。
            listView_main.setOnItemClickListener { _, _, position, _ ->
                val fruit = fruit_list[position]
                Toast.makeText(this, fruit.name, Toast.LENGTH_SHORT).show()
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    RecyclerView

    • ListView的运行效率和扩展性(只能纵向滚动)仍然有待提高,因此我们介绍一下RecyclerVIew。
    <?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="match_parent"
        tools:context=".MainActivity">
    
      <androidx.recyclerview.widget.RecyclerView
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:id="@+id/recyclerView"/>
    
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 为RecyclerView创建FruitAdapter适配器
    package com.example.listview
    
    import android.util.Log
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.ImageView
    import android.widget.TextView
    import android.widget.Toast
    import androidx.recyclerview.widget.RecyclerView
    
    class FruitAdapte(val fruitList : List<Fruit>,var onItemCLick : (view : View,position : Int)->Unit ) :
     RecyclerView.Adapter<FruitAdapte.ViewHolder>() {
        //viewHolder用来定义item实例
        inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
            val fruitImage: ImageView = view.findViewById(R.id.fruitImage)
            val fruitName: TextView = view.findViewById(R.id.fruitName)
        }
    
    //    todo:
    //    interface OnItemClickListener{
    //        fun onItemClick(view: View,position: Int)
    //    }
    //    private var mOnItemClickListener : OnItemClickListener? = null
    //    fun setOnItemClickListener(listener : OnItemClickListener?){
    //        mOnItemClickListener = listener
    //    }
    
        //首先定义一个函数类型,使用lambda语法,返回值是一个Unit,用一个变量进行接收
    //    lateinit var onItemClick : (view : View,position : Int) -> Unit
    
        //创建viewholder
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            //加载xml文件,转换成view
            val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
            val viewHolder = ViewHolder(view)
    //        viewHolder.fruitName.setOnClickListener{
    //            val position = viewHolder.adapterPosition
    //            val fruit = fruitList[position]
    //            Toast.makeText(parent.context,"you clicked view ${fruit.name}",Toast.LENGTH_SHORT).show()
    //        }
            viewHolder.fruitImage .setOnClickListener{
                val position = viewHolder.adapterPosition
                val fruit = fruitList[position]
                Toast.makeText(parent.context,"you clicked image ${fruit.name}",Toast.LENGTH_SHORT).show()
            }
            return viewHolder
        }
    
        //渲染数据
        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            //滚动到的数据加载进来,然后进行赋值
            val fruit = fruitList[position]
            holder.fruitImage.setImageResource(fruit.imgaeId)
            holder.fruitName.text = fruit.name
            //todo:
    //        holder.fruitName.setOnClickListener {
    //            mOnItemClickListener?.onItemClick(it,position)
    //        }
            holder.fruitName.setOnClickListener {
               onItemCLick(it,position)
            }
        }
    
        //返回列表长度
        override fun getItemCount(): Int
        {
            return fruitList.size
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 最后修改MainActivity中的代码
    package com.example.listview
    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.util.Log
    import android.view.View
    import android.widget.*
    import androidx.recyclerview.widget.LinearLayoutManager
    import androidx.recyclerview.widget.RecyclerView
    import androidx.recyclerview.widget.StaggeredGridLayoutManager
    import java.lang.StringBuilder
    import androidx.recyclerview.widget.RecyclerView.LayoutManager as LayoutManager1
    
    class MainActivity : AppCompatActivity() {
    
        private var fruitList = ArrayList<Fruit>()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            initFruits()
            val layoutManager = LinearLayoutManager(this)
            val recyclerView : RecyclerView= findViewById(R.id.recyclerView)
            recyclerView.layoutManager = layoutManager
            val adapter = FruitAdapte(fruitList){view,position ->
                val fruit = fruitList[position]
                Toast.makeText(this@MainActivity,fruit.name,Toast.LENGTH_SHORT).show()
            }
            //todo:
    //        adapter.setOnItemClickListener(object : FruitAdapte.OnItemClickListener{
    //            override fun onItemClick(view : View, position: Int) {
    //                val fruitname : TextView = view.findViewById(R.id.fruitName)
    //                val fruit = fruitList[position]
    //                Toast.makeText(this@MainActivity,fruit.name,Toast.LENGTH_SHORT).show()
    //            }
    //        })
    //        adapter.onItemClick = {view,position ->
    //            val fruit = fruitList[position]
    //            Toast.makeText(this@MainActivity,fruit.name,Toast.LENGTH_SHORT).show()
    //        }
    
            recyclerView.adapter = adapter
        }
    
        private  fun initFruits(){
            repeat(5){
                fruitList.add(Fruit(getRandomLengthString("1"),R.drawable.ic_launcher_background))
                fruitList.add(Fruit(getRandomLengthString("2"),R.drawable.ic_launcher_background))
                fruitList.add(Fruit(getRandomLengthString("3"),R.drawable.ic_launcher_background))
                fruitList.add(Fruit(getRandomLengthString("4"),R.drawable.ic_launcher_background))
                fruitList.add(Fruit(getRandomLengthString("5"),R.drawable.ic_launcher_background))
            }
        }
    
        private fun getRandomLengthString(str : String) : String{
            val n = (1..20).random()
            val builder = StringBuilder()
            repeat(n){
                builder.append(str)
            }
            return builder.toString()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    实现横向滚动和瀑布流布局

    • ListView扩展性不好的远影是只能进行纵向滚动,RecyclerVIew能做到横向滚动等
    • 实现的核心是在MainActivity.java中加上一句横向排列即可。
    layoutmanager.orientation = LinearLayoutManager.HORIZONTAL
    
    • 1
    • 相比ListView为什么如此简单?ListView给予自身管理,RecyclerVIew则将这个工作交给了LayoutManager,除了LinearLayoutManager之外,还有网络布局GridLayoutManager和瀑布布局StaggeredGridLayoutManager。下面谈谈瀑布流布局,还得先修改fruit_item.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:layout_margin="5dp"
        android:orientation="vertical">
     
        <ImageView
            android:id="@+id/fruit_image"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="10dp" />
    <!--    layout_gravity使得文字左对齐-->
        <TextView
            android:id="@+id/fruit_Name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:layout_marginTop="10dp" />
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 修改MainActivity的代码。
    package com.example.myapplication
     
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import androidx.recyclerview.widget.LinearLayoutManager
    import androidx.recyclerview.widget.StaggeredGridLayoutManager
    import kotlinx.android.synthetic.main.activity_main.*
    import java.lang.StringBuilder
     
    class MainActivity : AppCompatActivity() {
     
        private val fruit_list = ArrayList<Fruit>()
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            initFruits()
            //两个参数,第一个是几列;第二个是布局的排列方向,我们选择垂直
            val layoutmanager = StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL)
            recycler_view.layoutManager = layoutmanager
            //创建FruitAdapter实例并将水果数据传入FruitAdapter构造函数中
            val adapter = FruitAdapter(fruit_list)
            //最后调用setAdapter来完成适配器设置,从而完成RecyclerView与数据的关联
            recycler_view.adapter = adapter
        }
     
        private fun initFruits() {
            //repeat函数将水果数据重复两遍
            repeat(2) {
                //构造方法中将水果名字和水果id传入,将创建好的对象添加到水果列表中。
                fruit_list.add(Fruit(getRandomLengthString("Apple"), R.drawable.apple_pic))
                fruit_list.add(Fruit(getRandomLengthString("Banana"), R.drawable.banana_pic))
                fruit_list.add(Fruit(getRandomLengthString("Orange"), R.drawable.orange_pic))
                fruit_list.add(Fruit(getRandomLengthString("WaterMelon"), R.drawable.watermelon_pic))
                fruit_list.add(Fruit(getRandomLengthString("Pear"), R.drawable.pear_pic))
                fruit_list.add(Fruit(getRandomLengthString("PineApple"), R.drawable.pineapple_pic))
                fruit_list.add(Fruit(getRandomLengthString("StrawBerry"), R.drawable.strawberry_pic))
                fruit_list.add(Fruit(getRandomLengthString("Cherry"), R.drawable.cherry_pic))
            }
        }
        private fun getRandomLengthString(str:String):String{
            val n = (1..20).random()
            val builder = StringBuilder()
            repeat(n){
                builder.append(str)
            }
            return builder.toString()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    recyclerVIew的点击事件

    • RecyclerView没有setOnItemClickListener这样的注册监听器的方法,而是需要我们给子项具体的view去注册点击事件。
       //用于创建ViewHolder实例,将fruit_item加载进来,然后创建ViewHolder实例,最后将布局传入构造函数当中
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitAdapter.ViewHolder {
            val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
            val viewholder = ViewHolder(view)
            //为最外层的布局和ImageView都注册了点击事件,先获取用户点击的position,然后通过position获取相应的Fruit实例,最后Toast
            viewholder.itemView.setOnClickListener {
                val position = viewholder.adapterPosition
                val fruit = fruitList[position]
                Toast.makeText(parent.context, "you clicked view ${fruit.name}", Toast.LENGTH_SHORT)
                    .show()
            }
            viewholder.fruitImage.setOnClickListener{
                val position = viewholder.adapterPosition
                val fruit = fruitList[position]
                Toast.makeText(parent.context, "you clicked image ${fruit.name}", Toast.LENGTH_SHORT)
                    .show()
            }
            return viewholder
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Kotlin之延迟初始化和密封类

    对变量延迟初始化

    • 很多全局变量不可能为空,但由于kotlin语法特性,你不得不做许多非空判断保护,即使你很确定不能为空。
    class MainActivity : AppCompatActivity(), View.OnClickListener { 
       //因为初始化是在onCreate方法中进行,因此不得不将adapter赋值为null,同时将他的类型声明改成 MsgAdapter?
        private var adapter: MsgAdapter? = null
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
            adapter = MsgAdapter(msgList)
           
        }
        override fun onClick(p0: View?) {
              .....
             //肯定要进行判空处理,否则编译无法进行
             adapter?.notifyItemInserted(msgList.size - 1)
              ....
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 上述的代码全局变量实例越多,编写额外的判空处理代码就越多。因此我们使用lateinit延迟初始化,对上面的代码进行优化,告诉编译器我一会就会初始化,一开始不用设置为null。另外,我们需要判断全局变量是否已经完成了初始化,以避免某个变量重复初始化。
    class MainActivity : AppCompatActivity(), View.OnClickListener { 
        private lateinit var adapter: MsgAdapter
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
             //判断变量是否进行初始化,如果初始化,则不用重复对变量初始化,否则初始化
            if(!::adapter.isInitialized) {
                adapter = MsgAdapter(msgList)
            }
           
        }
     
        override fun onClick(p0: View?) {
              .....
             adapter.notifyItemInserted(msgList.size - 1)
              ....
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    使用密封类优化代码

    • 目的是解决为满足编译器要求编写无用条件分支的情况
    package com.example.myapplication
     
    import java.lang.IllegalArgumentException
    //定义一个接口,表示某个操作的执行结果
    interface Result
    //Success类用于表示成功时的结果
    class Success(val msg: String) : Result
    //Failure类用于表示失败时的结果
    class Failure(val error: String) : Result
    //接受一个Result参数,通过判断result类型返回不同的结果
    fun getResultMsg(result: Result) = when (result) {
        is Success -> result.msg
        is Failure -> result.error
        //else这块是完全执行不到的,但缺少的话代码将无法编译过。
        //如果新增一个UnKnown类并实现了Result接口,用于表示未知的执行结果,但忘记写分支,将会抛出异常使程序崩溃
        else -> throw IllegalArgumentException()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 解决方法就是在kotlin中使用密封类,密封类的关键字是sealed class。
    package com.example.myapplication
     
    import java.lang.IllegalArgumentException
    //密封类
    sealed class Result
    //继承类需要后面加上一对括号
    class Success(val msg: String) : Result()
     
    class Failure(val error: String) : Result()
     
    //class unkonwn(val time: String) : Result()
    //else条件已经没有了?为什么呢?
    //when传入密封类时,Kotlin会自动检查该密封类有哪些子类,并强制要求你对每一个都需要处理(若不处理,编译不会通过)。即使没有else,也不会出现遗漏分支
    fun getResultMsg(result: Result) = when (result) {
        is Success -> result.msg
        is Failure -> result.error
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    ————————————————————————
    ♥♥♥码字不易,大家的支持就是我坚持下去的动力♥♥♥
    版权声明:本文为CSDN博主「亚太地区百大最帅面孔第101名」的原创文章

  • 相关阅读:
    Mall脚手架总结(一)——SpringSecurity实现鉴权认证
    某大学R语言期末作业
    href=“#“与href=“javascript:void(0)“的区别
    【OpenCV图像处理14】图像分割与修复
    Linux Shell入门常用命令使用
    CrossOver2023(Mac电脑运行Windows软件)
    使用双动态令牌混合器学习全局和局部动态以进行视觉识别
    【藏经阁一起读】(72)__《Hologres 一站式实时数仓客户案例集》
    游戏党福音,Google play游戏明年登录Windows
    tensorflow-serving docker模型部署(以mnist为例)
  • 原文地址:https://blog.csdn.net/partworld/article/details/126305470