《第一行代码》第三版第四章

软件也要评脸蛋,UI开发的点点滴滴

    • 4.1 该如何编写程序界面
    • 4.2 常用控件的使用方法
      • 1. TextView
      • 2. Button
      • 3. EditText
      • 4. ImageView(i 大写!!!)
      • 5. ProgressBar
      • 6. AlertDialog
    • 4.3 详解3种基本布局
      • 1.LineaLayout
      • 2. RelativeLayout
      • 3.FrameLayout
    • 4.4 系统控件不过用?创建自定义控件
      • 1. 引入布局
      • 2. 创建自定义控件
    • 4.5最常用和最难用的控件:ListView
      • 1. ListView 的简单用法
      • 2. 定制ListView的界面
      • 3.提升ListView的运行效率
      • 4. ListView的点击事件
    • 4.6 更强大的滚动控件:RecycleView
      • 1. RecycleView的基本用法
      • 2. 实现横向滚动和瀑布流布局
      • 3.RecycleView 的点击事件
    • 4.7 编写界面的最佳实践
      • 1. 制作9-Patch 图片
      • 2.编写精美的聊天界面
    • 附录一:颜色查询

4.1 该如何编写程序界面

  1. 编写XML(书里使用此方法)
  2. 使用ConstrainLayou

4.2 常用控件的使用方法

1. TextView

主要用于显示文本信息

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

    <TextView
        android:id="@+id/textView"	//给当前控件定义一个为唯一一个标识符
        android:layout_width="match_parent"		//和父布局一样
        android:layout_height="wrap_content"//控件的大小刚刚好包含里面的内容
        android:gravity="center"
        // 指定文字的对齐方式 可选 top bottom start end center 等
  // | 指定多值    文中 center 等同于 center_vertical|center_horizontal
        android:text="This is TextView"/>
</LinearLayout>

TextView还有很多属性,需要的话前往阅读(太多属性还是建议直接百度想要的属性)
https://blog.csdn.net/lizijie7471619/article/details/51164564

2. Button

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"<Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        //android中默认按钮上英文字母大写   上行代码保留原始文字内容
        android:text="Button"/>
</LinearLayout>

小插曲:为button 的点击事件注册一个监听器

// 第一种函数式API注册监听器
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener{
        //在此添加
        }
    }
}
//使用实现接口的方式注册监听器
class MainActivity : AppCompatActivity(), View.OnClickListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener(this)
    }
    override fun onClick(v: View?) {
        when (v?.id){
            R.id.button ->{
                //在此添加
            }
        }
    }
}

3. EditText

用于与用户交互的控件,允许用户在控件里输入和编辑内容,并且可以对这些内容进行处理。

<EditText
        android:id="@+id/edittext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Type something here"	//显示提示性文字
        android:maxLines="2"/>	//最大限度为两行不会拉伸
//配合button使用 将输入内容传到button的toase
class MainActivity : AppCompatActivity(), View.OnClickListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener(this)
    }
    override fun onClick(v: View?) {
        when (v?.id){
            R.id.button ->{
                val inputText = edittext.text.toString()
                Toast.makeText(this,inputText,Toast.LENGTH_SHORT).show()
            }
        }
    }
}

4. ImageView(i 大写!!!)

在界面上展示图片的控件

<ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/img1" />	//用src属性给ImageView一张图片
//动态修改ImageView中的图片
override fun onClick(v: View?) {
        when (v?.id){
            R.id.button ->{
                imageView.setImageResource(R.drawable.ic_launcher_background)
//利用ImageView的SetImageResource()方法 对imageView对象经行修改
            }
        }
    }

5. ProgressBar

在界面上显示一个进度条,表示程序正在加载一些数据

<ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"
        //设置成水平进度条
        android:max="100" />	//进度条的最大值100
//利用getVisibility()方法判断进度条是否可见 显示与隐藏间切换
override fun onClick(v: View?) {
        when (v?.id){
            R.id.button ->{
                if (progressBar.visibility == View.VISIBLE){
                    progressBar.visibility = View.GONE
                }else{
                    progressBar.visibility = View.VISIBLE
                }
            }
        }
    }
//配合长进度条使用  每点击一次按钮 进度条进度加十 最大为100
override fun onClick(v: View?) {
        when (v?.id){
            R.id.button ->{
                progressBar.progress = progressBar.progress + 10
                }
            }
        }
    }

6. AlertDialog

提醒非常重要的内容或者警告信息

