Android 自定义控件: 圆形、圆角、边框、渐变、下拉关闭、阴影、模糊

效果图

WechatIMG24.png

源码

简单使用

  1. gradle 集成
api 'com.enowr.android:Widgets:0.0.8'
  1. 相关属性
        <attr name="vh_isAreaClick" format="boolean" />                                     <!--是否区域内可点击,默认 false-->
        <attr name="vh_isAreaPadding" format="boolean" />                                   <!--是否区域包含padding值,默认 true-->
        <attr name="vh_isDropdownClose" format="boolean" />                                 <!-- 是否开启滑动关闭,ViewGroup中使用,默认 false -->

        <!--圆形、圆角-->
        <attr name="vh_isCircle" format="boolean" />                                        <!--是否是圆形-->
        <attr name="vh_cornersRadius" format="dimension" />                                 <!--四周圆角角度-->
        <attr name="vh_cornersRadiusTopLeft" format="dimension" />                          <!--四周圆角角度 左上-->
        <attr name="vh_cornersRadiusTopRight" format="dimension" />                         <!--四周圆角角度 右上-->
        <attr name="vh_cornersRadiusBottomLeft" format="dimension" />                       <!--四周圆角角度 左下-->
        <attr name="vh_cornersRadiusBottomRight" format="dimension" />                      <!--四周圆角角度 右下-->

        <!--文本颜色-->
        <attr name="vh_textColor" format="color" />
        <attr name="vh_textColorNormal" format="color" />
        <attr name="vh_textColorPressed" format="color" />
        <attr name="vh_textColorUnable" format="color" />
        <attr name="vh_textColorChecked" format="color" />

        <!--前景-->
        <attr name="vh_prospectColor" format="color" />                                     <!--前景颜色-->
        <attr name="vh_prospectColorNormal" format="color" />
        <attr name="vh_prospectColorPressed" format="color" />
        <attr name="vh_prospectColorChecked" format="color" />
        <attr name="vh_prospectColorUnable" format="color" />

        <attr name="vh_prospectDrawable" format="reference" />                              <!--前景图片-->
        <attr name="vh_prospectDrawableNormal" format="reference" />
        <attr name="vh_prospectDrawablePressed" format="reference" />
        <attr name="vh_prospectDrawableChecked" format="reference" />
        <attr name="vh_prospectDrawableUnable" format="reference" />

        <!--背景-->
        <attr name="vh_backgroundColor" format="color" />                                   <!--背景颜色 会被系统背景覆盖-->
        <attr name="vh_backgroundColorNormal" format="color" />
        <attr name="vh_backgroundColorPressed" format="color" />
        <attr name="vh_backgroundColorChecked" format="color" />
        <attr name="vh_backgroundColorUnable" format="color" />

        <attr name="vh_backgroundDrawable" format="reference" />                            <!--背景图片 会被系统背景覆盖-->
        <attr name="vh_backgroundDrawableNormal" format="reference" />
        <attr name="vh_backgroundDrawablePressed" format="reference" />
        <attr name="vh_backgroundDrawableChecked" format="reference" />
        <attr name="vh_backgroundDrawableUnable" format="reference" />

        <!--边框-->
        <attr name="vh_borderColor" format="color" />                                       <!--边框颜色-->
        <attr name="vh_borderColorNormal" format="color" />
        <attr name="vh_borderColorPressed" format="color" />
        <attr name="vh_borderColorChecked" format="color" />
        <attr name="vh_borderColorUnable" format="color" />

        <attr name="vh_borderWidth" format="dimension" />                                   <!--边框宽度-->
        <attr name="vh_borderWidthNormal" format="dimension" />
        <attr name="vh_borderWidthPressed" format="dimension" />
        <attr name="vh_borderWidthChecked" format="dimension" />
        <attr name="vh_borderWidthUnable" format="dimension" />

        <attr name="vh_borderDashArray" format="reference" />                               <!--虚线间距数组,最少两个-->
        <attr name="vh_borderDashWidth" format="dimension" />                               <!--虚线边框宽度-->
        <attr name="vh_borderDashGap" format="dimension" />                                 <!--虚线边框间隔-->

        <!--颜色渐变 Gradient 相关,在背景色绘制之后-->
        <attr name="vh_gradientColorStart" format="color" />                                <!--渐变颜色开始-->
        <attr name="vh_gradientColorStartNormal" format="color" />
        <attr name="vh_gradientColorStartPressed" format="color" />
        <attr name="vh_gradientColorStartChecked" format="color" />
        <attr name="vh_gradientColorStartUnable" format="color" />
        <attr name="vh_gradientColorCenter" format="color" />                               <!--渐变颜色中间-->
        <attr name="vh_gradientColorCenterNormal" format="color" />
        <attr name="vh_gradientColorCenterPressed" format="color" />
        <attr name="vh_gradientColorCenterChecked" format="color" />
        <attr name="vh_gradientColorCenterUnable" format="color" />
        <attr name="vh_gradientColorEnd" format="color" />                                  <!--渐变颜色结束-->
        <attr name="vh_gradientColorEndNormal" format="color" />
        <attr name="vh_gradientColorEndPressed" format="color" />
        <attr name="vh_gradientColorEndChecked" format="color" />
        <attr name="vh_gradientColorEndUnable" format="color" />

        <attr name="vh_gradientColorArray" format="reference" />                            <!--渐变颜色数组,最少两个-->
        <attr name="vh_gradientColorArrayNormal" format="reference" />
        <attr name="vh_gradientColorArrayPressed" format="reference" />
        <attr name="vh_gradientColorArrayChecked" format="reference" />
        <attr name="vh_gradientColorArrayUnable" format="reference" />

        <attr name="vh_gradientColorPositionArray" format="reference" />                    <!--渐变颜色数组对应的位置数组,取值为0~1,若为null,颜色沿渐变线均匀分布,最少两个-->
        <attr name="vh_gradientColorPositionArrayNormal" format="reference" />
        <attr name="vh_gradientColorPositionArrayPressed" format="reference" />
        <attr name="vh_gradientColorPositionArrayChecked" format="reference" />
        <attr name="vh_gradientColorPositionArrayUnable" format="reference" />

        <attr name="vh_gradientType">                                                       <!--渐变类型-->
            <enum name="linear" value="0" />
            <enum name="radial" value="1" />
            <enum name="sweep" value="2" />
        </attr>
        <attr name="vh_gradientMode">                                                       <!--渐变模式,仅在 type=linear、radial 时适用-->
            <enum name="clamp" value="0"/>
            <enum name="repeat" value="1"/>
            <enum name="mirror" value="2"/>
        </attr>
        <attr name="vh_gradientAngle" format="float"/>                                      <!--渐变旋转角度,仅在 type=sweep、linear 时适用-->
        <attr name="vh_gradientRadius" format="dimension" />                                <!--渐变半径,仅在 type=radial 时适用-->
        <attr name="vh_gradientCenterX" format="float" />                                   <!--渐变中心X,比例值范围 0.0 ~ 1.0,仅在 type=linear、radial 时适用-->
        <attr name="vh_gradientCenterY" format="float" />                                   <!--渐变中心Y,比例值范围 0.0 ~ 1.0,仅在 type=linear、radial 时适用-->

        <!--模糊背景-->
        <attr name="vh_blurScale" format="float" />                                         <!--模糊图片缩放比例,(0,1]-->
        <attr name="vh_blurRadius" format="dimension" />

        <!--阴影-->
        <attr name="vh_shadowColor" format="color" />
        <attr name="vh_shadowColorNormal" format="color" />
        <attr name="vh_shadowColorPressed" format="color" />
        <attr name="vh_shadowColorChecked" format="color" />
        <attr name="vh_shadowColorUnable" format="color" />
        <attr name="vh_shadowRadius" format="dimension" />
        <attr name="vh_shadowRadiusNormal" format="dimension" />
        <attr name="vh_shadowRadiusPressed" format="dimension" />
        <attr name="vh_shadowRadiusChecked" format="dimension" />
        <attr name="vh_shadowRadiusUnable" format="dimension" />
        <attr name="vh_shadowOffsetX" format="dimension" />
        <attr name="vh_shadowOffsetXNormal" format="dimension" />
        <attr name="vh_shadowOffsetXPressed" format="dimension" />
        <attr name="vh_shadowOffsetXChecked" format="dimension" />
        <attr name="vh_shadowOffsetXUnable" format="dimension" />
        <attr name="vh_shadowOffsetY" format="dimension" />
        <attr name="vh_shadowOffsetYNormal" format="dimension" />
        <attr name="vh_shadowOffsetYPressed" format="dimension" />
        <attr name="vh_shadowOffsetYChecked" format="dimension" />
        <attr name="vh_shadowOffsetYUnable" format="dimension" />
  1. 简单使用
      <com.enowr.widgets.extension.TextViewExtension
            android:id="@+id/view_1"
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:gravity="center"
            android:text="圆角 点击字体和背景变色"
            android:textSize="12sp"
            android:onClick="onClick"
            app:vh_cornersRadius="3dp"
            app:vh_textColor="@android:color/white"
            app:vh_textColorPressed="@android:color/black"
            app:vh_backgroundColor="@android:color/holo_orange_dark"
            app:vh_backgroundColorPressed="@android:color/holo_blue_dark"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    TextViewExtension tve = findViewById(R.id.view_1);
    tve.getHelper().setCornersRadius(10);
  1. 扩展 使用ViewHelper

