无需自定义View,彻底放弃shape,selector吧

介绍一种无需自定义View即可实现shape和selector效果的方法,通过添加属性实现背景和交互样式定制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

作为一个android程序员,对于shape、selector这两个标签一定不陌生。每当UI设计师给我们设计出一个个button背景的时候,我们就需要去drawable文件夹下去新建一个bg_xxx.xml,然后很多时候区别仅仅是一个边框的颜色或者填充的颜色。这就导致了很多非常相似的.xml文件产生。
网上之前也有了一种通过自定义View,在xml中通过设置属性达到shape效果的控件。但是这种自定义的控件不太灵活,归根到底是一个自定义的button,如果我想改造项目的话就得去替换原有的button或者textView。接下来就给大家提供一种更加简单的方式:
无需自定义View,直接添加属性便可以实现shape、selector效果

具体内容

效果展示

话不多说,直接上代码。
使用方法:
1、在BaseActiviy的super.onCreate()之前调用一行代码,仅仅是一个方法


2、没有其他操作了,直接layout里开始写控件吧!

布局代码

我们来添加一些实例属性:


有没有觉得很熟悉,就是原生标签的tag名+_+属性名,很容易记住,而且不管是Button还是TextView,只要是View都可以。

效果

我们来看一下实际效果:

修改背景

现在UI设计师告诉我们要改一下背景,没事,我们只需要在xml添加或者修改属性就行。
我们来把圆形改成正方形,加个边框。5秒ok!

简单的原理解析

app:xxx

app:xxx属性就不用多说了,这些就是一些自定义属性而已。在这里我把shape、selector的部分属性转换成自定义的属性,这样就方便添加到已有原生控件中。

BackgroundLibrary.inject(this)

这个方法是这个框架唯一需要加入的代码。
inject中实际上是给LayoutInflater添加了一个LayoutInflater.Factory类。而Android的Activity在创建过程(也就是setContentView)中实际上是通过把xml转换成View的对象。而LayoutInflater.Factory相当于这中间的一个后门,它是xml解析创建成View的必经方法,google中的v7support包里很多内容就是通过LayoutInflater.Factory来实现向下兼容的。
在这里,我通过低入侵的方式,加入一个自定义的LayoutInflater.Factory,去解析添加的自定义属性,接下来就简单了。生成系统提供的GradientDrawable、RippleDrawable、StateListDrawable即可。
同时由于AppcompatActivity是已经实现了LayoutInflater.Factory,而Activity又设定一个Activity只能加入一个factory类,因此这里需要在super.onCreate之前调用该方法,利用AppcompatActivity的factory去创建View。
具体原理解释可以参考我的这篇文章:Android 常用换肤方式以及原理分析

具体使用方法:

添加依赖:

implementation 'com.noober.background:core:1.2.0'

BaseActivity的super.onCreate之前添加代码:

BackgroundLibrary.inject(context);

支持属性,命名规则就是标签名_标签属性名,支持shape所有属性:

   <attr name="shape" format="enum">
       <enum name="rectangle" value="0" />
       <enum name="oval" value="1" />
       <enum name="line" value="2" />
       <enum name="ring" value="3" />
   </attr>

   <attr name="solid_color" format="color"/>

   <attr name="corners_radius" format="dimension"/>
   <attr name="corners_bottomLeftRadius" format="dimension"/>
   <attr name="corners_bottomRightRadius" format="dimension"/>
   <attr name="corners_topLeftRadius" format="dimension"/>
   <attr name="corners_topRightRadius" format="dimension"/>

   <attr name="gradient_angle" format="integer"/>
   <attr name="gradient_centerX" format="float"/>
   <attr name="gradient_centerY" format="float"/>
   <attr name="gradient_centerColor" format="color"/>
   <attr name="gradient_endColor" format="color"/>
   <attr name="gradient_startColor" format="color"/>
   <attr name="gradient_gradientRadius" format="dimension"/>
   <attr name="gradient_type" format="enum">
       <enum name="linear" value="0" />
       <enum name="radial" value="1" />
       <enum name="sweep" value="2" />
   </attr>
   <attr name="gradient_useLevel" format="boolean"/>

   <attr name="padding_left" format="dimension"/>
   <attr name="padding_top" format="dimension"/>
   <attr name="padding_right" format="dimension"/>
   <attr name="padding_bottom" format="dimension"/>

   <attr name="size_width" format="dimension">
       <enum name="wrap_content" value="-2" />
       <enum name="match_parent" value="-1" />
   </attr>
   <attr name="size_height" format="dimension">
       <enum name="wrap_content" value="-2" />
       <enum name="match_parent" value="-1" />
   </attr>

   <attr name="stroke_width" format="dimension"/>
   <attr name="stroke_color" format="color"/>
   <attr name="stroke_dashWidth" format="dimension"/>
   <attr name="stroke_dashGap" format="dimension"/>

   <!--以下是selector事件-->
   <attr name="ripple_enable" format="boolean"/>
   <attr name="ripple_color" format="color"/>
   <attr name="unpressed_color" format="color"/>
   <attr name="pressed_color" format="color"/>