override fun onClick(v: View?) {
        when (v?.id){
            R.id.button ->{
                AlertDialog.Builder(this).apply {
                //AlertDialog.Builder()构建对话框
                //在apply中为对话框设置标题 内容 可否使用Back键关闭对话框
                    setTitle("this is Dialog")
                    setMessage("something important.")
                    setCancelable(false)
                    setPositiveButton("ok"){
                    //确认的点击事件
                        dialog, which ->
                    }
                    setNegativeButton("Cancle"){
                    //取消的点击事件
                        dialog , which ->
                    }
                    show()	//将对话框显示出来
                }
                }
            }
        }

4.3 详解3种基本布局

布局与控件的关系:
《第一行代码》第三版第四章_第1张图片

1.LineaLayout

又称线性布局, 有垂直和水平两种

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"  //默认为horizontal还可以选vertical
    //选择垂直和水平的值是都要注意控件是刚好包含内容还是和和父类匹配
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</LinearLayout>

layout_gravity: 决定按钮的位置

<Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top"
        android:text="Button 1"/>

android:layout_weight: 允许我们使用比例的方式来指定控件的大小

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <EditText
        android:id="@+id/input_message"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"	//表示水平方向平分宽度
        android:hint="Type something"/>
    <Button
        android:id="@+id/send"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"	//表示水平方向平分宽度
        android:text="Send" />
        //文本和按钮weight的“1”“1”表示各占1/2
</LinearLayout>

layout_weight: 先求出总权重,和屏幕适配,每个控件的大小 = 总长 * 比例

2. RelativeLayout

又称相对布局(常用布局)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="button 1"/>
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:text="button 2"/>
    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="button 3"/>
    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentBottom="true"
        android:text="button 4"/>
    <Button
        android:id="@+id/button5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:text="button 5"/>
</RelativeLayout>

效果图:
《第一行代码》第三版第四章_第2张图片
alignParentXXX :表示相对父类的位置

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android: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"/>
    <Button
        android: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"/>
    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="button 3"/>
    <Button
        android: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"/>
    <Button
        android: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>

效果图:
《第一行代码》第三版第四章_第3张图片
**layout_xxx="@+id/buttonx":**表示相对buttonx的相对位置

3.FrameLayout

又称帧布局 所有的控件默认摆放在布局的左上角

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

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:text="This is TextView"/>
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:text="Button"/>
</FrameLayout>

效果图:
《第一行代码》第三版第四章_第4张图片
其中,layout_gravity用法和LineaLayout的一样

4.4 系统控件不过用?创建自定义控件


所有的控件都是直接或者间接继承View的,所有的布局都是直接或者间接继承自ViewGroup的。

1. 引入布局

解决多个Activity采用同一个标题栏导致代码大量重复

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/title_bg">
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="5dp"
        android:background="@drawable/title_bg"
        android:text="Back"
        android:textColor="#fff"/>
        //定义一个back按钮 高宽恰好包住文本 文本颜色:白 
    <TextView
        android: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="#faf"
        android:textSize="24dp"/>
    <Button
        android:id="@+id/titleEdit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="5dp"
        android:background="@drawable/title_bg"
        android:text="Edit"
        android:textColor="#faf"/>
        //定义一个Edit按钮 高宽恰好包住文本  文本颜色白
</LinearLayout>
//activity_main中
<LinearLayout   xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <include layout="@layout/title" />
    //使用 include语句  引入标题栏
</LinearLayout>
//需要在MainActivity中隐藏自带的标题栏
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        supportActionBar?.hide()	
        //调用getSupportActionBar()  获得ActionBar实例 在调用它的hide()方法 将标题栏隐藏
    }
}

2. 创建自定义控件

解决每个Activity中都需要重新注册返回按钮的点击事件之类的事

//activity_main中
<LinearLayout   xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.uicustomviews.TitleLayout		//在布局文件中添加自定义控件
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>
</LinearLayout>

//MainActivity 中
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        supportActionBar?.hide()	// 隐藏系统自带的标题栏
           }
}
class TitleLayout(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
    init {		//定义一个结构体 对标题栏布局进行动态加载
        LayoutInflater.from(context).inflate(R.layout.title, this)
        //LayoutInflater.from(context)构建一个LayoutInflate对象
        //然后调用inflate方法 动态加载一个布局文件

//以下为按钮的点击事件注册
        titleBack.setOnClickListener{
            val activity = context as Activity
            activity.finish()
            //TitleLayout接受的context是一个Activity的实例  先将其转成Activity类型  调用当前的Activity的finish()销毁
        }
        titleEdit.setOnClickListener{
            Toast.makeText(context,"You clicked Edit button",Toast.LENGTH_SHORT).show()
            //定义一个Toast 文本为‘You clicked Edit button’
        }

    }

}

