自定義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);
        //默認(rèn)背景
        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);

        //四個角單獨(dú)設(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  項目中默認(rèn)的都是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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容