Andriod 自定義控件之建立能夠複用的組合控件

前面已學習了一種自定義控件的實現,是Andriod 自定義控件之音頻條,還沒學習的同窗能夠學習下,學習了的同窗也要去溫習下,必定要本身徹底的掌握了,再繼續學習,貪多嚼不爛可不是好的學習方法,咱們爭取學習了一種技術就會一種技術,並且不光看了就算了,最好的方法就是看完我本身再練習下,再擴展下,在原來的基礎上在添加一些東西,好比,增長一些功能實現等等。html

今天咱們打算學習下另一種自定義控件,就是建立可重複使用的組合控件,那麼問題來了:python

  • 什麼是可重複使用?
    就是在應用中,能夠在多個地方共同使用一套代碼。這樣不只能減小咱們的工做量,並且還能保持應用風格的一致,這種應用最多最直接的體現就是統一風格樣式的標題欄。
  • 那什麼又是組合控件呢?
    組合控件,顧名思義就是多個控件組合在一塊兒,相互協做共同完成某些特定的功能。

下面咱們就針對app應用中風格統一的標題欄來開始咱們的學習。android

首先,既然是一組組合的控件,那就必須有一個能夠來包含這些控件的容器,咱們所接觸的能夠存放控件的容器不少,好比LinearLayout、RelativeLayout等等多種Layout,今天咱們就選擇RelativeLayout來作咱們的容器。和之前同樣,咱們先定義一個CompositeViews類來繼承RelativeLayout類,並重寫它的構造方法,代碼以下:微信

public class CompositeViews extends RelativeLayout{

