Android中Paint,Canvas绘图与Shader着色器使用案例

前言

我们在用Android中的Canvas绘制各种图形时,可以通过Paint.setShader(shader)方法为画笔Paint设置shader(eg:mPaint.setShader(shader)),
这样就可以绘制出多彩的图形。那么Shader是什么呢?,Shader就是着色器的意思。我们可以这样理解,
Canvas中的各种drawXXX方法  eg: canvas.drawCircle()。
定义了图形的形状,画笔中的Shader则定义了图形的着色、外观,二者结合到一起就决定了最终Canvas绘制的被色彩填充的图形的样子。

android.graphics.Shader有五个子类,分别是:BitmapShader、LinearGradient、RadialGradient、SweepGradient和ComposeShader,下面主要对SweepGradientComposeShader类的使用详细说明。

 
 

SweepGradient

SweepGradient可以用来创建雷达扫描渐变效果,具体来说颜色是围绕中心点360度顺时针旋转的。

SweepGradient两个构造函数
 1.
/**
 * A Shader that draws a sweep gradient around a center point.
 *
 * @param cx       The x-coordinate of the center X轴中心坐标
 * @param cy       The y-coordinate of the center Y轴中心坐标
 * @param colors   The colors to be distributed between around the center.
 *                 There must be at least 2 colors in the array.
 * @param positions May be NULL. The relative position of
 *                 each corresponding color in the colors array, beginning
 *                 with 0 and ending with 1.0. If the values are not
 *                 monotonic, the drawing may produce unexpected results.
 *                 If positions is NULL, then the colors are automatically
 *                 spaced evenly.
 */
public SweepGradient(float cx, float cy,
        @NonNull @ColorInt int colors[], @Nullable float positions[]) {
    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;
    mCx = cx;
    mCy = cy;
    mColors = colors.clone();
    mPositions = positions != null ? positions.clone() : null;
}


2.
/**
 * A Shader that draws a sweep gradient around a center point.
 *
 * @param cx       The x-coordinate of the center X轴中心坐标
 * @param cy       The y-coordinate of the center Y轴中心坐标
 * @param color0   The color to use at the start of the sweep :在扫描开始时使用的颜色
 * @param color1   The color to use at the end of the sweep 在扫描结束时使用的颜色
 */
public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
    mType = TYPE_COLOR_START_AND_COLOR_END;
    mCx = cx;
    mCy = cy;
    mColor0 = color0;
    mColor1 = color1;
    mColors = null;
    mPositions = null;
}


需要实现效果:



具体实现步骤:


1.申明我们需要的变量值
private int mWidth, mHeight;

/**
 * 5个圆
 */
private float[] pots = {0.05f, 0.1f, 0.15f, 0.2f, 0.25f};
/**
 * 扫描渲染shader
 */
private Shader scanShader;
/**
 * 旋转需要的矩阵
 */
private Matrix matrix = new Matrix();
/**
 * 扫描速度
 */
private int scanSpeed = 5;
/**
 * 扫描旋转的角度
 */
private int scanAngle;
/**
 * 画圆用到的paint
 */
private Paint mPaintCircle;
/**
 * 扫描用到的paint
 */
private Paint mPaintRadar;


2.初始化
public RadarView(Context context) {
    super(context);

    // 画圆用到的paint
    mPaintCircle = new Paint();
    // 描边
    mPaintCircle.setStyle(Paint.Style.STROKE);
    // 宽度
    mPaintCircle.setStrokeWidth(1);
    // 透明度
    mPaintCircle.setAlpha(100);
    // 抗锯齿
    mPaintCircle.setAntiAlias(true);
    // 设置颜色 亮钢兰色
    mPaintCircle.setColor(Color.parseColor("#B0C4DE"));

    // 扫描用到的paint
    mPaintRadar = new Paint();
    // 填充
    mPaintRadar.setStyle(Paint.Style.FILL_AND_STROKE);
    mPaintRadar.setAntiAlias(true);

    post(run);

}

 
private Runnable run = new Runnable() {
    @Override
    public void run() {
        // 旋转角度 对360取余
        scanAngle = (scanAngle + scanSpeed) % 360;
        // 旋转矩阵
        matrix.postRotate(scanSpeed, mWidth / 2, mHeight / 2);
        // 通知view重绘
        invalidate(); 
        // 调用自身 重复绘制
        postDelayed(run, 50);
    }
};



