自定義View——ShapeTextView(可設(shè)置背景邊框呛凶,Selector選擇器)

簡介

日常項目開發(fā)中,經(jīng)常會遇到把TextView設(shè)置成一個按鈕,就像下面這個:


按鈕.png

而一般的實現(xiàn)方式就是在res/drawable目錄下定義一個drawable.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="10dp" />
    <solid android:color="@color/blue" />
</shape>

然后設(shè)置為TextView的背景弹谁,如果需要選中時改變狀態(tài),可以這么設(shè)置:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/bule" android:state_pressed="false" />
    <item android:drawable="@drawable/red" android:state_pressed="true" />
    <item android:drawable="@drawable/bule" />
</selector>

這樣寫的好處是簡單易懂句喜,但是如果項目中用到了很多不同風(fēng)格的邊框背景時预愤,就會創(chuàng)建非常多的相似的資源文件,比較麻煩藤滥。為了避免這種麻煩鳖粟,這里使用Java代碼來創(chuàng)建Drawable。


狀態(tài)說明.png

自定義ShapeTextView

使用自定義view繼承TextView來實現(xiàn)上面說的效果

定義屬性

在res/values/attrs.xml中添加自定義屬性

<!--自定義矩形邊框的TextView-->
    <declare-styleable name="ShapeTextView">
        <!--是否使用Selector選擇器-->
        <attr name="openSelector" format="boolean" />
        <!--填充色-->
        <attr name="solidColor" format="color" />
        <!--邊框色-->
        <attr name="strokeColor" format="color" />
        <!--按下填充色-->
        <attr name="solidTouchColor" format="color" />
        <!--按下邊框色-->
        <attr name="strokeTouchColor" format="color" />
        <!--邊框?qū)挾?->
        <attr name="strokeWidth" format="dimension" />
        <!--圓角弧度-->
        <attr name="radius" format="dimension" />
        <!--四個角的圓角弧度-->
        <attr name="topLeftRadius" format="dimension" />
        <attr name="topRightRadius" format="dimension" />
        <attr name="bottomLeftRadius" format="dimension" />
        <attr name="bottomRightRadius" format="dimension" />
        <!--虛線邊框?qū)挾?->
        <attr name="dashWidth" format="dimension" />
        <!--虛線邊框間隙-->
        <attr name="dashGap" format="dimension" />
    </declare-styleable>

自定義View繼承TextView

/**
 * @author Lin
 * @date 2019/8/22
 * @description 實現(xiàn)自定義圓角背景
 * 支持
 * 1.四邊圓角
 * 2.指定邊圓角
 * 3.支持填充色以及邊框色,邊框虛線
 * 4.支持按下效果
 */
@SuppressLint("AppCompatCustomView")
public class ShapeTextView extends TextView {
    private boolean openSelector;
    //自定背景邊框Drawable
    private GradientDrawable gradientDrawable;
    //按下時的Drawable
    private GradientDrawable selectorDrawable;
    //填充色
    private int solidColor = 0;
    //邊框色
    private int strokeColor = 0;
    //按下填充色
    private int solidTouchColor = 0;
    //按下邊框色
    private int strokeTouchColor = 0;
    //按下字體色
    private int textTouchColor = 0;
    //邊框?qū)挾?    private int strokeWidth = 0;
    //四個角的弧度
    private float radius;
    private float topLeftRadius;
    private float topRightRadius;
    private float bottomLeftRadius;
    private float bottomRightRadius;
    //邊框虛線的寬度
    float dashWidth = 0;
    //邊框虛線的間隙
    float dashGap = 0;
    //字體色
    private int textColor = 0;


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

