实现View的 上下滑动1--简单实现
来源:互联网 发布:python做有趣的事 编辑:程序博客网 时间:2024/05/29 17:55
不知道怎么写标题了
一直想实现一个下拉刷新与上拉加载,然而自己又比较懒.以前都是使用开源的框架,现在想自己实现。
这个只是用于简单记录和实现上下拉动效果,可参考以及copy修改。当然要用来做上拉加载与下拉刷新还有很多事情要做。后续的继续实现不知道什么时候完成,先记录一下,避免又弃坑了。
参考:https://github.com/HomHomLin/SlidingLayout/https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh
编写记录:
/** * Created by itzhu on 2017/6/7 0007. * desc 参考:https://github.com/HomHomLin/SlidingLayout/ * <p> * <p> * <p 2017-6-8> * 此实验失败,带下拉和上拉的通用控件编写失败。 * 因为上下拉没有事件拦截,当ACTION_DOWN事件没有拦截时,不管手指有没有滑动,按下时所在的按钮会一直处于pressed状态。 * 这个没有找到解决的办法。 * <p> * <p 2017-6-9> * 查看这个开源框架 * {android-Ultra-Pull-To-Refresh https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh}的PtrFrameLayout类, * 发现它在移动的过程copy MotionEvent,然后发送出去,试了一下,成功。这个框架的不足之处就是没有处理好多个手指界面跳动问题,可能就是几行代码的事情... :) * 这样一个简单的上下拉动界面就完成了。 * <p> * <p 总结1:2017-6-9 09:47> * <p> * 这里的多个手指点击,滑动是照着RecyclerView里面的onTouchEvent里面的写的,解决多个手指点击UI跳动问题。(当某个问题出现时,如果android自带控件没有问题,不妨参考一下它的源码实现...) * <p> * 完成这个初步的上下拉动,尝试了onInterceptTouchEvent 和onTouchEvent 实现,参考(SlidingLayout), * 发现当出现上下拉动的时候,childView是不会滚动的。这个是因为尝试了onInterceptTouchEvent返回true之后,事件会传递给自己的onTouchEvent,不会再传给childView. * 而我们的事件是需要在view与childView之间来回传递,就只能在dispatchTouchEvent里面处理了。 * 1.这里没有测试childView的横向滚动,应该会出问题,后面再继续优化 * 2.targeView应该自己设置,不能写死 * 3.没有暴露任何方法给别人 * ... * 慢点来。 * <p> * 发现问题,解决问题,这个对要学习View滑动效果的新手来说应该会有点用处,自己参考吧。 * 不足之处应该是chileView的横向滑动应该不行,会出现问题,这个也应该可以参考RecycleView里面滑动时对X的处理。 * 先记录这段代码,后面一步一步优化,使其通用。 */
下面直接贴代码了,代码写的很简单,应该很容易看懂。很多都是直接copy那两个开源框架里面的。
public class PullLayout extends FrameLayout { 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 targeView; private IBindChildScrollListener childScrollListener; /** * 当前状态,0-未偏移状态 1-偏移量大于0 -1-偏移量小于0 * <p> * 因为滑动的offset在快速往返滑动不一定会有为0的状态,故使用此字段标志滑动状态的切换,以此来初始化标记点markPointY{@link #markPointY} */ private static int scrollState = 0; public PullLayout(@NonNull Context context) { super(context); } public PullLayout(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public PullLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean dispatchTouchEvent(MotionEvent event) { //检测view能否向上滑动或者能否向下滑动 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 = 0; scrollState = 0; Log.e(TAG, "markPointY->" + markPointY); super.dispatchTouchEvent(event); return true; //break; case MotionEvent.ACTION_POINTER_DOWN: Log.e(TAG, "ACTION_POINTER_DOWN"); mScrollPointerId = event.getPointerId(actionIndex); markPointY = (int) (event.getY(actionIndex) + 0.5f); initOffsetY = getCurrentOffsetY(); 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) + initOffsetY;//偏移距离 /*----- // TODO: 2017/6/9 0009 阻尼效果-简单除2,高级一点的自己去实现-----*/ offsetY = offsetY / 2; /*-----------*/ Log.e(TAG, "offsetY-->" + offsetY); if (offsetY > 0 && !childScrollListener.canScrollUp()) { //Log.e(TAG, "dispatchTouchEvent----A->" + offsetY); if (scrollState <= 0) { initPointY(event, index); scrollState = 1; //开始下拉刷新 sendCancelEvent(event); return true; } smoothTo(targeView, offsetY, 0); //消费滑动,不往下传递 return true; } else if (offsetY < 0 && !childScrollListener.canScrollDown()) { //Log.d(TAG, "dispatchTouchEvent----B->" + offsetY); if (scrollState >= 0) { initPointY(event, index); scrollState = -1; //开始上拉加载 sendCancelEvent(event); return true; } smoothTo(targeView, offsetY, 0); return true; } else if (scrollState != 0) { scrollState = 0; smoothTo(targeView, 0, 0); sendDownEvent(event); return true; } break; case MotionEvent.ACTION_POINTER_UP: onPointerUp(event); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: offsetY = 0; initOffsetY = 0; smoothTo(targeView, offsetY, 0); break; } return super.dispatchTouchEvent(event); } private void sendCancelEvent(MotionEvent event) { Log.d(TAG, "send cancel event"); // The ScrollChecker will update position and lead to send cancel event when mLastMoveEvent is null. // fix #104, #80, #92 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); } /*-------------重新设置targeView的aCTION_CANCEL和ACTION_DOWN事件- 这段代码copy自PtrFrameLayout-----------*/ private void sendDownEvent(MotionEvent event) { Log.d(TAG, "send down event"); 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 * * @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 = getCurrentOffsetY(); } } /** * 初始化手指按下的位置 * * @param event * @param index */ private void initPointY(MotionEvent event, int index) { Log.d(TAG, "initPointY"); smoothTo(targeView, 0, 0); initOffsetY = 0; markPointY = (int) (event.getY(index) + 0.5f); } /** * 获取当前targeView的偏移量 * * @return */ public int getCurrentOffsetY() { return (int) (getTranslationY(targeView) + 0.5f); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (getChildCount() == 0) return; //// TODO: 2017/6/9 0009 只是简单的获取childView if (targeView == null) ensureTarget(); } /** * 获取targeview */ private void ensureTarget() { if (targeView == null) { targeView = getChildAt(getChildCount() - 1); childScrollListener = new SimpleChildScrollListener(targeView); } } /** * 得到当前view的偏移量 * * @param view * @return */ private float getTranslationY(View view) { if (view != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) return view.getTranslationY(); return 0; } private void smoothTo(View view, float y, long duration) { if (view == null) return; view.clearAnimation(); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { android.animation.ObjectAnimator.ofFloat(view, "translationY", y).setDuration(duration).start(); } }}
下面两个直接在slideLayout里面copy的,就是判断view能否继续上下滑动
public interface IBindChildScrollListener { /*能否向上滑动*/ boolean canScrollUp(); /*能否向下滑动*/ boolean canScrollDown();}
public class SimpleChildScrollListener implements IBindChildScrollListener { private View targeView; public SimpleChildScrollListener(View targeView) { this.targeView = targeView; } @Override public boolean canScrollUp() { if (targeView == null) return false; if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { if (targeView instanceof AbsListView) { final AbsListView absListView = (AbsListView) targeView; return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) .getTop() < absListView.getPaddingTop()); } else { return ViewCompat.canScrollVertically(targeView, -1) || targeView.getScrollY() > 0; } } else { return ViewCompat.canScrollVertically(targeView, -1); } } @Override public boolean canScrollDown() { if (targeView == null) return false; if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { if (targeView instanceof AbsListView) { final AbsListView absListView = (AbsListView) targeView; return absListView.getChildCount() > 0 && absListView.getAdapter() != null && (absListView.getLastVisiblePosition() < absListView.getAdapter().getCount() - 1 || absListView.getChildAt(absListView.getChildCount() - 1) .getBottom() < absListView.getPaddingBottom()); } else { return ViewCompat.canScrollVertically(targeView, 1) || targeView.getScrollY() > 0; } } else { return ViewCompat.canScrollVertically(targeView, 1); } }}
这里简单的xml里面添加就好了,recyclerview和ListView都可以
记得要修改这个quickly.common.me.customview.layout.pull.PullLayout
<quickly.common.me.customview.layout.pull.PullLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/slidingLayout" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/text" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#000000" android:gravity="center_horizontal" android:padding="15dp" android:text="pull view" android:textColor="@color/white" /> <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FFFFFF"> </ListView></quickly.common.me.customview.layout.pull.PullLayout>
阅读全文
0 0
- 实现View的 上下滑动1--简单实现
- 自定义view,实现上下滑动
- 实现View的简单滑动2
- Android 自定义View 实现手势监听,左右滑动,上下滑动
- View 滑动的实现
- android实现上下滑动
- [看书日记]上下滑动冲突的内部实现 和 view的工作原理
- Android实现浮层的上下滑动(支持内部添加View)
- 简单的实现ScrollView 其中的子view滑动悬浮
- android:滑动挂断自定义View的简单实现
- Scoller实现view的滑动
- View 滑动的实现方式
- View的滑动实现方式
- Scroller实现View的滑动
- Android实现上下滑动效果
- Android实现上下滑动效果
- WheelView实现上下滑动选择器
- jquery实现上下滑动图片
- 汇编复习Test6
- C++链表总结(持续更新)
- 解决某些Rom中EditText无法修改光标的问题
- 顺序的分数
- DNS原理及其解析过程【精彩剖析】
- 实现View的 上下滑动1--简单实现
- qq内嵌浏览器居中兼容
- MapReduce中的Shuffle和Sort分析
- 设计模式---备忘录模式
- Android中通过ViewHelper.setTranslationY实现View移动控制(NineOldAndroids开源项目)
- javascript来回拖拽dragg,其中dataTransfer传递数据
- 做项目过程中对于搜索框的实现
- 比特币交易(Bitcoin Transactions)
- Linux/Windows系统内核性能调优