3.测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    // 取屏幕的宽高
    mWidth = getMeasuredWidth();
    mHeight = getMeasuredHeight();
    mWidth = mHeight = Math.min(mWidth, mHeight);
}


4.绘制
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    for (int i = 0; i < pots.length; i++) {
        canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth * pots[i], mPaintCircle);
    }

    canvas.save();
    scanShader = new SweepGradient(mWidth / 2, mHeight / 2,
            new int[]{Color.TRANSPARENT, Color.parseColor("#84B5CA")}, null);
    // 设置着色器
    mPaintRadar.setShader(scanShader);
    canvas.concat(matrix);
    canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth * pots[4], mPaintRadar);

    canvas.restore();
}


 
 

ComposeShader(组合渲染)

ComposeShader,顾名思义,就是混合Shader的意思,它可以将两个Shader按照一定的Xfermode组合起来。

ComposeShader两个构造函数

定义:
/** A subclass of shader that returns the composition of two other shaders, combined by
    an {@link android.graphics.Xfermode} subclass.
    它返回另外两个着色器的组合,再加上Xfermode
*/
public class ComposeShader extends Shader {.....


/**
 * Xfermode is the base class for objects that are called to implement custom
 * "transfer-modes" in the drawing pipeline. The static function Create(Modes)
 * can be called to return an instance of any of the predefined subclasses as
 * specified in the Modes enum. When an Xfermode is assigned to an Paint, then
 * objects drawn with that paint have the xfermode applied.
 */
public class Xfermode {
    static final int DEFAULT = PorterDuff.Mode.SRC_OVER.nativeInt;
    int porterDuffMode = DEFAULT;
}
 
1./**
 * Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
 * When the mode is applied, it will be given the result from shader A as its
 * "dst", and the result from shader B as its "src".
 *
 * @param shaderA  The colors from this shader are seen as the "dst" by the mode
 * @param shaderB  The colors from this shader are seen as the "src" by the mode
 * @param mode     The PorterDuff mode that combines the colors from the two shaders.
*/
public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB,
        @NonNull PorterDuff.Mode mode) {
    this(shaderA, shaderB, mode.nativeInt);
}
     2.
  private ComposeShader(Shader shaderA, Shader shaderB, int nativeMode) {
    if (shaderA == null || shaderB == null) {
        throw new IllegalArgumentException("Shader parameters must not be null");
    }

    mShaderA = shaderA;
    mShaderB = shaderB;
    mPorterDuffMode = nativeMode;
}

实现效果:


实现核心代码:

需要图片:


 @Override
  protected void onDraw(Canvas canvas) {
      super.onDraw(canvas);
      canvas.drawColor(Color.WHITE);


      /**
       * 用ComposeShader即可实现心形图渐变效果
    * 创建BitmapShader
   */

Bitmap mBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.heart_img)).getBitmap();

BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

//创建LinearGradient,用以产生从左上角到右下角的颜色渐变效果 LinearGradient linearGradient = new LinearGradient(0, 0, mWidth, mHeight, Color.RED,

Color.BLUE, Shader.TileMode.CLAMP);

//bitmapShader对应目标像素,linearGradient对应源像素,像素颜色混合采用MULTIPLY模式

ComposeShader composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY); //将组合的composeShader作为画笔paint绘图所使用的shader

mPaint.setShader(composeShader);

//用composeShader绘制矩形区域 canvas.drawRect(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), mPaint); }


TileMode
TitleMode.CLAMP 拉伸最后一个像素去铺满剩下的地方(CLAMP表示,当所画图形的尺寸大于Bitmap的尺寸的时候,会用Bitmap四边的颜色填充剩余空间)
TitleMode.MIRROR 通过镜像翻转铺剩下的地方(与REPEAT类似,当绘制的图形尺寸大于Bitmap尺寸时,MIRROR也会用Bitmap重复平铺整个绘图区域,与REPEAT不同的是,两个相邻的Bitmap互为镜像)
TitleMode.REPEAT 重复图片平铺整个画面(设置壁纸)在图片平铺和显示区域大小不符的情况下进行扩展渲染(REPEAT表示,当我们绘制的图形尺寸大于Bitmap尺寸时,会用Bitmap重复平铺整个绘制的区域。)
 
 


相关阅读:

Android中Canvas绘图之PorterDuffXfermode使用及工作原理详解

Android中Canvas绘图基础详解(附源码下载)

我的Android博文整理汇总

Android中Canvas绘图之Shader使用图文详解


猜你喜欢

转载自blog.csdn.net/u014133119/article/details/80729966