4.5最常用和最难用的控件:ListView

1. ListView 的简单用法

class MainActivity : AppCompatActivity() {
    private 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)
        setContentView(R.layout.activity_main)
        //使用ArrayAdapter适配器将数据传给ListView
        val adapter = ArrayAdapter<String>	(this,android.R.layout.simple_list_item_1,data)
        //ArrayAdapter<泛型>	(Activity实例,ListView的子项布局的id,数据源)
        listView.adapter = adapter	//最后调用ListView的SetAdapter()方法,将构建好的适配器对象传递出去
        
    }
}

2. 定制ListView的界面

使ListView的界面显示更丰富的内容

package com.example.testlistview

import android.app.Activity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.ListView
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
import java.text.FieldPosition

class MainActivity : AppCompatActivity() {
    private val fruitList = ArrayList<Fruit>()
	//创建一个ArrayList实例 在initFruit()传入数据 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initFruits()        //该方法初始化水果数据
        val adapter = FruitAdapter(this, R.layout.fruit_item, fruitList)
        listView.adapter = adapter  //注意这里的list是小写的l
    }
    private fun initFruits(){   //
        repeat(2){
            //repeat函数是kotlin中的一个标准函数
            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("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))
            //将数据传入
        }
    }
}
class Fruit(val name:String,val imagedId:Int)
//定义一个实体类 name和imageid两个字段
class FruitAdapter(activity: Activity,val  resourId:Int,data: List<Fruit>)
    :ArrayAdapter<Fruit>(activity,resourId,data){
    //创建FruitAdapter类(自定义适配器) 继承ArrayAdapter
    //定义主构造函数 传递Activity实例 ListView 子项布局的id 和数据源
    override fun getView(position: Int,convertView: View?,parent:ViewGroup):View{
        val view = LayoutInflater.from(context).inflate(resourId,parent,false)
        /*使用LayoutInflater加载布局 inflate接受三个参数  
        第一个 加载的布局文件id  第二个是给加载好的布局添加一个父布局
        第三个指定成false  表示让父布局声明的layout属性失效
        */
        val fruitImage:ImageView =view.findViewById(R.id.fruitName)
        val fruitName :TextView = view.findViewById(R.id.fruitName)
        val fruit = getItem(position)
        if(fruit != null){
            fruitImage.setImageResource(fruit.imagedId)
            fruitName.text = fruit.name
        }
        return view
    }
}

3.提升ListView的运行效率

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:  View
        //在getView中判断 convertView 提高效率
        if (convertView == null) {
            view = LayoutInflater.from(context).inflate(resourceId,parent,false)
         }else{ view = convertView
         }
         val fruitImage: ImageView = view.findViewById(R.id.fruitImage)
         val fruitName: TextView = view.findViewById(R.id.fruitName)
         val fruit = getItem(position) //获取当前的Fruit实例
        	if (fruit != null){
          	fruitImage.setImageResource(fruit.imageId)
            fruitName.text = fruit.name
        }
        return view
    }
}

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:  View
        val viewHolder :ViewHolder
        if (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
            //创建一个ViewHolder对象 将控件的实例存放进去  最后用setTag()存储在view中
        }else{
            view = convertView
            viewHolder = view.tag as ViewHolder
            //调用getTag() 把ViewHolder重新取出
        }
        val fruit = getItem(position)
        if (fruit != null){
            viewHolder.fruitImage.setImageResource(fruit.imagedId)
            viewHolder.fruitName.text = fruit.name
        }
        return view
    }
}

4. ListView的点击事件

class MainActivity : AppCompatActivity() {
    private val fruitList = ArrayList<Fruit>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initFruits()
        val adapter = FruitAdapter(this, R.layout.fruit_item, fruitList)
        listView.adapter = adapter
        listView.setOnItemClickListener{
        //注册监听器 通过position参数判断用户点击的子项 四个参数 除了position 可以用_省略 不过位置不变
        _, _, position, _ ->
        val fruit = fruitList[position]
        Toast.makeText(this,fruit.name,Toast.LENGTH_SHORT).show()
    }
}

    private fun initFruits(){   //
        repeat(2){
            //repeat函数是kotlin中的一个标准函数
            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("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))

        }
    }
}

