《Android》Chap.4 UI开发

常用控件的使用方法

TextView

理论

属性含义用法
android:id唯一标识符\
android:layout_width控件宽度match_parent:让当前控件的大小和父布局的一样; wrap_content:让当前控件的大小正好适配里面的内容;
android:layout_height空间高度固定值:单位用dp能保证不同分辨率效果下屏幕显示效果尽量一致(与上栏用法相同)
android:gravity文字对齐方式centertopbottomstartend(字面意思)
android:textColor文字颜色#000000,六位16进制数字表示
android:textSize文字大小单位用sp能在用户调整文字大小的时候跟随调整
android:text文字内容\

实践

<TextViewandroid:id="@+id/textView"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:textColor="#00ff00"android:textSize="24sp"android:text="This is TextView"/>

在这里插入图片描述

Button

理论

大部分与TextView相同。
Button中默认显示出来的字母都是大写,如果不需要,则加上:android:textAllCaps="false"

实践

<Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="Button"android:textAllCaps="false"/>

在这里插入图片描述

点击事件监听器

函数式API方式

class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)binding.button.setOnClickListener {}}
}

在这里插入图片描述
补充:这里使用了ViewBinding

实现接口方式

class MainActivity : AppCompatActivity(),View.OnClickListener {private  lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)binding.button.setOnClickListener(this)}override fun onClick(v: View?) {when(v?.id){R.id.button -> {}}}}

在这里插入图片描述

EditText

理论

属性含义用法
android:hint输入提示字提示性文本,当用户需要输入的时候,文字自动消失
android:maxLines最大行数当输入内容超过规定的最大行数时就向上滚动,不会继续拉伸

实践

<EditTextandroid:id="@+id/editText"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Type something here"android:maxLines="2"/>

在这里插入图片描述

获取输入功能

class MainActivity : AppCompatActivity(),View.OnClickListener {private  lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)binding.button.setOnClickListener(this)}override fun onClick(v: View?) {when(v?.id){R.id.button -> {val inputText = binding.editText.text.toString()Toast.makeText(this, inputText, Toast.LENGTH_SHORT).show()}}}}

在这里插入图片描述

ImageView

由于现在主流手机屏幕的分辨率都是xxhdpi,所以再res目录下再新建一个drawable-xxhdpi的目录用于存放图片。

放入图片

<ImageViewandroid:id="@+id/imageView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/apple_pic"/>

在这里插入图片描述

改变图片

override fun onClick(v: View?) {when(v?.id){R.id.button -> {binding.imageView.setImageResource(R.drawable.banana_pic)}}
}

这样在点击button后,苹果就会变为香蕉
在这里插入图片描述

ProgressBar

圆形进度条

放入进度条

<ProgressBarandroid:id="@+id/progressBar"android:layout_width="match_parent"android:layout_height="wrap_content"/>

在这里插入图片描述

消失和出现

实现点击button后进度条消失,再点击后进度条出现。

override fun onClick(v: View?) {when(v?.id){R.id.button -> {if(binding.progressBar.visibility == View.VISIBLE){binding.progressBar.visibility = View.GONE}else{binding.progressBar.visibility = View.VISIBLE}}}
}

水平进度条

activity_main.xml

<ProgressBarandroid:id="@+id/progressBar"android:layout_width="match_parent"android:layout_height="wrap_content"style="?android:attr/progressBarStyleHorizontal"android:max="100"/>

MainActivity

override fun onClick(v: View?) {when(v?.id){R.id.button -> {binding.progressBar.progress = binding.progressBar.progress + 10}}
}

在这里插入图片描述
每点击一次,进度就会增加 1 / 10 1/10 1/10

AlertDialog

override fun onClick(v: View?) {when(v?.id){R.id.button -> {AlertDialog.Builder(this).apply {setTitle("This is Dialog")setMessage("Something important")setCancelable(false)setPositiveButton("OK"){dialog,which -> }setNegativeButton("Cancel"){dialog,which -> }show()}}}
}

在这里插入图片描述

基本布局

LinearLayout

LinearLayout又称作线性布局,这个布局会将它所包含的控件在线性方向上依次排列

布局的方向

  • horizontal:水平方向
  • vertical:竖直方向

