android自定義控件_深度解析自定義屬性

本文深入講解了自定義控件的自定義屬性,如有問題或疑問請大家及時私信或評論指出。

目錄


1 什麼是控件的屬性(以TextView和ImageView爲例源碼分析)?

    以TextView爲例,在書寫佈局時TextView控件中layout_width,layout_height就是系統的控件屬性。

 <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_marginLeft="14dp"
        android:layout_toRightOf="@+id/rl_iv"
        android:gravity="center_vertical"
        android:text="組合控件1" />

   我們在書寫佈局xml時或多或少都會有這樣的疑問,爲何在使用不同的控件時有不同的屬性?按照業務需求自定義控件時我們可以使用哪些系統自帶屬性,不能用的屬性又是爲何?再者我們能不能自己定義一個適用自己控件的屬性?原生控件的系統屬性又來自於何處呢?這些疑問我們一點點的來消除。
   在android 軟件開發包SDK中,路徑\platforms\android-xx\data\res\values\attrs.xml中包含所有系統自帶的屬性。源碼簡略如下:

<declare-styleable name="TextView">      
        ..................     
        <attr name="text" format="string" localization="suggested" />       
        <attr name="hint" format="string" />        
        <attr name="textColor" />       
        <attr name="textColorHighlight" />      
        <attr name="textColorHint" />            
        <attr name="textSize" />
        ...................
</declare-styleable>
<declare-styleable name="ImageView">

        <attr name="src" format="reference|color" />
        <attr name="scaleType">
            <enum name="matrix" value="0" />           
            <enum name="fitXY" value="1" />           
            <enum name="fitStart" value="2" />           
            <enum name="fitCenter" value="3" />          
            <enum name="fitEnd" value="4" />          
            <enum name="center" value="5" />            
            <enum name="centerCrop" value="6" />          
            <enum name="centerInside" value="7" />
        </attr>        
        ..................
</declare-styleable>

<declare-styleable name="LinearLayout">
     ..................   
    <attr name="orientation" />
    <attr name="gravity" />
    <attr name="baselineAligned" format="boolean" />
    ..................
 </declare-styleable>

    也就是說我們常用的原生控件屬性都來於此,並且以declare-styleable分組,declare-styleable旁邊有一個name屬性,這個name的取值就是對應所定義的控件類名;組內attr就指屬性,name值是屬性名稱,format限定當前定義的屬性值。
    比如TextView:
    屬性定義:<attr name="text" format="string" localization="suggested" />
    屬性使用:<TextView android:text="組合控件1" />

   由於View是所有控件的基類,所以其名下的屬性適用於所有控件,也就是說繼承關係中所有的子類都可以使用父類定義的屬性同時子類自己還有單獨擴展的一些屬性,這裏想必大家都能理解。下面是整理的一份android常用控件的關係圖,供大家參考。
android常用控件的關係圖
來源依據:
這裏寫圖片描述
    綜上所述,由於自定義控件必然繼承於View或者View子類,所以也就只能使用控件父類所具有的屬性。如果自定義的控件想擴展使用其他屬性(除父類屬性外)就必須自定義屬性了。


2 自定義的控件是否必須要自定義其屬性?

    自定義了控件並不一定要自定義其屬性。比如TextView在xml中沒有使用 android:text=”組合控件1 ” 屬性,但是在代碼中可以調用setText()方法賦值,同理自定義的控件有時也是。舉個自定義組合控件的列子,類似微信底部4個button,點擊時圖標和文字同時變化,自定義類Mybutton簡略代碼如下:

public class MyButton extends LinearLayout {
    private TextView btnText;
    private ImageView imageView;

