android图片裁剪拼接实现(二):触摸实现

前文

android图片裁剪拼接实现(一):Matrix基本使用中说到了如何自定义控件并如何使用Matrix对其进行缩放、旋转等处理,此次就说说怎么把这些控制实现到触摸上面。
java

android触摸机制

首先,当用户点下屏幕的时候,Linux会将触摸包装成Event,而后InputReader会收到来自EventBus发送过来的Event,最后InputDispatcher分发给ViewRootImpl,ViewRootImpl再传递给DecorView,这最终才到达了咱们的当前界面,接下来的传递以下图所示。android

android触摸传递


图画的很差,水平有限,望见谅。git

事件分发


那从这里咱们就知道,咱们要写的view,须要先从dispatchTouchEvent()里面分发触摸事件,而后再TouchEvent()里面进行事件的处理。如下是dispatchTouchEvent中的处理。github

@Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        // 分发各个img的触摸事件
        if (mViewMode != VIEW_MODE_IDLE && findIndex >= 0) {
            imgList.get(findIndex).onTouchEvent(event);
            return true;
        }
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                if (mViewMode == VIEW_MODE_IDLE) {
                    findIndex = findTouchImg(event);
                    if (findIndex >= 0) {
                        return true;
                    }
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                // 判断落点是否在img中
                if (mViewMode == VIEW_MODE_IDLE) {
                    findIndex = findTouchImg(event);
                    if (findIndex >= 0) {
                        imgList.get(findIndex).onTouchEvent(event);
                        if (getParent() != null)
                            getParent().requestDisallowInterceptTouchEvent(true);
                        return true;
                    }
                }
                break;
        }
        return false;
    }
复制代码


这里使用getActionMask()是为了更好的处理多点触控。使用findTouchImg()方法,判断若是点落到图片区域就消费此次事件,可是,后续的触摸事件,父控件仍是有可能拦截的,此次只是消费了此次按压触摸事件。若是是多点触控,就直接调用requestDisallowInterceptTouchEvent的方法,禁止父控件拦截子控件的后续事件,不过使用这个方法要记着后面释放。判断确实是多点触控以后,就直接在方法顶部执行Img的方法,避免下面没必要要的判断。这里findTouchImg()方法主要是根据每一个Img的DrawRect进行点的落位断定。方法以下canvas

/** * @return -1 is not find */
    private int findTouchImg(MotionEvent event) {
        final float touchX = event.getX();
        final float touchY = event.getY();
        for (int i = 0; i < imgList.size(); i++) {
            ImageData imageData = imgList.get(i);
            if (imageData.drawRect.contains(touchX, touchY)) {
                return i;
            }
        }
        return -1;
    }
复制代码

触摸事件处理

这里咱们主要实现两种效果,缩放和旋转。咱们把Img的touch处理封装到了ImageData里面,代码以下:ide