    public CompositeViews(Context context) {
        this(context,null);
    }
    public CompositeViews(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public CompositeViews(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
 }

接下來,咱們再定義三個TextView控件,分別是mLefeText,mRightText,textTitle用來顯示「返回」,「搜索」以及「標題」而且使用LayoutParams規定它們在容器裏面的對齊方式。請看代碼:app

public class CompositeViews extends RelativeLayout{
    private TextView mLefeText;
    private TextView mRightText;
    private TextView textTitle;
    private LayoutParams leftLayoutParams;
    private LayoutParams ridhtLayoutParams;
    private LayoutParams titleLayoutParams;

    public CompositeViews(Context context) {
        this(context,null);
    }
    public CompositeViews(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public CompositeViews(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

initView(context)方法中是用來初始化三個組合控件的,請看:eclipse

private void initView(Context context) {
        mLefeText = new TextView(context);
        mRightText = new TextView(context);
        textTitle = new TextView(context);
        /*
         * 左按鈕位置
         */
        leftLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        leftLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);
        mLefeText.setText("返回");
        mLefeText.setTextSize(22);
        /*
         * 右按鈕位置
         */
        ridhtLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        ridhtLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
        mRightText.setText("搜索");
        mRightText.setTextSize(22);
        /*
         * 中間標題位置
         */
        titleLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        titleLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);
        textTitle.setText("這是一個標題");
        textTitle.setTextSize(22);
    }

ok,以上的代碼已經實現了組合控件的顯示和對齊方式,咱們把定義的View添加到佈局文件中並在Activity加載吧ide

activity_main.xml文件佈局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:background="#999999"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <com.sanhuimusic.mycustomview.view.CompositeViews
        android:id="@+id/topBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</LinearLayout>

MainActivity:學習

public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

咱們先來運行看下結果ui

這裏寫圖片描述

ok,已顯示出來了,可是相信你們也看出來了,這上面的代碼中,各個控件中的屬性是都是咱們固定寫死的,既然咱們是建立可服用的控件,固定寫死的東西確定是不可取的,那麼咱們怎麼能夠靈活地獲取控件的屬性,以致於能達到複用呢?

這就必需要接觸另一種技術了,就是自定義屬性。用咱們自定義的屬於能夠在每次使用咱們定義的控件時爲其分配屬性便可。下面咱們來學習下自定義屬性。

自定義屬性其實也是至關的簡單,首先,咱們如今資源文件res下values目錄下新建一個attrs.xml文件(eclipse自帶,as自建),新建的attrs.xml是一個包含以下代碼的文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>

</resources>

在resources中有各類屬性供咱們使用,同窗們能夠本身看下。根據咱們如今的需求,咱們選擇使用declare-styleable來聲明咱們的屬性集,而後爲其定義特有的name屬性,這個name是供咱們在使用自定義屬性時,經過它能夠查找到裏面的全部屬性。請看以下代碼:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CompositeViews">
        <attr name="titleText" format="string"/>
        <attr name="titleTextSize" format="dimension"/>
        <attr name="titleColor" format="color"/>
        <attr name="titleBackground" format="color|reference"/>

        <attr name="leftTextColor" format="color"/>
        <attr name="leftBackground" format="color|reference"/>
        <attr name="leftText" format="string"/>
        <attr name="leftTextSize" format="dimension"/>

        <attr name="rightTextColor" format="color"/>
        <attr name="rightBackground" format="color|reference"/>
        <attr name="rightText" format="string"/>
        <attr name="rightTextSize" format="dimension"/>
    </declare-styleable>

</resources>

單獨拿一行屬性來解析下它所表明的含義:如 :attr表明的是一個屬性,它裏面所包含name字段是這條屬性名,經過該屬性名能夠獲取以format的約束爲真的屬性值;formate是該屬性的格式,分別包含string,dimension,color,reference等等,分別表明字符串,大小,顏色,引用。其餘的你們能夠自行學習resources所包含的屬性。

好了,自定義屬性咱們已學習完畢,那麼該怎麼使用咱們本身定義的屬性呢?其實也很簡單,在咱們的activity_main.xml文件中直接使用咱們定義的屬性就能夠了,可是在使用是以前必須在指定引用第三方控件的命名空間,在跟佈局文件中添加以下一行代碼:

xmlns:custom="http://schemas.android.com/apk/res-auto"

custom是咱們第三方命名空間的名字,能夠任意命名,咱們在使用自定義屬性時必須以它開頭。請看代碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:background="#999999"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <com.sanhuimusic.mycustomview.view.CompositeViews
        android:id="@+id/topBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"

        custom:titleText="@string/titleText"
        custom:titleColor="#000000"
        custom:titleTextSize="@dimen/titleTextSize"
        custom:titleBackground="#999999"
        custom:leftText="@string/leftText"
        custom:leftTextColor="#FFFFFF"
        custom:leftBackground="#666666"
        custom:leftTextSize="@dimen/leftTextSize"
        custom:rightText="@string/rightText"
        custom:rightTextColor="#FFFFFF"
        custom:rightBackground="#666666"
        custom:rightTextSize="@dimen/rightTextSize"
        />
 
</LinearLayout>

咱們是使用custom加上咱們自定義屬性裏面< attr name="titleText" format="string"/>裏的name值來動態設置屬性值的,如:custom:titleText="@string/titleText"。

ok,在咱們xml文件中已設定好屬性值,那麼該怎麼顯示出來呢?這個是須要經過一個類型組TypedArray來獲取的,它裏面包含各類從AttributeSet屬性集中獲取屬性的方法,因此咱們修改上面的構造方法和initView(context)方法,以下所示:

private void initView(Context context, AttributeSet attrs) {
        mLefeText = new TextView(context);
        mRightText = new TextView(context);
        textTitle = new TextView(context);

        /**
         * 獲取自定義屬性
         */
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CompositeViews);
        String titleText = typedArray.getString(R.styleable.CompositeViews_titleText);
        float titleTextSize = typedArray.getDimension(R.styleable.CompositeViews_titleTextSize, 16);
        int titleColor = typedArray.getColor(R.styleable.CompositeViews_titleColor,0);
        Drawable titleBackground = typedArray.getDrawable(R.styleable.CompositeViews_titleBackground);

        String leftText = typedArray.getString(R.styleable.CompositeViews_leftText);
        int leftTextColor = typedArray.getColor(R.styleable.CompositeViews_leftTextColor, 0);
        float leftTextSize = typedArray.getDimension(R.styleable.CompositeViews_leftTextSize, 16);
        Drawable leftBackground = typedArray.getDrawable(R.styleable.CompositeViews_leftBackground);

        String rightText = typedArray.getString(R.styleable.CompositeViews_rightText);
        int rightTextColor = typedArray.getColor(R.styleable.CompositeViews_rightTextColor, 0);
        float rightTextSize = typedArray.getDimension(R.styleable.CompositeViews_rightTextSize, 16);
        Drawable rightBackground = typedArray.getDrawable(R.styleable.CompositeViews_rightBackground);
        typedArray.recycle();
        /*
         * 左按鈕位置
         */
        leftLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        leftLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);
        mLefeText.setText(leftText);
        mLefeText.setTextColor(leftTextColor);
        mLefeText.setTextSize(leftTextSize);
        mLefeText.setBackground(leftBackground);
        addView(this.mLefeText,leftLayoutParams);
         /*
         * 右按鈕位置
         */
        ridhtLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        ridhtLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
        mRightText.setText(rightText);
        mRightText.setTextColor(rightTextColor);
        mRightText.setTextSize(rightTextSize);
        mRightText.setBackground(rightBackground);
        addView(mRightText,ridhtLayoutParams);
        /*
         * 中間標題位置
         */
        titleLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        titleLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);
        textTitle.setText(titleText);
        textTitle.setTextSize(titleTextSize);
        textTitle.setTextColor(titleColor);
        textTitle.setBackground(titleBackground);
        addView(textTitle,titleLayoutParams);
    }

