一、需求
弹幕进入时由透明,进入一定区域后变成透明,退出时由不透明变成透明。
看过自定义LoopView源码的都知道,在View上下两侧使用了遮罩(Mask)的去实现划出区域的item隐藏,但是在某些场景下,这种实现由天然的弊端,如果View下方播放视频,由于明亮度不同,会出现明显的反差。
二、利用Shader中的Gradient实现
2.1 在使用Gradient之前,我们需要了解Shader是什么?
Shader翻译成中文是"着色器“,很多人对其陌生又熟悉,主要原因没有很好的解释他是如何着色的,工作原理如何?这里我们来总结一下。
- Shader属于Paint的属性,用于为paint 着色
- Paint的Color和Shader会取交集,完成着色
- Paint 所绘制的图像和Shader形成交集,优先按照Shader的颜色和透明度着色,不产生交集的部分以Color为主
我们可以把Paint Color染色过的图形想象成“凸版印刷板”,上面已经雕刻出了凸版文字,把Shader想象成一个矩形墨盒,魔盒中是具备颜色排列,如线性、放射
= Android绘制小糊涂 +
当然,Shader具备又自己的起点、终点和方向。
2.2 Shader 起点与终点
Shader具备起点和终点,和绘制图像时需要文字一样,作为“凸版印刷墨盒” ,可以在墨盒的任意位置开始和结束,不在于Paint绘制几次。
对于弹幕而言,一行出现三句甚至更多弹幕的时候,只需要理解“文字”在墨盒水平和垂直方向的位置对应,进而给文字染色。
举例:
使用同一个Paint,按照三等分平分View,依次从每个等分区域中心绘制A、B、C三个字母(这种情况下我们一般至少drawText三次),另外Shader按照LinearGradient以三等分区域形成出红、绿、蓝墨盒,那么最终结果将是 A被染成红色、B被染成绿色、C被染成蓝色。
效果参考全民K歌TV版本的打分条
2.3 Color和Shader
- 图像和Shader墨盒产生交集,才会对图像染色,如果文字不再染色的区域,那么将以paint Color的形式展示文字,不产生交集的部分以Color为主颜色展示。
- Shader染色,不在于是否一次绘制和多次绘制,只要和墨盒产生交集,便会染色。
- 如果有透明度相关的染色,Shader透明度区域需要谨慎使用亮色,Shader亮色可能会叠加,如0x00FFFFFF和0x00000000相比,前者容易出现叠加问题,什么原因暂时未知
public boolean isLightColor(int color) { double darkness = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255; if (darkness < 0.5) { return true; // It's a light color } else { return false; // It's a dark color } }
四、弹幕需求实现
利用Gradient实现,效果如下
代码实现
1、TextView实现两端文字渐隐
LinearGradient gradient = new LinearGradient(0,0,testTv.getWidth(),0,new int[]{
0x66000000,
0xff000000,
0xff000000,
0x66000000
},new float[]{
0.0f,
0.2f,
0.8f,
0.85f
}, Shader.TileMode.CLAMP);
testTv.setText("12345678901234567890123456789012345678901234567890");
testTv.getPaint().setShader(gradient);
testTv.invalidate();
2、实现底部线条渐渐隐藏
public class GradientView extends View {
Paint paint;
private LinearGradient mGradient;
public GradientView(Context context) {
this(context,null);
}
public GradientView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public GradientView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(20);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mGradient==null) {
mGradient = new LinearGradient(100, 0, getWidth()-100, 0, new int[]{
0x33000000,
0xff000000,
0xff000000,
0x33000000
}, new float[]{
0.0f,
0.2f,
0.8f,
1f
}, Shader.TileMode.CLAMP);
}
paint.setShader(mGradient);
canvas.drawLine(100,20,getWidth()-100,20,paint);
}
}
颜色叠加问题,下面使用白色透明,会产生叠加
private void setPaintLinearFadeGradient(Paint paint,int fadeWith,int fadeHeight) {
if(paint==null) return;
if(fadeWith<0 || fadeHeight<0){
return;
}
Shader shader = paint.getShader();
if(shader!=null) return;
int color = paint.getColor();
LinearGradient gradient = new LinearGradient(0,fadeHeight/2,fadeWith,fadeHeight/2,new int[]{
0x00ffffff
color,
color,
0x00ffffff
},new float[]{
0.0f,
0.15f,
0.85f,
1f
}, Shader.TileMode.CLAMP);
paint.setShader(gradient);
}
下面改为0x00000000黑色透明之后,不会叠加
private void setPaintLinearFadeGradient(Paint paint,int fadeWith,int fadeHeight) {
if(paint==null) return;
if(fadeWith<0 || fadeHeight<0){
return;
}
Shader shader = paint.getShader();
if(shader!=null) return;
int color = paint.getColor();
LinearGradient gradient = new LinearGradient(0,fadeHeight/2,fadeWith,fadeHeight/2,new int[]{
Color.TRANSPARENT,
color,
color,
Color.TRANSPARENT
},new float[]{
0.0f,
0.15f,
0.85f,
1f
}, Shader.TileMode.CLAMP);
paint.setShader(gradient);
}