•前言
常用控件和布局的繼承結構,如下圖所示:
可以看到,我們所用的所有的控件都是直接或者間接的繼承自View的;
所用的所有布局都是直接或者間接繼承自ViewGroup的;
View 是 Android 中最基本的一種 UI 組件,它可以在屏幕上繪制一塊矩形區域,並能相應這塊區域的各種事件;
因此,我們使用的各種控件其實是在 View 的基礎之上又添加了各自特有的功能;
而 ViewGroup 是一種特殊的 View,他可以包含很多 View 和子 ViewGroup,是一個用於放置控件和布局的容器;
•引入布局——創建自定義標題欄
我們先來看一下標題欄的樣式:
面對這種標題欄的樣式,只需要加入兩個 Button 和一個 TextView,然后在布局中擺放好就可以了。
可是這樣做卻存在着一個問題,一般我們的程序中可能有很多個活動都需要這樣的標題欄;
如果在每個活動的布局中都編寫一遍同樣的標題欄代碼,明顯就會導致代碼的大量重復;
這個時候,我們就可以使用引入布局的方式來解決這個問題;
新建一個布局 title.xml,添加代碼如下:
<?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" android:orientation="horizontal" android:padding="10dp"> <Button android:id="@+id/title_back" android:layout_width="wrap_content" android:layout_height="50dp" android:text="Back" android:textAllCaps="false" android:textSize="20sp"/> <TextView android:id="@+id/title_text" android:layout_width="0dp" android:layout_height="50dp" android:layout_weight="1" android:gravity="center" android:text="Title Text" android:textSize="20sp" /> <Button android:id="@+id/title_edit" android:layout_width="wrap_content" android:layout_height="50dp" android:text="Edit" android:textAllCaps="false" android:textSize="20sp"/> </LinearLayout>可以看到,我們在 LinearLayout 中分別加入了兩個 Button 和一個 TextView;
左邊的 Button 可以用於返回,右邊的 Button 可用於編輯,中間的 TextView 則可以顯示一段標題文本;
•自定義標題欄的使用
現在標題欄布局已經編寫完成了,剩下的就是如何在程序中使用這個標題欄了;
修改 activity_main.xml 中的代碼,如下所示:
<?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"/> </LinearLayout>我們只需要一行 include 語句將標題欄布局引入進來就可以了。
最后別忘了在 MainActivity.java 中將系統自帶的標題欄隱藏掉,代碼如下:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ActionBar actionbar = getSupportActionBar(); if(actionbar != null) actionbar.hide(); } }通過調用 getSupportActionBar() 方法來獲得 ActionBar 的實例;
然后調用 ActionBar 的 hide() 方法將系統自帶的標題欄隱藏。
•創建自定義控件
引入布局的技巧確實解決了重復編寫布局代碼的問題;
但是如果布局中有一些控件要求能夠響應事件,我們還是需要在每個活動中為這些控件單獨編寫一次實踐注冊的代碼;
比如說標題欄中的返回按鈕,其實不管是在哪一個活動中,這個按鈕的功能都是相同的,即銷毀當前活動;
而如果在每一個活動中都需要重新注冊一遍返回按鈕的點擊事件,無疑會增加很多重復的代碼;
這種情況下最好使用自定義控件的方式來解決;
新建 TitleLayout.java 文件,並繼承自 LinearLayout,讓它成為我們自定義的標題欄控件,添加代碼如下:
public class TitleLayout extends LinearLayout { public TitleLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.title,TitleLayout.this);
}首先我們重寫了 LinearLayout 中帶有兩個參數的構造函數;
在布局中引入 TitleLayout 控件就會調用這個構造函數;
然后在構造函數中需要對標題欄布局進行動態加載,這就要借助 LayoutInflater 來實現;
通過 LayoutInflater 的 from() 方法可以構建出一個LayoutInflater 對象。
然后調用 inflate() 方法動態加載一個布局文件。
inflate() 方法接收兩個參數:
- 第一個參數是要加載的布局文件的 id
- 第二個參數是給加載好的布局再添加一個父布局
•自定義控件的使用
現在自定義控件已經創建好了,然后我們需要在布局文件中添加這個自定義控件;
修改 activity_main.xml 中的代碼,如下所示:
<?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"> <com.example.uicustomviews.TitleLayout android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>添加自定義控件和添加普通控件的方式基本上是一樣的;
只不過在添加自定義控件的時候,我們需要指明控件的完整類名,包名在這里是不可以省略的。
下面我們嘗試為標題欄中的按鈕注冊點擊事件,修改 TitleLayout 中的代碼,如下所示:
public class TitleLayout extends LinearLayout { public TitleLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.title,TitleLayout.this); Button back = findViewById(R.id.title_back); back.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { //為 back 按鈕設置點擊事件 } }); Button edit = findViewById(R.id.title_edit); edit.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { //為 edit 按鈕設置點擊事件 } }); } }