代碼解釋:首先經過上下文context獲取到屬性存放到TypedArray 中,而後經過TypedArray 裏封裝好的各類方法獲取對應的屬性值,而後再分別爲咱們的控件設置屬性。這樣就完成了,自定義屬性的使用,而且複用度高,每當須要使用標題欄是都只須要在xml中添加咱們定義的View控件,爲其配置屬性便可使用,節約了開發時間,提升了效率,而且還保持的app風格的一致。

好,到這裏感受已經講完了整個過程吧,其實還有一個重要的實現尚未講。咱們的控件已經能夠呈現出來了,可是怎麼完成裏面控件的做用呢?

這裏比較常見的作法是利用回調機制來實現功能的開發,首先咱們先定義一個接口,建立兩個方法,用於左右控件的點擊事件。

public interface TopBarClickListener{
            void leftClickListener();
            void rightClickListener();
    }

而後在構造方法中爲左右控件添加點擊事件,但不實現功能,等待調用者本身實現:

private void setListener() {
        mLefeText.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mTopBarClickListener.leftClickListener();
            }
        });
        mRightText.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mTopBarClickListener.rightClickListener();
            }
        });
    }

再者,把定義好的接口暴露給調用者:

public void setOnTopBarClickListener(TopBarClickListener topBarClickListener){
        mTopBarClickListener = topBarClickListener;
    }

最後,誰調用,誰實現。這就完成了不一樣界面複用控件實現不一樣的功能的便利。在這裏咱們只在MainActivity中打印Toast就能夠了。

public class MainActivity extends AppCompatActivity {
    private CompositeViews topBar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        topBar = (CompositeViews) findViewById(R.id.topBar);
        topBar.setOnTopBarClickListener(new CompositeViews.TopBarClickListener(){
            @Override
            public void leftClickListener() {
                ToastUtil.makeText(MainActivity.this,"您點擊了返回鍵",Toast.LENGTH_SHORT).show();
            }
            @Override
            public void rightClickListener() {
                ToastUtil.makeText(MainActivity.this,"您點擊了搜索鍵",Toast.LENGTH_SHORT).show();
            }
        });
    }
}

OK,看看結果吧

這裏寫圖片描述

好,已經能夠實現咱們的需求了,是否是學會不少呢。

今天主要講了android自定義View中另外一種的實現,而且還學習了自定義屬性,同窗們下去好好消化下,並本身動手現實一兩個例子吧,好了,今天就講到這裏,謝謝你們。

更多資訊請關注微信平臺,有博客更新會及時通知。愛學習愛技術。

這裏寫圖片描述

相關文章
相關標籤/搜索