    private boolean isSelected = false;
    private int imageDefault;
    private int imageSelected;
    private String text;
    public MyButton(Context context) {
        super(context);
        init(context, null);
    }
    public MyButton(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }
    public MyButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }
    /**
     * 初始化 根據佈局文件填充控件
     *
     * @param context
     * @param attrs
     */
    private void init(Context context, AttributeSet attrs) {
        //inflate 函數有3個參數 參數一是填充的佈局文件 參數二 本佈局添加到父控件中 作用類似於addView
        View butttonView = LayoutInflater.from(context).inflate(R.layout.layout_mybutton, this, true);
        imageView = (ImageView) butttonView.findViewById(R.id.mybtn_img);
        btnText = (TextView) butttonView.findViewById(R.id.mybtn_txt);
    }
    /**
     * 使用控件先初始化
     *
     * @param imageDefault 默認圖標
     * @param imageSelected 點擊之後的圖標
     * @param text  底部名稱
     */
    public void setButtonView(int imageDefault, int imageSelected, String text) {
        this.imageDefault = imageDefault;
        this.imageSelected = imageSelected;
        this.text = text;
        imageView.setImageResource(imageDefault);
        btnText.setText(text);
    }

使用時:

 <com.yezhu.myapplication.MyButton
      android:id="@+id/btn_text"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" />

找到id後:

 MyButton myButton = findViewById(R.id.btn_text);
        //調用setButtonView初始化
        myButton.setButtonView(R.drawable.icon_account, R.drawable.icon_account_selected, "賬單");
        myButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
               。。。。。。。。
            }
        });

效果如圖:
這裏寫圖片描述
    當然自定義屬性的話肯定也可以實現,而且比這樣的方式看上去更專業更便利。自定義屬性不需要java代碼去實現,寫在xml中比較簡潔,java代碼做對應的業務邏輯就行,這樣MVC結構層次分明! 綜上所述,當你自定義一個控件並不意味着一定要自定義其屬性,因爲系統自帶的屬性可能已經完全夠用了。

3 有自定義屬性需求如何自定義? 屬性標籤attr 的format都能接受什麼樣的屬性值?

    系統自帶的屬性attr.xml放於/res/values/文件夾中,同樣我們在項目工程中也存在一樣的目錄結構,因此需自定義屬性的時候在項目res/values中新建attrs.xml,然後仿着系統屬性格式書寫,比如我創建的適用於自定義控件SettingItemView ,自定義屬性attrs.xml文件代碼如下:

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

    <!--應用於 設置頁面的 item-->
    <declare-styleable name="SettingItemView">
        <!--二級標題內容-->
        <attr name="content" format="string" />
        <!--是否顯示左側icon-->
        <attr name="showLeftImage" format="boolean" />
        <!--顯示icon的話 資源引用-->
        <attr name="imagesrc" format="reference|color" />
        <!--是否顯示開關-->
        <attr name="showTurnOnToggle" format="boolean" />
        <!--打開開關-->
        <attr name="turnOnToggle" format="boolean" />
        <!--是否顯示右側 跳轉標誌>-->
        <attr name="showJump" format="boolean" />
        <!--加一個shape 頂部有圓角 中間無圓角 底部有-->
        <attr name="backgroundshape">
            <enum name="top" value="100" />
            <enum name="middle" value="101" />
            <enum name="bottom" value="102" />
        </attr>
    </declare-styleable>
</resources>

使用時:

 <com.yezhu.myapplication.SettingItemView
      android:id="@+id/siv_see"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:clickable="true"
      yezhu:backgroundshape="top"
      yezhu:content="看一看"
      yezhu:imagesrc="@drawable/setting_qqqun"
      yezhu:showJump="true"
      yezhu:showLeftImage="true" />

效果圖:
這裏寫圖片描述
    在書寫屬性時,如<attr name="imagesrc" format="reference|color" />
