簡介
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的強大之處。