天天看點

自定義view實戰之市場上app設定界面的通用SettingView需求圖解需求小結The end

自定義view聽起來就十分牛逼,但是日常開發中或許我們用不到完全的自定義,比如衆多app的設定界面的一些條目,我們隻需要組合成一套可伸縮,可定制的簡單的view就行了,本文就詳細的探讨一下設定界面通用的view設計流程。

需求圖

1 本文将要實作的需求圖

自定義view實戰之市場上app設定界面的通用SettingView需求圖解需求小結The end

qq設定的截圖

自定義view實戰之市場上app設定界面的通用SettingView需求圖解需求小結The end
如上需求圖:我們發現設定界面有很多條目,這些條目相似又不相似,相似在哪呢?左側是文字,最右側是箭頭圖示。可是有的很特别右側顯示文字(如20M)不顯示圖檔(頭像)。如果我們使用普通xml布局一個一個的寫這些條目。不說重複的布局問題,就是你的布局檔案至少也要上幾百行,如果你用ListView 或者recyclerView搭建又考慮不同條目類型的問題,啊啊啊真的好煩。

解需求

1、分析

試想我們假如有一個view:SettingView,在搭建布局時我們隻需向普通的TextView一樣

android:text="測試"
           

就可以輕松的設定文字,向Imageview一樣

android:src="@drawable/qq"
           

就可以輕松的設定圖檔,同時我們還具有view類似Visiable的性質。展示我們的右側圖檔,展示我們的右側文字。這時不就完美了嗎。于是我們想到了自定義view來實作我們的需求

2 實作的功能

自定義view實戰之市場上app設定界面的通用SettingView需求圖解需求小結The end

3 特殊的api

1 xml 中使用:
app:leftText="自定義屬性1"     //設定左側文字

app:showRightPic="true"    //展示右側圖檔
app:rightPic="@drawable/qq"// 設定右側圖檔 ps:隻有showRightPic="true"  設定圖檔才生效

app:showRightText="true"  // 展示右側文字
app:rightText="20M"  // 設定右側文字 ps:隻有showRightText="true"  設定右側文字才生效
2 java代碼中使用:
public void setLeftText(String text)   設定左側文字
public void setRightText(String text) // 設定右側文字
public void setHeadPortrait(Object imgType) // 設定右側圖檔
           

4 具體實作

啰嗦了一大堆終于要上代碼了啊哈,但是還要說下重點:無論是自定義屬性設定文字還是調用java方法設定文字其本質都是在我們的自定義view中獲得文字再設定。

4.1 首先定義SettingView類 這裡我們繼承了LinearLayout并進行了一些方法調用處理

public class SettingView extends LinearLayout {
  @RequiresApi(api = Build.VERSION_CODES.P)
    public SettingView(Context context) {
        this(context, null);
    }

    @RequiresApi(api = Build.VERSION_CODES.P)
    public SettingView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
 @RequiresApi(api = Build.VERSION_CODES.P)
    public SettingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        View view = LayoutInflater
                .from(context)
                .inflate(R.layout.view_setting_page, null);
        this.addView(view);
    }
}
           
可以看到我們的主要代碼在第三個構造方法中,最終使用者就是調用的他,在這個方法中我們把一個布局(R.layout.view_setting_page)轉換成了view添加到我們自定義的不布局中。

4.2 其實R.layout.view_setting_page就是我們的定義SettingView的布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
    android:background="#ffffff"
    android:id="@+id/rl_layout">

    <!-- 分割線 沒啥其他用途-->
    <TextView
        android:background="#ccc"
        android:layout_width="match_parent"
        android:layout_height="1dp" />


    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:text="測試"
        android:textColor="#000000" />

    <ImageButton
        android:focusable="false"
        android:clickable="false"
        android:id="@+id/ib"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:background="@null"
        android:src="@drawable/news_cate_arr" />

    <de.hdodenhof.circleimageview.CircleImageView
        android:visibility="invisible"
        android:id="@+id/img"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_toLeftOf="@id/ib"
        android:paddingRight="5dp"
        android:src="@drawable/qq"
        android:text="測試資料" />
    <!--  注意 上面細節 使用了 invisiable 而不用gone的差別-->
    <TextView
        android:gravity="center"
        android:visibility="gone"
        android:id="@+id/right_text"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_toLeftOf="@id/ib"
        android:paddingRight="5dp"
        android:text="測試資料" />

</RelativeLayout>
           
如果我們使用自定義view不設定又圖檔,不設定又文字,那麼條目就是需求圖中"自定義屬性2"的效果,也是本布局檔案的預設效果(隻設定左側文字時)

4.3 自定義方法(簡單)

