一个功能强大的画图板(二)

我们在上一篇博客画图板一:[画图板一](http://blog.csdn.net/songhengqian/article/details/51792420)中基本实现了绘画的功能,那么我们就要丰富我们画图板的功能了。
实现缩放和平移肯定要用到矩阵,而又设计到画笔的轨迹问题,又必须拿到逆矩阵,那么我们很好的就能够确定要用矩阵,而我们还只能用一个矩阵(两个的我们根本不会算啊,哈哈)。在画图板一种我们的前景后景图其实是不同步的,那么我们为了更好的操作,我们需要使他们操作同步下,在onSizeChanged()方法中更改:
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        if (bitmap_bg==null){
            bitmap_bg=Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_4444);
            Drawable drawable=getResources().getDrawable(R.drawable.p1);
            BitmapDrawable bitmapDrawable= (BitmapDrawable) drawable;
            bitmap_bg= bitmapDrawable.getBitmap();
            bitmapWidth= bitmap_bg.getWidth();
            bitmapHeight=bitmap_bg.getHeight();
        mBitmapBgmMatix.postScale(scaleX,scaleY);
        }
        if (bitmap_fg == null && bitmap_bg!=null) {
            bitmap_fg=Bitmap.createBitmap(bitmapWidth,bitmapHeight, Bitmap.Config.ARGB_4444);
            //将位图和画布都设置为透明色
            bitmap_fg.eraseColor(Color.alpha(0));
            mCanvas=new Canvas(bitmap_fg);//所有mCanvas画的东西都被保存在了mBitmap中
            mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            sourceBitmap=bitmap_fg;
        }
    }

同步了我们前景后景,声明下我们需要的变量常量吧:

 /**
     * 初始化状态常量
     */
    public static final int STATUS_INIT = 1;

    /**
     * 图片缩小状态常量
     */
    public static final int STATUS_ZOOM_OUT = 2;

    /**
     * 图片放大状态常量
     */
    public static final int STATUS_ZOOM_IN = 3;
    /**
     * 记录当前操作的状态,可选值为STATUS_INIT、STATUS_ZOOM_OUT、STATUS_ZOOM_IN和STATUS_MOVE
     */
    private int currentStatus;
 //用来存储前景图
    private Bitmap sourceBitmap;
    //自定义控件的宽高
    private int width;
    private int height;

    /**
     * 记录图片在矩阵上的横向偏移值
     */
    private float totalTranslateX;

    /**
     * 记录图片在矩阵上的纵向偏移值
     */
    private float totalTranslateY;

    /**
     * 记录图片在矩阵上的总缩放比例
     */
    private float totalRatio;

    /**
     * 记录手指移动的距离所造成的缩放比例
     */
    private float scaledRatio;

    /**
     * 记录图片初始化时的缩放比例
     */
    private float initRatio;
    /**
     * 记录当前图片的宽度,图片被缩放时,这个值会一起变动
     */
    private float currentBitmapWidth;

    /**
     * 记录当前图片的高度,图片被缩放时,这个值会一起变动
     */
    private float currentBitmapHeight;
    /**
     * 记录两指同时放在屏幕上时,中心点的横坐标值
     */
    private float centerPointX;

    /**
     * 记录两指同时放在屏幕上时,中心点的纵坐标值
     */
    private float centerPointY;

    /**
     * 记录手指在横坐标方向上的移动距离
     */
    private float movedDistanceX;

    /**
     * 记录手指在纵坐标方向上的移动距离
     */
    private float movedDistanceY;
    /**
     * 记录上次手指移动时的横坐标
     */
    private float lastXMove = -1;

    /**
     * 记录上次手指移动时的纵坐标
     */
    private float lastYMove = -1;
    /**
     * 记录上次两指之间的距离
     */
    private double lastFingerDis;

