300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Android 自定义View----触摸反馈

Android 自定义View----触摸反馈

时间:2022-02-11 23:48:26

相关推荐

Android 自定义View----触摸反馈

布局,绘制,触摸反馈;想要实现和用户手势交互,必须了解触摸反馈,简述下:

dispatchTouchEvent:分发事件onInterceptTouchEvent:拦截事件onTouchEvent:消费事件AAA------dispatchTouchEventAAA------onInterceptTouchEventBBB------dispatchTouchEventBBB------onInterceptTouchEventCCC------dispatchTouchEventCCC------onTouchEventBBB------onTouchEventAAA------onTouchEvent

事件从最上面的父view开始到最后一个子view,递归dispatchTouchEvent()然后调用onInterceptTouchEvent()检查自己是否拦截事件,如果都没有,然后反向递归onTouchEvent()

如果父view拦截事件“onInterceptTouchEvent()返回ture”后面的所有子view收不到任何事件;

如果子view设置了点击事件或者本身view是可点击的(即使没有设置点击事件),点击子view的时候所有父view都收不到onTouchEvent事件

public boolean onTouchEvent(MotionEvent event) {/*getAction()是以前的方法,多点触碰后新增getActionMasked()两个方法没有任何性能区别,只不过getAction()不支持多点触碰*/switch (event.getActionMasked()) {// 第一个手指按下case MotionEvent.ACTION_DOWN:break;// 手指移动case MotionEvent.ACTION_MOVE:break;// 手势被取消case MotionEvent.ACTION_CANCEL:break;// 最后一个手指抬起case MotionEvent.ACTION_UP:break;// 多个手指按下case MotionEvent.ACTION_POINTER_DOWN:break;// 手指离开了触摸屏(有其他手指还在触摸屏上,最后一根手指抬起不会触发)case MotionEvent.ACTION_POINTER_UP:break;}/*返回true可理解为所有权的标志,返回true代表消费了事件,后续的所有事件都不会往下传递这里返回true和在down事件返回true一样,当down事件返回true之后就代表消费事件*/return true;}

想要实现长按,双击,缩放等事件仅通过onTouchEvent()方法不太容易,可借助GestureDetectorCompat 和ScaleGestureDetector实现;

public class ViewC extends View implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {/*手势侦测器GestureDetector和GestureDetectorCompat是一个东西,一般带Compat都是为了兼容性出的使用GestureDetectorCompat需要三步:①实现对应方法②重写onTouchEvent方法,修改返回值为GestureDetectorCompat.onTouchEvent(event);③如果需要双击事件,修改返回值为GestureDetectorCompat.setOnDoubleTapListener(this)*/private GestureDetectorCompat gestureDetector;public ViewC(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}{// 创建手势侦测器,需要重写onTouchEventgestureDetector = new GestureDetectorCompat(getContext(), this);// 设置双击监听gestureDetector.setOnDoubleTapListener(this);/*只实现双击监听可用下面简便写法① onDown 返回true② 重写onTouchEvent()返回 detector.onTouchEvent(event)*/// gestureDetector=new GestureDetectorCompat(getContext(),new GestureDetector.SimpleOnGestureListener(){// @Override// public boolean onDown(MotionEvent e) {//return true;// }//// @Override// public boolean onDoubleTap(MotionEvent e) {//return super.onDoubleTap(e);// }// });}@Overridepublic boolean onTouchEvent(MotionEvent event) {// 使用GestureDetectorreturn gestureDetector.onTouchEvent(event);}/*收到ACTION_DOWN后onDown()就会被调用onDown()决定事件是否被消费,true为消费如果这里不消费,下面所有的实现方法都无效*/@Overridepublic boolean onDown(MotionEvent e) {return true;}// 预按下事件(父view是滑动控件会出现预按下事件)@Overridepublic void onShowPress(MotionEvent e) {}/*单击:每次按下抬起都会有响应(返回值无用,只有onDown里面的返回值有用)如果支持长按事件(按下500ms触发长按事件),在按下500ms后抬起不会触发onSingleTapUp()总:如果长按事件打开,按下后500ms内抬起触发;如果长按事件未打开,按下后无论何时抬起都会触发detector.setIsLongPressEnabled(false); // 关闭长按事件*/@Overridepublic boolean onSingleTapUp(MotionEvent e) {return false;}/*** 同步onMove(),和onMove()一样* 实现功能:图片放大后跟随手指滑动偏移** @param downEvent 按下的事件* @param event当前事件* @param distanceX 上一个点到当前点之间距离* @param distanceY 上一个点到当前点之间距离* @return 没用*/@Overridepublic boolean onScroll(MotionEvent downEvent, MotionEvent event, float distanceX, float distanceY) {return false;}// 长按事件@Overridepublic void onLongPress(MotionEvent e) {}// 惯性滑动,实现fling效果 velocity(速度:抬手一瞬间的 距离/时间 )@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {return false;}/*onSingleTapUp 单击事件 (长按事件未开启的话,只要手指按下抬起就会触发)onSingleTapConfirmed 单机确认事件当设置双击监听打开后,想要使用单机监听用onSingleTapConfirmed方法,否则双击屏幕会先调用onSingleTapUp方法,onDoubleTap*/@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {return false;}// 双击事件(两次触摸间隔<300ms)@Overridepublic boolean onDoubleTap(MotionEvent e) {return false;}// 双击不松手滑动触发@Overridepublic boolean onDoubleTapEvent(MotionEvent e) {return false;}}

public class ViewC extends View implements ScaleGestureDetector.OnScaleGestureListener {/*缩放手势侦测器使用方法和GestureDetectorCompat类似,需要两步:①实现对应方法②重写onTouchEvent方法,修改返回值为ScaleGestureDetector.onTouchEvent(event);*/private ScaleGestureDetector scaleGestureDetector;public ViewC(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}{scaleGestureDetector = new ScaleGestureDetector(getContext(), this);}@Overridepublic boolean onTouchEvent(MotionEvent event) {// 使用GestureDetectorreturn scaleGestureDetector.onTouchEvent(event);}// 在scale的时候@Overridepublic boolean onScale(ScaleGestureDetector detector) {// 倍数float scaleFactor = detector.getScaleFactor();// 焦点detector.getFocusX();detector.getFocusY();return false;}// 在scale之前,可做初始化工作@Overridepublic boolean onScaleBegin(ScaleGestureDetector detector) {// 使用放缩必须返回truereturn true;}// 在scale之后@Overridepublic void onScaleEnd(ScaleGestureDetector detector) {}}

如果想要同时使用双击,长按,缩放手势,可在onTouchEvent()方法中根据触摸屏幕手指数量判断;

public boolean onTouchEvent(MotionEvent event) {// 使用GestureDetectorif (event.getPointerCount()>1){return scaleGestureDetector.onTouchEvent(event);}else {return gestureDetector.onTouchEvent(event);}}

根据手势监听器实现一个图片双击缩放,滑动偏移加上fling效果的demo

/*** ① 计算图片smallScale和bigScale ----> 通过 canvas.scale()方法实现放大缩小* ② 重写双击事件 ----> 通过 GestureDetectorCompat 实现双击事件监听* ③ 根据双击事件切换scale ----> 设置标识记录大图小图* ④ 添加动画 ----> 通过 ObjectAnimator 实现大图小图切换动画* ⑤ 实现滑动偏移 ----> 在onScroll() 方法中实现手指滑动偏移* ⑥ 实现fling效果 ----> 在onFling() 方法中通过OverScroller实现fling效果*/public class ScalableImageView extends View implements Runnable {private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);private Bitmap bitmap;// 记录手指滑动距离float offsetX;float offsetY;float originalOffsetX;float originalOffsetY;float smallScale;float bigScale;// 判断是否是大图boolean big;// 动画float scaleFraction; // 0 - 1fObjectAnimator scaleAnimator;// 手势侦测器private GestureDetectorCompat detector;CustomGestureListener gestureListener = new CustomGestureListener();/*在onFling中使用,OverScroller算是一个计算器,需要在onFling中设置参数*/private OverScroller overScroller;public ScalableImageView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}{bitmap = getAvatar((int) UnitUtil.dp2px(300));detector = new GestureDetectorCompat(getContext(), gestureListener);overScroller = new OverScroller(getContext());}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);originalOffsetX = (getWidth() - bitmap.getWidth()) / 2f;originalOffsetY = (getHeight() - bitmap.getHeight()) / 2f;if ((float) bitmap.getWidth() / (float) bitmap.getHeight() > (float) getWidth() / (float) getHeight()) {smallScale = (float) getWidth() / (float) bitmap.getWidth();bigScale = (float) getHeight() / (float) bitmap.getHeight();} else {smallScale = (float) getHeight() / (float) bitmap.getHeight();bigScale = (float) getWidth() / (float) bitmap.getHeight();}bigScale = bigScale * 2f;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 给随手指偏移 乘以scaleFraction在缩小的时候自动居中canvas.translate(offsetX * scaleFraction, offsetY * scaleFraction);// float scale = big ? bigScale : smallScale;// 双击的时候给随这动画的执行scaleFraction会变化float scale = smallScale + (bigScale - smallScale) * scaleFraction;canvas.scale(scale, scale, getWidth() / 2, getHeight() / 2);// 图片居中canvas.drawBitmap(bitmap,originalOffsetX, originalOffsetY,mPaint);}// 使用GestureDetectorCompat@Overridepublic boolean onTouchEvent(MotionEvent event) {return detector.onTouchEvent(event);}@SuppressLint("NewApi")@Overridepublic void run() {// 动画是否还在进行中,如果结束了直接returnif (!puteScrollOffset()) return;// 更新xy值,计算之后可获取最新的值(每次获取的时候都需要让scroller先计算一下)puteScrollOffset();offsetX = overScroller.getCurrX();offsetY = overScroller.getCurrY();invalidate();postOnAnimation(this);}public float getScaleFraction() {return scaleFraction;}// 动画使用,set方法里面要加invalidate()刷新public void setScaleFraction(float scaleFraction) {this.scaleFraction = scaleFraction;invalidate();}private ObjectAnimator getScaleAnimator() {if (scaleAnimator == null) {scaleAnimator = ObjectAnimator.ofFloat(this, "scaleFraction", 0, 1);}return scaleAnimator;}Bitmap getAvatar(int width) {BitmapFactory.Options options = new BitmapFactory.Options();// inJustDecodeBounds为true,不返回bitmap,只返回这个bitmap的尺寸options.inJustDecodeBounds = true;// 从资源中读取(比较浪费资源,所以上面设置为true,只获取图片宽高)BitmapFactory.decodeResource(getResources(), R.drawable.ysdry, options);// 再设置为false,最后要返回bitmapoptions.inJustDecodeBounds = false;// 根据缩放比例重新计算宽高options.inDensity = options.outWidth;options.inTargetDensity = width;return BitmapFactory.decodeResource(getResources(), R.drawable.ysdry, options);}class CustomGestureListener extends GestureDetector.SimpleOnGestureListener {@Overridepublic boolean onDown(MotionEvent e) {return true;}// 预按下事件(父view是滑动控件会出现预按下事件)@Overridepublic void onShowPress(MotionEvent e) {}@Overridepublic boolean onSingleTapUp(MotionEvent e) {return false;}/*** 图片放大后跟随手指滑动偏移** @param downEvent 按下的事件* @param event当前事件* @param distanceX 上一个点到当前点之间距离* @param distanceY 上一个点到当前点之间距离* @return 没用*/@Overridepublic boolean onScroll(MotionEvent downEvent, MotionEvent event, float distanceX, float distanceY) {// 大图的时候移动,设置边界if (big) {offsetX -= distanceX;// (图片宽度 - 屏幕宽度)/ 2offsetX = Math.min(offsetX, (bitmap.getWidth() * bigScale - getWidth()) / 2);offsetX = Math.max(offsetX, -(bitmap.getWidth() * bigScale - getWidth()) / 2);offsetY -= distanceY;offsetY = Math.min(offsetY, (bitmap.getHeight() * bigScale - getHeight()) / 2);offsetY = Math.max(offsetY, -(bitmap.getHeight() * bigScale - getHeight()) / 2);}invalidate();return false;}// 长按事件@Overridepublic void onLongPress(MotionEvent e) {}// 惯性滑动,实现fling效果 velocity(速度:抬手一瞬间的 距离/时间 )@SuppressLint("NewApi")@Overridepublic boolean onFling(MotionEvent downEvent, MotionEvent event, float velocityX, float velocityY) {if (big) {// 不会自动刷新,只是设置好了overScroller,可通过overScroller计算和设置动画overScroller.fling(// 初始位置(原点)(int) offsetX, (int) offsetY,// velocity(速度:抬手一瞬间的 距离/时间)(int) velocityX, (int) velocityY,// x最大最小范围-(int) (bitmap.getWidth() * bigScale - getWidth()) / 2, (int) (bitmap.getWidth() * bigScale - getWidth()) / 2,// y最大最小范围-(int) (bitmap.getHeight() * bigScale - getHeight()) / 2, (int) (bitmap.getHeight() * bigScale - getHeight()) / 2,// 过渡滚动(类似IOS效果)100, 100);/*我们需要一个循环,不停的修改offsetX,offsetY进行刷新页面post()在主线程立即执行,postOnAnimation()在下一帧去主线程执行需要做动画,需要一帧一帧执行的时候用postOnAnimation()postOnAnimation作用:下一帧的时候执行run()方法*/postOnAnimation(ScalableImageView.this);}return false;}@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {return false;}// 双击事件(两次触摸间隔<300ms)@Overridepublic boolean onDoubleTap(MotionEvent e) {big = !big;// invalidate();if (big) {// 根据手指点击的位置进行偏移量计算,根据手指位置方法offsetX = (e.getX() - getWidth() / 2f) - (e.getX() - getWidth() / 2) * bigScale / smallScale;offsetY = (e.getY() - getHeight() / 2f) - (e.getY() - getHeight() / 2) * bigScale / smallScale;getScaleAnimator().start();} else {getScaleAnimator().reverse();}// 这里返回无所谓,只要onDown返回true即可return false;}// 双击不松手滑动触发@Overridepublic boolean onDoubleTapEvent(MotionEvent e) {return false;}}}

想实现缩放,添加缩放手势监听

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。