自定义视图-马赛克视图

原创 2016年05月30日 19:08:22

这里我是直接继承的ImageView,主要是为了能兼容之前的项目,因为之前是直接用的ImageView。

思路

  1. 根据原图片生成一个全马赛克的图片
  2. 监听手势,得到应该显示的马赛克方块的集合
  3. 根据方块的集合,刷新视图,这里用到了Paint的Xfermode(图片混合模式)
    1. 手势图和全马赛克图混合,在相交处绘制马赛克图
    2. 将上一步的图和原图混合,在相交处绘制上一步的图,在不相交处绘制原图,搞定收工!

效果图

这里写图片描述

图片混合模式

下图以黄圆为dest,蓝矩为src,展示了各种图片混合模式:

这里写图片描述

这里以mosaicBitmap为dest,以touchBitmap为src,
设置DST_IN模式:在相交处取dest

canvas.drawBitmap(mosaicBitmap, 0, 0, paint);//dest
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));//设置DST_IN模式
canvas.drawBitmap(touchBitmap, 0, 0, paint);//src

源码

package com.che.carcheck.support.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;

import java.util.ArrayList;
import java.util.List;

/**
 * 马赛克视图
 * <p/>
 * 作者:余天然 on 16/5/30 下午6:04
 */
public class MosaicView extends ImageView {

    private Bitmap bitmap;//原图
    private Bitmap mosaicBitmap;//全马赛克图
    private Bitmap mergeBitmap;//合成图
    private int strokeWidth;// 画笔宽度px
    private List<Rect> mosaicRects;//马赛克集合
    public static final int min_mosaic_block_size = 4;//马赛克的最小粒度
    private Paint paint;