界面初始化的时候我们需要保证图片的居中显示:

   /**
     * 对图片进行初始化操作,包括让图片居中,以及当图片大于屏幕宽高时对图片进行压缩。
     *
     * @param canvas
     */
    private void initBitmap(Canvas canvas) {
        if (sourceBitmap != null) {
            mMatrix.reset();
            int bitmapWidth = sourceBitmap.getWidth();
            int bitmapHeight = sourceBitmap.getHeight();

            if (bitmapWidth > width || bitmapHeight > height) {

                if (bitmapWidth - width > bitmapHeight - height) {
                    // 当图片宽度大于屏幕宽度时,将图片等比例压缩,使它可以完全显示出来
                    float ratio = width / (bitmapWidth * 1.0f);
                    mMatrix.postScale(ratio, ratio);
                    float translateY = (height - (bitmapHeight * ratio)) / 2f;
                    // 在纵坐标方向上进行偏移,以保证图片居中显示
                    mMatrix.postTranslate(0, translateY);
                    totalTranslateY = translateY;
                    totalRatio = initRatio = ratio;
                } else {
                    // 当图片高度大于屏幕高度时,将图片等比例压缩,使它可以完全显示出来
                    float ratio = height / (bitmapHeight * 1.0f);
                    mMatrix.postScale(ratio, ratio);
                    float translateX = (width - (bitmapWidth * ratio)) / 2f;
                    // 在横坐标方向上进行偏移,以保证图片居中显示
                    mMatrix.postTranslate(translateX, 0);
                    totalTranslateX = translateX;
                    totalRatio = initRatio = ratio;
                }
                currentBitmapWidth = bitmapWidth * initRatio;
                currentBitmapHeight = bitmapHeight * initRatio;
            } else {
                // 当图片的宽高都小于屏幕宽高时,直接让图片居中显示
                float translateX = (width - sourceBitmap.getWidth()) / 2f;
                float translateY = (height - sourceBitmap.getHeight()) / 2f;

                mMatrix.postTranslate(translateX, translateY);
                totalTranslateX = translateX;
                totalTranslateY = translateY;
                totalRatio = initRatio = 1f;
                currentBitmapWidth = bitmapWidth;
                currentBitmapHeight = bitmapHeight;
            }
            canvas.drawBitmap(bitmap_bg, mMatrix, mPaint);
            canvas.drawBitmap(sourceBitmap, mMatrix, mPaint);
        }
    }

界面初始化完成后我们就可以开始我们的缩放平移了

   /**
     * 对图片进行缩放处理。
     *
     * @param canvas
     */
    private void scale(Canvas canvas) {
        mMatrix.reset();

        // 将图片按总缩放比例进行缩放
        mMatrix.postScale(totalRatio, totalRatio);
        float scaledWidth = sourceBitmap.getWidth() * totalRatio;
        float scaledHeight = sourceBitmap.getHeight() * totalRatio;
        float translateX = 0f;
        float translateY = 0f;
        // 如果当前图片宽度小于屏幕宽度,则按屏幕中心的横坐标进行水平缩放。否则按两指的中心点的横坐标进行水平缩放
        if (currentBitmapWidth < width) {
            translateX = (width - scaledWidth) / 2f;
        } else {
            translateX = totalTranslateX * scaledRatio + centerPointX * (1 - scaledRatio);

            // 进行边界检查,保证图片缩放后在水平方向上不会偏移出屏幕
            if (translateX > 0) {
                translateX = 0;

            } else if (width - translateX > scaledWidth) {
                translateX = width - scaledWidth;
                Log.e(TAG, "translateX......" + translateX);
                Log.e(TAG, "scaledWidth....." + scaledWidth);
            }
        }

        // 如果当前图片高度小于屏幕高度,则按屏幕中心的纵坐标进行垂直缩放。否则按两指的中心点的纵坐标进行垂直缩放
        if (currentBitmapHeight < height) {
            translateY = (height - scaledHeight) / 2f;

        } else {

            translateY = totalTranslateY * scaledRatio + centerPointY * (1 - scaledRatio);
            // 进行边界检查,保证图片缩放后在垂直方向上不会偏移出屏幕
            if (translateY > 0) {
                translateY = 0;
            } else if (height - translateY > scaledHeight) {
                translateY = height - scaledHeight;

            }
        }

        // 缩放后对图片进行偏移,以保证缩放后中心点位置不变
        mMatrix.postTranslate(translateX, translateY);
        totalTranslateX = translateX;
        totalTranslateY = translateY;
        currentBitmapWidth = scaledWidth;
        currentBitmapHeight = scaledHeight;
        canvas.drawBitmap(bitmap_bg, mMatrix, mPaint);
        canvas.drawBitmap(sourceBitmap, mMatrix, mPaint);
    }

    /**
     * 对图片进行平移处理
     *
     * @param canvas
     */
    private void translate(Canvas canvas) {
        mMatrix.reset();
        // 根据手指移动的距离计算出总偏移值
        float translateX = totalTranslateX + movedDistanceX;
        float translateY = totalTranslateY + movedDistanceY;
        // 先按照已有的缩放比例对图片进行缩放
        mMatrix.postScale(totalRatio, totalRatio);
        // 再根据移动距离进行偏移
        mMatrix.postTranslate(translateX, translateY);
        totalTranslateX = translateX;
        totalTranslateY = translateY;
        canvas.drawBitmap(bitmap_bg, mMatrix, mPaint);
        canvas.drawBitmap(sourceBitmap, mMatrix, mPaint);
    }

  /**
     * 缩放和平移的边界判断
     *
     * @param event
     */
    private void move(MotionEvent event) {
        if (event.getPointerCount() == 1) {
            // 只有单指按在屏幕上移动时,为拖动状态
            float xMove = event.getX();
            float yMove = event.getY();
            if (lastXMove == -1 && lastYMove == -1) {
                lastXMove = xMove;
                lastYMove = yMove;
            }
            currentStatus = STATUS_MOVE;
            movedDistanceX = xMove - lastXMove;
            movedDistanceY = yMove - lastYMove;
            // 进行边界检查,不允许将图片拖出边界
            if (totalTranslateX + movedDistanceX > 0) {
                movedDistanceX = 0;
            } else if (width - (totalTranslateX + movedDistanceX) > currentBitmapWidth) {
                movedDistanceX = 0;
            }
            if (totalTranslateY + movedDistanceY > 0) {
                movedDistanceY = 0;
            } else if (height - (totalTranslateY + movedDistanceY) > currentBitmapHeight) {
                movedDistanceY = 0;
            }
            // 调用onDraw()方法绘制图片
            invalidate();
            lastXMove = xMove;
            lastYMove = yMove;
        } else if (event.getPointerCount() == 2) {
            // 有两个手指按在屏幕上移动时,为缩放状态
            centerPointBetweenFingers(event);

            double fingerDis = distanceBetweenFingers(event);
            if (fingerDis > lastFingerDis) {
                currentStatus = STATUS_ZOOM_OUT;
            } else {
                currentStatus = STATUS_ZOOM_IN;
            }
            // 进行缩放倍数检查,最大只允许将图片放大4倍,最小可以缩小到初始化比例
            if ((currentStatus == STATUS_ZOOM_OUT && totalRatio < 4 * initRatio)
                    || (currentStatus == STATUS_ZOOM_IN && totalRatio > initRatio)) {
                scaledRatio = (float) (fingerDis / lastFingerDis);
                totalRatio = totalRatio * scaledRatio;
                if (totalRatio >= 4 * initRatio) {
                    totalRatio = 4 * initRatio;
                } else if (totalRatio < initRatio) {
                    totalRatio = initRatio;
                }
                // 调用onDraw()方法绘制图片
                invalidate();
                lastFingerDis = fingerDis;
            }
        }
    }

    /**
     * 计算两个手指之间的距离。
     *
     * @param event
     * @return 两个手指之间的距离
     */
    private double distanceBetweenFingers(MotionEvent event) {
        float disX = Math.abs(event.getX(0) - event.getX(1));
        float disY = Math.abs(event.getY(0) - event.getY(1));
        return Math.sqrt(disX * disX + disY * disY);
    }

    /**
     * 计算两个手指之间中心点的坐标。
     *
     * @param event
     */
    private void centerPointBetweenFingers(MotionEvent event) {
        float xPoint0 = event.getX(0);
        float yPoint0 = event.getY(0);
        float xPoint1 = event.getX(1);
        float yPoint1 = event.getY(1);
        centerPointX = (xPoint0 + xPoint1) / 2;
        centerPointY = (yPoint0 + yPoint1) / 2;
    }

