简介
Shader :顾名思义,也就是着色器,通常也有人叫做渲染器。我们看下Android源码中是如何介绍Shader的。
/**
* Shader is the based class for objects that return horizontal spans of colors
* during drawing. A subclass of Shader is installed in a Paint calling
* paint.setShader(shader). After that any object (other than a bitmap) that is
* drawn with that paint will get its color(s) from the shader.
*/
复制代码
这是Android源码的介绍,Shader 是一个基类对象,在绘制时会返回一个水平跨越的颜色对象,主要功能是在绘制时通过setShader方法设置着色器的子类对象之后,任何对象(除了位图之外)都可从着色器中得到它的想要的颜色。
也就是说,Shader 只是一个基类,我们主要还是通过使用Shader的子类,来获取我们想要的颜色。
那么Shader 有那些子类呢?
其实Shader 总共有五个子类:
1)BimapShader:位图的图像渲染器
2)LinearGradient:线性渲染器
3)RadialGradient:环形渲染器,一般的水波纹效果,充电水波纹扩散效果、调色板都可以使用该渲染器实现。
4)SweepGradient:梯度渲染器(即扫描渲染),可以使用该渲染器实现,如:微信等雷达扫描效果、手机卫士垃圾扫描。
5)ComposeShader:组合渲染器
BimapShader
BitmapShader位图的图像渲染器,使用时,需要创建BitmapShader对象,其构造函数如下:
/**
* Call this to create a new shader that will draw with a bitmap.
*
* @param bitmap The bitmap to use inside the shader
* @param tileX The tiling mode for x to draw the bitmap in.
* @param tileY The tiling mode for y to draw the bitmap in.
*/
public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) {
this(bitmap, tileX.nativeInt, tileY.nativeInt);
}
复制代码
创建BitmapShader对象时,需要传入Bitmap,那么构造函数的后面两个函数干嘛用的呢?
tileX:X轴上绘制位图的tiling模式。
tileY:Y轴上绘制位图的tiling模式。
其中有三种tiling模式(TileMode):
1)TileMode.CLAMP:拉伸最后一个像素去铺满剩下的地方;
2)TileMode.MIRROR:通过镜像翻转铺满剩下的地方;
3)TileMode.REPEAT:重复图片平铺整个画面(如电脑设置壁纸)。
接下来我们看下分别设置这几种模式下的图像的效果。
TileMode.CLAMP
从上图中可以看出该模式下,确实是只拉伸图像的最后一个像素去铺满控件剩下的地方。
TileMode.MIRROR
这就是MIRROR模式下,通过镜像翻转铺满剩下的地方的效果图。
TileMode.REPEAT
上图就是REPEAT模式的效果,就是通过重复画面来铺满整个画面。
以上都是通过拉伸,或者重复画面来铺满真个View,效果都不太好,宽/高不一致,有没有办法解决宽/高一致,然后让bitmap铺满画面呢?
答案当然是有的,可以通过以下方法来解决:
1)设置像素矩阵,来调整大小,可以参考《Android BitmapShader 实战 实现圆形、圆角图片》这篇文章。
2)通过shapeDrawable
public class MyShaderView extends View {
private Bitmap mBitmap;
private Paint mPaint;
private BitmapShader mBitmapShader;
private int width;
private int height;
public MyShaderView(Context context) {
super(context);
mBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.animationtarget)).getBitmap();
mPaint = new Paint();
width = mBitmap.getWidth();
height = mBitmap.getHeight();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);//画布颜色
mBitmapShader = new BitmapShader(mBitmap, TileMode.CLAMP, TileMode.CLAMP);
ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape());
shapeDrawable.getPaint().setShader(mBitmapShader);
shapeDrawable.setBounds(0, 0, width, width);
shapeDrawable.draw(canvas);
}
}
复制代码
BitmapShader应用,制作放大镜
public class ZoomImageView extends View {
private Bitmap bitmap;
private ShapeDrawable drawable;
//放大倍数
private static final int FACTOR = 3;
//放大镜的半径
private static final int RADIUS = 100;
private Matrix matrix = new Matrix();
public ZoomImageView(Context context) {
super(context);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.animationtarget);
Bitmap bmp = bitmap;
//放大后的整个图片
bmp = Bitmap.createScaledBitmap(bmp, bmp.getWidth() * FACTOR, bmp.getHeight() * FACTOR, true);
//制作一个圆形的图片(放大的局部),盖在canvas上面
BitmapShader shader = new BitmapShader(bmp, TileMode.CLAMP, TileMode.CLAMP);
drawable = new ShapeDrawable(new OvalShape());
drawable.getPaint().setShader(shader);
//切出矩形区域---用于绘制圆(内切圆)
drawable.setBounds(0, 0, RADIUS * 2, RADIUS * 2);
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
canvas.drawBitmap(bitmap, 0, 0, null);
//画制作好的圆形图片
drawable.draw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
matrix.setTranslate(RADIUS - x * FACTOR, RADIUS - y * FACTOR);
drawable.getPaint().getShader().setLocalMatrix(matrix);
drawable.setBounds(x - RADIUS, y - RADIUS, x + RADIUS, y + RADIUS);
invalidate();
return true;
}
}
复制代码
上图中使用放大镜放大了男孩的头部。
LinearGradient
LinearGradient也就是线性渲染器,也叫做线性渐变。 初始化时的构造方法:
public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile)
复制代码
x0、y0:渲染的起始点;
x1、y1:渲染的结束点;
colors: 渲染的颜色,也就是中间依次要出现的几个颜色,它是一个颜色数组,数组长度必须大于等于2;
positions:数组大小跟colors数组一样大,中间依次摆放的几个颜色分别放置在那个位置上(参考比例从左往右);
tile:平铺方式,有CLAMP、REPEAT和MIRROR这三种。
使用:
LinearGradient linearGradient = new LinearGradient(0, 0, 400, 400, colors, null, TileMode.REPEAT);
mPaint.setShader(linearGradient);
canvas.drawRect(0, 0, 400, 400, mPaint);
复制代码
接下来我们主要分析下TileMode的三种模式。
CLAMP
该模式是表示:重复最后一种颜色直到该View结束的地方
LinearGradient linearGradient = new LinearGradient(0, 0, getWidth()/2, 0, colors, null, TileMode.CLAMP);
mPaint.setShader(linearGradient);
canvas.drawRect(0, 0, 800, 400, mPaint);
复制代码
上图便是CLAMP模式的效果图,从图中可以看出重复了最后一种颜色(黄色),直到该View结束的地方。
CLAMP
该模式表示着色器在水平或者垂直方向上对控件进行重复着色。
LinearGradient linearGradient = new LinearGradient(0, 0, getWidth()/2, 0, colors, null, TileMode.REPEAT);
mPaint.setShader(linearGradient);
canvas.drawRect(0, 0, 800, 400, mPaint);
复制代码
上图是REPEAT模式下的线性渲染器的效果图,可以看到在水平方向上是重复着色的。
MIRROR
该模式表示:在水平方向或者垂直方向上以镜像的方式进行渲染,这种渲染方式的一个特征就是具有翻转的效果。
LinearGradient linearGradient = new LinearGradient(0, 0, getWidth()/2, 0, colors, null, TileMode.MIRROR);
mPaint.setShader(linearGradient);
canvas.drawRect(0, 0, 800, 400, mPaint);
复制代码
效果图中的就是在水平方向上以镜像方式进行翻转的渲染效果。
LinearGradient 应用
public class LinearGradientTextView extends AppCompatTextView {
private TextPaint paint;
private LinearGradient linearGradient;
private Matrix matrix;
private float translateX;
private float deltaX = 20;
public LinearGradientTextView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
paint = getPaint();
//GradientSize=两个文字的大小
String text = getText().toString();
float textWidth = paint.measureText(text);
int GradientSize =(int) (3*textWidth/text.length());
linearGradient = new LinearGradient(-GradientSize, 0, 0, 0, new int[]{0x22ffffff,0xffffffff,0x22ffffff}, new float[]{0,0.5f,1}, Shader.TileMode.CLAMP);//边缘融合
paint.setShader(linearGradient);
matrix = new Matrix();
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
float textWidth = getPaint().measureText(getText().toString());
translateX += deltaX;
if(translateX > textWidth + 1|| translateX < 1){
deltaX = -deltaX;
}
matrix.setTranslate(translateX, 0);
linearGradient.setLocalMatrix(matrix);
postInvalidateDelayed(50);
}
}
复制代码
xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#ff000000"
tools:context="com.main.shader.ShaderActivity">
<com.main.shader.LinearGradientTextView
android:id="@+id/linearGradientTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="走的最慢的人,只要他不丧失目标,也比漫无目的徘徊的人走得快。"
android:textColor="#ff000000"/>
</LinearLayout>
复制代码
RadialGradient
RadialGradient:环形渲染器,呈现一般的水波纹渲染效果。
使用:
RadialGradient radialGradient = new RadialGradient(300, 300, 100, colors, null, TileMode.REPEAT);
mPaint.setShader(radialGradient);
canvas.drawCircle(300, 300, 300, mPaint);
复制代码
RadialGradient的构造方法
RadialGradient(float centerX, float centerY, float radius,@NonNull @ColorInt int colors[], @Nullable float stops[],@NonNull TileMode tileMode)
复制代码
centerX、centerY:环形中心点坐标;
radius:环形半径;
colors:渲染的颜色;
stops:中间依次摆放的颜色分别放置的位置;
TileMode :平铺模式。
在RadialGradient 中同样提供了三种TileMode平铺模式:
1)CLAMP
2)REPEAT
3)MIRROR
这三种模式呈现的效果对应LinearGradient的TileMode的三种模式呈现效果。该渲染其的应用,可以参考《自定义控件三部曲之绘图篇(二十)——RadialGradient与水波纹按钮效果》这篇文章。
SweepGradient
扫描渲染器,使用:
SweepGradient sweepGradient = new SweepGradient(300, 300, colors, null);
mPaint.setShader(sweepGradient);
canvas.drawCircle(300, 300, 300, mPaint);
复制代码
我们看下SweepGradient的构造方法:
public SweepGradient(float cx, float cy,@NonNull @ColorInt int colors[], @Nullable float positions[])
复制代码
参数解析:
cx、cy:扫描中心点坐标;
colors:颜色梯度,分布在中心点周围,至少提供两种颜色;
positions:依次摆放的颜色分别放置的位置;
关于扫描渲染的应用,网上很多优秀的demo,比如:自定义控件之圆形颜色渐变进度条--SweepGradient
ComposeShader
组合渲染器ComposeShader,顾名思义,可以组合多种渲染器。如下代码所示:
ComposeShader composeShader = new ComposeShader(linearGradient, mBitmapShader, PorterDuff.Mode.SRC_OVER);
mPaint.setShader(composeShader);
canvas.drawRect(0, 0, 800, 1000, mPaint);
复制代码
构造方法的最后一个参数PorterDuff.Mode,也就是过度模式,总共有17种模式,关于这十七种模式,读者可以参考《各个击破搞明白PorterDuff.Mode》这篇文章,写得很详细,相信读者读完之后都会感到ComposeShader的强大之处。