主頁 > 移動端開發 > 《Android》Chap.4 UI開發

《Android》Chap.4 UI開發

2021-12-16 08:48:57 移動端開發

常用控制元件的使用方法

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文字內容\

實踐

<TextView
    android: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"

實踐

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

在這里插入圖片描述

點擊事件監聽器

函式式API方式

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override 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: ActivityMainBinding

    override 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最大行數當輸入內容超過規定的最大行數時就向上滾動,不會繼續拉伸

實踐

<EditText
    android: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: ActivityMainBinding

    override 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的目錄用于存放圖片,

放入圖片

<ImageView
    android: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

圓形進度條

放入進度條

<ProgressBar
    android: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

<ProgressBar
    android: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"
    >
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="button 1"
        />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:text="button 2"
        />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="button 3"
        />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentBottom="true"
        android:text="button 4"
        />

    <Button
        android: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"
    >

    <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/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/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>

在這里插入圖片描述

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">

    <Button
        android: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"
        />

    <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="#000"
        android:textSize="24sp"
        />

    <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/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 Activity
            activity.finish()
        }
        titleEdit.setOnClickListener {
            Toast.makeText(context, "You clicked Edit button", Toast.LENGTH_SHORT).show()
        }
    }

}

注意這里沒有使用 viewBinding
然后在布局檔案中添加這個自定義控制元件

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

在這里插入圖片描述

ListView

簡單用法

class MainActivity : AppCompatActivity() {
    private  lateinit var binding: ActivityMainBinding

    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)

        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">

    <ImageView
        android:id="@+id/fruitImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp"
        />

    <TextView
        android: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: ActivityMainBinding

    private 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: 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
        }else{
            view = convertView
            viewHolder = 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: ActivityMainBinding

    private 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

        binding.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.RecyclerView
    android: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: ActivityMainBinding
    private 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 = layoutManager
        
        val 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">

    <ImageView
        android:id="@+id/fruitImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="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>

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">

    <ImageView
        android:id="@+id/fruitImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="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 lateinit var binding: ActivityMainBinding
    private 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 = layoutManager

        val 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.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
}

在這里插入圖片描述

撰寫界面

制作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.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 someThing 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

class Msg(val content: String, val type: Int) {
    companion object{
        const val TYPE_RECEIVED = 0
        const 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"
    >

    <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: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.content
            is RightViewHolder -> holder.rightMsg.text = msg.content
            else -> 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: ActivityMainBinding
    private val msgList = ArrayList<Msg>()
    private var adapter: MsgAdapter ?= null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        initMsg()

        val layoutManager = LinearLayoutManager(this)
        binding.recyclerView.layoutManager = layoutManager
        adapter = MsgAdapter(msgList)
        binding.recyclerView.adapter = adapter
        binding.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的條件分支才能讓代碼編譯通過,

密封類及其所有子類只能定義在同一個檔案的頂層位置,不能嵌套在其他類中,這是被密封類底層的實作機制所限制的,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/382018.html

標籤:其他

上一篇:如何訪問手機瀏覽器cookies

下一篇:UI繪制流程之測量流程

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more