在这里插入图片描述
在这里插入图片描述

控件的排列

  • 因为当前布局的排列方向为horizontal,所以layout_gravity只能指定垂直方向上的排列方向

在这里插入图片描述

控件大小比例

在这里插入图片描述
在这里插入图片描述

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"><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_alignParentTop="true"android:text="button 1"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_alignParentTop="true"android:text="button 2"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="button 3"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_alignParentBottom="true"android:text="button 4"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_alignParentBottom="true"android:text="button 5"/></RelativeLayout>

在这里插入图片描述

相对于控件进行定位

注意:当控件a要去引用控件b时,控件b要定义在控件a前面

<?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"><Buttonandroid:id="@+id/button3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="button 3"/><Buttonandroid:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@id/button3"android:layout_toLeftOf="@id/button3"android:text="button 1"/><Buttonandroid:id="@+id/button2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@id/button3"android:layout_toRightOf="@id/button3"android:text="button 2"/><Buttonandroid:id="@+id/button4"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/button3"android:layout_toLeftOf="@id/button3"android:text="button 4"/><Buttonandroid:id="@+id/button5"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/button3"android:layout_toRightOf="@id/button3"android:text="button 5"/></RelativeLayout>

在这里插入图片描述

FrameLayout

FrameLayout又称作帧布局,这种布局没有丰富的定位方式,所有的控件都会默认摆放在布局的左上角
在这里插入图片描述
因为Button是在TextView之后添加的,所以按钮在文字的上面。
在这里插入图片描述

自定义控件

引入布局

尝试定义一个标题栏布局 title.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"tools:viewBindingIgnore="true"android:layout_height="wrap_content"><Buttonandroid:id="@+id/titleBack"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="5dp"android:background="@drawable/apple_pic"android:text="Back"android:textColor="#fff"/><TextViewandroid:id="@+id/titleText"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_weight="1"android:gravity="center"android:text="Title Text"android:textColor="#000"android:textSize="24sp"/><Buttonandroid:id="@+id/titleEdit"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="5dp"android:background="@drawable/banana_pic"android:text="Edit"android:textColor="#fff"/></LinearLayout>

在这里插入图片描述
activity_main.xml中使用
在这里插入图片描述
注意在MainActivity中将系统自带的标题栏隐藏

supportActionBar?.hide()

这样重复使用这个标题的时候就可以大大减少代码量

创建自定义控件

新建TitleLayout继承自LinearLayout,让它成为自定义的标题栏控件

class TitleLayout(context: Context, attrs: AttributeSet) :LinearLayout(context,attrs) {init {LayoutInflater.from(context).inflate(R.layout.title,this)val titleBack = findViewById<Button>(R.id.titleBack)val titleEdit = findViewById<Button>(R.id.titleEdit)titleBack.setOnClickListener {val activity = context as Activityactivity.finish()}titleEdit.setOnClickListener {Toast.makeText(context, "You clicked Edit button", Toast.LENGTH_SHORT).show()}}}

注意这里没有使用 viewBinding
然后在布局文件中添加这个自定义控件

<com.example.uiwidgettest.TitleLayoutandroid:id="@+id/titlelayout"android:layout_width="match_parent"android:layout_height="67dp" />

在这里插入图片描述

ListView

简单用法

class MainActivity : AppCompatActivity() {private  lateinit var binding: ActivityMainBindingprivate val data = listOf("Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango","Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)val adapter = ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data)binding.listView.adapter = adapter}
}

可以滚动的list
在这里插入图片描述

定制ListView界面

定义实体类fruit

class Fruit(val name: String,val imageId: Int) {
}

layout目录下新建一个fruit_item.xml用于自定义布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="60dp"><ImageViewandroid:id="@+id/fruitImage"android:layout_width="40dp"android:layout_height="40dp"android:layout_gravity="center_vertical"android:layout_marginLeft="10dp"/><TextViewandroid:id="@+id/fruitName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginLeft="10dp"/></LinearLayout>

创建继承于ArrayAdapter的自定义适配器FruitAdapter,并将泛型指定为Fruit

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 {val view = LayoutInflater.from(context).inflate(resourceId,parent,false)val fruitImage: ImageView = view.findViewById(R.id.fruitImage)val fruitName: TextView = view.findViewById(R.id.fruitName)val fruit = getItem(position)if (fruit != null){fruitImage.setImageResource(fruit.imageId)fruitName.text = fruit.name}else{Log.d("++++++++++","null")}return view}
}