4.6 更强大的滚动控件:RecycleView

升级版ListView

1. RecycleView的基本用法

为了让所有版本的android都可以使用 需要添加RecycleView库的依赖,如图:

《第一行代码》第三版第四章_第5张图片
下面分别是activity_main FruitAdapter Fruit Mainactivity 的代码


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

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

</LinearLayout>
package com.example.recyclerview

import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater

class FruitAdapter(val fruitList: List<Fruit>) : RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
//新建一个FruitAdapter类 为RecycleView准备一个适配器
    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val fruitImage: ImageView = view.findViewById(R.id.fruitImage)
        val fruitName: TextView = view.findViewById(R.id.fruitName)
    }
	/重写onCreateViewHolder  用于创建ViewHolder的实例
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitAdapter.ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
        return ViewHolder(view)
    }
	//重写onBindViewHolder 用于子项数据的赋值
    override fun onBindViewHolder(holder: FruitAdapter.ViewHolder, position: Int) {
        val fruit = fruitList[position]
        holder.fruitImage.setImageResource(fruit.imageId)
        holder.fruitName.text = fruit.name
    }
    override fun getItemCount() = fruitList.size
    //用于告诉RecycleView数据一共有多少子项   直接返回数据源长度即可
}

package com.example.recyclerview
class Fruit(val name:String,val imageId:Int)
package com.example.recyclerview

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fruit_item.view.*

class MainActivity : AppCompatActivity() {
    private val fruitList = ArrayList<Fruit>()
	
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initFruits()	//初始化水果数据
        val layoutManager = LinearLayoutManager(this)
        //LinearLayoutManager是线性布局的意思 实现与ListView类似的效果
        recyclerView.layoutManager = layoutManager
        val adaper = FruitAdapter(fruitList)
        //创建FruitAdapter实例 并且将水果数据传入FruitAdapter的构造函数中
        recyclerView.adapter = adaper
} 

    private fun initFruits(){   //
        repeat(2){
            //repeat函数是kotlin中的一个标准函数
            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("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))

        }
    }
}

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

1.横向滚动

//fruit_item中:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="80dp"
android:layout_height="wrap_content">
//LinearLayout 改成垂直方向排列 宽度为80dp(更雅观 不然有大有大小)
<ImageView
    android:id="@+id/fruitImage"
    android:layout_width="40dp"
    android:layout_height="40dp"
    android:layout_gravity="center_horizontal" //设置为在布局中水平居中
    android:layout_marginLeft="10dp"/>	//为了让图片保持距离
<TextView
    android:id="@+id/fruitName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"//设置为在布局中水平居中
    android:layout_marginTop="10dp"/>	//为了让图片保持距离
</LinearLayout>

//MainActivity中的部分文件:
class MainActivity : AppCompatActivity() {
    private val fruitList = ArrayList<Fruit>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initFruits()
        val layoutManager = LinearLayoutManager(this)
        layoutManager.orientation = LinearLayoutManager.HORIZONTAL
        //使用layoutManager的setOrientation() 让布局横向排列
        recyclerView.layoutManager = layoutManager
        val adaper = FruitAdapter(fruitList)
        recyclerView.adapter = adaper
}
  1. 瀑布流布局
//适当修改  使得瀑布流布局更美观
<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">  //这里

    <ImageView
        android:id="@+id/fruitImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginLeft="10dp"/>
    <TextView
        android: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 val fruitList = ArrayList<Fruit>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initFruits()
        val layoutManager = StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL)
        //创建一个StaggeredGridLayoutManager   该构造函数中 第一个参数用于指定布局的列数 第二个 用于指定布局的排列方式
        recyclerView.layoutManager = layoutManager
        val adaper = FruitAdapter(fruitList)
        recyclerView.adapter = adaper
}

    private fun initFruits(){   //
        repeat(2){
            //repeat函数是kotlin中的一个标准函数
            /*为了显示瀑布流布局的效果  定义getRandomLengthString函数  调用Range对象的random()函数
            来创造一个 随机数  使得各个水果的名字的长度随机增倍 且 差异较大*/
            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("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()
    }
}

3.RecycleView 的点击事件

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): FruitAdapter.ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
        // 创建一个viewHolder实例  然后对最外层布局(itemView)和imageView注册点击事件
        val viewHolder = ViewHolder(view)
        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
    }

    override fun onBindViewHolder(holder: FruitAdapter.ViewHolder, position: Int) {
        val fruit = fruitList[position]
        holder.fruitImage.setImageResource(fruit.imageId)
        holder.fruitName.text = fruit.name
    }
    override fun getItemCount() = fruitList.size
}