例如加一个边框背景,如下即可:

<TextView
    android:layout_width="130dp"
    android:layout_height="36dp"
    android:gravity="center"
    android:text="TextView"
    android:textColor="#8c6822"
    android:textSize="20sp"
    app:corners_radius="4dp"
    app:solid_color="#E3B666"
    app:stroke_color="#8c6822"
    app:stroke_width="2dp" />

如果需要selector效果,需要同时添加

app:unpressed_color
app:pressed_color

如果需要水波纹效果,5.0以上才支持:

app:ripple_enable="true"//打开水波纹开关
app:solid_color="xxx"//设置默认填充颜色
app:ripple_color="xxx"//设置水波纹颜色

注意:
1、如果直接给原生控件添加属性,在xml中会如下报红色异常,这时候不用理会即可。


但是红色多了,显示总归难受,可以在根节点添加

tools:ignore="MissingPrefix" 

即可


2、如果Button设置背景的时候文字显示不全,需要设置padding为0,这是button的系统默认风格导致的。

结尾

项目地址:https://github.com/JavaNoober/BackgroudLibrary
后续更新完善内容会在项目里标明,大家的star是我继续研究的动力☺!

【最新版本已支持selector,详情查看 https://github.com/JavaNoober/BackgroundLibrary

<think>好的,用户想在自定义Android View中实现Shape外部线条的渐变效果。首先,我需要回忆一下Android中如何定义形状和渐变色。根据引用[1],使用shape标签可以定义View的外观,并且可以通过gradient标签设置渐变色填充。不过,用户的问题是关于外部线条的渐变,而不仅仅是填充。 接下来,我需要考虑stroke标签。通常,stroke用于定义形状的边框,比如颜色和宽度。但是默认情况下,stroke的color属性只接受单一颜色。要实现边框的渐变效果,可能需要寻找其他方法。引用[3]提到shapeselector的结合使用,但可能不直接解决渐变边框的问题。 查看引用[1]中的示例,渐变色是应用于填充而不是边框。用户可能需要使用其他技巧,比如图层叠加或者自定义绘制。可能需要在自定义View的onDraw方法中使用Paint的Shader来实现渐变线条。例如,使用LinearGradient设置Paint的shader,然后绘制边框。 另外,引用[2]提到使用ImageView的src或background属性来应用shape,但直接使用shape可能无法实现边框渐变。因此,自定义绘制可能是更可行的方案。可能需要创建一个自定义View,重写onDraw方法,在画布上绘制带有渐变效果的边框。 总结步骤:1. 创建自定义View类。2. 重写onDraw方法。3. 使用Paint对象设置LinearGradient。4. 绘制路径或形状的边框。需要确保正确设置Shader的参数,比如起始和结束点,颜色等。这样就能实现外部线条的渐变效果了。</think>在Android自定义View中实现Shape外部线条渐变效果,可以通过以下两种方法实现: ### 方法一:XML Shape结合自定义属性(API 24+) ```xml <!-- res/drawable/gradient_border.xml --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <!-- 渐变边框实现 --> <stroke android:width="4dp" android:color="#FF000000"> <!-- 设置默认颜色 --> <gradient android:angle="0" android:startColor="#FF00FF00" android:endColor="#FFFF0000" android:type="linear"/> </stroke> <!-- 可选填充 --> <solid android:color="@android:color/transparent"/> </shape> ``` *注意:此方法需要API Level 24及以上版本支持[^3]* ### 方法二:自定义View绘制(兼容方案) ```java public class GradientBorderView extends View { private Paint borderPaint; private Path path = new Path(); private RectF rect = new RectF(); public GradientBorderView(Context context) { super(context); init(); } private void init() { borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); borderPaint.setStyle(Paint.Style.STROKE); borderPaint.setStrokeWidth(8f); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); rect.set(4, 4, getWidth()-4, getHeight()-4); // 创建渐变效果 LinearGradient gradient = new LinearGradient( 0, 0, getWidth(), getHeight(), Color.GREEN, Color.RED, Shader.TileMode.CLAMP); borderPaint.setShader(gradient); path.addRoundRect(rect, 16, 16, Path.Direction.CW); canvas.drawPath(path, borderPaint); } } ``` ### 实现要点说明: 1. **XML实现限制**: - 需要Android 7.0(API 24)及以上版本 - 渐变效果仅支持线性渐变 - 边框宽度需要配合渐变参数调整[^1] 2. **自定义绘制优势**: - 兼容所有Android版本 - 支持更复杂的渐变效果(径向/扫描渐变) - 可自定义边框形状(圆形/圆角矩形/异形) 3. **效果优化建议**: ```java // 在初始化时添加硬件加速 setLayerType(LAYER_TYPE_HARDWARE, null); // 使用PathEffect实现虚线渐变 borderPaint.setPathEffect(new DashPathEffect(new float[]{10,5}, 0)); ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值