Android-自定義控件開發(fā)

前言

今天總結(jié)一下Android開發(fā)中的自定義控件的開發(fā)徙赢,Android中所有控件和布局的基類都是View翎朱,自定義控件也就是繼承View或者View的派生類,然后再重寫類中的內(nèi)部方法。
通常來說自定義控件分為三種:
1.自定義View:繼承View
2.基于現(xiàn)有組件:繼承View的派生類
3.組合的方式:自定義控件中包含了其他的組件
下圖體現(xiàn)了各種控件的繼承關(guān)系:


View派生類.png

來實(shí)踐一下

有一個(gè)需求是這樣的喘漏,需要在頁面中構(gòu)建一種菜單列表,菜單單個(gè)項(xiàng)目如圖所示:

item.png

當(dāng)我們構(gòu)建這樣的菜單列表华烟,不必每一個(gè)item在布局里都重寫一次翩迈,可以提取相同的元素,將單個(gè)項(xiàng)目封裝成控件盔夜,需要時(shí)直接引用即可负饲。
思路:
分析一下布局中的元素,左邊的icon喂链,左邊的titleText返十,右邊的hintText,右邊的icon椭微,分割線等洞坑。抽取出相同的部分,不同的部分通過自定義樣式類型來區(qū)分蝇率,實(shí)現(xiàn)最大程度上的復(fù)用检诗。我使用第三種組合的方式來編寫。

1.定義組件布局:
按需求編寫出對應(yīng)布局瓢剿,包含了所有元素

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/menu_item_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/white"
    android:orientation="vertical">

    <View
        android:id="@+id/divide_line_view"
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:layout_marginLeft="49.5dp"
        android:background="@color/divide"
        android:visibility="gone" />

    <View
        android:id="@+id/divide_area_view"
        android:layout_width="match_parent"
        android:layout_height="10dp"
        android:background="@color/main_bg"
        android:visibility="gone" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@drawable/ripple_with_mask"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:paddingLeft="15dp"
        android:paddingRight="15dp">

        <ImageView
            android:id="@+id/menu_item_icon_img"
            android:layout_width="24dp"
            android:layout_height="24dp" />

        <TextView
            android:id="@+id/menu_item_text"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10.5dp"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:textSize="15sp"
            tools:text="標(biāo)題文字" />

        <TextView
            android:id="@+id/menu_item_text_hint"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/mc_txt_gray_bg"
            android:textSize="13sp"
            android:layout_marginRight="10dp"
            tools:text="提示文字" />

        <ImageView
            android:id="@+id/menu_item_red_hint"
            android:layout_width="10dp"
            android:layout_height="10dp"
            android:layout_marginRight="10dp"
            android:src="@drawable/msg_red"
            android:visibility="gone" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/arrow_right" />
    </LinearLayout>

</LinearLayout>

1.1.color.xml:

<!--布局顏色-->
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="white">#ffffff</color>
    <color name="divide">#e6ebf0</color>
    <color name="main_bg">#F3F3F3</color>
    <color name="mc_txt_gray_bg">#a6a6a6</color>
    <color name="ripple_color">#19333333</color>
</resources>

1.2.attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MenuItemLayout">
        <attr name="title_text" format="string" />
        <attr name="hint_text" format="string" />
        <attr name="icon_reference" format="reference" />
        <attr name="icon_uri" format="string" />
        <attr name="jump_url" format="string" />
        <attr name="divide_line_style" format="integer" />
    </declare-styleable>
</resources>

1.3.紅點(diǎn)圖片msg_red.png和箭頭圖標(biāo)arrow_right.png

