Android实用View系列之SuperButton(实现shape的大部分功能)

在开发过程中我们的按钮基本都有自己的样式风格,有的时候需要搞个圆角,有时候需要边框,有的时候需要虚线边框亦或者需要一个圆形的按钮。想大部分的实现方式都是写一个shape文件,在里边定义边框圆角等相关属性,如果项目中类似按钮很多切风格各不相同,那我们就需要写很多很多shape文件,这是我们无法忍受的。而SuperButton的出现可以帮你解决以上问题,从此远离shape文件的编写。

有图有真相

SuperButton.png


github源码地址传送门

开篇

开始之前我们先来看看shape文件是如何编写的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape=["rectangle" | "oval" | "line" | "ring"] >
<corners
android:radius="integer"
android:topLeftRadius="integer"
android:topRightRadius="integer"
android:bottomLeftRadius="integer"
android:bottomRightRadius="integer" />
<gradient
android:angle="integer"
android:centerX="integer"
android:centerY="integer"
android:centerColor="integer"
android:endColor="color"
android:gradientRadius="integer"
android:startColor="color"
android:type=["linear" | "radial" | "sweep"]
android:useLevel=["true" | "false"] />
<size
android:width="integer"
android:height="integer" />
<solid
android:color="color" />
<stroke
android:width="integer"
android:color="color"
android:dashWidth="integer"
android:dashGap="integer" />
</shape>

以上shape文件列出了常用的属性,当然并不是所有属性都会用到,我们就以这个shape文件作为参考开始本篇文章的分析。

我们知道写好shape文件之后是通过android:background=”@drawable/shape”这样的方式引入的,那么Android有没有方法去获取shape相关属性的类呐,经过一番查阅发现还是有个GradientDrawable类可以实现的,关于GradientDrawable的介绍请自行Google了解。

通过GradientDrawable的源码可以看到里边有很多方法和shape各个节点的名称一样,所以这正是我们需要的类,剩下的就是开始用GradientDrawable实现我们的封装之路。

具体实现

由于代码不复杂,看下注释也都能理解,就直接贴代码了

先初始化一下gradientDrawable = new GradientDrawable();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 设置shape类型,对应shape的四个属性
*/
private void setShape() {
switch (shapeType) {
case RECTANGLE:
gradientDrawable.setShape(GradientDrawable.RECTANGLE);
break;
case OVAL:
gradientDrawable.setShape(GradientDrawable.OVAL);
break;
case LINE:
gradientDrawable.setShape(GradientDrawable.LINE);
break;
case RING:
gradientDrawable.setShape(GradientDrawable.RING);
break;
}
}

通过一下方法可以整体设置四个圆角也可以对各个角分别设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 只有类型是矩形的时候设置圆角半径才有效
*/
private void setRadius() {
if (shapeType == GradientDrawable.RECTANGLE) {
if (cornersRadius != 0) {
gradientDrawable.setCornerRadius(dip2px(mContext, cornersRadius));//设置圆角的半径
} else {
//1、2两个参数表示左上角,3、4表示右上角,5、6表示右下角,7、8表示左下角
gradientDrawable.setCornerRadii(
new float[]
{
cornersTopLeftRadius, cornersTopLeftRadius,
cornersTopRightRadius, cornersTopRightRadius,
cornersBottomRightRadius, cornersBottomRightRadius,
cornersBottomLeftRadius, cornersBottomLeftRadius
}
);
}
}
}