最后编写MainActivity中的代码


class MainActivity : AppCompatActivity() {private  lateinit var binding: ActivityMainBindingprivate val fruitList = ArrayList<Fruit>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)initFruits()val adapter = FruitAdapter(this, R.layout.fruit_item,fruitList)binding.listView.adapter = adapter}private fun initFruits(){repeat(2){fruitList.add(Fruit("Apple",R.drawable.apple_pic))fruitList.add(Fruit("Banana",R.drawable.banana_pic))fruitList.add(Fruit("Orange",R.drawable.orange_pic))fruitList.add(Fruit("Watermelon",R.drawable.watermelon_pic))fruitList.add(Fruit("Pear",R.drawable.pear_pic))fruitList.add(Fruit("Grape",R.drawable.grape_pic))fruitList.add(Fruit("Pineapple",R.drawable.pineapple_pic))fruitList.add(Fruit("Strawberry",R.drawable.strawberry_pic))fruitList.add(Fruit("Cherry",R.drawable.cherry_pic))fruitList.add(Fruit("Mango",R.drawable.mango_pic))}}
}

在这里插入图片描述

提升ListView的运行效率

当前ListView的运行效率是很低的,因为在FruitAdaptergetView()方法中,每次都将布局重新加载了一遍,当ListView快速滚动的时候,这就会成为性能的瓶颈。

使用convertView参数

getView()方法中还有⼀个convertView参数,这个参数用于将之前加载好的布局进行缓存,以便之后进行重用,我们可以借助这个参数来进行性能优化。

val view: View
if (convertView == null){view =  LayoutInflater.from(context).inflate(resourceId,parent,false)
}else{view = convertView
}
  • 如果convertViewnull,则使用LayoutInflater去加载布局
  • 如果不为null,则直接对convertView进行重用。

使用内部类ViewHolder

class FruitAdapter(activity: Activity, val resourceId: Int, data: List<Fruit>):ArrayAdapter<Fruit>(activity,resourceId,data){inner class ViewHolder(val fruitImage: ImageView, val fruitName: TextView)override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {val view: Viewval viewHolder: ViewHolderif (convertView == null){view =  LayoutInflater.from(context).inflate(resourceId,parent,false)val fruitImage: ImageView = view.findViewById(R.id.fruitImage)val fruitName: TextView = view.findViewById(R.id.fruitName)viewHolder = ViewHolder(fruitImage,fruitName)view.tag = viewHolder}else{view = convertViewviewHolder = view.tag as ViewHolder}val fruit = getItem(position)if (fruit != null){viewHolder.fruitImage.setImageResource(fruit.imageId)viewHolder.fruitName.text = fruit.name}return view}
}

内部类ViewHolder,用于对ImageViewTextView的控件实例进行缓存
Kotlin中使用 inner class关键字来定义内部类。

  • convertViewnull的时候,创建⼀个ViewHolder对象,并将控件的实例存放在ViewHolder里,然后调用ViewsetTag()方法,将ViewHolder对象存储在View中。
  • convertView不为null的时候,则调用ViewgetTag()方法,把ViewHolder重新取出。这样所有控件的实例都缓存在了ViewHolder里,就没有必要每次都通过findViewById()方法来获取控件实例了。

ListView的点击事件

使用setOnItemClickListener()方法为ListView注册了一个监听器,当用户点击了ListView中的任何⼀个子项时,就会回调到Lambda表达式中。这里我们可以通过position参数判断用户点击的是哪一个子项,然后获取到相应的水果。

