笔记101--摘自爱哥02

爱哥原文地址:http://blog.csdn.net/aigestudio/article/details/41316141

深挖如何去绘制更复杂的view。

一、ColorFilter及其子类

Paint.setColorFilter(ColorFilter filter);设置颜色过滤。

ColorFilter没有颜色相关的方法,所以肯定是通过其子类来控制的。有三个直接子类:ColorMatrixColorFilter、LightingColorFilter、PorterDuffColorFilter。

1、ColorMatrixColorFilter
a、简介及how用

色彩矩阵颜色过滤器。先了解下色彩矩阵,在Android中图片是以RGBA像素点的形式加载到内存中的,修改这些像素信息需要一个叫做ColorMatrix类的支持,其定义了一个4X5的float[]类型的矩阵:

ColorMatrix colorMatrix = new ColorMatrix(new float[]{  
        1, 0, 0, 0, 0,  
        0, 1, 0, 0, 0,  
        0, 0, 1, 0, 0,  
        0, 0, 0, 1, 0,  
});

Paint.setColorFilter(ColorFilter filter)传入ColorMatrixColorFilter对象后,是怎么计算最后要显示的颜色呢?通过色彩矩阵与原色彩相乘得出色彩(复习下矩阵的乘法):

使用方法:定义一个ColorMatrixColorFilter对象,然后传入Paint.setFilter()。

b、适用场景

这么复杂的实现只是为了改变颜色,你是不是想起来还有setColor()。是没错,但是setColor只是针对单一颜色使用的,如果是一张图片(包含了至少两种颜色),那你就需要此方法了。通过此方法你可以打到跟PS相同的效果。

2、LightingColorFilter
a、简介及用法

光照颜色过滤器。只有一个构造方法:LightingColorFilter(int mul, int add);mul全称是colorMultiply意为色彩倍增,而add全称是colorAdd意为色彩添加,这两个值都是16进制的色彩值0xAARRGGBB。

用法同上:定义对象,传入Paint.setFilter()。比如想过滤绿色,则只需将第一个参数ARGB中G的值设为0即可:

同样想增强某个颜色值只需将第二个参数的值增强即可。

// 设置颜色过滤  
mPaint.setColorFilter(new LightingColorFilter(0xFFFF00FF, 0x00000000));

b、适用场景

如果有这个需求:点击一个图片改变他的颜色,而不是替换成另一张点击效果的图片。那么这个类就有用了。

3、PorterDuffColorFilter
a、简介及用法

只有一个构造方法:PorterDuffColorFilter(int color, PorterDuff.Mode mode);color是16进制的颜色值;mode是PorterDuff内部类Mode中的一个常量,这个值表示混合模式:是将color值和画布上的元素混合。

PorterDuff.Mode中的模式不仅仅是应用于图像色彩混合,还应用于图形混合。
用法同上。

PS中图层的混合模式跟PorterDuff.Mode提供的其实差不多。

二、setXfermode(Xfermode xfermode)及其子类

译为过渡模式或图像混合模式。同setColorFilter一样,没有公开的方法,所以还是直接看它的子类吧:AvoidXfermode、PixelXorXfermode和PorterDuffXfermode。

1、AvoidXfermode

因不支持硬件加速在API 16已经过时了,如果想在高于API 16的机子上测试这玩意,必须关闭硬件加速(在清单文件中可设置)。

2、PixelXorXfermode

也在API 16过时了。。。

3、PorterDuffXfermode
a、简介及用法

只有一个构造函数:PorterDuffXfermode(PorterDuff.Mode mode);先来看一张API DEMO里的图片:


在API中android为我们提供了18种模式(比上图多了两种:ADD和OVERLAY)。src为源图像,即为要绘制的图像;Dis为目标图像,即为要把源图像绘制到的图像。

用法:

@Override  
    protected void onDraw(Canvas canvas) {  
        super.onDraw(canvas);  
        canvas.drawColor(Color.WHITE);  
  
        /* 
         * 将绘制操作保存到新的图层(更官方的说法应该是离屏缓存)我们将在1/3中学习到Canvas的全部用法这里就先follow me 
         */  
        int sc = canvas.saveLayer(0, 0, screenW, screenH, null, Canvas.ALL_SAVE_FLAG);  
  
        // 先绘制dis目标图  
        canvas.drawBitmap(bitmapDis, x, y, mPaint);  
  
        // 设置混合模式  
        mPaint.setXfermode(porterDuffXfermode);  
  
        // 再绘制src源图  
        canvas.drawBitmap(bitmapSrc, x, y, mPaint);  
  
        // 还原混合模式  
        mPaint.setXfermode(null);  
  
        // 还原画布  
        canvas.restoreToCount(sc);  
    }  

@Override  
    protected void onDraw(Canvas canvas) {  
        super.onDraw(canvas);  
        canvas.drawColor(Color.WHITE);  
  
        /* 
         * 将绘制操作保存到新的图层(更官方的说法应该是离屏缓存)我们将在1/3中学习到Canvas的全部用法这里就先follow me 
         */  
        int sc = canvas.saveLayer(0, 0, screenW, screenH, null, Canvas.ALL_SAVE_FLAG);  
  
        // 先绘制一层颜色  
        canvas.drawColor(0xFF8f66DA);  
  
        // 设置混合模式  
        mPaint.setXfermode(porterDuffXfermode);  
  
        // 再绘制src源图  
        canvas.drawBitmap(bitmapSrc, x, y, mPaint);  
  
        // 还原混合模式  
        mPaint.setXfermode(null);  
  
        // 还原画布  
        canvas.restoreToCount(sc);  
    } 
