自定義View分類
Android 中自定義 View 個(gè)人覺得分類有兩種:
- 自定義的 ViewGroup ,通過繼承現(xiàn)有的 ViewGroup 將已有的控件組合起來方便實(shí)用。
- 自定義 View 新啼,系統(tǒng)完全沒有的控件需要自己繪制燥撞。這類自定義 View 的發(fā)揮余地也最大物舒,可以隨意揮動(dòng)你的畫筆實(shí)現(xiàn)各種炫酷的形狀火诸。
今天要說的組合大法即第一種方式置蜀,將現(xiàn)有的控件組合起來實(shí)用悉盆。先看一張圖:
這樣的一個(gè)菜單 Item 布局是不是很常見秋秤,如果每次都來寫這樣的布局再一個(gè)個(gè)找到上面的控件再進(jìn)行賦值是不是會(huì)覺得很煩脚翘,做了很多重復(fù)的工作来农。這時(shí)候如果吧這個(gè)布局封裝成一個(gè)控件备图,就能少寫重復(fù)代碼。
定制步驟
1抠藕、寫出這個(gè)控件的布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@drawable/menuitem_background"
android:elevation="3dp"
android:orientation="vertical"
android:padding="4dp">
<ImageView
android:id="@+id/icon_left"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="4dp"
android:scaleType="fitCenter"
android:src="@drawable/image_reward" />
<TextView
android:id="@+id/menu_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="4dp"
android:layout_toEndOf="@id/icon_left"
android:gravity="center_vertical"
android:text="菜單" />
<ImageView
android:id="@+id/icon_right"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:padding="8dp"
android:src="@drawable/arrow_right" />
</RelativeLayout>
ps :里面的字符串資源和長度還是老老實(shí)實(shí)按需要寫在 string 和 dimens 文件中。這里是個(gè)不好的習(xí)慣零院,實(shí)際開發(fā)注意就行
2告抄、創(chuàng)建 attrs 文件打洼,如果有的話直接在里面寫自定義控件的一些屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MenuItemView">
<!--布局中的文字-->
<attr name="text" format="string" />
<!--布局中左邊的icon-->
<attr name="leftIcon" format="reference" />
<!--布局中右邊的icon-->
<attr name="rightIcon" format="reference" />
<!--文字的大小-->
<attr name="textSize" format="dimension" />
<!--文字的顏色-->
<attr name="textColor" format="color" />
</declare-styleable>
</resources>
這樣在需要寫的布局中就可以像 android:text="xxx" 一樣對這些屬性進(jìn)行設(shè)置
attrs 文件定義屬性時(shí)需要注意炫惩,屬性名稱重復(fù)時(shí)需要將該屬性提取為公共屬性:
//如果再定義一個(gè)自定義控件阿浓,屬性還按照這樣來編譯會(huì)報(bào)錯(cuò)體會(huì)屬性名已經(jīng)出現(xiàn)過
<declare-styleable name="RowText">
<!--布局中的文字-->
<attr name="text" format="string" />
<!--文字的大小-->
<attr name="textSize" format="dimension" />
<!--文字的顏色-->
<attr name="textColor" format="color" />
</declare-styleable>
//解決辦法筋蓖,將共有屬性提取出來
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--布局中的文字-->
<attr name="text" format="string" />
<!--文字的大小-->
<attr name="textSize" format="dimension" />
<!--文字的顏色-->
<attr name="textColor" format="color" />
<declare-styleable name="MenuItemView">
<!--布局中左邊的icon-->
<attr name="leftIcon" format="reference" />
<!--布局中右邊的icon-->
<attr name="rightIcon" format="reference" />
<!--布局中的文字-->
<attr name="text" />
<!--文字的大小-->
<attr name="textSize" />
<!--文字的顏色-->
<attr name="textColor" />
</declare-styleable>
<declare-styleable name="RowText">
<!--布局中的文字-->
<attr name="text" />
<!--文字的大小-->
<attr name="textSize" />
<!--文字的顏色-->
<attr name="textColor" />
</declare-styleable>
</resources>
3、繼承ViewGroup自定義自己的ViewGroup,并在構(gòu)造方法中將attrs的屬性與ViewGroup中的控件關(guān)聯(lián)起來
例如本例使用的 RelativeLayout 设哗,則自定義 View 的代碼如下:
public MenuItem(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public MenuItem(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
inflate(context, R.layout.menuitem_layout, this);
ButterKnife.bind(this);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MenuItemView);
//關(guān)聯(lián)textColor
int textColor = ta.getColor(R.styleable.MenuItemView_textColor, Color.GRAY);
menuText.setTextColor(textColor);
//關(guān)聯(lián)TextSize
float textSize = ta.getDimension(R.styleable.MenuItemView_textSize, getResources().getDimension(R.dimen.default_textsize));
menuText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
//關(guān)聯(lián)Icon
int leftIconId = ta.getResourceId(R.styleable.MenuItemView_leftIcon, R.drawable.image_reward);
iconLeft.setImageDrawable(getResources().getDrawable(leftIconId, null));
int rightIconId = ta.getResourceId(R.styleable.MenuItemView_rightIcon, R.drawable.arrow_right);
iconRight.setImageDrawable(getResources().getDrawable(rightIconId, null));
//關(guān)聯(lián)Text
String text = ta.getString(R.styleable.MenuItemView_text);
if (text != null)
menuText.setText(text);
ta.recycle();
}
如果要獲取控件的或者設(shè)置控件的一些屬性可再寫一些 public 方法提供回調(diào):
//設(shè)置text
public void setMenuText(String text) {
menuText.setText(text);
}
//獲取text
public CharSequence getText() {
return menuText.getText();
}
4、最后在布局中使用該控件:
<com.pandaq.mvpdemo.customview.MenuItem
android:id="@+id/https_unfriendly"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:text="HTTPS unfriendly"
app:textColor="@color/black"
app:textSize="14sp" />
關(guān)于 attrs 中的屬性說明
format 一共有如下幾種類型:
- reference // 資源Id战虏,如圖片之類的
//定義
<attr name="leftIcon" format="reference" />
//控件初始化獲取方式
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MenuItemView);
//這里以圖片資源為例,第一個(gè)參數(shù)為attrs中定義的烦感,第二個(gè)為取值失敗時(shí)的默認(rèn)值
int reference = ta.getResourceId(R.styleable.MenuItemView_leftIcon,R.drawable.image_reward);
- color // 顏色屬性
//定義
<attr name="textColor" format="color" />
//控件初始獲取方式
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MenuItemView)
//第一個(gè)參數(shù)為attrs中定義的參數(shù),第二個(gè)為取值失敗時(shí)的默認(rèn)值
int color = ta.getColor(R.styleable.MenuItemView_textColor,Color.RED);
- boolean // 布爾值
//定義
<attr name="focusable" format="boolean"/>
//代碼中獲取方式
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MenuItemView)
//第一個(gè)參數(shù)為attrs中定義的參數(shù)绿渣,第二個(gè)為取值失敗時(shí)的默認(rèn)值
boolean focusable = ta.getBoolean(R.styleable.MenuItemView_focusable,true);
- dimension //尺寸值,dp燕耿,px中符,sp等
//定義
<attr name="textSize" format="dimension" />
/**
*getDimension() 返回值為float類型
*getDimensionPixelSize() 返回的是四舍五入后的整型值
*getDimensionPixeOffset() 返回的是舍棄小數(shù)部分的整型值
*/
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MenuItemView)
float textSize = ta.getDimension(R.styleable.MenuItemView_textSize, getResources().getDimension(R.dimen.default_textsize));
- enum // 枚舉類型
//定義
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
- float //浮點(diǎn)型
//定義
<attr name="floatNumber" format="float"/>
//java代碼中獲取屬性
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MenuItemView)
float number = ta.getFloat(R.styleable.floatNumber,0.0);
- fraction //百分?jǐn)?shù)
//定義
<attr name="fraction" format="fraction"/>
//java代碼中獲取屬性
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MenuItemView)
float fraction = ta.getFraction(R.styleable.fraction,100,30,0.3);
- integer //整型
//定義
<attr name="number" format="integer"/>
//java代碼中獲取屬性
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MenuItemView)
int number = ta.getInt(R.styleable.number,0);
//或者得到一個(gè)Integer對象
Integer a = ta.getInteger(R.styleable.number, 0);
- string //字符串類型
//定義
<attr name="text" format="string"/>
//java代碼中獲取屬性
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MenuItemView)
String text = ta.getString(R.styleable.MenuItemView_text);