class MainActivity : AppCompatActivity() {private  lateinit var binding: ActivityMainBindingprivate val fruitList = ArrayList<Fruit>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)initFruits()val adapter = FruitAdapter(this, R.layout.fruit_item, fruitList)binding.listView.adapter = adapterbinding.listView.setOnItemClickListener{ parent, view, position, id ->val fruit = fruitList[position]Toast.makeText(this, fruit.name, Toast.LENGTH_SHORT).show()}}private fun initFruits(){repeat(2){fruitList.add(Fruit("Apple",R.drawable.apple_pic))fruitList.add(Fruit("Banana",R.drawable.banana_pic))fruitList.add(Fruit("Orange",R.drawable.orange_pic))fruitList.add(Fruit("Watermelon", R.drawable.watermelon_pic))fruitList.add(Fruit("Pear",R.drawable.pear_pic))fruitList.add(Fruit("Grape",R.drawable.grape_pic))fruitList.add(Fruit("Pineapple",R.drawable.pineapple_pic))fruitList.add(Fruit("Strawberry",R.drawable.strawberry_pic))fruitList.add(Fruit("Cherry",R.drawable.cherry_pic))fruitList.add(Fruit("Mango",R.drawable.mango_pic))}}
}

在这里插入图片描述
Kotlin允许将没有用到的参数使用下划线来替代,这种写法也是合法且更加推荐

binding.listView.setOnItemClickListener{ _, _, position, _ ->val fruit = fruitList[position]Toast.makeText(this, fruit.name, Toast.LENGTH_SHORT).show()
}

RecyclerView

先在app/build.gradleRecyclerView库导入到项目中

implementation 'androidx.recyclerview:recyclerview:1.0.0'

基本用法

还是之前的fruitfruit_item.xml代码
activity_main.xml中加入

<androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="match_parent"/>

新建FruitAdapter类为RecyclerView的适配器,让这个适配器继承自RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder。其中,ViewHolder是我们在FruitAdapter中定义的⼀个内部类

class FruitAdapter(val fruitList: List<Fruit>) :RecyclerView.Adapter<FruitAdapter.ViewHolder>(){inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view){val fruitImage: ImageView = view.findViewById(R.id.fruitImage)val fruitName: TextView = view.findViewById(R.id.fruitName)}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item,parent,false)return ViewHolder(view)}override fun onBindViewHolder(holder: ViewHolder, position: Int) {val fruit = fruitList[position]holder.fruitImage.setImageResource(fruit.imageId)holder.fruitName.text = fruit.name}override fun getItemCount() = fruitList.size
}

这是RecyclerView适配器标准的写法
首先定义了⼀个内部类ViewHolder,它要继承自RecyclerView.ViewHolder。然后ViewHolder的主构造函数中要传入⼀个View参数,这个参数通常就是RecyclerView子项的最外层布局,那么就可以通过findViewById()方法来获取布局中ImageViewTextView的实例了。
FruitAdapter中也有⼀个主构造函数,它⽤于把要展示的数据源传进 来,后续的操作都将在这个数据源的基础上进行。

  • onCreateViewHolder()方法是用于创建ViewHolder实例的,我们在这个方法中将fruit_item布局加载进来,然后创建⼀个ViewHolder实例,并把加载出来的布局传入构造函数当中,最后将ViewHolder的实例返回
  • onBindViewHolder()方法用于对RecyclerView子项的数据进行赋值,会在每个子项被滚动到屏幕内的时候执行,这里我们通过position参数得到当前项的Fruit实例,然后再将数据设置到ViewHolderImageViewTextView当中即可。
  • getItemCount()方法用于告诉RecyclerView⼀共有多少子项,直接返回数据源的长度就可以了。

上面的准备工作就绪后,就可以在MainActivity中使用RecyclerView

class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingprivate val fruitList = ArrayList<Fruit>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)initFruits()val layoutManager = LinearLayoutManager(this)binding.recyclerView.layoutManager = layoutManagerval adapter = FruitAdapter(fruitList)binding.recyclerView.adapter = adapter}private fun initFruits(){repeat(2){fruitList.add(Fruit("Apple",R.drawable.apple_pic))fruitList.add(Fruit("Banana",R.drawable.banana_pic))fruitList.add(Fruit("Orange",R.drawable.orange_pic))fruitList.add(Fruit("Watermelon", R.drawable.watermelon_pic))fruitList.add(Fruit("Pear",R.drawable.pear_pic))fruitList.add(Fruit("Grape",R.drawable.grape_pic))fruitList.add(Fruit("Pineapple",R.drawable.pineapple_pic))fruitList.add(Fruit("Strawberry",R.drawable.strawberry_pic))fruitList.add(Fruit("Cherry",R.drawable.cherry_pic))fruitList.add(Fruit("Mango",R.drawable.mango_pic))}}
}