b、混合模式到底有什么用

看下面这张图,怎么画呢?


善于观察,善于利用混合模式:


是不是突然感觉简单很多。

橡皮擦效果:通过手指不断的触摸屏幕绘制Path,再以Path作遮罩遮掉填充的色块,显示下层的图像:

public class EraserView extends View {  
    private static final int MIN_MOVE_DIS = 5;// 最小的移动距离:如果我们手指在屏幕上的移动距离小于此值则不会绘制  
  
    private Bitmap fgBitmap, bgBitmap;// 前景橡皮擦的Bitmap和背景我们底图的Bitmap  
    private Canvas mCanvas;// 绘制橡皮擦路径的画布  
    private Paint mPaint;// 橡皮檫路径画笔  
    private Path mPath;// 橡皮擦绘制路径  
  
    private int screenW, screenH;// 屏幕宽高  
    private float preX, preY;// 记录上一个触摸事件的位置坐标  
  
    public EraserView(Context context, AttributeSet set) {  
        super(context, set);  
  
        // 计算参数  
        cal(context);  
  
        // 初始化对象  
        init(context);  
    }  
  
    /** 
     * 计算参数 
     *  
     * @param context 
     *            上下文环境引用 
     */  
    private void cal(Context context) {  
        // 获取屏幕尺寸数组  
        int[] screenSize = MeasureUtil.getScreenSize((Activity) context);  
  
        // 获取屏幕宽高  
        screenW = screenSize[0];  
        screenH = screenSize[1];  
    }  
  
    /** 
     * 初始化对象 
     */  
    private void init(Context context) {  
        // 实例化路径对象  
        mPath = new Path();  
  
        // 实例化画笔并开启其抗锯齿和抗抖动  
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);  
  
        // 设置画笔透明度为0是关键!我们要让绘制的路径是透明的,然后让该路径与前景的底色混合“抠”出绘制路径  
        mPaint.setARGB(128, 255, 0, 0);  
  
        // 设置混合模式为DST_IN  
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));  
  
        // 设置画笔风格为描边  
        mPaint.setStyle(Paint.Style.STROKE);  
  
        // 设置路径结合处样式  
        mPaint.setStrokeJoin(Paint.Join.ROUND);  
  
        // 设置笔触类型  
        mPaint.setStrokeCap(Paint.Cap.ROUND);  
  
        // 设置描边宽度  
        mPaint.setStrokeWidth(50);  
  
        // 生成前景图Bitmap  
        fgBitmap = Bitmap.createBitmap(screenW, screenH, Config.ARGB_8888);  
  
        // 将其注入画布  
        mCanvas = new Canvas(fgBitmap);  
  
        // 绘制画布背景为中性灰  
        mCanvas.drawColor(0xFF808080);  
  
        // 获取背景底图Bitmap  
        bgBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.a4);  
  
        // 缩放背景底图Bitmap至屏幕大小  
        bgBitmap = Bitmap.createScaledBitmap(bgBitmap, screenW, screenH, true);  
    }  
  
    @Override  
    protected void onDraw(Canvas canvas) {  
        // 绘制背景  
        canvas.drawBitmap(bgBitmap, 0, 0, null);  
  
        // 绘制前景  
        canvas.drawBitmap(fgBitmap, 0, 0, null);  
  
        /* 
         * 这里要注意canvas和mCanvas是两个不同的画布对象 
         * 当我们在屏幕上移动手指绘制路径时会把路径通过mCanvas绘制到fgBitmap上 
         * 每当我们手指移动一次均会将路径mPath作为目标图像绘制到mCanvas上,而在上面我们先在mCanvas上绘制了中性灰色 
         * 两者会因为DST_IN模式的计算只显示中性灰,但是因为mPath的透明,计算生成的混合图像也会是透明的 
         * 所以我们会得到“橡皮擦”的效果 
         */  
        mCanvas.drawPath(mPath, mPaint);  
    }  
  
    /** 
     * View的事件将会在7/12详解 
     */  
    @Override  
    public boolean onTouchEvent(MotionEvent event) {  
        /* 
         * 获取当前事件位置坐标 
         */  
        float x = event.getX();  
        float y = event.getY();  
  
        switch (event.getAction()) {  
        case MotionEvent.ACTION_DOWN:// 手指接触屏幕重置路径  
            mPath.reset();  
            mPath.moveTo(x, y);  
            preX = x;  
            preY = y;  
            break;  
        case MotionEvent.ACTION_MOVE:// 手指移动时连接路径  
            float dx = Math.abs(x - preX);  
            float dy = Math.abs(y - preY);  
            if (dx >= MIN_MOVE_DIS || dy >= MIN_MOVE_DIS) {  
                mPath.quadTo(preX, preY, (x + preX) / 2, (y + preY) / 2);  
                preX = x;  
                preY = y;  
            }  
            break;  
        }  
  
        // 重绘视图  
        invalidate();  
        return true;  
    }  
}  
效果图:




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值