Android開發(fā)學(xué)習(xí)教程(17)- Android自定義控件
—— 你不能把這個世界讓給你所鄙視的人
前幾篇我們講了基本控件和常用布局的基本用法,這里來從整體上看看他們的繼承關(guān)系。
可以看到,我們所用的控件最終都是繼承自View,View是Android中最基本的用戶界面組件,它占據(jù)屏幕上的一塊矩形區(qū)域,并且負責(zé)繪圖和事件處理。我們所用的常用布局是繼承自ViewGroup,ViewGroup是一種特殊的View,它包含了多個擁有LayoutParams屬性的View。
引入布局
如果你經(jīng)常使用APP你會發(fā)現(xiàn),很多APP的界面頂部都會有一個標(biāo)題欄,標(biāo)題欄左右是一個返回圖標(biāo),中間是當(dāng)前頁面的名字。對于這種多個頁面都有的樣式,如果在每個布局中都編寫一遍同樣的標(biāo)題欄樣式,明顯就會導(dǎo)致代碼的大量重復(fù)并且不好維護,比如以后改變需求,需要在最右邊增加一個分享圖標(biāo),那豈不是每個都要去修改一遍。這個時候我們可以使用引入布局的方式來解決這個問題,新建一個布局?layout_mytitle.xml,代碼如下:
<?
xml
?version
=
"1.0"
?encoding
=
"utf-8"
?>
<
androidx.constraintlayout.widget.ConstraintLayout
?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"
????
android:layout_height
=
"match_parent"
????
tools:context
=
".MainActivity"
>
????
<
FrameLayout
????????
android:layout_width
=
"match_parent"
????????
android:layout_height
=
"wrap_content"
????????
app:layout_constraintEnd_toEndOf
=
"parent"
????????
app:layout_constraintStart_toStartOf
=
"parent"
????????
app:layout_constraintTop_toTopOf
=
"parent"
>
????????
<
ImageView
????????????
android:id
=
"@+id/img_back"
????????????
android:layout_width
=
"wrap_content"
????????????
android:layout_height
=
"wrap_content"
????????????
android:padding
=
"10dp"
????????????
android:src
=
"@drawable/back_black1"
?/>
????????
<
TextView
????????????
android:id
=
"@+id/tv_title"
????????????
android:layout_width
=
"wrap_content"
????????????
android:layout_height
=
"wrap_content"
????????????
android:layout_gravity
=
"center"
????????????
android:text
=
"標(biāo)題"
????????????
android:textColor
=
"#222222"
????????????
android:textSize
=
"16sp"
?/>
????
</
FrameLayout
>
</
androidx.constraintlayout.widget.ConstraintLayout
>
然后在每個需要用到此標(biāo)題欄的布局里引入就可以了,如下:
<?
xml
?version
=
"1.0"
?encoding
=
"utf-8"
?>
<
androidx.constraintlayout.widget.ConstraintLayout
?xmlns:android
=
"http://schemas.android.com/apk/res/android"
????
xmlns:tools
=
"http://schemas.android.com/tools"
????
android:layout_width
=
"match_parent"
????
android:layout_height
=
"match_parent"
????
tools:context
=
".MainActivity"
>
????
<
include
?layout
=
"@layout/layout_mytitle"
?/>
</
androidx.constraintlayout.widget.ConstraintLayout
>
運行看看效果:
創(chuàng)建自定義控件
引入布局的技巧確實解決了重復(fù)編寫布局代碼的問題,但是如果布局中有一些控件要求能夠響應(yīng)事件,我們還需要在每個Activity中為這些控件單獨編寫一次事件注冊的代碼。比如說標(biāo)題欄中的返回圖標(biāo),其實不管是在哪一個活動中,這個按鈕的功能都是相同的,即退出當(dāng)前活動。而如果在每一個活動中都需要重新注冊一遍返回按鈕的點擊事件,無疑又是增加了很多重復(fù)代碼,這種情況最好是使用自定義控件的方式來解決。
新建 MyTitleLayout 繼承自 FramLayout,讓它成為我們自定義的標(biāo)題欄控件,代碼如下:
import
?android.content.Context;
import
?android.util.AttributeSet;
import
?android.view.LayoutInflater;
import
?android.view.View;
import
?android.widget.FrameLayout;
import
?android.widget.Toast;
public
?class
?MyTitleLayout?
extends
?FrameLayout {
????
public
?MyTitleLayout(Context context, AttributeSet attr) {
????????
super
(context, attr);
????????
View view = LayoutInflater.from(context).inflate(R.layout.layout_mytitle,?
this
);
????????
view.findViewById(R.id.img_back).setOnClickListener(
new
?OnClickListener() {
????????????
@Override
????????????
public
?void
?onClick(View v) {
????????????????
Toast.makeText(context,?
"點擊了返回圖標(biāo)"
, Toast.LENGTH_SHORT).show();
????????????
}
????????
});
????
}
}
首先我們重寫了 FrameLayou中的帶有兩個參數(shù)的構(gòu)造函數(shù),這樣我們在布局中引入 MyTitleLayout控件就會調(diào)用這個構(gòu)造函數(shù)。然后在構(gòu)造函數(shù)中需要對標(biāo)題欄布局進行動態(tài)加載,這就要借助 LayoutInflater 來實現(xiàn)了。通過 LayoutInflater 的 from()方法可以構(gòu)建出一個LayoutInflater 對象,然后調(diào)用 inflate()方法就可以動態(tài)加載一個布局文件,inflate()方法接收兩個參數(shù),第一個參數(shù)是要加載的布局文件的 id,這里我們傳入 R.layout.layout_mytitle,第二個參數(shù)是給加載好的布局添加一個父布局,這里我們想要指定為 MyTitleLayout,于是直接傳入 this。
現(xiàn)在自定義控件已經(jīng)創(chuàng)建好了,然后我們需要在布局文件中添加這個自定義控件,修改activity_main.xml:
<?
xml
?version
=
"1.0"
?encoding
=
"utf-8"
?>
<
androidx.constraintlayout.widget.ConstraintLayout
?xmlns:android
=
"http://schemas.android.com/apk/res/android"
????
xmlns:tools
=
"http://schemas.android.com/tools"
????
android:layout_width
=
"match_parent"
????
android:layout_height
=
"match_parent"
????
xmlns:app
=
"http://schemas.android.com/apk/res-auto"
????
tools:context
=
".MainActivity"
>
????
<
com.example.myapplication1.MyTitleLayout
????????
android:layout_width
=
"match_parent"
????????
android:layout_height
=
"wrap_content"
????????
app:layout_constraintEnd_toEndOf
=
"parent"
????????
app:layout_constraintStart_toStartOf
=
"parent"
????????
app:layout_constraintTop_toTopOf
=
"parent"
?/>
</
androidx.constraintlayout.widget.ConstraintLayout
>
添加自定義控件和添加普通控件的方式基本是一樣的,只不過在添加自定義控件的時候我們需要指明控件的完整類名,包名在這里是不可以省略的。
重新運行程序,你會發(fā)現(xiàn)此時效果和使用引入布局方式的效果是一樣的。
源碼鏈接:https://yunjunet.cn/876774.html