Android view手势拖动和缩放
2016-03-11 22:53
781 查看
在实际开发中很多地方都要用自定义的手势缩放和可移动的view,在下在开发不久的时候就遇到啦,当时还不懂手势拖动,经过了各种摸索和查阅终于是自己写出来了,感觉收获颇多,这里我是用的矩阵来对自定义的ImageView进行手势缩放和移动的,感觉matrix真是强大的,稍后我定要详细研究一下Matrix, 之后也会写一片博客大家一起讨论啊,学好数学还真是有用,这里我用的OntouchEvent来做的,下面看代码:
第一点
这里先添加一个view, 我使用的是bitmap来添加的,后面我要使用它的宽度和高度,大家都知道能不用bitmap尽量不用,如果谁有好的方案要给我留言啊,我表示非常感谢,这里我使用一个list来保存所有的可移动的view(下面统称为view), 使用selectImageCount 来确定你选中和要移动的是哪个view, 这里主要的是要记录当前view的坐标,以便后续对它进行缩放和移动
第二点
这里就是最重要的方法 onTouchEvent 了,这里要说明的有几点吧, 一个是 event.getRawX() 获取的是相对于屏幕的坐标,其他getX() 等等的方法有什么区别大家可以查阅一下, 还有就是mode = DRAG这个模式一定要设置,因为如果你双指缩放抬起的时候会和单指移动冲突,这里有俩个主要的矩阵的缩放和移动方法: matrix.postScale(scale, scale, midP.x, midP.y);
前俩个参数是缩放倍数, 后面的坐标,
matrix.postRotate(newRotation, midP.x, midP.y);
第一个参数是双指旋转的角度, 后面是围绕哪个点来旋转, 之后在ACTION_MOVE事件最后要把当前的 矩阵做的变换保存到当前的View。
以上代码我基本写了注释, 有什么地方写的不对或者有什么不懂给我留言,这里实现的功能来算是比较全的吧, 可以满足开发需求了, 可以改变透明度, 可以单指缩放和移动, 双指缩放旋转,并且可以添加多个view, 有额外需求也可以随意扩展, 源码地址:
https://github.com/jinguangyue/MyFrameWork
其中GesturesView为当前项目,其他可忽略。
第一点
这里先添加一个view, 我使用的是bitmap来添加的,后面我要使用它的宽度和高度,大家都知道能不用bitmap尽量不用,如果谁有好的方案要给我留言啊,我表示非常感谢,这里我使用一个list来保存所有的可移动的view(下面统称为view), 使用selectImageCount 来确定你选中和要移动的是哪个view, 这里主要的是要记录当前view的坐标,以便后续对它进行缩放和移动
// 添加贴纸图片 private void addGesturesView(Bitmap b) { //将其他贴纸取消选中 if (b != null) { for (int i = (list_V.size() - 1); i >= 0; i--) { StickersHolder stickersHolder = list_V.get(i); if (stickersHolder.getImgV().isSelect()) { stickersHolder.getImgV().setSelect(false); break; } } curruntEditImageView = new MyCustomView(this, b); curruntEditImageView.setLayoutParams(new ViewGroup.LayoutParams(MyApplication.getInstance().getScreenWidth(), MyApplication.getInstance().getScreenHeight())); curruntEditImageView.setScaleType(ImageView.ScaleType.MATRIX); curruntEditImageView.setImageBitmap(b); curruntEditImageView.setSelect(true); curruntEditImageView.invalidate(); frame.addView(curruntEditImageView); curruntTiezhiBitmap = b; int bW = b.getWidth(); int bH = b.getHeight(); Matrix imageMatrix = new Matrix(curruntEditImageView.getImageMatrix()); //设置图片的宽度 为了适应屏幕 int newW = MyApplication.getInstance().getScreenWidth() / 3; //等比例缩放 int newH = (int) (((float) newW / (float) bW) * (float) bH); float scale = ((float) newW) / bW; imageMatrix.postScale(scale, scale, 0, 0);// 缩放图片大小 int x1 = MyApplication.getInstance().getScreenWidth() / 3; int y1 = MyApplication.getInstance().getScreenHeight() / 3; // 原图左上角 curruntEditImageView.leftTop.set(x1, y1); // 原图右上角 curruntEditImageView.rightTop.set(x1 + newW, y1); // 原图左下角 curruntEditImageView.leftBottom.set(x1, y1 + newH); // 原图右下角 curruntEditImageView.rightBottom.set(x1 + newW, y1 + newH); imageMatrix.postTranslate(x1, y1);// 图片平移 //保存位置 上下左右 ImageState state1 = new ImageState(); state1.setLeft(x1); state1.setTop(y1); state1.setRight(x1 + newW); state1.setBottom(y1 + newH); //设置当前view的矩阵 curruntEditImageView.setImageMatrix(imageMatrix); StickersHolder stickersHolder1 = new StickersHolder(); stickersHolder1.setImgV(curruntEditImageView); stickersHolder1.setState(state1); list_V.add(stickersHolder1); selectImageCount = list_V.size() - 1; } }
第二点
这里就是最重要的方法 onTouchEvent 了,这里要说明的有几点吧, 一个是 event.getRawX() 获取的是相对于屏幕的坐标,其他getX() 等等的方法有什么区别大家可以查阅一下, 还有就是mode = DRAG这个模式一定要设置,因为如果你双指缩放抬起的时候会和单指移动冲突,这里有俩个主要的矩阵的缩放和移动方法: matrix.postScale(scale, scale, midP.x, midP.y);
前俩个参数是缩放倍数, 后面的坐标,
matrix.postRotate(newRotation, midP.x, midP.y);
第一个参数是双指旋转的角度, 后面是围绕哪个点来旋转, 之后在ACTION_MOVE事件最后要把当前的 矩阵做的变换保存到当前的View。
@Override public boolean onTouchEvent(MotionEvent event) { //这里根据实际情况做了一下计算 亲测 位置准确 float event_x = event.getRawX(); // float event_y = event.getY() - 30; float event_y = event.getRawY() - getStatusBarHeight(); int tempInt = getStatusBarHeight() + 10; int addint = -20; switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: baseValue = 0; startPoinF.set(event_x, event_y);// 保存刚开始按下的坐标 //选中的图片 selectImG(event_x, event_y); if (selectImageCount != -1) { mode = DRAG; matrix.set(list_V.get(selectImageCount).getImgV() .getImageMatrix()); savedMatrix.set(matrix);// 把以前的图片大小保存起来 MyCustomView edImg = list_V.get(selectImageCount).getImgV(); //拿到当前view的坐标 在addGesturesView方法里面设置过了 PointF leftTop = edImg.leftTop;// 图片zuo 上角的坐标 PointF rightBottom = edImg.rightBottom;// 图片右下角的坐标 PointF leftBottom = edImg.leftBottom; PointF rightTop = edImg.rightTop; //生成4个矩形 用来判断点击区域是不是在这个范围内 下面有每一个的注释 Rect moveRect = new Rect((int) rightBottom.x - tempInt, (int) rightBottom.y - tempInt, (int) rightBottom.x + tempInt + addint, (int) rightBottom.y + tempInt + addint); Rect deleteRect = new Rect((int) leftTop.x - tempInt, (int) leftTop.y - tempInt, (int) leftTop.x + tempInt + addint, (int) leftTop.y + tempInt + addint); Rect flipRect = new Rect((int) leftBottom.x - tempInt, (int) leftBottom.y - tempInt, (int) leftBottom.x + tempInt + addint, (int) leftBottom.y + tempInt + addint); Rect transparencyRect = new Rect((int) rightTop.x - tempInt, (int) rightTop.y - tempInt, (int) rightTop.x + tempInt + addint, (int) rightTop.y + tempInt + addint); if (moveRect.contains((int) event_x, (int) event_y)) { // 点中了变换 midP = midPoint(edImg.leftTop, edImg.rightBottom); imgLengHalf = spacing(midP, edImg.rightBottom); oldRotation = rotation(midP, startPoinF); mode = ZOOM; } else if (deleteRect.contains((int) event_x, (int) event_y)) { // 点中了删除 deleteTieZi(); } else if (flipRect.contains((int) event_x, (int) event_y)) { // 点中了翻转 Matrix m = new Matrix(); m.postScale(-1, 1); curruntTiezhiBitmap = list_V.get(selectImageCount).getImgV().getBitmapSelf(); curruntTiezhiBitmap = Bitmap.createBitmap(curruntTiezhiBitmap, 0, 0, curruntTiezhiBitmap.getWidth(), curruntTiezhiBitmap.getHeight(), m, true); list_V.get(selectImageCount).getImgV().setImageBitmap(curruntTiezhiBitmap); list_V.get(selectImageCount).getImgV().setBitmapSelf(curruntTiezhiBitmap); list_V.get(selectImageCount).getImgV().invalidate(); frame.invalidate(); } else if (transparencyRect.contains((int) event_x, (int) event_y)) { // 点中了改变透明度 curruntEditImageAlpha = curruntEditImageAlpha - 51; if (selectImageCount != -1) { curruntEditImageView.setAlpha(curruntEditImageAlpha); if (curruntEditImageAlpha == 51) { curruntEditImageAlpha = 306; } } } } break; case MotionEvent.ACTION_POINTER_DOWN: if (curruntEditImageView != null) { //保存俩个点的中点 双指缩放时候用 midP = midPoint(curruntEditImageView.leftTop, curruntEditImageView.rightBottom); //得到俩个点的距离 imgLengHalf = spacing(midP, curruntEditImageView.rightBottom); //起始角度 oldRotation = rotationforTwo(event); } break; case MotionEvent.ACTION_UP: mode = NONE; break; case MotionEvent.ACTION_MOVE: if (event.getPointerCount() == 2 && curruntEditImageView != null) { //模式一定要设置 双指缩放 如果不设置模式可能会和单指冲突 mode = DOUBLE_ZOOM; //点击的俩个点 float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); float value = (float) Math.sqrt(x * x + y * y);// 计算两点的距离 // float newRotation = (float) Math.toDegrees(radians) - oldRotation; float newRotation = rotationforTwo(event) - oldRotation; if (baseValue == 0) { baseValue = value; } else { if (value - baseValue >= 10 || value - baseValue <= -10) { float scale = value / baseValue;// 当前两点间的距离除以手指落下时两点间的距离就是需要缩放的比例。 matrix.set(savedMatrix); //计算出缩放比例和旋转角度之后使用矩阵来进行变换 matrix.postScale(scale, scale, midP.x, midP.y); matrix.postRotate(newRotation, midP.x, midP.y); } } } else if (event.getPointerCount() == 1) { //通过selectImageCount判断是否有View选中 if (selectImageCount != -1) { if (mode == DRAG) { //判断边缘 if (event_x < MyApplication.getInstance().getScreenWidth() - 50 && event_x > 50 && event_y > 100 && event_y < MyApplication.getInstance().getScreenHeight() - 100) { matrix.set(savedMatrix); // 图片移动的距离 float translateX = event_x - startPoinF.x; float translateY = event_y - startPoinF.y; //平移 matrix.postTranslate(translateX, translateY); } } else if (mode == ZOOM) { //如果点中了变换 PointF movePoin = new PointF(event_x, event_y); float moveLeng = spacing(startPoinF, movePoin); float newRotation = rotation(midP, movePoin) - oldRotation; if (moveLeng > 10f) { float moveToMidLeng = spacing(midP, movePoin); float scale = moveToMidLeng / imgLengHalf; matrix.set(savedMatrix); matrix.postScale(scale, scale, midP.x, midP.y); matrix.postRotate(newRotation, midP.x, midP.y); } } } } //ACTION_MOVE 对图片的最后位置进行确定 if (selectImageCount != -1) { //拿到当前的view MyCustomView imgView = list_V.get(selectImageCount).getImgV(); //拿到当前view的矩阵 float[] f = new float[9]; matrix.getValues(f); Rect rect = imgView.getDrawable().getBounds(); int bWidth = rect.width(); int bHeight = rect.height(); // 原图左上角 float x1 = f[2]; float y1 = f[5]; imgView.leftTop.set(x1, y1); // 原图右上角 float x2 = f[0] * bWidth + f[2]; float y2 = f[3] * bWidth + +f[5]; imgView.rightTop.set(x2, y2); // 原图左下角 float x3 = f[1] * bHeight + f[2]; float y3 = f[4] * bHeight + f[5]; imgView.leftBottom.set(x3, y3); // 原图右下角 float x4 = f[0] * bWidth + f[1] * bHeight + f[2]; float y4 = f[3] * bWidth + f[4] * bHeight + f[5]; imgView.rightBottom.set(x4, y4); // 最左边x float minX = 0; // 最右边x float maxX = 0; // 最上边y float minY = 0; // 最下边y float maxY = 0; minX = Math.min(x4, Math.min(x3, Math.min(x1, x2))) - 30; maxX = Math.max(x4, Math.max(x3, Math.max(x1, x2))) + 30; minY = Math.min(y4, Math.min(y3, Math.min(y1, y2))) - 30; maxY = Math.max(y4, Math.max(y3, Math.max(y1, y2))) + 30; list_V.get(selectImageCount).getState().setLeft(minX); list_V.get(selectImageCount).getState().setTop(minY); list_V.get(selectImageCount).getState().setRight(maxX); list_V.get(selectImageCount).getState().setBottom(maxY); //最后保存 确定位置 list_V.get(selectImageCount).getImgV().setImageMatrix(matrix); } break; } return false; }
以上代码我基本写了注释, 有什么地方写的不对或者有什么不懂给我留言,这里实现的功能来算是比较全的吧, 可以满足开发需求了, 可以改变透明度, 可以单指缩放和移动, 双指缩放旋转,并且可以添加多个view, 有额外需求也可以随意扩展, 源码地址:
https://github.com/jinguangyue/MyFrameWork
其中GesturesView为当前项目,其他可忽略。
相关文章推荐
- android 自定义坐标曲线图
- Android Studio插件-自动根据布局生成Activity等代码(插件代码开源)
- Mars《Android开发视频教程》
- Android控件第4类——ProgressBar
- Android之NetworkOnMainThreadException异常
- Android开发-Gradle基础
- [Android开发那点破事]解决android.os.NetworkOnMainThreadException
- 出现android.os.NetworkOnMainThreadException异常
- android.os.NetworkOnMainThreadException 异常处理
- 仿Android5.0 水波扩散效果(Ripple)简单实现
- 识别输入装置ID与InputDevice装置(Improved event management)
- Android中的PendingIntent 原理
- android源码设计模式解析与实战 读书笔记 2 单例模式(下)
- Android中用到的几大设计模式
- [android] 常用数据适配器SimpleAdapter
- android 的jpush极光推送
- [置顶] Android第三方库学习清单
- android launchmode
- Android开发学习之路--RxAndroid之初体验
- Android开发学习之路--RxAndroid之初体验