    public ShapeTextView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ShapeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
        //默認背景
        gradientDrawable = getNeedDrawable(new float[]{topLeftRadius, topLeftRadius, topRightRadius, topRightRadius,
                        bottomRightRadius, bottomRightRadius, bottomLeftRadius, bottomLeftRadius},
                solidColor, strokeWidth, strokeColor, dashWidth, dashGap);
        //如果設(shè)置了選中時的背景
        if (openSelector) {
            selectorDrawable = getNeedDrawable(new float[]{topLeftRadius, topLeftRadius, topRightRadius, topRightRadius,
                            bottomRightRadius, bottomRightRadius, bottomLeftRadius, bottomLeftRadius},
                    solidTouchColor, strokeWidth, strokeTouchColor, dashWidth, dashGap);

            //動態(tài)生成Selector
            StateListDrawable stateListDrawable = new StateListDrawable();
            //是否按下
            int pressed = android.R.attr.state_pressed;

            stateListDrawable.addState(new int[]{pressed}, selectorDrawable);
            stateListDrawable.addState(new int[]{}, gradientDrawable);

            setBackground(stateListDrawable);
        } else {
            setBackground(gradientDrawable);
        }
    }

    /**
     * 初始化參數(shù)
     *
     * @param context
     * @param attrs
     */
    private void init(Context context, AttributeSet attrs) {
        TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ShapeTextView, 0, 0);

        openSelector = ta.getBoolean(R.styleable.ShapeTextView_openSelector, false);

        solidColor = ta.getInteger(R.styleable.ShapeTextView_solidColor, 0x00000000);
        strokeColor = ta.getInteger(R.styleable.ShapeTextView_strokeColor, 0x00000000);

        solidTouchColor = ta.getInteger(R.styleable.ShapeTextView_solidTouchColor, 0x00000000);
        strokeTouchColor = ta.getInteger(R.styleable.ShapeTextView_strokeTouchColor, 0x00000000);
        textTouchColor = ta.getInteger(R.styleable.ShapeTextView_textTouchColor, 0x00000000);
        textColor = getCurrentTextColor();
        strokeWidth = (int) ta.getDimension(R.styleable.ShapeTextView_strokeWidth, 0);

        //四個角單獨設(shè)置會覆蓋radius設(shè)置
        radius = ta.getDimension(R.styleable.ShapeTextView_radius, 0);
        topLeftRadius = ta.getDimension(R.styleable.ShapeTextView_topLeftRadius, radius);
        topRightRadius = ta.getDimension(R.styleable.ShapeTextView_topRightRadius, radius);
        bottomLeftRadius = ta.getDimension(R.styleable.ShapeTextView_bottomLeftRadius, radius);
        bottomRightRadius = ta.getDimension(R.styleable.ShapeTextView_bottomRightRadius, radius);

        dashGap = ta.getDimension(R.styleable.ShapeTextView_dashGap, 0);
        dashWidth = ta.getDimension(R.styleable.ShapeTextView_dashWidth, 0);

        ta.recycle();
    }

    /**
     * @param radius      四個角的半徑
     * @param colors      漸變的顏色
     * @param strokeWidth 邊框?qū)挾?     * @param strokeColor 邊框顏色
     * @return
     */
    public static GradientDrawable getNeedDrawable(float[] radius, int[] colors, int strokeWidth, int strokeColor) {
        //TODO:判斷版本是否大于16  項目中默認的都是Linear散射 都是從左到右 都是只有開始顏色和結(jié)束顏色
        GradientDrawable drawable;
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            drawable = new GradientDrawable();
            drawable.setOrientation(GradientDrawable.Orientation.LEFT_RIGHT);
            drawable.setColors(colors);
        } else {
            drawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, colors);
        }

        drawable.setCornerRadii(radius);
        drawable.setStroke(strokeWidth, strokeColor);
        drawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
        return drawable;
    }

    /**
     * @param radius      四個角的半徑
     * @param bgColor     背景顏色
     * @param strokeWidth 邊框?qū)挾?     * @param strokeColor 邊框顏色
     * @return
     */
    public static GradientDrawable getNeedDrawable(float[] radius, int bgColor, int strokeWidth, int strokeColor) {
        GradientDrawable drawable = new GradientDrawable();
        drawable.setShape(GradientDrawable.RECTANGLE);
        drawable.setCornerRadii(radius);
        drawable.setStroke(strokeWidth, strokeColor);
        drawable.setColor(bgColor);
        return drawable;
    }

    /**
     * @param radius      四個角的半徑
     * @param bgColor     背景顏色
     * @param strokeWidth 邊框?qū)挾?     * @param strokeColor 邊框顏色
     * @param dashWidth   虛線邊框?qū)挾?     * @param dashGap     虛線邊框間隙
     * @return
     */
    public static GradientDrawable getNeedDrawable(float[] radius, int bgColor, int strokeWidth, int strokeColor, float dashWidth, float dashGap) {
        GradientDrawable drawable = new GradientDrawable();
        drawable.setShape(GradientDrawable.RECTANGLE);
        drawable.setCornerRadii(radius);
        drawable.setStroke(strokeWidth, strokeColor, dashWidth, dashGap);
        drawable.setColor(bgColor);
        return drawable;
    }
}