2.繼承FrameLayout逢慌,復(fù)寫三個(gè)構(gòu)造函數(shù)。
2.1.讀取布局文件中的屬性參數(shù)(見init方法):
如果在布局中傳入了自定義的參數(shù)间狂,可以在構(gòu)造函數(shù)中從AttributeSet讀取并設(shè)置給控件攻泼。
2.2.在MenuItemLayout中找到了所有的控件并初始化它們,分割線樣式設(shè)置了三種:白色區(qū)域、灰色分割線忙菠、以及無樣式何鸡。onclickId是為了給動(dòng)態(tài)生成的布局設(shè)置點(diǎn)擊事件,根據(jù)屬性來控制控件的顯示牛欢。

package com.example.tianxiying.mytestapplication;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;


/**
 * Created by tianxiying on 2018/3/1.
 */

public class MenuItemLayout extends FrameLayout {
    private Context mContext;
    private View mView;
    private TextView titleTv;
    private TextView hintTv;
    private ImageView redHintImg;
    private ImageView iconImg;
    private OnClickListener onClickListener;
    private String titleText;
    private String hintText;
    private String iconImgUri;
    private String jumpUrl;
    private int iconImgId;
    private String onclickId;
    public static final int NO_LINE = 0;
    public static final int DIVIDE_LINE = 1;
    public static final int DIVIDE_AREA = 2;
    public int divideLineStyle = NO_LINE;
    private boolean isShowRedHintImg = false;

    public int getIconImgId() {
        return iconImgId;
    }

    public void setIconImgId(int iconImgId) {
        if (iconImgId != 10000) {
            this.iconImgId = iconImgId;
            iconImg.setImageResource(iconImgId);
        }
    }

    public String getTitleText() {
        return titleText;
    }

    public void setTitleText(String titleText) {
        if (titleText != null) {
            this.titleText = titleText;
            titleTv.setText(titleText);
        }
    }

    public String getHintText() {
        return hintText;
    }

    public void setHintText(String hintText) {
        if (hintText != null) {
            this.hintText = hintText;
            hintTv.setText(hintText);
        }
    }

    public boolean isShowRedHintImg() {
        return isShowRedHintImg;
    }

    public void setShowRedHintImg(boolean showRedHintImg) {
        isShowRedHintImg = showRedHintImg;
        redHintImg.setVisibility(showRedHintImg ? VISIBLE : GONE);
    }

    public String getJumpUrl() {
        return jumpUrl;
    }

    public void setJumpUrl(String jumpUrl) {
        if (jumpUrl != null) {
            this.jumpUrl = jumpUrl;
        }
    }

    public String getOnclickId() {
        return onclickId;
    }

    public void setOnclickId(String onclickId) {
        this.onclickId = onclickId;
    }

    public MenuItemLayout(Context context) {
        this(context, null);
    }

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

    public MenuItemLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        mContext = context;
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mView = inflater.inflate(R.layout.item_menu_layout, this, true);
        titleTv = (TextView) mView.findViewById(R.id.menu_item_text);
        hintTv = (TextView) mView.findViewById(R.id.menu_item_text_hint);
        iconImg = (ImageView) mView.findViewById(R.id.menu_item_icon_img);
        redHintImg = (ImageView) mView.findViewById(R.id.menu_item_red_hint);

        TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.MenuItemLayout);
        setTitleText(a.getString(R.styleable.MenuItemLayout_title_text));
        setHintText(a.getString(R.styleable.MenuItemLayout_hint_text));
        setIconImgId(a.getResourceId(R.styleable.MenuItemLayout_icon_reference, 10000));
        setJumpUrl(a.getString(R.styleable.MenuItemLayout_jump_url));
        divideLineStyle = a.getInt(R.styleable.MenuItemLayout_divide_line_style, NO_LINE);
        setDivideLine(divideLineStyle);
    }

    public void setDivideLine(int bootomLineStyle) {
        View lineView = findViewById(R.id.divide_line_view);
        View areaView = findViewById(R.id.divide_area_view);
        lineView.setVisibility(GONE);
        areaView.setVisibility(GONE);
        if (bootomLineStyle == DIVIDE_LINE) {
            lineView.setVisibility(VISIBLE);
        } else if (bootomLineStyle == DIVIDE_AREA) {
            areaView.setVisibility(VISIBLE);
        }
    }

    public void setViewOnlickListener(OnClickListener onlickListener) {
        this.onClickListener = onlickListener;
        mView.setOnClickListener(onlickListener);
    }

    public TextView getTitleTv() {
        return titleTv;
    }

    public TextView getHintTv() {
        return hintTv;
    }
}