View 中

public class TextViewExtension extends AppCompatTextView implements Checkable {
    private ViewHelper mHelper = new ViewHelper();

    public TextViewExtension(Context context) {
        super(context);
        mHelper.init(this);
    }

    public TextViewExtension(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHelper.init(this, attrs);
    }

    public TextViewExtension(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mHelper.init(this, attrs, defStyleAttr);
    }

    public ViewHelper<FrameLayoutExtension> getHelper() {
        return mHelper;
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mHelper.onAttachedToWindow(this);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mHelper.onDetachedFromWindow();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mHelper.onSizeChanged(w, h);
    }

    @Override
    public void draw(Canvas canvas) {
        mHelper.saveLayer(canvas);
        super.draw(canvas);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mHelper.onClipDraw(canvas);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return mHelper.dispatchTouchEvent(ev) && super.dispatchTouchEvent(ev);
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        mHelper.drawableStateChanged();
    }

    @Override
    public boolean isChecked() {
        return mHelper.isChecked();
    }

    @Override
    public void setChecked(boolean checked) {
        mHelper.setChecked(checked);
    }

    @Override
    public void toggle() {
        setChecked(!mHelper.isChecked());
    }
}

ViewGroup 中

public class FrameLayoutExtension extends FrameLayout implements Checkable {
    private ViewHelper<FrameLayoutExtension> mHelper = new ViewHelper<>();