缩放平移的方法有了,对屏幕的边界检测也实现了,那么我们就该让我们的触摸方法来调用方法,真正实现我们的缩放平移功能了。

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()){
            case MotionEvent.ACTION_DOWN:
                onTouchDown(event);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                onTouchMove(event);
                if (paintState==paint.PEN){
                    mCanvas.drawPath(mPath,mPaint);
                    currentStatus=0;
                }else if (paintState==paint.ERASER){
                    mCanvas.drawPath(mPath,mEraser);
                    currentStatus=0;
                }else if(paintState==paint.NONE) {
                    move(event);
                }
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                if(paintState==paint.NONE) {
                    // 手指离开屏幕时将临时值还原
                    lastXMove = -1;
                    lastYMove = -1;
                }else{
                    onTouchUp(event);
                    invalidate();
                }

                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if(paintState==paint.NONE) {
                    if (event.getPointerCount() == 2) {
                        // 当有两个手指按在屏幕上时,计算两指之间的距离
                        lastFingerDis = distanceBetweenFingers(event);
                    }
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                if (paintState == paint.NONE) {
                    if (event.getPointerCount() == 2) {
                        // 手指离开屏幕时将临时值还原
                        lastXMove = -1;
                        lastYMove = -1;
                    }
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                break;
            default:
                break;
        }
        return true;
    }

   protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (bitmap_fg!=null){
            Log.e(TAG,"ondraw....");
            switch (currentStatus) {
                case STATUS_ZOOM_OUT:

                case STATUS_ZOOM_IN:

                    scale(canvas);
                    break;
                case STATUS_MOVE:
                    translate(canvas);
                    break;
                case STATUS_INIT:

                    initBitmap(canvas);
                default:
                    canvas.drawBitmap(bitmap_bg, mMatrix, mPaint);
                    canvas.drawBitmap(sourceBitmap, mMatrix, mPaint);
                    break;
            }

        }
    }

OK大功告成了,没有什么难点,我们主要是通过,变量来保存上一次的缩放比例,和平移的量,这样我们不管是平移还是缩放只需要用一个矩阵就可以解决了。到这里大家是不是觉得已经很棒了,不过可不要满足于此,再下一篇博客中我们将实现笔画的撤销与恢复以及图片保存的功能,还请大家继续关注。
源码下载链接:画图板二

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值