Android着色器Shader使用误区

977 阅读3分钟

[TOC]

  Shader的基本使用不多说了,请参考blog.csdn.net/iispring/ar…,这里我们只讲解shader使用过程中的小细节或误区。

BitmapShader

BitmapShader加载的原图如下:

image

测试代码如下:

public class BitmapShaderView extends View {
    private Paint mPaint;
    private BitmapShader mShader;
    private int mWidth;
    private int mHeight;
    private int mBitmapWidth;
    private int mBitmapHeight;
    private Matrix mMatrix;
    private int mViewMin;
    private int mBitmapMin;

    public BitmapShaderView(Context context) {
        this(context, null);
    }

    public BitmapShaderView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BitmapShaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        init(context);
    }

    private void init(Context context) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_test);

        mBitmapWidth = bitmap.getWidth();
        mBitmapHeight = bitmap.getHeight();

        mMatrix = new Matrix();
        mShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mWidth = getWidth();
        mHeight = getHeight();
    }

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

        //测试调用函数
        testX(canvas);
    }
}

XML中定义如下:

<com.example.robincxiao.androidcanvas.shader.BitmapShaderView
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:layout_marginTop="10dp" />

为了进行测试,我们将BitmapShaderView的宽高都定义为200dp,这个值大于Bitmap的原始宽高。

绘制图形大小与Bitmap大小关系

1.绘制图形大小与Bitmap大小相等

用如下方法替换上面的testX方法

private void test0(Canvas canvas) {
    canvas.drawColor(Color.GRAY);
    mPaint.setShader(mShader);
    canvas.drawRect(0, 0, mBitmapWidth, mBitmapHeight, mPaint);
}

此时绘制的Rect与Bitmap的实际大小一致,得到如下效果:

image

2.绘制图形大小大于Bitmap实际大小

我们将test0中代码修改如下

private void test0(Canvas canvas) {
    canvas.drawColor(Color.GRAY);
    mPaint.setShader(mShader);
    canvas.drawRect(0, 0, mWidth, mHeight, mPaint);
}

此时绘制的Rect大小是整个View,远大于Bitmap的实际大小,得到如下效果:

image

对比“1.绘制图形大小与Bitmap大小相等”的结果,此时发现BitmapShader的第2、3个参数有作用了

BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)

小结:当绘制的图形区域大于Bitmap实际大小时(其实这个说法不准备,具体看后面分析),会按照BitmapShader的第2、3个参数的设置进行着色。

3.绘制图形大小小于Bitmap实际大小

我们将test0中代码修改如下

private void test0(Canvas canvas) {
    canvas.drawColor(Color.GRAY);
    mPaint.setShader(mShader);
    canvas.drawRect(0, 0, mBitmapWidth/2, mBitmapHeight/2, mPaint);
}

此时绘制的Rect大小是只有Bitmap实际大小的一般,得到如下效果:

image

对比“1.绘制图形大小与Bitmap大小相等”的结果,发现源图像只有左上角2/3区域被显示出来了。

总结:在使用BitmapShader进行着色时,要理解绘制图形大小与Bitmap实际大小最终对着色结果的影响。

坐标系对BitmapShader的影响

private void test0(Canvas canvas) {
    canvas.drawColor(Color.GRAY);
    mPaint.setShader(mShader);
    canvas.translate(mWidth/2, mHeight/2);
    canvas.drawRect(0, 0, mBitmapWidth, mBitmapHeight, mPaint);
}

我们将坐标系远点移动到BitmapShaderView的中心,得到的结果如下:

image

发现源Bitmap绘制的启动已经转移到BitmapShaderView的中心,但这还说明不了什么,因为drawRect的启动也是从中心开始的,得到上图所示的结果也是理所当然的。下面我们再修改test0代码如下:

private void test0(Canvas canvas) {
    canvas.drawColor(Color.GRAY);
    mPaint.setShader(mShader);
    canvas.translate(mWidth/2, mHeight/2);
    canvas.drawRect(-mBitmapWidth/2, -mBitmapHeight/2, mBitmapWidth/2, mBitmapHeight/2, mPaint);
}

我们以BitmapShaderView的中心为中心画一个正方形,这个正方形的大小与源Bitmap大小是一致的,得到的结果如下:
(注:BitmapShader的第2、3个参数为REPEAT)

image

得到这样的结果可能会让很多人觉得奇怪,按照“1.绘制图形大小与Bitmap大小相等”的分析,得到的结果应该与“1.绘制图形大小与Bitmap大小相等”的结果是一致的。这就是我想讲清楚的关键点:

  • BitmapShader所加载的Bitmap,显示的规则是:从当前坐标系的圆点从左到右、从上到下的显示;因此Bitmap的绘制是受坐标系影响的;
  • canvas实际绘制区域成为A,Bitmap自身绘制的区域B;A与B相交的区域显示B的内容,A中其它区域将按照BitmapShader的第二、三参数显示;

LinearGradient、RadialGradient、SweepGradient的使用过程中,同样要注意上面的问题。