使用示例

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingTop="50dp">

    <com.lin.module_base.view.ShapeTextView
        android:id="@+id/text1"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_margin="5dp"
        android:gravity="center"
        android:text="@string/about"
        app:openSelector="true"
        app:radius="10dp"
        app:solidColor="@color/white"
        app:solidTouchColor="@color/gray_light"
        app:strokeColor="@color/blue"
        app:strokeTouchColor="@color/red"
        app:strokeWidth="1dp"
        app:textTouchColor="@color/red" />

    <com.lin.module_base.view.ShapeTextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_margin="5dp"
        android:gravity="center"
        android:text="@string/about"
        app:bottomRightRadius="20dp"
        app:openSelector="true"
        app:solidColor="@color/white"
        app:solidTouchColor="@color/gray_light"
        app:strokeColor="@color/blue"
        app:strokeTouchColor="@color/red"
        app:strokeWidth="1dp"
        app:textTouchColor="@color/red"
        app:topLeftRadius="20dp" />

    <com.lin.module_base.view.ShapeTextView
        android:id="@+id/text2"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_margin="5dp"
        android:gravity="center"
        android:text="@string/about"
        app:dashGap="1dp"
        app:dashWidth="2dp"
        app:openSelector="true"
        app:radius="10dp"
        app:solidColor="@color/white"
        app:strokeColor="@color/blue"
        app:strokeTouchColor="@color/red"
        app:strokeWidth="0.5dp"
        app:textTouchColor="@color/red" />

    <com.lin.module_base.view.ShapeTextView
        android:id="@+id/text3"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:layout_margin="5dp"
        android:gravity="center"
        android:text="@string/about"
        app:openSelector="true"
        app:radius="25dp"
        app:solidColor="@color/blue"
        app:strokeColor="@color/blue"
        app:strokeTouchColor="@color/red"
        app:strokeWidth="1dp"
        app:textTouchColor="@color/red" />

</LinearLayout>

效果如下圖:


image.png

踩坑

如果需要selector選擇器的效果拙绊,需要在xml中設(shè)置app:openSelector="true"
然后要給View設(shè)置監(jiān)聽事件

 text1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

總結(jié)

這里我們使用了Java代碼來創(chuàng)建Drawable向图,可以把常用方法提取到工具類中泳秀。
日常開發(fā)中也有其他類似的需要定義CheckBox、RadioButton等背景的榄攀,也可以參考這種做法嗜傅。

參考:
Drawable子類之——StateListDrawable (選擇器)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市檩赢,隨后出現(xiàn)的幾起案子吕嘀,更是在濱河造成了極大的恐慌,老刑警劉巖贞瞒,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件偶房,死亡現(xiàn)場離奇詭異,居然都是意外死亡军浆,警方通過查閱死者的電腦和手機棕洋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乒融,“玉大人掰盘,你說我怎么就攤上這事≡藜荆” “怎么了愧捕?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長申钩。 經(jīng)常有香客問我次绘,道長,這世上最難降的妖魔是什么典蜕? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任断盛,我火速辦了婚禮,結(jié)果婚禮上愉舔,老公的妹妹穿的比我還像新娘钢猛。我一直安慰自己,他們只是感情好轩缤,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布命迈。 她就那樣靜靜地躺著,像睡著了一般火的。 火紅的嫁衣襯著肌膚如雪壶愤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天馏鹤,我揣著相機與錄音征椒,去河邊找鬼。 笑死湃累,一個胖子當(dāng)著我的面吹牛勃救,可吹牛的內(nèi)容都是我干的碍讨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼蒙秒,長吁一口氣:“原來是場噩夢啊……” “哼勃黍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晕讲,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤覆获,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后瓢省,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弄息,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年净捅,在試婚紗的時候發(fā)現(xiàn)自己被綠了疑枯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛔六,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出废亭,到底是詐尸還是另有隱情国章,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布豆村,位于F島的核電站液兽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏掌动。R本人自食惡果不足惜四啰,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粗恢。 院中可真熱鬧柑晒,春花似錦、人聲如沸眷射。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妖碉。三九已至涌庭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間欧宜,已是汗流浹背坐榆。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留冗茸,地道東北人席镀。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓匹中,卻偏偏與公主長得像,于是被迫代替她去往敵國和親愉昆。 傳聞我的和親對象是個殘疾皇子职员,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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

  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,358評論 0 17
  • 前言 如上圖所示跛溉,相信可愛的安卓程序猿們在開發(fā)中經(jīng)常會遇到這種樣式的UI開發(fā)焊切。其實上面這種布局很簡單,沒有難度芳室,只...
    笑哥哥閱讀 3,809評論 0 4
  • 【Android 動畫】 動畫分類補間動畫(Tween動畫)幀動畫(Frame 動畫)屬性動畫(Property ...
    Rtia閱讀 6,106評論 1 38
  • 很早看過這篇文章专肪,并做了筆記,后來看到群里的小伙伴有問相關(guān)Drawable的問題堪侯,就把這篇翻譯過來的文章給放出來了...
    Kotyo閱讀 1,421評論 0 5
  • 1 背景 不能只分析源碼呀嚎尤,分析的同時也要整理歸納基礎(chǔ)知識,剛好有人微博私信讓全面說說Android的動畫伍宦,所以今...
    未聞椛洺閱讀 2,693評論 0 10