總會存在疑問fomat到底支持什麼類型的屬性值?自定義的時候該怎麼選擇呢?簡單的歸納如下:

  • reference:引用資源
    舉例說明ImageView:
    屬性定義:<attr name="src" format="reference|color" />
    屬性使用:<ImageView android:src="@mipmap/back" />

  • string:字符串
    舉例說明TextView :
    屬性定義:<attr name="text" format="string" localization="suggested" />
    屬性使用:<TextView android:text="博客"/>

  • Color:顏色
    舉例說明TextView:
    屬性定義:<attr name="textColor" />
    屬性使用:<TextView android:textColor="#e62753" />

  • boolean:布爾值
    屬性定義:<attr name="clickable" format="boolean" />
    屬性使用:<Button android:clickable="true" />

  • dimension:尺寸值
    屬性定義:<attr name="padding" format="dimension" />
    屬性使用:<View android:layout_width="30dp"/>
  • float:浮點型
    屬性定義:<attr name="layout_weight" format="float" />
    屬性使用:<LinearLayout android:layout_weight="1.0"/>
  • integer:整型
    屬性定義:<attr name="startYear" format="integer" />
    屬性使用:<DatePicker android:startYear="2018"/>

  • fraction:百分數(動畫常用,以平移動畫爲例)
    屬性定義:

 <declare-styleable name="TranslateAnimation">
        <attr name="fromXDelta" format="float|fraction" />
        <attr name="toXDelta" format="float|fraction" />
        <attr name="fromYDelta" format="float|fraction" />
        <attr name="toYDelta" format="float|fraction" />
    </declare-styleable>

屬性使用: TranslateAnimation 類的構造中使用到。

 public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
        throw new RuntimeException("Stub!");
    }
  • enum:枚舉類型
    屬性定義:
<declare-styleable name="ImageView">
         <attr name="scaleType">            
            <enum name="matrix" value="0" />         
            <enum name="fitXY" value="1" />           
            <enum name="fitStart" value="2" />            
            <enum name="fitCenter" value="3" />           
            <enum name="fitEnd" value="4" />            
            <enum name="center" value="5" />        
            <enum name="centerCrop" value="6" />           
            <enum name="centerInside" value="7" />
        </attr>
</declare-styleable>

屬性使用:

 <ImageView
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:scaleType="center" />

圖示:
這裏寫圖片描述

  • flag:位或運算
    屬性定義:<attr name="clickable" format="boolean" />
    屬性使用:<LinearLayout android:showDividers="none"/>
    注:android:showDividers 添加分割線。

以上講述瞭如何自定義屬性以及format支持的屬性類型。

最後補充一點 命名空間 的問題。

通常來說,命名空間是唯一識別的一套名字,這樣當對象來自不同的地方但是名字相同的時候就不會含糊不清了。使用擴展標記語言的時候,XML的命名空間是所有元素類別和屬性的集合。元素類別和屬性的名字是可以通過唯一XML命名空間來唯一。 —— [ 百度百科 ]

  在使用系統屬性時都帶着android:,這裏的android:就是在根佈局中引入的命名空間xmlns:android="http://schemas.android.com/apk/res/android"意味着到android系統中查找該屬性來源。只有引入了命名空間,xml文件才知道控件該去哪裏找屬性。這就提醒我們自定義屬性時要引入命名空間,有2種方式

  • 1) xmlns:yezhu="http://schemas.android.com/apk/res-auto",res-auto是自動幫你查找;
  • 2)xmlns:yezhu="http://schemas.android.com/apk/com.yezhu.myapplication" com.yezhu.myapplication是包名,直接指出到我們應用程序下查找。

4 設置format屬性值後自定義類代碼中又如何才能獲取到?

   在此問題之前,大家肯定用過LayoutInflate.from(上下文).inflate(參數)這個方法,如下:

 View settingView = LayoutInflater.from(context).inflate(R.layout.layout_item_setting, this, true);

   那麼這個方法到底是如何解析視圖的呢?如何把佈局xml文件轉換爲View?曾看過一個博主分析其過程(文章末附有鏈接),解析過程大體如下:
