实现View的简单滑动2
来源:互联网 发布:威海志成网络 编辑:程序博客网 时间:2024/06/08 11:09
上一篇把主要的要点说明了,这里只是稍微改变
实现View的 上下滑动1–简单实现
pulllayout改为了MonitorLayout(名字什么的不好取啦)
这个类的用途是把手指滑动状态暴露出去,不涉及到UI的部分
/** * 只用于滑动状态的监听,不管视图的变化 * 目的:将手指的滑动状态暴露出去 */public class MonitorLayout extends RelativeLayout { private static final String TAG = "PullLayout"; /** * 标记的pointY * 滑动距离以此为原点计算 */ private int markPointY = 0; /** * 滑动手指ID */ private int mScrollPointerId = -1; /** * 偏移位置 */ private int offsetY = 0; /** * 初始偏移量 * 当多个手指按下状态变换时,初始偏移量改变 */ private int initOffsetY = 0; /** * 需要监控View的滑动状态 */ private View scrollView; private IBindChildScrollListener mChildScrollListener; private IScrollChangeListener mScrollChangeListener; /** * scrollEnable = false时会执行super.dispatchEvent() */ private boolean scrollEnable = true; /** * 当前状态,0-未偏移状态 1-偏移量大于0 -1-偏移量小于0 * <p> * 因为滑动的offset在快速往返滑动不一定会有为0的状态,故使用此字段标志滑动状态的切换,以此来初始化标记点markPointY{@link #markPointY} */ private static int scrollState = 0; public MonitorLayout(@NonNull Context context) { super(context); } public MonitorLayout(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public MonitorLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { super(context, attrs, defStyleAttr); } public void setScrollChangeListener(IScrollChangeListener mScrollChangeListener) { this.mScrollChangeListener = mScrollChangeListener; } public void setScrollEnable(boolean scrollEnable) { this.scrollEnable = scrollEnable; } public boolean isScrollEnable() { return scrollEnable; } @Override public boolean dispatchTouchEvent(MotionEvent event) { if (mChildScrollListener == null || scrollView == null) return super.dispatchTouchEvent(event); //是否允许滑动 if (!isScrollEnable()) return super.dispatchTouchEvent(event); final int action = MotionEventCompat.getActionMasked(event); final int actionIndex = MotionEventCompat.getActionIndex(event); switch (action) { case MotionEvent.ACTION_DOWN: Log.e(TAG, "ACTION_DOWN"); mScrollPointerId = event.getPointerId(0);// 获取索引为0的手指id markPointY = (int) (event.getY() + 0.5f); initOffsetY = mScrollChangeListener.currentOffsetY();//当前偏移量 scrollState = 0; Log.e(TAG, "markPointY->" + markPointY); break; case MotionEvent.ACTION_POINTER_DOWN: Log.e(TAG, "ACTION_POINTER_DOWN"); mScrollPointerId = event.getPointerId(actionIndex); markPointY = (int) (event.getY(actionIndex) + 0.5f); initOffsetY = mScrollChangeListener.currentOffsetY(); Log.e(TAG, "markPointY->" + markPointY); break; case MotionEvent.ACTION_MOVE: final int index = event.findPointerIndex(mScrollPointerId); if (index < 0) { Log.e(TAG, "Error processing scroll; pointer index for id " + mScrollPointerId + " not found. Did any MotionEvents get skipped?"); return false; } offsetY = (int) (event.getY(index) - markPointY);//偏移距离 Log.e(TAG, "---A--initOffsetY-->" + initOffsetY); /*----- // TODO: 2017/6/9 0009 阻尼效果-简单除2,高级一点的自己去实现-----*/ offsetY = offsetY / 2 + initOffsetY; /*-----------*/ Log.e(TAG, "---A--offsetY-->" + offsetY + " ddd" + mChildScrollListener.canScrollDown()); if (offsetY > 0 && !mChildScrollListener.canScrollUp()) { //Log.e(TAG, "dispatchTouchEvent----A->" + offsetY); if (scrollState <= 0) { markPointY = (int) (event.getY(index) + 0.5f); scrollState = 1; //开始下拉刷新 sendCancelEvent(event); return true; } sendScrollOffsetY(offsetY); return true; } else if (offsetY < 0 && !mChildScrollListener.canScrollDown()) { Log.d(TAG, "dispatchTouchEvent----B->" + offsetY); if (scrollState >= 0) { markPointY = (int) (event.getY(index) + 0.5f); scrollState = -1; //开始上拉加载 sendCancelEvent(event); return true; } Log.e(TAG, "---A--offsetY-->" + offsetY); sendScrollOffsetY(offsetY); return true; } else if (scrollState != 0) { Log.d(TAG, "scrollState-->" + scrollState); scrollState = 0; initOffsetY = mScrollChangeListener.currentOffsetY(); sendScrollOffsetY(0); sendDownEvent(event); return true; } break; case MotionEvent.ACTION_POINTER_UP: onPointerUp(event); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (scrollState == 0) break; if (mScrollChangeListener != null) mScrollChangeListener.scrollEnd(offsetY); break; } return super.dispatchTouchEvent(event); } private void sendScrollOffsetY(int offsetY) { Log.d(TAG, "sendScrollOffsetY-->" + offsetY); if (mScrollChangeListener != null) mScrollChangeListener.scrollChanged(offsetY); } /*-------------重新设置targeView的aCTION_CANCEL和ACTION_DOWN事件- 这段代码copy自PtrFrameLayout-----------*/ private void sendCancelEvent(MotionEvent event) { if (event == null) return; MotionEvent last = event; MotionEvent e = MotionEvent.obtain(last.getDownTime(), last.getEventTime() + ViewConfiguration.getLongPressTimeout(), MotionEvent.ACTION_CANCEL, last.getX(), last.getY(), last.getMetaState()); dispatchTouchEventSupper(e); } private void sendDownEvent(MotionEvent event) { if (event == null) return; final MotionEvent last = event; MotionEvent e = MotionEvent.obtain(last.getDownTime(), last.getEventTime(), MotionEvent.ACTION_DOWN, last.getX(), last.getY(), last.getMetaState()); dispatchTouchEventSupper(e); } public boolean dispatchTouchEventSupper(MotionEvent e) { return super.dispatchTouchEvent(e); } /*-----------------------------------*/ /** * 这里不能使用onInterceptTouchEvent * onInterceptTouchEvent在一次按下滑动 拦截事件后就不会被执行了。(可以理解为在action_move事件开始的很短时间内会执行,之后就不会执行了。) */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.d(TAG, "onInterceptTouchEvent"); return super.onInterceptTouchEvent(ev); } /** * 如果onInterceptTouchEvent 没有返回true,onTouchEvent是不会执行的 */ @Override public boolean onTouchEvent(MotionEvent event) { Log.d(TAG, "onTouchEvent"); return super.onTouchEvent(event); } /** * 手指抬起,参考RecyclerView{@link android.support.v7.widget.RecyclerView#onPointerUp(MotionEvent)} * * @param e */ private void onPointerUp(MotionEvent e) { final int actionIndex = MotionEventCompat.getActionIndex(e); if (e.getPointerId(actionIndex) == mScrollPointerId) { // Pick a new pointer to pick up the slack. final int newIndex = actionIndex == 0 ? 1 : 0; mScrollPointerId = e.getPointerId(newIndex); markPointY = (int) (e.getY(newIndex) + 0.5f); initOffsetY = mScrollChangeListener.currentOffsetY(); } } /** * 设置targeView * * @param scrollView */ public void setScrollView(View scrollView) { this.scrollView = scrollView; } /** * 设置ChildScrollListener * * @param mChildScrollListener */ public void setChildScrollListener(IBindChildScrollListener mChildScrollListener) { this.mChildScrollListener = mChildScrollListener; } public View getScrollView() { return scrollView; }}
通过IScrollChangeListener 来检测view的滑动状态,并修改View的状态。这里面有一个currentOffsetY();这个东西我也觉得很danteng。不好设计了,它的用处不好说明,字面意思是当前的偏移量,至于记录的是那个的偏移量,还需要根据具体View来判断。
/** * Created by itzhu on 2017/6/9 0009. * desc */public interface IScrollChangeListener { /** * @param offsetY 偏移量 */ void scrollChanged(int offsetY); void scrollEnd(int offsetY); /** * 当前偏移量 */ int currentOffsetY();}
刷新的视图部分,实现IRefreshView接口就好了
/** * Created by itzhu on 2017/6/13 0013. * desc */public interface IRefreshView { void move(int offsetY); void end(int offsetY); void changeHeaderView(int state); void changeFooterView(int state); int getHeaderHeight(); int getFooterHeight(); View getHeaderView(); View getFooterView(); View getView();}
使用上面的代码很容易实现一个上下拉动的View
** * Created by itzhu on 2017/6/14 0014. * desc 重新编写pulllayout */public class RePullLayout extends MonitorLayout implements IScrollChangeListener { private IScrollChangeListener mScrollChangeListener; public RePullLayout(@NonNull Context context) { super(context); init(); } public RePullLayout(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public RePullLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public void init() { super.setScrollChangeListener(this); } @Override public void setScrollChangeListener(IScrollChangeListener mStateChangeListener) { this.mScrollChangeListener = mStateChangeListener; } @Override public void scrollChanged(int offsetY) { getScrollView().setTranslationY(offsetY); if (this.mScrollChangeListener != null) this.mScrollChangeListener.scrollChanged(offsetY); } @Override public void scrollEnd(int offsetY) { AnimationUtil.smoothTo(getScrollView(), 0, 150); if (this.mScrollChangeListener != null) this.mScrollChangeListener.scrollEnd(offsetY); } @Override public int currentOffsetY() { return (int) (getScrollView().getTranslationY() + 0.5f); }}
这个的用处就是实现各种View的上下拉动。
也可以用来实现上拉刷新与下拉加载。
DEMO的话请看https://github.com/yanjingzj/commondemo
不讨论item的横向滑动,如果涉及到item的滑动菜单,又加上上下拉动,个人建议还是是直接继承recyclerView或者listView来实现。
demo里面有下拉刷新与上拉加载,类似https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh 这样的,但是不会有多个手指滑动的问题。
没有考虑item的左右滑动,后面有时间再看。
阅读全文
0 0
- 实现View的简单滑动2
- View 滑动的实现
- 实现View的 上下滑动1--简单实现
- 简单的实现ScrollView 其中的子view滑动悬浮
- android:滑动挂断自定义View的简单实现
- Scoller实现view的滑动
- View 滑动的实现方式
- View的滑动实现方式
- Scroller实现View的滑动
- 实现View滑动的方法总结
- 【Android】Android View的滑动实现
- 实现View滑动的3种方法
- 实现View滑动的七种方法
- 使用Scroller实现View的弹性滑动
- 实现View 滑动的几种方法
- View的滑动三种实现方案
- 实现view滑动的几种方法
- 通过Scroller实现View的流畅滑动
- 【翻译】Faster-R-CNN: Towards Real-Time Object Detection with Region Proposal Networks
- 端午个人赛-A return of Nim (Nim+威左夫,拓:巴什)
- MediaStore.Audio.Media属性(本地媒体库)
- Android常用开源项目(二十八)
- nginx配合modsecurity实现WAF功能
- 实现View的简单滑动2
- HDU 4794 斐波那契数列循环节
- 用Dialog实现底部弹窗
- 史上最全SSM框架整合(三)-----SSM框架的整合
- 守护进程
- 占位。。。。。。。。。。。。。
- iptables: No chain/target/match by that name.
- 海思 3536C音频调试
- 《Java编程思想》学习笔记2——对象初始化和面向对象特性