Android手摸手实现一个画板功能(一)——View的拖拽
一、概述
从之前项目中抽取出来的一个“画板”功能模块,就是可以在一个空白布局上,添加不同的元素,实现自由组合,暂时没想到啥好名字,姑且叫它“画板”吧。
主要实现了View的拖拽、缩放、旋转、复制、View导出图片、文本编辑、磁力连接线、上一步和下一步状态备忘等功能。该项目主要涉及的知识点:View的事件分发、手势多点触控、View坐标系、备忘录设计模式等。
由于该项目是为特定pad机型定制项目,未做其他机型兼容性处理,但是这并不影响本文对其原理的讲解,建议使用1200 x 1920平板模拟器或真机运行工程以获得最佳体验。
无图言屌?上图:
二、解析
2.1 侧边栏长按拖拽到画布
思路大概是酱紫:
第一步,为侧边栏的每个Imageview设置OnLongClickListener、OnTouchListener;
第二步,长按时生成一个新的Imageview对象,根据当前长按的Imageview的id,设置相应的ImageResource,并添加到画布中;
第三步,为刚刚生成的Imageview对象设置OnTouchListener,在onTouch方法中,不断的更新ImageView的xy坐标,从而实现view的拖拽。
看代码:
2.1.1 setOnLongClickListener()、setOnTouchListener()
ImageView allImageView = (ImageView) findViewById(R.id.allIcon);allImageView.setOnTouchListener(mTouchListener);allImageView.setOnLongClickListener(mLongClickListener);ImageView smileImageView = (ImageView) findViewById(R.id.smileIcon);smileImageView.setOnTouchListener(mTouchListener);smileImageView.setOnLongClickListener(mLongClickListener);ImageView jewelryImageView = (ImageView) findViewById(R.id.jewelryIcon);jewelryImageView.setOnTouchListener(mTouchListener);jewelryImageView.setOnLongClickListener(mLongClickListener);ImageView hotImageView = (ImageView) findViewById(R.id.hotIcon);hotImageView.setOnTouchListener(mTouchListener);hotImageView.setOnLongClickListener(mLongClickListener);ImageView lineImageView = (ImageView) findViewById(R.id.lineIcon);lineImageView.setOnTouchListener(mTouchListener);lineImageView.setOnLongClickListener(mLongClickListener);ImageView rect = (ImageView) findViewById(R.id.rectIcon);rect.setOnTouchListener(mTouchListener);rect.setOnLongClickListener(mLongClickListener);
2.1.2 长按事件处理:
private View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {final ImageView imageView = new ImageView(MainActivity.this);mCurrentImageView = imageView;ViewInfo viewInfo = new ViewInfo(v.getId(), 0);viewInfo.type = ViewInfo.TYPE_IMAGEVIEW;viewInfo.color = mCurrentColor;viewInfo.realId = ++mRealInfoId;imageView.setTag(viewInfo);imageView.setScaleType(ImageView.ScaleType.FIT_XY);setImageResource(imageView, true);int[] location = new int[2];v.getLocationOnScreen(location);locationX = location[0];locationY = location[1];imageView.setX(locationX + 5);imageView.setY(locationY + 5);FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(v.getWidth(), v.getHeight());mRootView.addView(imageView, params);mViewList.add(imageView);imageView.setOnTouchListener(new MyTouchListener(imageView));return true;}};//根据不同的id设置不同的图片资源public void setImageResource(ImageView v, boolean focus) {ViewInfo viewInfo = (ViewInfo) v.getTag();switch (viewInfo.id) {case R.id.allIcon:realSetImageResource(v, viewInfo, focus, R.drawable.all_selected, R.drawable.ic_all_black, R.drawable.ic_all_green, R.drawable.ic_all_red);break;case R.id.smileIcon:realSetImageResource(v, viewInfo, focus, R.drawable.smile_selected, R.drawable.ic_smile_black, R.drawable.ic_smile_green, R.drawable.ic_smile_red);break;case R.id.jewelryIcon:realSetImageResource(v, viewInfo, focus, R.drawable.jewelry_selected, R.drawable.ic_jewelry_black, R.drawable.ic_jewelry_green, R.drawable.ic_jewelry_red);break;case R.id.hotIcon:realSetImageResource(v, viewInfo, focus, R.drawable.hot_selected, R.drawable.ic_hot_black, R.drawable.ic_hot_green, R.drawable.ic_hot_red);break;case R.id.lineIcon:if (mLineBitmap == null) {mLineBitmapBlack = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);mLineBitmapGreen = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);mLineBitmapRed = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);Paint paint = new Paint();paint.setColor(Color.BLACK);paint.setStrokeWidth(STROKE_WIDTH);Canvas canvas = new Canvas(mLineBitmapBlack);canvas.drawLine(0, 50, 100, 50, paint);paint.setColor(Color.RED);canvas = new Canvas(mLineBitmapRed);canvas.drawLine(0, 50, 100, 50, paint);paint.setColor(Color.GREEN);canvas = new Canvas(mLineBitmapGreen);canvas.drawLine(0, 50, 100, 50, paint);mLineBitmap = mLineBitmapBlack;if (viewInfo.color == 2) {mLineBitmap = mLineBitmapRed;} else if (mCurrentColor == 1) {mLineBitmap = mLineBitmapGreen;}}if (focus) {v.setImageResource(R.drawable.line_selected);} else {v.setImageBitmap(mLineBitmap);}break;case R.id.rectIcon:if (focus) {v.setBackgroundResource(R.drawable.border_shape_focus);} else {v.setBackgroundResource(R.drawable.border_shape);}break;default:break;}}
2.1.3 处理View的拖拽
private View.OnTouchListener mTouchListener = new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, final MotionEvent event) {int action = event.getAction();if (mCurrentImageView == null && MotionEvent.ACTION_DOWN != action) {return false;}switch (action) {case MotionEvent.ACTION_DOWN:fdownX = event.getX();fdownY = event.getY();LogUtils.d("fdownX: " + fdownX + " ###fdownY: " + fdownY);getLineCoordinate();break;case MotionEvent.ACTION_MOVE:float disX = event.getX() - fdownX - OFFSET;float disY = event.getY() - fdownY - OFFSET;LogUtils.d("disX: " + disX + " ### disY: " + disY + " ### getX: " + event.getX() + " ### getY: " + event.getY());mCurrentImageView.setX(mCurrentImageView.getX() + disX);mCurrentImageView.setY(mCurrentImageView.getY() + disY);LogUtils.i("mCurrentImageView.getX(): " + mCurrentImageView.getX() + " ### mCurrentImageView.getY(): " + mCurrentImageView.getY());fdownX = event.getX() - OFFSET;fdownY = event.getY() - OFFSET;LogUtils.v("fdownX: " + fdownX + " ### fdownY: " + fdownY);imageW = mCurrentImageView.getWidth();imageH = mCurrentImageView.getHeight();mCurrentImageView.setBackgroundResource(android.R.color.transparent);setImageResource(mCurrentImageView, true);return true;case MotionEvent.ACTION_UP:float x = mCurrentImageView.getX();float y = mCurrentImageView.getY();if (x <= 212) {cancelMoveView(x, y);return true;} else {if (x > 212 && x < 312) {x = 312;} else if (x > (mDisplayMetrics.widthPixels - 100)) {x = mDisplayMetrics.widthPixels - 100;}if (y <= 106) {y = 106;} else if (y > mDisplayMetrics.heightPixels - 100 - mStatusBarHeight) {y = mDisplayMetrics.heightPixels - 100 - mStatusBarHeight;}mCurrentImageView.setX(x - 312);mCurrentImageView.setY(y - 107);setImageResource(mCurrentImageView, false);mRootView.removeView(mCurrentImageView);mContent.addView(mCurrentImageView);createMemento(mCurrentImageView, false, true);if (((ViewInfo) mCurrentImageView.getTag()).id == R.id.rectIcon) {mCurrentImageView.setBackgroundResource(R.drawable.border_shape);} else {mCurrentImageView.setBackgroundResource(android.R.color.transparent);}}mCurrentImageView = null;break;case MotionEvent.ACTION_CANCEL:float x1 = mCurrentImageView.getX();float y1 = mCurrentImageView.getY();cancelMoveView(x1, y1);break;}return false;}};
处理拖拽的难点在于View新的x、y坐标计算,如果能够准确计算出View新的坐标,那么拖拽问题就可迎刃而解!
首先拿到ACTION_DOWN事件按下的(x,y),对应fdownX、fdownY,其次在ACTION_MOVE时获取新的(x,y),通过新的(x,y)-旧的(x,y),就可以得到移动距离disX、disY,再将View的(x,y)坐标设置成:原来的坐标+移动距离,就可以实现View移动,从而实现拖拽;最后,别忘了,ACTION_MOVE事件是会持续触发的,所以每一个新的坐标相对于下一次移动坐标,都会变成旧的坐标,因此拖拽完View之后,还需要对手指的按下位置重新赋值。
核心代码如下:
//OFFSET:由于体验问题,手指按在View上会遮挡住当前View,所以设置了一个偏移量来错开一定距离,该值可以不设置case MotionEvent.ACTION_DOWN:fdownX = event.getX();fdownY = event.getY();LogUtils.d("fdownX: " + fdownX + " ###fdownY: " + fdownY);getLineCoordinate();break;case MotionEvent.ACTION_MOVE:float disX = event.getX() - fdownX - OFFSET;float disY = event.getY() - fdownY - OFFSET;LogUtils.d("disX: " + disX + " ### disY: " + disY + " ### getX: " + event.getX() + " ### getY: " + event.getY());mCurrentImageView.setX(mCurrentImageView.getX() + disX);mCurrentImageView.setY(mCurrentImageView.getY() + disY);LogUtils.i("mCurrentImageView.getX(): " + mCurrentImageView.getX() + " ### mCurrentImageView.getY(): " + mCurrentImageView.getY());fdownX = event.getX() - OFFSET;fdownY = event.getY() - OFFSET;LogUtils.v("fdownX: " + fdownX + " ### fdownY: " + fdownY);imageW = mCurrentImageView.getWidth();imageH = mCurrentImageView.getHeight();mCurrentImageView.setBackgroundResource(android.R.color.transparent);setImageResource(mCurrentImageView, true);return true;
三、一句话总结
View的移动本质上就是x、y坐标值的变换,拖拽就是在ontouch()事件中,改变View的x、y值。
由于本文的篇幅已经较长,为了能够让各位大佬获得更好的阅读体验(我要偷懒了^_^),笔者打算将其他几个知识点分到其他章节讲解,现提供完整工程,可以先睹为快,地址如下:
DrawLayoutSample
喜欢就star一下吧,fork也行,你开心就好,如果有啥问题欢迎在issue或者评论区讨论。
Android手摸手实现一个画板功能(一)——View的拖拽相关推荐
- 手摸手产品研究院 | 玲珑沙龙-一个可以“撕逼”的女性文化社区
手摸手产品研究院是由PMCAFF发起的深度研究产品的产品经理精华小分队,旨在每天一起研究一款产品,并且由阿德老师手摸手指导写分析报告. 1-玲珑沙龙? 什么是玲珑沙龙,那些人在玩玲珑沙龙,为什么会玩? ...
- 每天研究一个产品,阿德老师“手摸手”带你写产品分析报告 |
作为一个产品经理,要高频地去把玩各种最新产品,所以我们想把那些对世界充满好奇心.勇于探索新鲜事物的产品经理都聚在一起.一起深入研究国内外最新/奇产品,一起发现有趣的事情,并把研究心得都整理成文章沉淀下 ...
- android videoview 拉伸,手摸手带你用 VideoView 实现英语流利说炫酷引导页
效果图: 一直听说英语流利说是个做的非常不错的app,于是乎抱着崇拜的心态下了一个瞅瞅,在打开app后就被引导页吸引了,继续抱着崇拜的心态去思考这是如何实现的. 刚开始的思路属性动画?(可以实现,但是 ...
- CSS —— 手摸手实现一个文字霓虹灯闪烁特效
CSS -- 手摸手实现一个文字霓虹灯闪烁特效 一.了解 text-shadow 属性 text-shadow 属性应用于阴影文本,属于 CSS3 的属性,默认值为 none. text-shadow ...
- IN-我的生活in记 | 手摸手产品研究院
手摸手产品研究院是由PMCAFF发起的深度研究产品的产品经理精华小分队,旨在每天一起研究一款产品,并且由阿德老师手摸手指导写分析报告. 引言 IN是一款基于女性和品牌的时尚品位分享移动端社区,以图片社 ...
- 手摸手教你做动态壁纸
手摸手教你做动态壁纸 Android · jeasonwong · 于 5 天前发布 · 最后由 xingstarx 于 2 天前回复 · 440 次阅读 项目地址:https://github.co ...
- 快应用之手摸手,跟我走(1)
快应用发布快两周啦.这两天有空,就捣鼓了一个快应用.整体感觉来说,交互很流畅,基本功能和组件都有.上手也很快.希望官网推广能做好.好了,话不多说,先上 gitHub (传送门) gankQuick-快 ...
- 招聘行业颠覆者【伯小乐】| 手摸手产品研究院
手摸手产品研究院是由PMCAFF发起的深度研究产品的产品经理精华小分队,旨在每天一起研究一款产品,并且由阿德老师手摸手指导写分析报告. 作者微信:weihe2416 "伯小乐" 是 ...
- 短视频Gif快手-有点意思 | 手摸手产品研究院
手摸手产品研究院是由PMCAFF发起的深度研究产品的产品经理精华小分队,旨在每天一起研究一款产品,并且由阿德老师手摸手指导写分析报告. ...
最新文章
- .net下的富文本编辑器FCKeditor的配置方法(图)原创
- visualSVN-server的安装图解
- VB6.0 怎样启用控件comdlg32.ocx
- 手撕 MySQL 事务,发生了什么?
- php坐标轴取整,PHP取整函数:ceil,floor,round,intval的区别详细解析
- Linux就该这么学---第七章(LVM逻辑卷管理器)
- hdu1686:KMP板子
- 【教程】条形码组件Spire.Barcode 教程:如何在C#中创建DataMatrix条码
- Oracle 11g R2 ADG 运维
- android 同步list数据,android SharedPreferences保存list数据
- Java包装类介绍与类型之间相互转换
- php 判断 小米 手机浏览器,小米2下的chrome调试
- Spring Cloud 基础教程 - 程序猿DD
- 最新最全的阿里云产品手册出炉
- 星星泡饭伴奏_星星泡饭 - Ayo_Lvlv - 5SING中国原创音乐基地
- 8脚51单片机DIY时间显示+闹钟技术分享(一)
- 对高尔顿数据集实现线性回归分析
- java linest_java基础:学生管理系统
- Pikachu靶场之越权漏洞详解
- 叶新伟 php,基于php+mysql技术bbs论坛设计的开发与实现最终版(样例3)
热门文章
- 阿兰•图灵与人工智能
- 拓嘉恒业:拼多多开店条件分享
- Python我的世界小游戏源代码
- 高防CDN和BGP高防有哪些区别和优势
- 什么是大数据?带你深度了解大数据
- serial.serialutil.SerialException: could not open port 'COM1': PermissionError(13, '拒绝访问。', None, 5)
- java 函数 作为参数_如何在Java中将函数作为参数传递?
- js的高亮关键写法,简单粗暴,行之有效
- 迄今最全的国内引进的凯迪克获奖绘本书单172本(1938年-2016年)
- react-Mobx基本使用