導讀:
- 不管是剛學習安卓的同學,還是在工作的小夥伴,自定義控件這個點都是非常重要的
- 雖然安卓本身已經有一部分做好的控件,但是實現的功能以及顯示效果也是很有限的,那麼自定義控件就是我們最好的選擇
- 下文是本人學習中總結的內容分享給大家..0.0
本文將以一個自定義組合控件作爲例子
配合該老師講解的視頻食用更佳:
http://www.imooc.com/course/programdetail/pid/24
ps: 如果有什麼理解錯誤,歡迎評論指正修改
效果圖:
點擊 “Back” 或 “More” 彈出吐司,並且中間的自定義標題也會變成點擊的內容
實現方式:
一般實現自定義控件會有三種方式:
1.繼承已有的控件實現 (自定義控件)
2.組合已有的控件實現 (自定義組合控件)
3.完全自定義控件(自定義View)
實現步驟:
一. 在res/Values目錄下創建attrs.xml文件,設計需要的屬性
屬性 | 作用 |
---|---|
reference | 某一資源ID |
color | 顏色值 |
boolean | 布爾值 |
dimension | 尺寸值 |
float | 浮點值 |
integer | 整型值 |
string | 字符串 |
fraction | 百分數 |
enum | 枚舉值 (多值選一) |
flag | 位或運算 (多值組合) |
<!--自定義屬性名(自定義就好)-->
<declare-styleable name="Topbar">
<!--自定義的屬性;屬性類型-->
<attr name="title" format="string"/>
<attr name="titleTextSize" format="dimension"/>
<attr name="titleTextColor" format="color"/>
<attr name="leftTextColor" format="color"/>
<attr name="leftText" format="string"/>
<attr name="leftBackground" format="reference|color"/>
<attr name="rightTextColor" format="color"/>
<attr name="rightText" format="string"/>
<!--平常在定義 Background 屬性,不僅能通過字節碼調用顏色,還能@調用資源文件-->
<attr name="rightBackground" format="reference|color"/>
</declare-styleable>
注意:
1.定義一個Background(Drawable屬性),設置要 format="reference|color" 因爲我們平常在定義Background 不僅能通過字節碼調用顏色,還能@調用資源文件
2.attrs.xml 設置好屬性以後,系統會在R.styleable 文件中生成 類似 R.styleable.Topbar_leftTextColor 的值,用於 TypedArray 獲取attr便籤定義的屬性
3.enum 或flag 標籤
<attr name="postion" value="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
調用:
typeArray.getInteger(R.styleable_XX,0); 得到對應值的屬性
二. 實現一個自定義的”View”(如 自定義類 繼承 RelativeLayout)
2.1.實現”View”的構造函數
四個構造函數(一般關注前兩個即可) | 作用 |
---|---|
“view”(Context context) | 常規構造函數(代碼中實例化) |
“view”(Context context,AttributeSet attrs) | 用於在XML中使用,可以指定自定義屬性 |
“view”(Context context, AttributeSet attrs, int defStyleAttr) | 用於在XML中使用,可以指定自定義屬性,並指定樣式 |
“view”(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) | 用於在XML中使用,可以指定自定義屬性,並指定樣式及其資源 |
/**
* 其他類通過new TopBar() 時被調用
*
* @return
*/
public TopBar(Context context) {
super(context);
}
/**
* 實現父類View的構造函數
*
* @param context
* @param attrs
*/
public TopBar(Context context, AttributeSet attrs) {
super(context, attrs);
initData(context, attrs);
//initView(context);
initView();
initEvent();
}
2.2.獲取 attrs.xml 自定義的屬性
/**
*
*
* @param context 上下文
* @param attrs TopBar(Context context, AttributeSet attrs) 由構造函數傳過來
*/
private void initData(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Topbar);
//左側按鈕數據
leftTextColor = typedArray.getColor(R.styleable.Topbar_leftTextColor, 0);
leftBackground = typedArray.getDrawable(R.styleable.Topbar_leftBackground);
leftText = typedArray.getString(R.styleable.Topbar_leftText);
//右側按鈕數據
rightTextColor = typedArray.getColor(R.styleable.Topbar_rightTextColor, 0);
rightBackground = typedArray.getDrawable(R.styleable.Topbar_rightBackground);
rightText = typedArray.getString(R.styleable.Topbar_rightText);
//自定義標題數據
titleTextSize = typedArray.getDimension(R.styleable.Topbar_titleTextSize, 0);
title = typedArray.getString(R.styleable.Topbar_title);
titleTextColor = typedArray.getColor(R.styleable.Topbar_titleTextColor, 0);
//回收,避免浪費資源以及由於避免緩存報的錯誤
typedArray.recycle();
}
2.3.設置控件之間的佈局關係(看需求,使用1或2都可以)
1. 用代碼構建自定義控件佈局
//addRule() 是 RelativeLayout 特有的Api
//左側Button佈局
leftParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams
.WRAP_CONTENT);
leftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, TRUE);//居左對齊
addView(leftButton, leftParams);//將leftButton添加到我們的RelativeLayout 中
//右側Button佈局
rightParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams
.WRAP_CONTENT);
rightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, TRUE);//居右對齊
addView(rightButton, rightParams);//將rightButton添加到我們的RelativeLayout 中
//自定義標題佈局
titleParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams
.MATCH_PARENT);
titleParams.addRule(RelativeLayout.CENTER_IN_PARENT, TRUE);//居中對齊
addView(tvTitle, titleParams);//將tvTitle添加到我們的RelativeLayout 中
2. 用打氣筒xml文件構建自定義控件佈局
//佈局文件,根佈局對象,是否作爲根佈局
View rootView = View.inflate(getContext(),R.layout.custom_controls, this);
leftButton = (Button) rootView.findViewById(R.id.btn_left);
rightButton = (Button) rootView.findViewById(R.id.btn_right);
tvTitle = (TextView) rootView.findViewById(R.id.tv_title);
2.4.將自定義屬性和View綁定並顯示出來
leftButton = new Button(context);
rightButton = new Button(context);
tvTitle = new TextView(context);
//左側Button
leftButton.setTextColor(leftTextColor);
leftButton.setBackground(leftBackground);
leftButton.setText(leftText);
//右側Button
rightButton.setTextColor(rightTextColor);
rightButton.setBackground(rightBackground);
rightButton.setText(rightText);
//自定義標題
tvTitle.setTextColor(titleTextColor);
tvTitle.setText(title);
tvTitle.setTextSize(titleTextSize);
tvTitle.setGravity(Gravity.CENTER); //居中顯示文本
注意:
1.使用TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Topbar); 獲取屬性
記得 typedArray.recycle(); 避免浪費資源以及由於避免緩存報的錯誤
2.獲取屬性值函數: obtainStyledAttributes
public final TypedArray obtainStyledAttributes(
AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
@StyleRes int defStyleRes) {
return getTheme().obtainStyledAttributes(
set, attrs, defStyleAttr, defStyleRes);
}
四個參數的意思分別是:
set:屬性值的集合,一般爲第二個構造函數中的attrs
attrs:我們要獲取的屬性的資源ID的一個數組
defStyleAttr:這個是當前Theme中的一個attribute,是指向style的一個引用,當在layout xml中和style中都沒有爲View指定屬性時,會從Theme中這個attribute指向的Style中查找相應的屬性值,如果這個參數傳入0表示不向Theme中搜索默認值
defStyleRes:這個也是指向一個Style的資源ID,但是僅在defStyleAttr爲0或defStyleAttr不爲0但Theme中沒有爲defStyleAttr屬性賦值時起作用
函數返回值:
TypedArray 是一個類,包含要獲取值的一個集合
3.舊版獲取屬性(通過命名空間,屬性名獲取):
Android Studio: String title = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "title");
eclipse: String title = attrs.getAttributeValue("http://schems.android.com/apk/res/(自定義" View " 的工程包名)", "title");
4.優先級問題
在xml佈局文件指定屬性,在style文件指定屬性,在Theme文件指定屬性存在優先級;
-->
在XML中直接指定>在style中指定值>在Activity theme中指定了defStyleAttr(>0)>在Application theme中指定了defStyleAttr(>0)>指定了defStyleRes(>0)>在Activity theme中指定的值>在Application theme中指定的值
三. 在佈局文件中引用我們的自定義控件
- 在根佈局定義定義xmlns(命名空間) 如:xmlns:app(自定義)=”http://schems.android.com/apk/res-auto”
- 引用我們的自定義控件包名
<RelativeLayout
android:id="@+id/activity_main"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="zs.xmx.customcontrols.MainActivity">
<zs.xmx.customcontrols.TopBar
android:id="@+id/topbar"
android:layout_width="match_parent"
android:layout_height="40dp"
custom:leftBackground="#000000"
custom:leftText="Back"
custom:leftTextColor="#FF0000"
custom:rightBackground="#000000"
custom:rightText="More"
custom:rightTextColor="#FFFFFF"
custom:title="自定義標題"
custom:titleTextColor="#123412"
custom:titleTextSize="10sp"
/>
</RelativeLayout>
注意:
1. xmlns(xml命名空間) 主要用來調用xml文件
2. 在Android Studio 中:xmlns:app(自定義)="http://schems.android.com/apk/res-auto"
3. 在eclipse 中: xmlns:app(自定義)="http://schems.android.com/apk/res/(自定義" View " 的工程包名)"
四. 動態的控制自定義控件
兩種方式:
- 接口回調機制實現
在自定義的 “View” 中,寫一個方法,讓外部類調用
- 接口回調機制方法實現
- 定義一個接口,將我們要實現的方法定義
- 接口回調機制方法實現
public interface topbarClickListener{
public void leftClick();
public void rightClick();
}
2.定義我們的接口作爲成員變量
private topbarClickListenr listener;
- 定義一個方法暴露給調用者,類似button.setOnClickListen(),這樣就可以將我們接口裏定義的方法以匿名內部類的形式傳遞進來
public void setOnTopbarClickListenr(topbarClickListener listener){
this.listener=listener;
}
4.在我們原來要操作的邏輯方法改成接口裏定義的方法,讓外部類實現
//例子:
button.setOnClickListener(new OnClickListener()){
@Override
public void onClick(View v){
listener.leftClick();
}
}
- 在自定義的 “View” 中,寫一個方法,讓外部類調用
// 例子:(外部類 find 到這個控件,用 . 的方式調用即可)
public void setLeftIsVisable(boolean flag){
if(flag){
leftButton.setVisibility(View.VISIBLE);
}else{
leftButton.setVisibility(View.GONE);
}
}
總結:
常用的自定義控件基本如文章所示,自定義View後續補上