/** * imageData的触摸处理事件 * * @param e 触摸事件 */
        protected void onTouchEvent(MotionEvent e) {
            switch (e.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    requestDisallowInterceptTouchEvent(true);
                    distanceStub = getPointDistance(e);
                    angleStub = getPointAngle(e);
                    break;
                case MotionEvent.ACTION_MOVE:
                    // confirm multi touch
                    if (e.getPointerCount() > 1) {
                        float tempDistance = getPointDistance(e);
                        float tempAngle = getPointAngle(e);
                        float tempScale = this.getScale();
                        float tempRotateAngle = this.getRotateAngle();

                        tempScale += (tempDistance / distanceStub) - 1;
                        tempRotateAngle += tempAngle - angleStub;

                        angleStub = tempAngle;
                        distanceStub = tempDistance;

                        this.setRotateAngle(tempRotateAngle);
                        this.setScale(tempScale);
                        reDraw();
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    runAngleAdsorbentAnim(findIndex);
                    requestDisallowInterceptTouchEvent(false);
                    if (getParent() != null)
                        getParent().requestDisallowInterceptTouchEvent(false);
                    distanceStub = 0;
                    angleStub = 0;
                    findIndex = -1;
                    break;
            }
        }
复制代码


当多点触控的时候,记录下最早两个触摸点的距离和斜率角度,在随后发生滑动的时候,计算与以前触摸点距离和斜率角度发生的变化,再对Bitmap进行即时调整。计算距离和斜率角度的方法以下:性能

private float getPointDistance(MotionEvent e) {
            if (e.getPointerCount() > 1) {
                final float touchX1 = e.getX(0);
                final float touchY1 = e.getY(0);
                final float touchX2 = e.getX(1);
                final float touchY2 = e.getY(1);
                return (float) Math.abs(Math.sqrt(Math.pow(touchX2 - touchX1, 2) + 
                Math.pow(touchY2 - touchY1, 2)));
            }
            return 0;
        }

        private float getPointAngle(MotionEvent e) {
            if (e.getPointerCount() > 1) {
                final float touchX1 = e.getX(0);
                final float touchY1 = e.getY(0);
                final float touchX2 = e.getX(1);
                final float touchY2 = e.getY(1);
                return (float) (Math.atan2(touchY2 - touchY1, touchX2 - touchX1) * (180f 
                / Math.PI));
            }
            return 0;
        }

复制代码


计算两点距离很简单,中学的计算公式动画

两点之间求距离公式
求两点相减的平方求根以后就是直线距离了。
求斜率也是借助中学的计算公式
斜率公式
算出来斜率,不过此时的斜率不能直接计算,要转换成角度。而转换成角度,只须要乘以(180÷π)便可。


那么咱们求出角度和距离公式以后,只须要跟上一次记录的数据进行比对,便可改变数据。咱们看看实现效果。this

触摸效果


可是到这一步尚未完,咱们还要加上吸附动画。spa

动画

咱们先直接看看吸附动画的代码:

private void runAngleAdsorbentAnim(int pos) {
        // force run animation
        if (pos >= imgList.size() || pos < 0)
            return;
        mViewMode = VIEW_MODE_RUN_ANIMATION;
        final ImageData imageData = imgList.get(pos);
        /* 吸附运算方式: e.g: space = 100; left point = 100; right point = 200; x = 161; calc process: 161+50 = 211 211/100 = 2 2x100=200 x = 149 calc process: 149+50 = 199 199/100 = 1 1x100 = 100 为了保证运算方式的结果, 以int形式进行计算,运算 结果出来以后再转换为rate */
        final int adsorbentAngle = 90;
        final int orgAngle = (int) imageData.rotateAngle;
        int toAngle = ((orgAngle + (adsorbentAngle / 2)) / adsorbentAngle) * adsorbentAngle;
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(orgAngle, toAngle);
        valueAnimator.setDuration(DEFAULT_ANIMATION_TIME);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                imageData.rotateAngle = (float) animation.getAnimatedValue();
                reDraw();
            }
        });
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mViewMode = VIEW_MODE_IDLE;
            }
        });
        valueAnimator.start();
    }
复制代码

吸附的运算原理在注释中已经详细距离说明了。这里就再也不解释了。传参进来一个img的坐标,使用ValueAnimator对其属性进行改变,调用reDraw()方法便可完成一帧的动画。
最后再来看看添加完吸附动画以后的效果:

带有吸附效果的触摸实现

最后咱们到这里基本的控制操做就完成了,还差最后一步,就是最终的图片拼接。

图片拼接

android的View给咱们提供了getDrawingCache()方法来得到当前view的绘制界面,不过这个方法受不少因素影响,不能每次均可以调用成功,而且可能会发生不可预知的后续操做,开启DrawingCache会产生性能影响。因此咱们本身建立一个Cavans,传给onDraw()方法,让其把当前最新的界面绘制到咱们传给他的Cavans上面。代码以下:

private Thread handleBitmapThread = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                outputBitmap = Bitmap.createBitmap(getMeasuredWidth(),
                        getMeasuredHeight(), Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(outputBitmap);
                draw(canvas);
                generateBitmapHandler.sendEmptyMessage(BITMAP_GENERATE_RESULT);
            } catch (Exception e) {
                // 扔到主线程抛出
                Message message = new Message();
                message.what = BITMAP_GENERATE_ERROR;
                Bundle bundle = new Bundle();
                bundle.putSerializable(BITMAP_ERROR, e);
                message.setData(bundle);
                generateBitmapHandler.sendMessage(message);
            }
        }
    });
复制代码

使用本身建立的Cavans还能够限定画布大小,达到裁剪的目的。onDraw()方法执行完成以后,界面绘制到了咱们传递的Bitmap上面,就能够把Bitmap抛出给处理方法来实现显示或者存储等一系列操做。


本文代码:github.com/Kongdy/Imag…
我的github地址:github.com/Kongdy
我的掘金主页:juejin.im/user/595a64…
csdn主页:blog.csdn.net/u014303003

相关文章
相关标签/搜索