    public FrameLayoutExtension(Context context) {
        super(context);
        mHelper.init(this);
    }

    public FrameLayoutExtension(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHelper.init(this, attrs);
    }

    public FrameLayoutExtension(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mHelper.init(this, attrs, defStyleAttr);
    }

    public ViewHelper<FrameLayoutExtension> getHelper() {
        return mHelper;
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mHelper.onAttachedToWindow(this);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mHelper.onDetachedFromWindow();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mHelper.onSizeChanged(w, h);
    }

    @Override
    public void draw(Canvas canvas) {
        mHelper.saveLayer(canvas);
        super.draw(canvas);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        mHelper.trySaveLayer(canvas);
        super.dispatchDraw(canvas);
        mHelper.onClipDraw(canvas);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return mHelper.dispatchTouchEvent(ev) && super.dispatchTouchEvent(ev);
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        mHelper.drawableStateChanged();
    }

    @Override
    public boolean isChecked() {
        return mHelper.isChecked();
    }

    @Override
    public void setChecked(boolean checked) {
        mHelper.setChecked(checked);
    }

    @Override
    public void toggle() {
        setChecked(!mHelper.isChecked());
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mHelper.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return mHelper.onTouchEvent(event) || super.onTouchEvent(event);
    }
}

说明

由于阴影和模糊的特殊性,单独将ViewHelper进行封装;
ViewHelper 支持:设置范围是否包含Padding,默认true;是否只有设置范围内点击生效,默认false;是否是圆形,默认false;圆角设置;文本颜色设置;背景颜色、图设置;前景颜色、图设置;渐变色设置;边框设置;同时这些属性可以设置不同状态的值(默认、按下、选中、不可点击);下拉关闭(用于ViewGroup,可实现微信朋友圈的图片查看器);

  1. 使用阴影将不支持:前景图、背景图、边框、下拉关闭;
  2. 使用模糊将不支持:前景、背景、渐变、边框、下拉关闭;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容