设置渐变色的相关属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/**
* 设置背景颜色
* 如果设定的有Orientation 就默认为是渐变色的Button,否则就是纯色的Button
*/
private void setOrientation() {
if (gradientOrientation != -1) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
gradientDrawable.setOrientation(getOrientation(gradientOrientation));
if (gradientCenterColor == -1) {
gradientDrawable.setColors(new int[]{gradientStartColor, gradientEndColor});
} else {
gradientDrawable.setColors(new int[]{gradientStartColor, gradientCenterColor, gradientEndColor});
}
switch (gradientType) {
case linear:
gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
break;
case radial:
gradientDrawable.setGradientType(GradientDrawable.RADIAL_GRADIENT);
gradientDrawable.setGradientRadius(gradientGradientRadius);
break;
case sweep:
gradientDrawable.setGradientType(GradientDrawable.SWEEP_GRADIENT);
break;
}
gradientDrawable.setUseLevel(gradientUseLevel);
if (gradientCenterX != 0 && gradientCenterY != 0) {
gradientDrawable.setGradientCenter(gradientCenterX, gradientCenterY);
}
}
} else {
gradientDrawable.setColor(solidColor);
}
}
/**
* 设置颜色渐变类型
*
* @param gradientOrientation gradientOrientation
* @return Orientation
*/
private GradientDrawable.Orientation getOrientation(int gradientOrientation) {
GradientDrawable.Orientation orientation = null;
switch (gradientOrientation) {
case TOP_BOTTOM:
orientation = GradientDrawable.Orientation.TOP_BOTTOM;
break;
case TR_BL:
orientation = GradientDrawable.Orientation.TR_BL;
break;
case RIGHT_LEFT:
orientation = GradientDrawable.Orientation.RIGHT_LEFT;
break;
case BR_TL:
orientation = GradientDrawable.Orientation.BR_TL;
break;
case BOTTOM_TOP:
orientation = GradientDrawable.Orientation.BOTTOM_TOP;
break;
case BL_TR:
orientation = GradientDrawable.Orientation.BL_TR;
break;
case LEFT_RIGHT:
orientation = GradientDrawable.Orientation.LEFT_RIGHT;
break;
case TL_BR:
orientation = GradientDrawable.Orientation.TL_BR;
break;
}
return orientation;
}

对应shape中的size设置shape布局大小及设置边框和分割线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
* 设置size的值
*/
private void setSize() {
if (shapeType == RECTANGLE) {
gradientDrawable.setSize(sizeWidth, sizeHeight);
}
}
/**
* 设置边框 宽度 颜色 虚线 间隙
*/
private void setBorder() {
gradientDrawable.setStroke(dip2px(mContext, strokeWidth), strokeColor, strokeDashWidth, strokeDashGap);
}

至此需要的方法都已经写完了,剩下的就是自定义view继承button,添加自己定义的属性

然后在代码中这样使用

1
2
3
4
5
6
7
8
<com.allen.library.SuperButton
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_margin="5dp"
android:text="圆角边框"
stv:sCornersRadius="5dp"
stv:sStrokeColor="@color/colorAccent"
stv:sStrokeWidth="0.2dp" />

image.png

通过配置shapeType的类型可以以此实现矩形,椭圆,线和圆环

至此关于shape代码实现的部分已经完成了,可以实现shape很多中效果,不过细心的你发现按钮按下的触摸反馈没有了,以前都是通过selector去实现的,那么代码能否实现呐。答案肯定是OK啦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 获取设置之后的Selector
*
* @return stateListDrawable
*/
public StateListDrawable getSelector() {
StateListDrawable stateListDrawable = new StateListDrawable();
//注意该处的顺序,只要有一个状态与之相配,背景就会被换掉
//所以不要把大范围放在前面了,如果sd.addState(new[]{},normal)放在第一个的话,就没有什么效果了
stateListDrawable.addState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}, getDrawable(android.R.attr.state_pressed));
stateListDrawable.addState(new int[]{-android.R.attr.state_enabled}, getDrawable(-android.R.attr.state_enabled));
stateListDrawable.addState(new int[]{}, getDrawable(android.R.attr.state_enabled));
return stateListDrawable;
}

然后把selector设置到setBackground中就好了

1
2
3
4
5
if (Build.VERSION.SDK_INT < 16) {
setBackgroundDrawable(useSelector ? getSelector() : getDrawable(0));
} else {
setBackground(useSelector ? getSelector() : getDrawable(0));
}

在运行一下,看看效果

触摸效果.gif

最后

看了以上介绍是不是感觉很简单,那就赶快撸起袖子,自己撸一个玩玩吧!毕竟以后不用在写那么多shape文件了,哈哈!以后可以少搬点砖了

搬砖.png