    public MosaicView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mosaicRects = new ArrayList<>();
        strokeWidth = 20;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                int x = (int) event.getX();
                int y = (int) event.getY();
                //记录应该显示马赛克的矩形集合
                int radius = strokeWidth / 2;
                int left = Math.max(x - radius, 0);
                int right = Math.min(x + radius, bitmap.getWidth());
                int top = Math.max(y - radius, 0);
                int bottom = Math.min(y + radius, bitmap.getHeight());
                Rect rect = new Rect(left, top, right, bottom);
                mosaicRects.add(rect);
                // FIXME: 16/5/30 这里本来打算调用onDraw的,不知道setImageBitmap那里怎么出了问题
//                invalidate();
                updateMosaicList();
                break;
        }
        return true;
    }

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

    private void updateMosaicList() {
        Canvas canvas = new Canvas();
        doDraw(canvas);
        setImageBitmap(mergeBitmap);
    }

    private void doDraw(Canvas canvas) {
        //手势图
        Bitmap touchBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
        canvas.setBitmap(touchBitmap);
        for (Rect rect : mosaicRects) {
            canvas.drawRect(rect, paint);
        }

        //合成图
        mergeBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
        canvas.setBitmap(mergeBitmap);
        canvas.drawBitmap(mosaicBitmap, 0, 0, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(touchBitmap, 0, 0, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
        canvas.drawBitmap(bitmap, 0, 0, paint);
    }

    //生成全马赛克的图片
    private Bitmap makeMosaic(Bitmap bitmap, Rect targetRect,
                              int blockSize) throws OutOfMemoryError {
        if (bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0
                || bitmap.isRecycled()) {
            throw new RuntimeException("bad bitmap to add mosaic");
        }
        if (blockSize < min_mosaic_block_size) {
            blockSize = min_mosaic_block_size;
        }
        if (targetRect == null) {
            targetRect = new Rect();
        }
        int bw = bitmap.getWidth();
        int bh = bitmap.getHeight();
        if (targetRect.isEmpty()) {
            targetRect.set(0, 0, bw, bh);
        }
        //
        int rectW = targetRect.width();
        int rectH = targetRect.height();
        int[] bitmapPxs = new int[bw * bh];
        // fetch bitmap pxs
        bitmap.getPixels(bitmapPxs, 0, bw, 0, 0, bw, bh);
        //
        int rowCount = (int) Math.ceil((float) rectH / blockSize);
        int columnCount = (int) Math.ceil((float) rectW / blockSize);
        int maxX = bw;
        int maxY = bh;
        for (int r = 0; r < rowCount; r++) { // row loop
            for (int c = 0; c < columnCount; c++) {// column loop
                int startX = targetRect.left + c * blockSize + 1;
                int startY = targetRect.top + r * blockSize + 1;
                dimBlock(bitmapPxs, startX, startY, blockSize, maxX, maxY);
            }
        }
        return Bitmap.createBitmap(bitmapPxs, bw, bh, Bitmap.Config.ARGB_8888);
    }

    //从块内取样,并放大,从而达到马赛克的模糊效果
    private static void dimBlock(int[] pxs, int startX, int startY,
                                 int blockSize, int maxX, int maxY) {
        int stopX = startX + blockSize - 1;
        int stopY = startY + blockSize - 1;
        if (stopX > maxX) {
            stopX = maxX;
        }
        if (stopY > maxY) {
            stopY = maxY;
        }
        //
        int sampleColorX = startX + blockSize / 2;
        int sampleColorY = startY + blockSize / 2;
        //
        if (sampleColorX > maxX) {
            sampleColorX = maxX;
        }
        if (sampleColorY > maxY) {
            sampleColorY = maxY;
        }
        int colorLinePosition = (sampleColorY - 1) * maxX;
        int sampleColor = pxs[colorLinePosition + sampleColorX - 1];// 像素从1开始,但是数组层0开始
        for (int y = startY; y <= stopY; y++) {
            int p = (y - 1) * maxX;
            for (int x = startX; x <= stopX; x++) {
                // 像素从1开始,但是数组层0开始
                pxs[p + x - 1] = sampleColor;
            }
        }
    }

    /*设置原图*/
    public void setBitmap(Bitmap bitmap) {
        this.bitmap = bitmap;
        this.mosaicBitmap = makeMosaic(bitmap, null, strokeWidth);
        updateMosaicList();
    }

    /*获取合成图*/
    public Bitmap getMergeBitmap() {
        return mergeBitmap;
    }

    /*恢复初始的原图*/
    public void reset() {
        this.mosaicRects.clear();
        updateMosaicList();
    }

    /*设置画笔宽度*/
    public void setStrokeWidth(int strokeWidth) {
        this.strokeWidth = strokeWidth;
        this.mosaicBitmap = makeMosaic(bitmap, null, strokeWidth);
        updateMosaicList();
    }


}
版权声明:本文为博主原创文章,未经博主允许不得转载。

Android中自定义视图View

  • 2015年01月12日 10:44
  • 2.44MB
  • 下载

asp.net mvc 自定义视图引擎

  • 2016年03月12日 16:12
  • 414KB
  • 下载

Android中自定义视图View之---进阶篇(Canvas的使用)

更多技术内容请移步:我的个人博客一、前言今天是周日,昨天刚刚写完了一篇关于如何搭建LNMP环境,让自己可以DIY有个性的个人主页:http://blog.csdn.net/jiangwei091041...

Android自定义罗盘视图

  • 2015年08月07日 14:39
  • 1.42MB
  • 下载

好友推荐---环信发送名片(自定义视图)消息

在集成环信即时通讯的基础上,要做好友推荐的功能,查了下SDK发现里面提供了自定义消息的接口,接下来我们就可以根据自己的需要去自定义消息视图了- (UITableViewCell *)messageVi...

VC 2010 + MFC:自定义文档视图框架,去除新建、保存、打开等菜单功能,让文档程序个性化

1. 去除自定义功能. 不区分大小写,搜索Customize,再启动自定义菜单、工具栏等代码中,将TRUE改成FALSE。并删除相应函数。 2. 在CMainFra...
  • vlily
  • vlily
  • 2012年03月22日 16:10
  • 5838
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:自定义视图-马赛克视图
举报原因:
原因补充:

(最多只允许输入30个字)