3.在布局中引用MenuItemLayout:
創(chuàng)建MainActivity為應(yīng)用的入口骡男,修改activity_main.xml代碼:
在布局中我隨便寫了幾個(gè)菜單作為參考,菜單項(xiàng)目可以再布局中寫傍睹,也可以在代碼中動(dòng)態(tài)生成然后添加進(jìn)布局中(可以支持后臺(tái)動(dòng)態(tài)配置)隔盛。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="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:orientation="vertical"
    android:background="@color/main_bg"
    tools:context="com.example.tianxiying.mytestapplication.MainActivity">

    <com.example.tianxiying.mytestapplication.MenuItemLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:divide_line_style="0"
        app:icon_reference="@mipmap/ic_launcher"
        app:title_text="個(gè)人中心" />

    <com.example.tianxiying.mytestapplication.MenuItemLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:divide_line_style="1"
        app:icon_reference="@mipmap/ic_launcher"
        app:hint_text="查看收藏過的書簽"
        app:title_text="我的收藏" />

    <com.example.tianxiying.mytestapplication.MenuItemLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:divide_line_style="1"
        app:icon_reference="@mipmap/ic_launcher"
        app:hint_text="我關(guān)注過的人在這里"
        app:title_text="我的關(guān)注" />

    <com.example.tianxiying.mytestapplication.MenuItemLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:divide_line_style="2"
        app:icon_reference="@mipmap/ic_launcher"
        app:hint_text="有什么問題點(diǎn)這里"
        app:title_text="我的客服" />

    <com.example.tianxiying.mytestapplication.MenuItemLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:divide_line_style="1"
        app:icon_reference="@mipmap/ic_launcher"
        app:title_text="設(shè)置" />
</LinearLayout>

運(yùn)行起來如圖所示:


運(yùn)行圖.png

結(jié)尾:
以上只是組合自定義控件的一個(gè)簡單的例子,目的在于敘述自定義控件的方法和思路拾稳,如果想要更多的效果可以重寫組件的onMeasure吮炕、onLayout、onDraw來實(shí)現(xiàn)访得。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末龙亲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子悍抑,更是在濱河造成了極大的恐慌鳄炉,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搜骡,死亡現(xiàn)場離奇詭異迎膜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)浆兰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來珊豹,“玉大人簸呈,你說我怎么就攤上這事〉瓴瑁” “怎么了蜕便?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贩幻。 經(jīng)常有香客問我轿腺,道長,這世上最難降的妖魔是什么丛楚? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任族壳,我火速辦了婚禮,結(jié)果婚禮上趣些,老公的妹妹穿的比我還像新娘仿荆。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布拢操。 她就那樣靜靜地躺著锦亦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪令境。 梳的紋絲不亂的頭發(fā)上杠园,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音舔庶,去河邊找鬼抛蚁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛栖茉,可吹牛的內(nèi)容都是我干的篮绿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼吕漂,長吁一口氣:“原來是場噩夢啊……” “哼亲配!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起惶凝,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對情侶失蹤吼虎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后苍鲜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體思灰,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年混滔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了洒疚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坯屿,死狀恐怖油湖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情领跛,我是刑警寧澤乏德,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站吠昭,受9級(jí)特大地震影響喊括,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矢棚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一郑什、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蒲肋,春花似錦蹦误、人聲如沸劫拢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舱沧。三九已至,卻和暖如春偶洋,著一層夾襖步出監(jiān)牢的瞬間熟吏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工玄窝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牵寺,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓恩脂,卻偏偏與公主長得像帽氓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子俩块,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容