之前在做直播产品的时候,有个需求:弹幕列表在滑入滑出的时候要有渐变效果。当时想了几种方案但是都没有做到满意的效果,后面便搁置了。直到我看到了这个库:Android任意View边沿渐变透明,看了下效果,正是我当时需要的。虽然时过境迁,现在没有在意这个需求了,但是从心底里还是想知道它是如何实现的。
将代码clone下来后,发现代码其实就一个类:
1 | public class EdgeTransparentView extends FrameLayout { |
这里开始一点点分析与学习。
自定义属性:
1
2
3
4
5
6
7
8
9
10
11
12
13<resources>
<!--边沿透明View-->
<declare-styleable name="EdgeTransparentView">
<attr name="edge_position">
<flag name="top" value="0x01"/>
<flag name="bottom" value="0x02"/>
<flag name="left" value="0x04"/>
<flag name="right" value="0x08"/>
</attr>
<attr name="edge_width" format="dimension"/>
</declare-styleable>
</resources>定义了2个属性,一个是要透明的边(上下左右),一个是要透明的宽度。这里利用0x01、0x02十六进制这样的标识,后面再通过“&”操作符来用于标志位的判断真是个不错的idea。
渐变:
代码中的渐变是利用给画笔设置shader来实现的。关键代码:1
mPaint.setShader(new LinearGradient(0, 0, 0, drawSize, mGradientColors, mGradientPosition, Shader.TileMode.CLAMP));
看下LinearGradient的构造函数:
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/** Create a shader that draws a linear gradient along a line.
@param x0 The x-coordinate for the start of the gradient line
@param y0 The y-coordinate for the start of the gradient line
@param x1 The x-coordinate for the end of the gradient line
@param y1 The y-coordinate for the end of the gradient line
@param colors The colors to be distributed along the gradient line
@param positions May be null. The relative positions [0..1] of
each corresponding color in the colors array. If this is null,
the the colors are distributed evenly along the gradient line.
@param tile The Shader tiling mode
*/
public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
TileMode tile) {
if (colors.length < 2) {
throw new IllegalArgumentException("needs >= 2 number of colors");
}
if (positions != null && colors.length != positions.length) {
throw new IllegalArgumentException("color and position arrays must be of equal length");
}
mType = TYPE_COLORS_AND_POSITIONS;
mX0 = x0;
mY0 = y0;
mX1 = x1;
mY1 = y1;
mColors = colors;
mPositions = positions;
mTileMode = tile;
init(nativeCreate1(x0, y0, x1, y1, colors, positions, tile.nativeInt));
}可以知道前面4个参数都是设置起始、结束坐标。colors是渐变的颜色:0xffffffff–>0x00000000,tile是shader mode。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public enum TileMode {
/**
* replicate the edge color if the shader draws outside of its
* original bounds
*/
CLAMP (0),
/**
* repeat the shader's image horizontally and vertically
*/
REPEAT (1),
/**
* repeat the shader's image horizontally and vertically, alternating
* mirror images so that adjacent images always seam
*/
MIRROR (2);
TileMode(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}画:在drawChild中,会优先调用
super.drawChild(canvas, child, drawingTime);
因为我们的layer要遮罩在子View上,才能有这种效果,所以画layer一定要在其之后执行。根据position的判断,来判断上下左右4条边哪些边需要画layer,通过旋转画布来画不同的边,画完后恢复现场,进行下一个判断。setXfermode:可能刚开始会没注意到这个方法的调用,但是它的作用不可谓不大。看到核心代码:
1
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
这个便是给画笔设置xfermode,没有这句代码效果可就很不如意咯。这里便要拓展学习一下了,贴一下爱哥的博客。