1)使用Pull解析方式遍歷xml文件中的所有節點
2)遍歷到具體結點時,根據節點名稱生成對應的View對象
3)在生成View對象是,將AttributeSet(屬性集)以及Context傳遞給View對象的構造方法,在構造方法中,View或者其子類將通過AttributeSet獲取自身的屬性列表,並用來初始化View。

   一語中的,總結的非常到位。

   那麼,我們自定義控件時同理,在xml佈局文件中引用控件時,走的是兩個參數的構造,控件屬性都於AttributeSet集中,要想獲得控件的自定義屬性也是從參數attributeSet中獲取。獲取屬性之後根據屬性值再具體代碼實現。舉例代碼如下:

 private void initView(Context context, AttributeSet attrs) {
        //初始化時 把佈局文件轉換成view並填充
        View settingView = LayoutInflater.from(context).inflate(R.layout.layout_item_setting, this, true);
        tvContent = (TextView) settingView.findViewById(R.id.tv_content);
        scToggle = (SwitchCompat) settingView.findViewById(R.id.sc_toggle);

        TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.SettingItemView);
        String content = attributes.getString(R.styleable.SettingItemView_content);
        tvContent.setText(content);
        //佈局文件中 默認是否打開開關
        boolean turnOnToggle = attributes.getBoolean(R.styleable.SettingItemView_turnOnToggle, false);
        boolean showTurnOnToggle = attributes.getBoolean(R.styleable.SettingItemView_showTurnOnToggle, false);
        if (showTurnOnToggle) {
            scToggle.setVisibility(View.VISIBLE);
            scToggle.setChecked(turnOnToggle);
        } else {
            scToggle.setVisibility(GONE);
        }
    }

關於AttributeSet和TypedArray:

構造函數中有個參數是AttributeSet attrs,api文檔中如此定義AttributeSet :A collection of attributes, as found associated with a tag in an XML document. if you use AttributeSet directly then you will need to manually check for resource references (with getAttributeResourceValue(int, int)) and do the resource lookup yourself if needed.

   簡譯:與XML文檔相關聯的屬性集合。如果直接使用AttributeSet,則需要手動檢查資源引用。

XmlPullParser parser = resources.getXml(myResource);
AttributeSet attributes = Xml.asAttributeSet(parser);

   深究的話 AttributeSet 內部是一個pull解析器,解析xml中的控件的屬性,並以鍵值對形式保存,通過AttributeSet 源碼就可以看到:

public interface AttributeSet {
    int getAttributeCount();
    String getAttributeName(int var1);
    String getAttributeValue(int var1);

    ........
    int getAttributeListValue(String var1, String var2, String[] var3, int var4);
    boolean getAttributeBooleanValue(String var1, String var2, boolean var3);
    int getAttributeResourceValue(String var1, String var2, int var3);
    int getAttributeIntValue(String var1, String var2, int var3);

    .........
}

於是便可以通過下面的代碼來獲取控件屬性:

       //測試屬性
        int attributeCount = attrs.getAttributeCount();
        for (int i = 0; i < attributeCount; i++) {
            String attrName = attrs.getAttributeName(i);
            String attrVal = attrs.getAttributeValue(i);
            Log.e("SettingItemView", "attrName = " + attrName + " , attrVal = " + attrVal);
        }

log輸出:

03-02 14:51:11.393 16226-16226/com.yezhu.myapplication E/SettingItemView: attrName = id , attrVal = @2131427437
03-02 14:51:11.393 16226-16226/com.yezhu.myapplication E/SettingItemView: attrName = clickable , attrVal = true
03-02 14:51:11.393 16226-16226/com.yezhu.myapplication E/SettingItemView: attrName = layout_width , attrVal = -1
03-02 14:51:11.393 16226-16226/com.yezhu.myapplication E/SettingItemView: attrName = layout_height , attrVal = -2
03-02 14:51:11.393 16226-16226/com.yezhu.myapplication E/SettingItemView: attrName = content , attrVal = 看一看
03-02 14:51:11.393 16226-16226/com.yezhu.myapplication E/SettingItemView: attrName = showLeftImage , attrVal = true
03-02 14:51:11.393 16226-16226/com.yezhu.myapplication E/SettingItemView: attrName = imagesrc , attrVal = @2130837608
03-02 14:51:11.393 16226-16226/com.yezhu.myapplication E/SettingItemView: attrName = showJump , attrVal = true
03-02 14:51:11.393 16226-16226/com.yezhu.myapplication E/SettingItemView: attrName = backgroundshape , attrVal = 100

   毫無疑問,通過AttributeSet可以獲得佈局文件中定義的所有屬性的key和value。但是log這一條:attrName = imagesrc , attrVal = @2130837608 拿到的引用變成了@數字,這看不懂啊,我本將心向明月,奈何明月照溝渠啊。然而通過typeArray獲取可以直接獲取到引用,如下:

03-02 15:05:03.463 17799-17799/com.yezhu.myapplication I/System.out: --typedArray2130837608

   拿到這樣的引用我們可以直接使用。因爲android所有資源文件在R文件中都會對應一個整型常量,通過此整型常量ID值就可以拿到資源文件。區別在哪呢?使用AttributeSet 去獲取屬性值,第一步先拿到id,第二步再去解析id;TypedArray正是簡化了此過程。

5 舉例: 設置界面中每一個item格式都相似, 自定義詳細過程

   先看一下效果,如圖,上中下三個按鈕background是shape,點擊有select效果;佈局文件中可以寫明是否需要左側icon、右側toggle或者跳轉箭頭。動圖:
這裏寫圖片描述

SettingItemView類:

/**
 * 一般是設置頁面 中
 */

public class SettingItemView extends RelativeLayout {

    private TextView tvContent;
    private SwitchCompat scToggle;
    private ImageView ivIcon;
    private ImageView ivTump;
    private RelativeLayout rlRoot;
    private static final int TOP = 100;
    private static final int MID = 101;
    private static final int BOTTOM = 102;

    public SettingItemView(Context context) {
        this(context, null);

    }

    public SettingItemView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SettingItemView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context, attrs);
    }

    /**
     * 初始化佈局
     *
     * @param context
     * @param attrs
     */
    @SuppressLint("WrongConstant")
    private void initView(Context context, AttributeSet attrs) {

        //初始化時 把佈局文件轉換成view並填充
        View settingView = LayoutInflater.from(context).inflate(R.layout.layout_item_setting, this, true);
        rlRoot = (RelativeLayout) settingView.findViewById(R.id.rl_root);
        tvContent = (TextView) settingView.findViewById(R.id.tv_content);
        scToggle = (SwitchCompat) settingView.findViewById(R.id.sc_toggle);
        ivIcon = (ImageView) settingView.findViewById(R.id.iv_icon);
        ivTump = (ImageView) settingView.findViewById(R.id.iv_tump);

        TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.SettingItemView);

        String content = attributes.getString(R.styleable.SettingItemView_content);
        tvContent.setText(content);

        //佈局文件中 默認是否打開開關
        boolean turnOnToggle = attributes.getBoolean(R.styleable.SettingItemView_turnOnToggle, false);
        boolean showTurnOnToggle = attributes.getBoolean(R.styleable.SettingItemView_showTurnOnToggle, false);
        if (showTurnOnToggle) {
            scToggle.setVisibility(View.VISIBLE);
            scToggle.setChecked(turnOnToggle);
        } else {
            scToggle.setVisibility(GONE);
        }

        //佈局文件中 是否顯示左側的icon
        boolean showLeftImage = attributes.getBoolean(R.styleable.SettingItemView_showLeftImage, false);
        int resourceId = attributes.getResourceId(R.styleable.SettingItemView_imagesrc, 0);
        if (showLeftImage) {
            ivIcon.setVisibility(View.VISIBLE);
            ivIcon.setImageResource(resourceId);
        } else {
            ivIcon.setVisibility(View.GONE);
        }

        //佈局文件中 是否顯示右側跳轉圖片
        boolean showJump = attributes.getBoolean(R.styleable.SettingItemView_showJump, false);
        if (showJump) {
            ivTump.setVisibility(VISIBLE);
        } else {
            ivTump.setVisibility(GONE);
        }

        //佈局文件中 當前item的shape和selector效果
        int bgShape = attributes.getInt(R.styleable.SettingItemView_backgroundshape, MID);
        switch (bgShape) {
            case TOP:
                rlRoot.setBackgroundResource(R.drawable.top_layout_selector);
                break;

            case MID:
                rlRoot.setBackgroundResource(R.drawable.mid_layout_selector);
                break;

            case BOTTOM:
                rlRoot.setBackgroundResource(R.drawable.bottom_layout_selector);
                break;

            default:

                break;
        }