LayoutManager用于指定RecyclerView的布局方式,这里使用的LinearLayoutManager是线性布局的意思,可以实现和ListView类似的效果。
在这里插入图片描述

横向滚动

ListView中无法实现横向滚动,而RecyclerView中就可以轻易做到
首先要调整一下fruit_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="80dp"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/fruitImage"android:layout_width="40dp"android:layout_height="40dp"android:layout_gravity="center_horizontal"android:layout_marginTop="10dp"/><TextViewandroid:id="@+id/fruitName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:layout_marginTop="10dp"/></LinearLayout>

MainActivitylayoutManager后添加一行代码

layoutManager.orientation = LinearLayoutManager.HORIZONTAL

调用LinearLayoutManagersetOrientation()方法设置布局的排列方向。默认是纵向排列的,传入LinearLayoutManager.HORIZONTAL表示让布局横行排列,这样RecyclerView就可以横向滚动了。
在这里插入图片描述

瀑布流布局

首先要调整一下fruit_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="5dp"><ImageViewandroid:id="@+id/fruitImage"android:layout_width="40dp"android:layout_height="40dp"android:layout_gravity="center_horizontal"android:layout_marginTop="10dp"/><TextViewandroid:id="@+id/fruitName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="left"android:layout_marginTop="10dp"/></LinearLayout>

然后修改MainActivity

class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingprivate val fruitList = ArrayList<Fruit>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)initFruits()val layoutManager = StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL)binding.recyclerView.layoutManager = layoutManagerval adapter = FruitAdapter(fruitList)binding.recyclerView.adapter = adapter}private fun initFruits(){repeat(2){fruitList.add(Fruit(getRandomLengthString("Apple"),R.drawable.apple_pic))fruitList.add(Fruit(getRandomLengthString("Banana"),R.drawable.banana_pic))fruitList.add(Fruit(getRandomLengthString("Orange"),R.drawable.orange_pic))fruitList.add(Fruit(getRandomLengthString("Watermelon"),R.drawable.watermelon_pic))fruitList.add(Fruit(getRandomLengthString("Pear"),R.drawable.pear_pic))fruitList.add(Fruit(getRandomLengthString("Grape"),R.drawable.grape_pic))fruitList.add(Fruit(getRandomLengthString("Pineapple"),R.drawable.pineapple_pic))fruitList.add(Fruit(getRandomLengthString("Strawberry"),R.drawable.strawberry_pic))fruitList.add(Fruit(getRandomLengthString("Cherry"),R.drawable.cherry_pic))fruitList.add(Fruit(getRandomLengthString("Mango"),R.drawable.mango_pic))}}private fun getRandomLengthString(str: String): String{val n = (1..20).random()val builder = StringBuilder()repeat(n){builder.append(str)}return builder.toString()}
}

onCreate()方法中创建⼀个StaggeredGridLayoutManager的实例。
StaggeredGridLayoutManager的构造函数接收两个参数:
第一个参数用于指定布局的列数,传入3表示会把布局分为3列;
第二个参数用于指定布局的排列方向,传入StaggeredGridLayoutManager.VERTICAL表示会让布局纵向排列。
最后把创建好的实例设置到RecyclerView当中就可以了
在这里插入图片描述

设置点击事件

只需修改FruitAdapterinCreateViewHolder方法的代码

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item,parent,false)val viewHolder = ViewHolder(view)viewHolder.itemView.setOnClickListener {val position = viewHolder.adapterPositionval fruit = fruitList[position]Toast.makeText(parent.context,"you clicked view ${fruit.name}",Toast.LENGTH_SHORT).show()}viewHolder.fruitImage.setOnClickListener{val position = viewHolder.adapterPositionval fruit = fruitList[position]Toast.makeText(parent.context,"you clicked image ${fruit.name}",Toast.LENGTH_SHORT).show()}return viewHolder
}

在这里插入图片描述

编写界面

制作9-Patch图片