/**
     * 設定左側文字的内容
     * @param text  要設定的字元串
     */
    public void setLeftText(String text) {
        leftText.setText(text);
    }

    /**
     * 設定右面側文字的内容
     * @param text  要設定的字元串
     *
     * 注意使用時要控件顯示,由于xml屬性原因預設隐藏的
     */
    public void setRightText(String text){
        rightText.setText(text);
    }

    /**
     * 設定頭像
     *
     * @param imgType 圖檔的類型
     *
     *                注意使用時要控件顯示,由于xml屬性原因預設隐藏的
     */
    public void setHeadPortrait(Object imgType) {
        if (imgType instanceof Drawable) {
            headPortrait.setImageDrawable((Drawable) imgType);
        } else if (imgType instanceof Integer) {
            headPortrait.setImageResource((Integer) imgType);
        } else if (imgType instanceof Bitmap) {
            headPortrait.setImageBitmap((Bitmap) imgType);
        } else if (imgType instanceof Uri) {
            headPortrait.setImageURI((Uri) imgType);
        } else {
            throw new IllegalArgumentException("imgType just can be type:Drawable or recourceId or Bitmap or Uri");
        }

    }
           

4.4自定義屬性 (本文詳解)

自定義view實戰之市場上app設定界面的通用SettingView需求圖解需求小結The end

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--  1、  聲明給哪個自定義控件 添加屬性新的-->
    <declare-styleable name="SettingView">
        <!-- 2 、屬性定義(解釋如下) -->

        <!-- 定義屬性名為leftText,屬性值為String類型-->
        <attr name="leftText" format="string" />

        <!-- 定義屬性名為showRightPic,屬性值為布爾類型-->
        <attr name="showRightPic" format="boolean"/>
        <!-- 定義屬性名為rightPic,屬性值為引用類型,簡單地說就是可以使用用資源id的形式(R.xxx.xxx)-->
        <attr name="rightPic" format="reference"/>

        <attr name="showRightText" format="boolean"/>
        <attr name="rightText" format="string"/>


    </declare-styleable>

</resources>
           
如上圖 到values檔案夾下建立名為attrs的資源檔案

4.4 自定義view中擷取自定義屬性(思考上文具體實作中設定文字本質)

三種擷取方式:(參看下面具體實作代碼注釋,及參考連結)

  1. attrs.getAttributeXXXValue(命名空間,自定義屬性名字)
  2. for (int i=0;i<attrs.getAttributeCount();i++) 從屬性集合中擷取
  3. 使用TypedArray

具體實作

/**
     * 自定義屬性初始化工作
     *
     * @param attrs 屬性集合
     */
    private void initSelfAttrs(AttributeSet attrs, Context context) {
        // 1 左側text的xml設定
        String leftText = attrs.getAttributeValue(NAME_SPACE, "leftText");
        setLeftText(leftText);

        // 獲得xml中設定 showRightPic 屬性值 預設 false
        boolean showRightPic = attrs.getAttributeBooleanValue(NAME_SPACE, "showRightPic", false);
        boolean showRightText = attrs.getAttributeBooleanValue(NAME_SPACE, "showRightText", false);

        // 3 右面text的設定
        if (showRightText){
            rightText.setVisibility(VISIBLE);
            String rightText = attrs.getAttributeValue(NAME_SPACE, "rightText");
            setRightText(rightText);
        }
        // 圖檔的設定還是使用TypeArray 擷取設定
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SettingView);//參見你的attrs中定義<declare-styleable name="SettingView">
        for (int i = 0; i < typedArray.getIndexCount(); i++) {
            int index = typedArray.getIndex(i);
            switch (index) {
                //1  左側文本的xml設定(使用這種方式也行)
//                case R.styleable.SettingView_leftText:
//                    String leftText  = typedArray.getString(index);
//                    setLeftText(leftText);
//                    break;
                case R.styleable.SettingView_rightPic:
                    // 2 右側圖檔的顯示
                    if (showRightPic) {
                        headPortrait.setVisibility(VISIBLE);
                        Drawable drawable = typedArray.getDrawable(index);
                        setHeadPortrait(drawable);
                    }

                    break;
            }
        }
        typedArray.recycle();//回收
    }
           

ps:

1 命名空間使用http://schemas.android.com/apk/res-auto這個字元串就行。

自定義屬性名就是你的attrs中定義的name

2 如果使用typedArray時:context.obtainStyledAttributes(attrs, R.styleable.自定義view類名)

case R.styleable.自定義類名_自定義屬性名 固定格式

attr各種屬性使用詳解參考

命名空間使用及屬性擷取參考

小結

經過一番探讨終于完成了設定界面通用View的設計,本文中也有一些細節不知道讀者們讀出來了嗎,比如圖檔設定時使用了invisiable占位而不用gone,還有一個坑,圖檔文字同時出的現問題這是沒有解決的。作者也懶了就沒實作哈,還有本文的細節,箭頭圖示處點選事件的處理,這是事件分發機制的邏輯這裡就不越俎代庖了。

具體源碼下載下傳 這是我的開源項目中的位址哦,也歡迎小夥伴們下載下傳觀看設定界面的具體邏輯是實作。

The end