自定義view聽起來就十分牛逼,但是日常開發中或許我們用不到完全的自定義,比如衆多app的設定界面的一些條目,我們隻需要組合成一套可伸縮,可定制的簡單的view就行了,本文就詳細的探讨一下設定界面通用的view設計流程。
需求圖
1 本文将要實作的需求圖
qq設定的截圖
如上需求圖:我們發現設定界面有很多條目,這些條目相似又不相似,相似在哪呢?左側是文字,最右側是箭頭圖示。可是有的很特别右側顯示文字(如20M)不顯示圖檔(頭像)。如果我們使用普通xml布局一個一個的寫這些條目。不說重複的布局問題,就是你的布局檔案至少也要上幾百行,如果你用ListView 或者recyclerView搭建又考慮不同條目類型的問題,啊啊啊真的好煩。
解需求
1、分析
試想我們假如有一個view:SettingView,在搭建布局時我們隻需向普通的TextView一樣
android:text="測試"
就可以輕松的設定文字,向Imageview一樣
android:src="@drawable/qq"
就可以輕松的設定圖檔,同時我們還具有view類似Visiable的性質。展示我們的右側圖檔,展示我們的右側文字。這時不就完美了嗎。于是我們想到了自定義view來實作我們的需求
2 實作的功能
3 特殊的api
1 xml 中使用:
app:leftText="自定義屬性1" //設定左側文字
app:showRightPic="true" //展示右側圖檔
app:rightPic="@drawable/qq"// 設定右側圖檔 ps:隻有showRightPic="true" 設定圖檔才生效
app:showRightText="true" // 展示右側文字
app:rightText="20M" // 設定右側文字 ps:隻有showRightText="true" 設定右側文字才生效
2 java代碼中使用:
public void setLeftText(String text) 設定左側文字
public void setRightText(String text) // 設定右側文字
public void setHeadPortrait(Object imgType) // 設定右側圖檔
4 具體實作
啰嗦了一大堆終于要上代碼了啊哈,但是還要說下重點:無論是自定義屬性設定文字還是調用java方法設定文字其本質都是在我們的自定義view中獲得文字再設定。
4.1 首先定義SettingView類 這裡我們繼承了LinearLayout并進行了一些方法調用處理
public class SettingView extends LinearLayout {
@RequiresApi(api = Build.VERSION_CODES.P)
public SettingView(Context context) {
this(context, null);
}
@RequiresApi(api = Build.VERSION_CODES.P)
public SettingView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@RequiresApi(api = Build.VERSION_CODES.P)
public SettingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
View view = LayoutInflater
.from(context)
.inflate(R.layout.view_setting_page, null);
this.addView(view);
}
}
可以看到我們的主要代碼在第三個構造方法中,最終使用者就是調用的他,在這個方法中我們把一個布局(R.layout.view_setting_page)轉換成了view添加到我們自定義的不布局中。
4.2 其實R.layout.view_setting_page就是我們的定義SettingView的布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:background="#ffffff"
android:id="@+id/rl_layout">
<!-- 分割線 沒啥其他用途-->
<TextView
android:background="#ccc"
android:layout_width="match_parent"
android:layout_height="1dp" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="測試"
android:textColor="#000000" />
<ImageButton
android:focusable="false"
android:clickable="false"
android:id="@+id/ib"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@null"
android:src="@drawable/news_cate_arr" />
<de.hdodenhof.circleimageview.CircleImageView
android:visibility="invisible"
android:id="@+id/img"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/ib"
android:paddingRight="5dp"
android:src="@drawable/qq"
android:text="測試資料" />
<!-- 注意 上面細節 使用了 invisiable 而不用gone的差別-->
<TextView
android:gravity="center"
android:visibility="gone"
android:id="@+id/right_text"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/ib"
android:paddingRight="5dp"
android:text="測試資料" />
</RelativeLayout>
如果我們使用自定義view不設定又圖檔,不設定又文字,那麼條目就是需求圖中"自定義屬性2"的效果,也是本布局檔案的預設效果(隻設定左側文字時)
4.3 自定義方法(簡單)
/**
* 設定左側文字的内容
* @param text 要設定的字元串
*/
public void setLeftText(String text) {
leftText.setText(text);
}
/**
* 設定右面側文字的内容
* @param text 要設定的字元串
*
* 注意使用時要控件顯示,由于xml屬性原因預設隐藏的
*/
public void setRightText(String text){
rightText.setText(text);
}
/**
* 設定頭像
*
* @param imgType 圖檔的類型
*
* 注意使用時要控件顯示,由于xml屬性原因預設隐藏的
*/
public void setHeadPortrait(Object imgType) {
if (imgType instanceof Drawable) {
headPortrait.setImageDrawable((Drawable) imgType);
} else if (imgType instanceof Integer) {
headPortrait.setImageResource((Integer) imgType);
} else if (imgType instanceof Bitmap) {
headPortrait.setImageBitmap((Bitmap) imgType);
} else if (imgType instanceof Uri) {
headPortrait.setImageURI((Uri) imgType);
} else {
throw new IllegalArgumentException("imgType just can be type:Drawable or recourceId or Bitmap or Uri");
}
}
4.4自定義屬性 (本文詳解)
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 1、 聲明給哪個自定義控件 添加屬性新的-->
<declare-styleable name="SettingView">
<!-- 2 、屬性定義(解釋如下) -->
<!-- 定義屬性名為leftText,屬性值為String類型-->
<attr name="leftText" format="string" />
<!-- 定義屬性名為showRightPic,屬性值為布爾類型-->
<attr name="showRightPic" format="boolean"/>
<!-- 定義屬性名為rightPic,屬性值為引用類型,簡單地說就是可以使用用資源id的形式(R.xxx.xxx)-->
<attr name="rightPic" format="reference"/>
<attr name="showRightText" format="boolean"/>
<attr name="rightText" format="string"/>
</declare-styleable>
</resources>
如上圖 到values檔案夾下建立名為attrs的資源檔案
4.4 自定義view中擷取自定義屬性(思考上文具體實作中設定文字本質)
三種擷取方式:(參看下面具體實作代碼注釋,及參考連結)
- attrs.getAttributeXXXValue(命名空間,自定義屬性名字)
- for (int i=0;i<attrs.getAttributeCount();i++) 從屬性集合中擷取
- 使用TypedArray
具體實作
/**
* 自定義屬性初始化工作
*
* @param attrs 屬性集合
*/
private void initSelfAttrs(AttributeSet attrs, Context context) {
// 1 左側text的xml設定
String leftText = attrs.getAttributeValue(NAME_SPACE, "leftText");
setLeftText(leftText);
// 獲得xml中設定 showRightPic 屬性值 預設 false
boolean showRightPic = attrs.getAttributeBooleanValue(NAME_SPACE, "showRightPic", false);
boolean showRightText = attrs.getAttributeBooleanValue(NAME_SPACE, "showRightText", false);
// 3 右面text的設定
if (showRightText){
rightText.setVisibility(VISIBLE);
String rightText = attrs.getAttributeValue(NAME_SPACE, "rightText");
setRightText(rightText);
}
// 圖檔的設定還是使用TypeArray 擷取設定
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SettingView);//參見你的attrs中定義<declare-styleable name="SettingView">
for (int i = 0; i < typedArray.getIndexCount(); i++) {
int index = typedArray.getIndex(i);
switch (index) {
//1 左側文本的xml設定(使用這種方式也行)
// case R.styleable.SettingView_leftText:
// String leftText = typedArray.getString(index);
// setLeftText(leftText);
// break;
case R.styleable.SettingView_rightPic:
// 2 右側圖檔的顯示
if (showRightPic) {
headPortrait.setVisibility(VISIBLE);
Drawable drawable = typedArray.getDrawable(index);
setHeadPortrait(drawable);
}
break;
}
}
typedArray.recycle();//回收
}
ps:
1 命名空間使用http://schemas.android.com/apk/res-auto這個字元串就行。
自定義屬性名就是你的attrs中定義的name
2 如果使用typedArray時:context.obtainStyledAttributes(attrs, R.styleable.自定義view類名)
case R.styleable.自定義類名_自定義屬性名 固定格式
attr各種屬性使用詳解參考
命名空間使用及屬性擷取參考
小結
經過一番探讨終于完成了設定界面通用View的設計,本文中也有一些細節不知道讀者們讀出來了嗎,比如圖檔設定時使用了invisiable占位而不用gone,還有一個坑,圖檔文字同時出的現問題這是沒有解決的。作者也懶了就沒實作哈,還有本文的細節,箭頭圖示處點選事件的處理,這是事件分發機制的邏輯這裡就不越俎代庖了。
具體源碼下載下傳 這是我的開源項目中的位址哦,也歡迎小夥伴們下載下傳觀看設定界面的具體邏輯是實作。