普通图片作为背景时,就会被均匀拉伸,如下图所示,效果很不好。
在这里插入图片描述
9-Patch图片是一种被特殊处理过的png图片,能够指定哪些区域可以被拉伸、哪些区域不可以。
插入聊天框图片,右击选择下图中蓝色框的Creat 9-Patch file
在这里插入图片描述
进入界面后可以在图片的4个边框绘制⼀个个的小黑点

  • 在上边框和左边框绘制的部分表示当图片需要拉伸时就拉伸黑点标记的区域
  • 在下边框和右边框绘制的部分表示内容允许被放置的区域。

使用鼠标在图片的边缘拖动就可以进行绘制了,按住Shift键拖动可以进行擦除。
在这里插入图片描述
将新图片设为背景时:

在这里插入图片描述

编写聊天界面

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="vertical"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#d8e0e8"tools:context=".MainActivity"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><EditTextandroid:id="@+id/inputText"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:hint="Type someThing here"android:maxLines="2"/><Buttonandroid:id="@+id/send"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Send"/></LinearLayout></LinearLayout>

实体类Msg

class Msg(val content: String, val type: Int) {companion object{const val TYPE_RECEIVED = 0const val TYPE_SENT = 1}
}

msg_left_item.xml

<?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="wrap_content"android:padding="10dp"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="left"android:background="@drawable/message_left"><TextViewandroid:id="@+id/leftMsg"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="10dp"android:textColor="#fff"/></LinearLayout></FrameLayout>

msg_right_item.xml

msg_left_item.xml左右对称,不再赘述

适配器类MsgAdapter

class MsgAdapter(val msgList: List<Msg>) : RecyclerView.Adapter<RecyclerView.ViewHolder>(){inner class LeftViewHolder(view: View) : RecyclerView.ViewHolder(view){val leftMsg: TextView = view.findViewById(R.id.leftMsg)}inner class RightViewHolder(view: View) : RecyclerView.ViewHolder(view){val rightMsg: TextView = view.findViewById(R.id.rightMsg)}override fun getItemViewType(position: Int): Int {val msg = msgList[position]return msg.type}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = if (viewType == Msg.TYPE_RECEIVED){val view = LayoutInflater.from(parent.context).inflate(R.layout.msg_left_item,parent,false)LeftViewHolder(view)}else{val view = LayoutInflater.from(parent.context).inflate(R.layout.msg_right_item,parent,false)RightViewHolder(view)}override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {val msg = msgList[position]when (holder){is LeftViewHolder -> holder.leftMsg.text = msg.contentis RightViewHolder -> holder.rightMsg.text = msg.contentelse -> throw IllegalArgumentException()}}override fun getItemCount() = msgList.size}

根据不同的viewType创建不同的界面。
首先先定义了LeftViewHolderRightViewHolder这两个ViewHolder,分别用于缓存msg_left_item.xmlmsg_right_item.xml布局中的控件。
然后要重写getItemViewType()方法,并在这个方法中返回当前position对应的消息类型。

MainActivity

class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingprivate val msgList = ArrayList<Msg>()private var adapter: MsgAdapter ?= nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)initMsg()val layoutManager = LinearLayoutManager(this)binding.recyclerView.layoutManager = layoutManageradapter = MsgAdapter(msgList)binding.recyclerView.adapter = adapterbinding.send.setOnClickListener {val content = binding.inputText.text.toString()if (content.isNotEmpty()){val msg = Msg(content, Msg.TYPE_SENT)msgList.add(msg)adapter?.notifyItemInserted(msgList.size - 1)binding.recyclerView.scrollToPosition(msgList.size - 1)binding.inputText.setText("")}}}private fun initMsg(){val msg1 = Msg("Hello guy.",Msg.TYPE_RECEIVED)msgList.add(msg1)val msg2 = Msg("Hello. Who is that?",Msg.TYPE_SENT)msgList.add(msg2)val msg3 = Msg("This is Tom.",Msg.TYPE_RECEIVED)msgList.add(msg3)}}

