1. 程式人生 > 實用技巧 >下導航欄3-封裝例項講解

下導航欄3-封裝例項講解

效果圖:

1:先分析裡面要的組合方式,是上面一個圖片,下面一個文字,我們可以用自定義LInearLayout來實現是比較容易的:

自定義的方法看上兩篇前,

我們需要的屬性,但是系統又沒有提供的,或者系統沒能夠滿足我們的就可以自己去新增,新增的方法看上兩篇前的,

這個是對每一個按鈕的自定義的屬性:當點下一個按鈕是上一個按鈕的選中狀態就需要恢復為未選中狀態,在這我是用兩

組不同的照片來進行交換實現的,就是正常時的圖片normalIcon,和選中時的圖片selectedIcon,同樣文字(title)的顏色的變化也有兩種,

misselected是記錄按鈕是否被點選時的狀態,

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Bottom_bar">
<attr name="normalIcon" format="reference"/>
<attr name="selectedIcon" format="reference"/>
<attr name="title" format="string"/>
<attr name="hightlighttitle" format="string"/>
<attr name="color" format="color"/>
<attr name="hightlightcolor" format="color"/>
<attr name="misselected" format="boolean"/>
</declare-styleable>
<declare-styleable name="myViewGroup">
<attr name="number" format="integer"/>
</declare-styleable>
</resources>

2:解析屬性以得到自定義的屬性,在解析屬性之後就可以將屬性取出應用了,我們需要xml的配置,

也需要程式碼的配置,所以在兩個構造方法中都需要初始化:方法看上兩篇

constructor(context: Context):super(context){
Log.v("lfl","程式碼")
initView() 初始化圖片與文字
}
constructor(context: Context,attributeSet: AttributeSet?):super(context,attributeSet){

//獲取自定義的屬性
var typedArray = context.obtainStyledAttributes(attributeSet,
R.styleable.Bottom_bar)
//獲取單個的屬性
normalIconID = typedArray.getResourceId(R.styleable.Bottom_bar_normalIcon,
R.drawable.home)
selectedIconID = typedArray.getResourceId(R.styleable.Bottom_bar_selectedIcon,
R.drawable.home_selected)
title = typedArray.getString(R.styleable.Bottom_bar_title).toString()
Log.v("lfl","xml")
//初始化檢視
initView()
//要改變的值
misselected = typedArray.getBoolean(R.styleable.Bottom_bar_misselected,false)
textColor = typedArray.getColor(R.styleable.Bottom_bar_color,Color.BLACK)
//釋放
typedArray.recycle()

}

3:將解析出來的各種屬性用起來,初始化圖片與文字

用到的方法有:

(1):轉換畫素:

//獲取手機的畫素
fun dpTopx(dp:Int):Int = dp*(context.resources.displayMetrics.density).toInt()

(2):更新圖片與文字 ,我們需要在多個地方更新,所以將其抽出

private fun UpdateUI(){
    Log.v("lfl","******************************")
if (misselected){
//切換成沒有被選中的狀態
image?.setImageResource(selectedIconID)
textView?.setTextColor(Color.RED)
}else{
//選中狀態
image?.setImageResource(normalIconID)
textView?.setTextColor(Color.BLACK)
}
}
//初始化
fun initView(){
orientation = VERTICAL 設定內容的填充方向
gravity = Gravity.CENTER_VERTICAL
//新增圖片
image = ImageView(context).apply {
var layoutParams = LinearLayout.LayoutParams(dpTopx(32),dpTopx(32)) 設定圖片的大小,
addView(this,layoutParams)
}

//新增文字
textView = TextView(context).apply {
//setText(title)
//text = title
}.also {
val layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT)
addView(it,layoutParams)
}

UpdateUI()
}

4:改變按鈕的點選狀態,當我們點選一個按鈕時,若該按鈕是點選狀態則不響應,若該按鈕沒有被點選時

則將該按鈕設定為被點選狀態,並且更新圖片與文字,對於一個按鈕而言只需要這樣,點選事件可以在點下去時

實現,也可以在手離開螢幕那一刻時實現,這是在點下去時實現的,並且這個事件已經被消費了,所以返回true

override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event?.action == MotionEvent.ACTION_DOWN){
if (!misselected){
//改變狀態
misselected = !misselected
selectCallback?.let {
it(index) //呼叫高階函式,點下去時改變狀態的同時要將上一個被點選
的按鈕還原
}
}

}
//事件已經被消費了就返回true
return true
}

只要狀態改變,那麼就更新資料,因此可以重寫misselected的set方法,

//記錄按鈕被點選的狀態
var misselected:Boolean = false
set(value) {
field = value
Log.v("lfl","misselected")
UpdateUI()
}

一個按鈕實現,接下來就實現多個按鈕,多個按鈕也可以通過自定義linearLayout來實現,一個按鈕裡面初始化的是圖片和文字,
多個按鈕的實現,就是將多個按鈕放在一個容器裡面,初始化按鈕即可
(1)xml裡面的應用:
<com.example.myapplication.myViewGroup
android:id="@+id/myviewGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:number="3" 有多少個按鈕,是個自定義屬性
/>
(2)初始化按鈕,程式碼,xml皆要初始化
 constructor(context: Context):super(context){
//初始化
initView()
}
constructor(context: Context,attributeSet: AttributeSet):super(context,attributeSet){
//解析
val typedArray = context.obtainStyledAttributes(attributeSet,
R.styleable.myViewGroup)
number = typedArray.getInteger(R.styleable.myViewGroup_number,4)
typedArray.recycle()
initView()
}

//初始化每一個檢視
private fun initView() {
orientation = HORIZONTAL
gravity = Gravity.CENTER_VERTICAL
for (i in 0 until number){
Bottom_bar(context).also {
val lp = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
weight = 1f
}
addView(it,lp)
Bottom_bar_array.add(it)

//為每一個按鈕新增監聽事件
it.selectCallback = {index->
//還原之前選中的欄目的狀態
Bottom_bar_array[current].misselected = false
//儲存索引
current = index

}
}
}
}
(3):通過一個模型將資料從外部傳進來
a:需要先定義一個模型,模型裡面放的全是屬性,模型就作為傳輸資料的中間人,外部將資料給模型,模型在將資料傳給物件
data class Bottom_bar_model (
var normalIconID:Int,
var selectedIconID:Int,
var title:String,
var misselected:Boolean
)

b:將通過模型的傳輸進來的資料給到每一個按鈕
//更新每一個檢視
private fun initBottom_bar(){
for ((i,iteam) in Bottom_bar_array.withIndex()){
iteam.index = i
iteam.title = model[i].title
iteam.normalIconID = model[i].normalIconID
iteam.selectedIconID = model[i].selectedIconID
iteam.misselected = model[i].misselected
}


在外部呼叫時就只需要將資料給模型就好了
myviewGroup.model = arrayOf(
Bottom_bar_model(
R.drawable.home,
R.drawable.home_selected,
"主頁",
true
),
Bottom_bar_model(
R.drawable.home,
R.drawable.home_selected,
"主頁",
false
),
Bottom_bar_model(
R.drawable.home,
R.drawable.home_selected,
"主頁",
false
)
)


GitHub連線:https://github.com/luofangli/Bottom_bar