? ? ??實際編程中,系統提供的控件往往無法滿足我們的需求,一來是樣子丑陋,二來是一些復雜的組合需要多次使用的話,每次都寫一堆控件的組合會很耗費時間,所以我們將這些組件的組合自定義為一個新的控件,以后使用的時候直接用該控件,方便又簡單。最常見的例子就是軟件中的titleTar
?
? ? ??實現自定義控件的步驟:
????? 1.設置控件的屬性
????? 2.實現我們的View
????? 3.引用我們自定的View
官方文檔:http://developer.android.com/training/custom-views/create-view.html
下面開始:
設置控件的屬性:
分析一下上面四個titleBar,都是由一個居中的文本和一個右邊一個圖片按鈕組成,只不過后面兩個沒有按鈕而已
那么我們自定義的這個組件就得包含一下幾個基本屬性:文本內容,文本大小,文本顏色,按鈕路徑
在value中新建一個xml文件attrs.xml,把這些屬性創建出來
<?xml version="1.0" encoding="utf-8"?> <resources><declare-styleable name="MyCustomTitleBar"><attr name="titleText" format="string"/><attr name="titleTextSize" format="dimension"/><attr name="titleTextColor" format="color"/><attr name="titleButtonImage" format="reference"/></declare-styleable> </resources>
declare-styleable標簽是用來定義自定義控件的屬性的,我們的控件屬性都以attr形式存放在declare-styleable標簽中,format是用來形容該屬性的類型,有如下值可選:string,color,dimension,integer,enum,reference,float,boolean,fraction,flag
實現我們的View
????? 定義一個繼承自RelativeLayout的類MyCustomTitltBar,然后在他的構造方法中獲取我們自定義的控件樣式
package cn.lixyz.customviewdemo;import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.ImageButton; import android.widget.RelativeLayout; import android.widget.TextView;@SuppressLint("NewApi") public class MyCustomTitleBar extends RelativeLayout {// 定義自定義控件包含的組件private TextView title;private ImageButton button;// 定義控件的屬性private String titleText;private float titleTextSize;private int titleTextColor;private Drawable titleButtonImage;// 為每個控件定義LayoutParamsprivate LayoutParams textLayoutParams;private LayoutParams buttonLayoutParams;public MyCustomTitleBar(Context context, AttributeSet attrs) {super(context, attrs);// 獲取我們定義的屬性TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyCustomTitleBar);titleText = array.getString(R.styleable.MyCustomTitleBar_titleText);titleTextColor = array.getColor(R.styleable.MyCustomTitleBar_titleTextColor, 0);titleTextSize = array.getDimension(R.styleable.MyCustomTitleBar_titleTextSize, 10);titleButtonImage = array.getDrawable(R.styleable.MyCustomTitleBar_titleButtonImage);// 回收,以防出錯 array.recycle();// 新建包含的子組件title = new TextView(context);button = new ImageButton(context);// 為子組件賦值 title.setText(titleText);title.setTextColor(titleTextColor);title.setTextSize(titleTextSize);button.setBackground(titleButtonImage);// 設置背景色setBackgroundColor(0xFF38373c);// 設置包含控件的屬性并添加到view中textLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);textLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);addView(title, textLayoutParams);buttonLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);buttonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);addView(button, buttonLayoutParams);} }
?其中LayoutParams是用于child view(子視圖) 向 parent view(父視圖)傳達自己的意愿的一個東西
在獲取屬性的時候,android定義的格式為控件名_屬性名 ?例如上面的MyCustomTitleBar_titleText、MyCustomTitleBar_titleTextSize
????? LayoutParams繼承于Android.View.ViewGroup.LayoutParams.?
????? LayoutParams相當于一個Layout的信息包,它封裝了Layout的位置、高、寬等信息。假設在屏幕上一塊區域是由一個Layout占領的,如果將一個View添加到一個Layout中,最好告訴Layout用戶期望的布局方式,也就是將一個認可的layoutParams傳遞進去。?
????? 可以這樣去形容LayoutParams,在象棋的棋盤上,每個棋子都占據一個位置,也就是每個棋子都有一個位置的信息,如這個棋子在4行4列,這里的“4行4列”就是棋子的LayoutParams。?
????? 但LayoutParams類也只是簡單的描述了寬高,寬和高都可以設置成三種值:?
?????????? 1,一個確定的值;?
?????????? 2,FILL_PARENT,即填滿(和父容器一樣大小);?
?????????? 3,WRAP_CONTENT,即包裹住組件就好。?
?????????? http://byandby.iteye.com/blog/816718
TypedArray實例是個屬性的容器,通過context.obtainStyledAttributes()方法返回得到
引用我們自定的View
????? 在布局文件中引入我們自定義的控件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:custom="http://schemas.android.com/apk/res/cn.lixyz.customviewdemo"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"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="cn.lixyz.customviewdemo.MainActivity" ><cn.lixyz.customviewdemo.MyCustomTitleBarandroid:layout_width="wrap_content"android:layout_height="wrap_content"custom:titleButtonImage="@drawable/image"custom:titleText="通訊錄"custom:titleTextColor="#ffffff"custom:titleTextSize="5dp" ></cn.lixyz.customviewdemo.MyCustomTitleBar><cn.lixyz.customviewdemo.MyCustomTitleBarandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="10dp"custom:titleButtonImage="@drawable/plus"custom:titleText="微信"custom:titleTextColor="#ffffff"custom:titleTextSize="5dp" ></cn.lixyz.customviewdemo.MyCustomTitleBar><cn.lixyz.customviewdemo.MyCustomTitleBarandroid:layout_width="wrap_content"android:layout_height="30dp"android:layout_marginTop="10dp"custom:titleText="發現"custom:titleTextColor="#ffffff"custom:titleTextSize="5dp" ></cn.lixyz.customviewdemo.MyCustomTitleBar><cn.lixyz.customviewdemo.MyCustomTitleBarandroid:layout_width="wrap_content"android:layout_height="30dp"android:layout_marginTop="10dp"custom:titleText="我"custom:titleTextColor="#ffffff"custom:titleTextSize="5dp" ></cn.lixyz.customviewdemo.MyCustomTitleBar></LinearLayout>
? ? ??一定要在布局中引入我們的命名空間
格式為:xmlns:自定義名稱="http://schemas.android.com/apk/res/包名"
因為只有引入了我們的命名空間,才可以使用xxx:xxx格式來設置我們定義的屬性,我們平時使用的android:xx也是因為上面的xmlns:android="http://schemas.android.com/apk/res/android"
這樣運行一下軟件查看效果
? ? ??為按鈕設置點擊事件
????? 觀察我們平時為按鈕設置點擊事件時候的方法:
bt.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stub }});
? ? ??我們通過按鈕的setOnClickListener方法傳入一個匿名內部類OnClickListener為按鈕設置點擊事件,我們查看OnClickListener的介紹
/*** Interface definition for a callback to be invoked when a view is clicked.*/public interface OnClickListener {/*** Called when a view has been clicked.** @param v The view that was clicked.*/void onClick(View v);}
? ? ??我們也可以仿照這個形式,為我們的MyCustomTieleBar設置一個setCustomTitleBarListener,然后傳入一個內部類來讓這個按鈕的點擊調用這個類的click方法
? ? ? 修改MyCustomTitleBar:
package cn.lixyz.customviewdemo;import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import android.widget.ImageButton; import android.widget.RelativeLayout; import android.widget.TextView;@SuppressLint("NewApi") public class MyCustomTitleBar extends RelativeLayout {// 定義自定義控件包含的組件private TextView title;private ImageButton button;// 定義控件的屬性private String titleText;private float titleTextSize;private int titleTextColor;private Drawable titleButtonImage;// 為每個控件定義LayoutParamsprivate LayoutParams textLayoutParams;private LayoutParams buttonLayoutParams;private customTitleBarListener listener;public interface customTitleBarListener {public void click();}public void setCustomTitleBarListener(customTitleBarListener listener) {this.listener = listener;}public MyCustomTitleBar(Context context, AttributeSet attrs) {super(context, attrs);// 獲取我們定義的屬性TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyCustomTitleBar);titleText = array.getString(R.styleable.MyCustomTitleBar_titleText);titleTextColor = array.getColor(R.styleable.MyCustomTitleBar_titleTextColor, 0);titleTextSize = array.getDimension(R.styleable.MyCustomTitleBar_titleTextSize, 10);titleButtonImage = array.getDrawable(R.styleable.MyCustomTitleBar_titleButtonImage);// 回收,以防出錯 array.recycle();// 新建包含的子組件title = new TextView(context);button = new ImageButton(context);// 為子組件賦值 title.setText(titleText);title.setTextColor(titleTextColor);title.setTextSize(titleTextSize);button.setBackground(titleButtonImage);// 設置背景色setBackgroundColor(0xFF38373c);// 設置包含控件的屬性并添加到view中textLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);textLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);addView(title, textLayoutParams);buttonLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);buttonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);addView(button, buttonLayoutParams);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {listener.click();}});} }
這樣我們在MainActivity中就可以為按鈕設置點擊事件了
package cn.lixyz.customviewdemo;import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast;public class MainActivity extends Activity {private MyCustomTitleBar addressListBar, wechatBar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button bt = new Button(this);bt.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stub }});addressListBar = (MyCustomTitleBar) findViewById(R.id.addressListBar);wechatBar = (MyCustomTitleBar) findViewById(R.id.wechatBar);addressListBar.setCustomTitleBarListener(new MyCustomTitleBar.customTitleBarListener() {@Overridepublic void click() {Toast.makeText(MainActivity.this, "通訊錄按鈕被點擊", Toast.LENGTH_SHORT).show();}});wechatBar.setCustomTitleBarListener(new MyCustomTitleBar.customTitleBarListener() {@Overridepublic void click() {Toast.makeText(MainActivity.this, "微信按鈕被點擊", Toast.LENGTH_SHORT).show();}});}}
?
?
?