Android自定義view組合控制元件解析
阿新 • • 發佈:2019-02-06
##使用場景
在開發中,或許一個業務需求中會出現很多系統控制元件組合成的佈局,並且經常需要複用。
比如下圖中 qq或者微博的title欄,在一款app中,可能不同的介面 類似的view要出現很多次,這個時候 就可以設計自己的view控制元件,就稱為組合控制元件吧。
建立靈活的模板,通過呼叫自己的view,提高開發效率、降低耦合度。好處不言而喻
這邊以微博的title為例,建立一個組合控制元件
首先分析一下,上圖微博的當前頁面似乎包含了三四個控制元件,具體看你要怎麼做了:
ImageButton、TextView+DrwableRight、ImageButton
或者是 ImageButton、TextView+ImageView、ImageButton。
這邊以三個控制元件為例,建立一個佈局,在裡面新增三個控制元件,然後控制其屬性
建立控制元件
- 為控制元件新增自定義屬性:
在資原始檔res-values資料夾下建立一個xml檔案attrs,這時候可以考慮一下控制元件中需要哪些屬性,比如(左邊的控制元件是否可見、中間的text文字、整個title的背景等等),可以根據需求去新增。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TitleBar">
<!--定義自己想要的屬性,在程式碼中引用,format指的是返回到程式碼中的格式-->
<!--中間文字-->
<attr name="centerText" format="string" />
<!--中間的空間是否顯示右側的圖示-->
<attr name="centerIsShowRightIcon" format="string" />
<!--title的背景色//背景可能會包括顏色或者是資原始檔 所以format這樣表示-->
<attr name="titleBackground" format="reference|color" />
<!--左右兩邊的圖-->
<attr name="leftImageDrwable" format="reference" />
<attr name="rightImageDrwable" format="reference" />
</declare-styleable>
</resources>
- 建立控制元件,並加入系統自帶控制元件
使用佈局加入控制元件,並關聯
建立一個佈局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@android:color/holo_green_dark"
tools:context="com.example.skym.customviewcontrol.MainActivity">
<ImageButton
android:id="@+id/ib_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="sinaWeibo"
android:textColor="@android:color/white"
android:textSize="15dp" />
<ImageButton
android:id="@+id/ib_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:src="@mipmap/ic_launcher" />
</RelativeLayout>
//使用佈局新增
private void initUiViews(Context context) {
//關聯佈局
View.inflate(context, R.layout.layout_title_bar, this);
ibLeft = (ImageButton) findViewById(R.id.ib_left);
ibRight = (ImageButton) findViewById(R.id.ib_right);
tvTitle = (TextView) findViewById(R.id.tv_title);
}
使用程式碼加入控制元件,並關聯
private void initCodeViews(Context context) {
ibRight = new ImageButton(context);
ibLeft = new ImageButton(context);
tvTitle = new TextView(context);
//控制元件new出來後 接下來設定三個控制元件的大小 位置 並加入到當前的佈局
mleftLayoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mleftLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_START, TRUE);
addView(ibLeft, mleftLayoutParams);
mrightLayoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mrightLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_END, TRUE);
addView(ibRight, mrightLayoutParams);
mtextLayoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mtextLayoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL, TRUE);
mtextLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL, TRUE);
addView(tvTitle, mtextLayoutParams);
}
兩種用法應該是一樣的,可以挑一個方便的,個人認為用ui比較方便
//關聯屬性
private void contactAttrs() {
//關聯屬性後 只要在引用控制元件的xml中設定centerText就可以,其他屬性也是一樣
tvTitle.setText(mCenterTextViewText);
//右側的icon是否顯示,這邊簡單舉例 需要靈活設定圖片 還需要自己封裝一個方法
tvTitle.setCompoundDrawables(null,
null,
mIsShowCenterIconToRight ? getResources().getDrawable(R.drawable.ic_launcher) : null,
null);
//這個屬性設定的是當前控制元件的背景,這個控制元件繼承了RelativeLayout,所以直接呼叫this裡的方法
this.setBackground(mBackground);
ibLeft.setImageDrawable(mLeftDrawble);
ibRight.setImageDrawable(mLeftDrawble);
}
到這一部 靜態佈局已經做好了
- 在layout中使用控制元件
直接在佈局中引用
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
//這邊注意新增這句程式碼,因為我們用到了自定義屬性,所以要加入自定義屬性的名稱空間
//名稱可以自定義,這邊我就用"auto"
**xmlns:auto="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="com.example.skym.customviewcontrol.MainActivity">
<com.example.skym.customviewcontrol.TitleBar
android:id="@+id/titlebar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
auto:centerIsShowRightIcon="true"
auto:centerText="2016年10月13日"
auto:leftImageDrwable="@drawable/ic_launcher"
auto:rightImageDrwable="@drawable/ic_launcher"
auto:titleBackground="@android:color/holo_red_dark" />
</RelativeLayout>
事件處理、程式碼控制控制元件屬性
為了複用,可以定義一個回撥介面進行各種操作
//設定中間文字內容
public void setCenterText(String text){
tvTitle.setText(text);
}
public void setmCallback(TitleBarCliclCallback mCallback) {
this.mCallback = mCallback;
}
//按鈕監聽回撥
public interface TitleBarCliclCallback {
public void leftClick();
public void rightClick();
}
private void setListener() {
ibLeft.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCallback.leftClick();
}
});
ibRight.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCallback.rightClick();
}
});
}
在MainActivity中使用
mTitleBar = (TitleBar) findViewById(R.id.titlebar);
mTitleBar.setmCallback(new TitleBar.TitleBarCliclCallback() {
@Override
public void leftClick() {
Toast.makeText(MainActivity.this,"leftClick",Toast.LENGTH_SHORT).show();
}
@Override
public void rightClick() {
Toast.makeText(MainActivity.this,"rightClick",Toast.LENGTH_SHORT).show();
}
});
mTitleBar.setCenterText("mainActivity");
}
下面是控制元件完整程式碼
public class TitleBar extends RelativeLayout {
private String mCenterTextViewText;
private boolean mIsShowCenterIconToRight;
private Drawable mBackground;
private Drawable mLeftDrawble;
private Drawable mRightDrawble;
private ImageButton ibLeft, ibRight;
private TextView tvTitle;
private LayoutParams mleftLayoutParams, mrightLayoutParams, mtextLayoutParams;
//有自定義的屬性,要用到attrs。所以需要兩個引數的構造方法
public TitleBar(Context context, AttributeSet attrs) {
super(context, attrs);
initAttrs(attrs);
initCodeViews(context);
// initUiViews(context);
contactAttrs();
}
//關聯屬性
private void contactAttrs() {
//關聯屬性後 只要在引用控制元件的xml中設定centerText就可以,其他屬性也是一樣
tvTitle.setText(mCenterTextViewText);
//右側的icon是否顯示,這邊簡單舉例 需要靈活設定圖片 還需要自己封裝一個方法
tvTitle.setCompoundDrawables(null,
null,
mIsShowCenterIconToRight ? getResources().getDrawable(R.drawable.ic_launcher) : null,
null);
//這個屬性設定的是當前控制元件的背景,這個控制元件繼承了RelativeLayout,所以直接呼叫this裡的方法
this.setBackground(mBackground);
ibLeft.setImageDrawable(mLeftDrawble);
ibRight.setImageDrawable(mLeftDrawble);
}
//使用ui的方式加入空間
private void initUiViews(Context context) {
//關聯佈局
View.inflate(context, R.layout.layout_title_bar, this);
ibLeft = (ImageButton) findViewById(R.id.ib_left);
ibRight = (ImageButton) findViewById(R.id.ib_right);
tvTitle = (TextView) findViewById(R.id.tv_title);
}
//與initUiViews 方法作用一致 可以挑一個用 個人認為用ui加入比較方便
//使用程式碼加入控制元件,並關聯屬性
private void initCodeViews(Context context) {
ibRight = new ImageButton(context);
ibLeft = new ImageButton(context);
tvTitle = new TextView(context);
//控制元件有了 接下來要設定控制元件大小 位置 並加入到當前的佈局
mleftLayoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mleftLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_START, TRUE);
addView(ibLeft, mleftLayoutParams);
mrightLayoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mrightLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_END, TRUE);
addView(ibRight, mrightLayoutParams);
mtextLayoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mtextLayoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL, TRUE);
mtextLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL, TRUE);
addView(tvTitle, mtextLayoutParams);
}
//獲取到在attrs中建立的各個屬性
private void initAttrs(AttributeSet attrs) {
final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.TitleBar);
//TitleBar_centerText 是系統生成的屬性名預設格式 聯想就能找到了 對應xml中TitleBar下的centerText
//這個屬性返回的是String,其他的屬性寫法一樣
mCenterTextViewText = a.getString(R.styleable.TitleBar_centerText);
//預設值false
mIsShowCenterIconToRight = a.getBoolean(R.styleable.TitleBar_centerIsShowRightIcon, false);
mBackground = a.getDrawable(R.styleable.TitleBar_titleBackground);
mLeftDrawble = a.getDrawable(R.styleable.TitleBar_leftImageDrwable);
mRightDrawble = a.getDrawable(R.styleable.TitleBar_rightImageDrwable);
//用完a後 回收TypedArray 節省資源
a.recycle();
}
}