4.7 编写界面的最佳实践

1. 制作9-Patch 图片

可以直接看以下链接:
https://www.jianshu.com/p/9fe0b3d9a1dd

上文中还是比较繁琐的 我的理解是:可以当成聊天气泡 你输入的字多 气泡越长或者越宽 然后你该做的就是锁定一个区域让它可以自动伸缩(只是你指定的区域可以,其他区域不行) 就像你气泡的边框一样

2.编写精美的聊天界面

//activity_main文件中:
<?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="match_parent"
    android:background="#d8e0e8">
    <android.recycleview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:id="@+id/inputText"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="Type somgthing here"
            android:maxLines="2"/>
        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Send"/>
    </LinearLayout>
</LinearLayout>

//创建Msg类:
package com.example.uibestpractice

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

//msg_left_item文件中:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:background="@drawable/message_left">
        <TextView
            android:id="@+id/leftMsg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textColor="#fff"/>
    </LinearLayout>
</FrameLayout>

//msg_right_item文件中:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:background="@drawable/message_right">
        <TextView
            android:id="@+id/rightMsg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="10dp"
            android:textColor="#1000"/>
    </LinearLayout>

</FrameLayout>

//创建MsgAdapter类:
package com.example.uibestpractice

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.os.persistableBundleOf
import androidx.recyclerview.widget.RecyclerView

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.leftMsg)
    }

    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.content
            is RightViewHolder-> holder.rightMsg.text = msg.content
        }
    }

    override fun getItemCount() = msgList.size
}


//MainActivity中:
//package com.example.uibestpractice
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() ,View.OnClickListener{
    private val msgList = ArrayList<Msg>()

    private var adapter: MsgAdapter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initMsg()
        val layoutManager = LinearLayoutManager(this)
        recyclerView.layoutManager = layoutManager
        adapter = MsgAdapter(msgList)
        recyclerView.adapter = adapter
        send.setOnClickListener(this)
    }
    override fun onClick(v: View?){
        when(v){
            send -> {
                val content = inputText.text.toString()
                if (content.isNotEmpty()){
                    val msg = Msg(content,Msg.TYPE_SEND)
                    msgList.add(msg)
                    adapter?.notifyItemInserted(msgList.size - 1 )
                    //当有新消息时 刷新RecyclerView中的显示
                    recyclerView.scrollToPosition(msgList.size - 1)
                    //将RecyclerView定位到最后一行
                    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_SEND)
        msgList.add(msg2)
        val msg3 = Msg("This is Tom.Nice talking to you ",Msg.TYPE_RECEIVED)
        msgList.add(msg3)

    }

}

标准函数repeat:
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/repeat.html

附录一:颜色查询

感叹号前面为代码
#FFFFE0!–亮黄色 -->

#FFFF00!–黄色 -->

#FFFAFA!–雪白色 -->

#FFFAF0!–花白色 -->

#FFFACD!–柠檬绸色 -->

#FFF8DC!–米绸色 -->

#FFF5EE!–海贝色 -->

#FFF0F5!–淡紫红 -->

#FFEFD5!–番木色 -->

#FFEBCD!–白杏色 -->

#FFE4E1!–浅玫瑰色 -->

#FFE4C4!–桔黄色 -->

#FFE4B5!–鹿皮色 -->

#FFDEAD!–纳瓦白 -->

#FFDAB9!–桃色 -->

#FFD700!–金色 -->

#FFC0CB!–粉红色 -->

#FFB6C1!–亮粉红色 -->

#FFA500!–橙色 -->

#FFA07A!–亮肉色 -->

#FF8C00!–暗桔黄色 -->

#FF7F50!–珊瑚色 -->

#FF69B4!–热粉红色 -->

#FF6347!–西红柿色 -->

#FF4500!–红橙色 -->

#FF1493!–深粉红色 -->

#FF00FF

#FF00FF!–红紫色 -->

#FF0000–红色 -->

以下是一些个人在学习过程中的经验或者说犯的错误
1.

//activity_main文件
<?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.uicustomviews.TitleLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>
  1. 看书过程在自己编写代码的时候很容易就把大小写看错 还有多或者少了字母,程序错误时候先检查这个。

你可能感兴趣的:(第一行源代码,android)