在发送按钮的点击事件里获取EditText中的内容,如果内容不为空字符串,则创建⼀个新的Msg对象并添加到msgList列表中去。之后又调用了适配器的notifyItemInserted()方法,用于通知列表有新的数据插入,这样新增的⼀条消息才能够在RecyclerView中显示出来。接着调用RecyclerViewscrollToPosition()方法将显示的数据定位到最后一行,以保证一定可以看得到最后发出的⼀条消息。最后调用EditTextsetText()方法将输入的内容清空。
在这里插入图片描述
这个实践真的太妙了

Kotlin小课堂

延迟初始化

参考传送门
当你对一个全局变量使用了lateinit关键字时,请⼀定要确保它在被任何地方调用之前已经完成了初始化工作,否则Kotlin将无法保证程序的安全性。
在这里插入图片描述
对结果进行取反,如果还没有初始化,那么就立即对adapter变量进行初始化,否则什么都不用做。

密封类

在这里插入图片描述
在这里不得不再编写⼀个else条件,否则Kotlin编译器会认为这里缺少条件分支,代码无法编译通过。另外,如果现在新增了⼀个Unknown类并实现Result接口,用于表示未知的执行结果,但是忘记在getResultMsg()方法中添加相应的条件分支,编译器在这种情况下是不会提醒的,而是在运行的时候进入else条件里面,从而抛出异常并导致程序崩溃。

通过密封类就可以解决这个问题
关键字sealed cladd
在这里插入图片描述
when语句中传入一个密封类变量作为条件时,Kotlin编译器会自动检查该密封类有哪些子类,并强制要求你将每一个子类所对应的条件全部处理。这样就可以保证,即使没有编写else条件,也不可能会出现漏写条件分支的情况。而如果我们现在新增一个Unknown类,并也让它继承自Result,此时
getResultMsg()方法就一定会报错,必须增加一个Unknown的条件分支才能让代码编译通过。

密封类及其所有子类只能定义在同一个文件的顶层位置,不能嵌套在其他类中,这是被密封类底层的实现机制所限制的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/64259.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

类微信界面设计

类微信界面设计 v1.0 一、程序运行效果及界面二、实现代码1.xml文件界面设计2.class类文件控制3.主类 一、程序运行效果及界面 程序的界面包括上边框、下边框和中间的内容&#xff0c;上边框显示软件名“微信”&#xff0c;下边框内有四个部分&#xff0c;分别为“消息”、“好…

聊天界面的制作(一)——基本布局的实现

基本功能 1. 自定义标题栏。&#xff08;标题栏不做任何功能&#xff09; 2. 有左右发送按钮。&#xff08;这个只能自己和自己聊天哦&#xff0c;所以有左右发送按钮&#xff09; &#xff08;1&#xff09;点击左边按钮发送按钮&#xff0c;在ListView的左侧显示。   &…

AS中类微信界面设计

用Android studio编写类微信应用之基本界面 要编写一个类似微信那样的应用&#xff0c;第一步是写出一个类似那样的界面&#xff0c;之后的各种功能都是在完成界面之后实现的。完成之后界面大致是这样的。 XML文件的编写 要实现的功能是一个类似微信的界面&#xff0c;并且选…

移动开发一:类微信门户界面框架设计

设计目标&#xff1a; 本作业框架需要使用fragment&#xff0c;activity来实现一个类微信门户界面的设计。 功能说明&#xff1a; 这个框架需要设计出的要点有两点&#xff1a; 1. 顶部标识需要常驻 2. 底部按钮切换时可以变换样式或者颜色&#xff0c;同时主页面内容切换 代码…

安卓开发 微信ui界面设计 (Android Studio)

功能&#xff1a; 开发一个类似微信的主页面框架,UI布局为上中下结构,包含4个tab界面&#xff1a; 开发技术为&#xff1a; layout xml、控件、监听&#xff0c;fragment&#xff1b; 设计流程&#xff1a; 创建项目 改下项目名&#xff0c;编程语言为java UI界面 UI界面由多个…

移动开发技术一:微信门户界面设计

目录 一、设计目标二、功能说明三、代码解析1.顶部代码2.底部代码3.基本布局代码4.中部布局代码5.MainActivity.java 四、运行展示五、源码仓库地址Gitee 总结 一、设计目标 1&#xff09;完成类微信的门户页面框架设计&#xff0c;APP最少必须包含4个tab页面&#xff0c;框架设…