//
//        //測試屬性
//        int attributeCount = attrs.getAttributeCount();
//        for (int i = 0; i < attributeCount; i++) {
//            String attrName = attrs.getAttributeName(i);
//            String attrVal = attrs.getAttributeValue(i);
//            Log.e("--attributes", "attrName = " + attrName + " , attrVal = " + attrVal);
//        }
//        System.out.println("--------------------");
//
//        System.out.println("--typedArray" + resourceId);

    }
}

int bgShape = attributes.getInt(R.styleable.SettingItemView_backgroundshape, MID)是根據佈局中定義的Top(有圓角)、Middle(無圓角)和Bottom(有圓角)分別設置不同shape背景。

屬性文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:yezhu="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="3dp"
    tools:context="com.yezhu.myapplication.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <!--<com.yezhu.myapplication.MyButton-->
        <!--android:id="@+id/btn_text"-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="wrap_content" />-->

        <com.yezhu.myapplication.SettingItemView
            android:id="@+id/siv_see"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:clickable="true"
            yezhu:backgroundshape="top"
            yezhu:content="看一看"
            yezhu:imagesrc="@drawable/setting_qqqun"
            yezhu:showJump="true"
            yezhu:showLeftImage="true" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@color/border" />

        <com.yezhu.myapplication.SettingItemView
            android:id="@+id/siv_shake"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:clickable="true"
            yezhu:backgroundshape="middle"
            yezhu:content="搖一搖"
            yezhu:imagesrc="@drawable/setting_pingfen"
            yezhu:showLeftImage="true"
            yezhu:showTurnOnToggle="false" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@color/border" />

        <com.yezhu.myapplication.SettingItemView
            android:id="@+id/siv_sound_open"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:clickable="true"
            yezhu:backgroundshape="bottom"
            yezhu:content="聲音開關"
            yezhu:imagesrc="@drawable/setting_tuijian"
            yezhu:showLeftImage="true"
            yezhu:showTurnOnToggle="true"
            yezhu:turnOnToggle="true" />
    </LinearLayout>

</LinearLayout>

activity中引用:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.siv_see).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, SeeSomethingActivity.class));
            }
        });

佈局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:yezhu="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="3dp"
    tools:context="com.yezhu.myapplication.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <!--<com.yezhu.myapplication.MyButton-->
        <!--android:id="@+id/btn_text"-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="wrap_content" />-->

        <com.yezhu.myapplication.SettingItemView
            android:id="@+id/siv_see"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:clickable="true"
            yezhu:backgroundshape="top"
            yezhu:content="看一看"
            yezhu:imagesrc="@drawable/setting_qqqun"
            yezhu:showJump="true"
            yezhu:showLeftImage="true" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@color/border" />

        <com.yezhu.myapplication.SettingItemView
            android:id="@+id/siv_shake"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:clickable="true"
            yezhu:backgroundshape="middle"
            yezhu:content="搖一搖"
            yezhu:imagesrc="@drawable/setting_pingfen"
            yezhu:showLeftImage="true"
            yezhu:showTurnOnToggle="false" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@color/border" />

        <com.yezhu.myapplication.SettingItemView
            android:id="@+id/siv_sound_open"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:clickable="true"
            yezhu:backgroundshape="bottom"
            yezhu:content="聲音開關"
            yezhu:imagesrc="@drawable/setting_tuijian"
            yezhu:showLeftImage="true"
            yezhu:showTurnOnToggle="true"
            yezhu:turnOnToggle="true" />
    </LinearLayout>

</LinearLayout>

最後效果如上圖所示。
代碼地址:點擊下載代碼_android深度解析自定義屬性


總結

學習總結自定義控件的過程,主要能熟悉

  • 自定義屬性
  • xml與View的關係
  • 事件分發機制

東西有點多,花費了好多精力才理清楚、梳理明白,大家有問題或疑問可以留言或者私信我。回首向來蕭瑟處,也無風雨也無晴。麻煩點個贊可好?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章