第004天:APP在平板上的UI布局设计

当今是移动设备发展非常迅速的时代&#xff0c;不仅手机已经成为了生活必需品&#xff0c;就连平板电脑也变 得越来越普及。平板电脑和手机最大的区别就在于屏幕的大小&#xff0c;一般手机屏幕的大小会在3英寸 到6英寸之间&#xff0c;而一般平板电脑屏幕的大小会在7英寸到10英…

android开发——类微信界面设计

功能要求 开发一个类微信的主界面框架&#xff0c;UI布局为上中下布局&#xff0c;包含4个tab界面&#xff0c;当点击选择底部部件的时候进行页面切换 开发技术 layout xml、控件、监听、fragment 页面设计 微信的界面布局分为上中下三个部分。 &#xff08;1&#xff09…

类微信界面设计1

1. 设计目标 实现一个类似微信的底部tab栏切换 2. 功能说明 一个类微信的app&#xff0c;现阶段完成功能为点击对应按钮能够切换到对应界面 3. 代码解析 1).头部 使用LinearLayout布局。 <?xml version"1.0" encoding"utf-8"?> <LinearLayo…

马原强化考点

马原核心考点 考点1 非重点 马克思主义的内涵和构成&#xff08;马克思主义是什么&#xff09; 内涵&#xff1a; 1.创立者 &#xff1a;马恩创立&#xff0c;后继者发展 2.内容 &#xff1a;自然&#xff0c;社会&#xff0c;人类思维&#xff08;唯物史观不包括自然&#xff…

[哲学部分]马克思主义基本原理概论思维导图

2020/3/3 更新 之前链接关了补一个 链接&#xff1a;https://pan.baidu.com/s/1tsmAkdRG7jE1eMz34Ea4qQ 提取码&#xff1a;7y2j 2019/10/23 更新 由于最近比较忙 没时间一一回复大家的评论和邮件&#xff0c;我把文件放到了百度云&#xff0c;大家自取 谢谢大家支持 链接&…

马原,期末复习部分知识点,思维导图

原链接&#xff1a;https://gitmind.cn/app/doc/39a4c3a6e1a3d7892c79030d028cadbf 感谢观看&#xff01;

马原复习思维导图-前三章

一天一个奇迹系列&#xff0c;这个思维导图做的时候很爽&#xff0c;然而并没有什么用 ……马原这种东西看不懂就是看不懂&#xff0c;要应付考试还是要专心听课555 绪论部分 第一章 第二章 第三章

《马克思主义基本原理》复习重点

什么是马克思主义&#xff1f; 1、马克思主义是马克思和恩格斯共同创立并为后继者所不断发展的科学理论体系。 2、马克思理论是关于科学、社会、人类思维发展一般规律的学说&#xff0c;是关于社会主义必将代替资本主义&#xff0c;最终实现共产主义的学说 3、马克思主义是关…

《马克思主义基本原理》复习整理

马原是我第一门认真学习的政治类课程&#xff0c;当然要记录一下。听说马原期末很难&#xff0c;所以认真看了下书和提纲&#xff0c;学习马原之后&#xff0c;对事物的本质有了更深的理解&#xff0c;理论知识也没有想象中那么晦涩难懂&#xff0c;顺便整理出以下的资料&#…

考研马原 知识点 做题技巧 同类比较 重要会议 1800易错题 思维导图整理

本文的思维导图将考研马原进行了整理并标记出重点内容&#xff0c;同时对于同类事件&#xff0c;按时间顺序的时间都进行了整理&#xff0c;而且对于1800中的易错题目等重点内容也有整理 思维导图源文件已经发布在我的资源当中&#xff0c;有需要的可以去 我的主页 了解更多学…

GPT对SaaS领域有什么影响?

GPT火了&#xff0c;Chat GPT真的火了。 突然之间&#xff0c;所有人都在讨论AI&#xff0c;最初的访客是程序员、工程师、AI从业者&#xff0c;从早高峰写字楼电梯里讨论声&#xff0c;到村里大爷们的饭后谈资&#xff0c;路过的狗子都要和它讨论两句GPT的程度。 革命的前夜…