Android中NestedScrollingParent嵌套ListView
来源:互联网 发布:蛋鸡存栏量数据 编辑:程序博客网 时间:2024/05/18 04:25
要实现这种效果,使用CoordinatorLayout,AppBarLayout,RecyclerView很容易就能完成。由于当前开发的工程由于一些原因不能使用AndroidDesignSupport包。只能自己解决滑动嵌套问题,实现了这个功能,顺便学习了下NestedScrollingParent,NestedScrollingChild的用法。如果想结合代码看文章源码链接在底部。
简单的说下NestedScrollingParent,NestedScrollingChild就是两个接口,在新的android.support.v4包中,两个接口定义了一些操作,然后通过NestedScrollingChildHelper把两者联系起来。
在子View中要联动滚动之前需要调用startNestedScroll(),这个时候NestedScrollingChildHelper中就会向父View寻找实现了NestedScrollingParent的View,并把他保存起来。当滑动事件传递到子View的时候,子View一般要去询问父View是否要滚动,然后方法返回后子View在决定自身是否要滚动。
子View可以通过传给helper的consumed,offsetInWindow数组得到父View消耗的距离,与自身在屏幕的偏移距离。这样子View根据父View的返回在决定自己是否滚动。大概调用关系如下图。
动画中的View布局关系如下图,先滚动1(也有可能不滚动),在滚动2.
这里是NestedScrollParentLayout的简单实现,调用scrollBy方法滚动
public class NestedScrollParentLayout extends RelativeLayout implements NestedScrollingParent { private NestedScrollingParentHelper mParentHelper; private int mTitleHeight; private View mTitleTabView; public NestedScrollParentLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } public NestedScrollParentLayout(Context context) { super(context); init(); } private void init() { mParentHelper = new NestedScrollingParentHelper(this); } //获取子view @Override protected void onFinishInflate() { mTitleTabView = this.findViewById(R.id.title_input_container); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mTitleHeight = mTitleTabView.getMeasuredHeight(); super.onMeasure(widthMeasureSpec, heightMeasureSpec + mTitleHeight); } //接口实现-------------------------------------------------- //在此可以判断参数target是哪一个子view以及滚动的方向,然后决定是否要配合其进行嵌套滚动 @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { if (target instanceof NestedListView) { return true; } return false; } @Override public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) { mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes); } @Override public void onStopNestedScroll(View target) { mParentHelper.onStopNestedScroll(target); } //先于child滚动 //前3个为输入参数,最后一个是输出参数 @Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { if (dy > 0) {//手势向上滑动 if (getScrollY() < mTitleHeight) { scrollBy(0, dy);//滚动 consumed[1] = dy;//告诉child我消费了多少 } } else if (dy < 0) {//手势向下滑动 if (getScrollY() > 0) { scrollBy(0, dy);//滚动 consumed[1] = dy;//告诉child我消费了多少 } } } //后于child滚动 @Override public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { } //返回值:是否消费了fling @Override public boolean onNestedPreFling(View target, float velocityX, float velocityY) { return false; } //返回值:是否消费了fling @Override public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {// if (!consumed) {// return true;// } return false; } @Override public int getNestedScrollAxes() { return mParentHelper.getNestedScrollAxes(); } //scrollBy内部会调用scrollTo //限制滚动范围 @Override public void scrollTo(int x, int y) { if (y < 0) { y = 0; } if (y > mTitleHeight) { y = mTitleHeight; } super.scrollTo(x, y); }}
这里是NestedListView的实现,onTouchEvent部分代码主要来自RecyclerView的onTouchEvent中
public class NestedListView extends ListView implements NestedScrollingChild { private NestedScrollingChildHelper mChildHelper; private int[] mNestedOffsets = new int[2]; private int[] mScrollConsumed = new int[2]; private int[] mScrollOffset = new int[2]; private int mScrollPointerId; private int mLastTouchX; private int mLastTouchY; private final static String TAG = "NestedListView"; public NestedListView(Context context) { super(context); init(); } public NestedListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public NestedListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public NestedListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } private void init() { mChildHelper = new NestedScrollingChildHelper(this); setNestedScrollingEnabled(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } private boolean isFirst = true;//DOWN事件没执行暂时 private int lastDy;//暂时解决第一次MOVE与后序符号相反,导致的抖动问题 @Override public boolean onTouchEvent(MotionEvent e) { //下述代码主要复制于RecyclerView final MotionEvent vtev = MotionEvent.obtain(e); final int action = MotionEventCompat.getActionMasked(e); final int actionIndex = MotionEventCompat.getActionIndex(e); if (action == MotionEvent.ACTION_DOWN) { mNestedOffsets[0] = mNestedOffsets[1] = 0; } vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]); switch (action) { case MotionEvent.ACTION_DOWN: { //不知道为啥没有执行 resetScroll(e); } break; case MotionEventCompat.ACTION_POINTER_DOWN: { mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex); mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f); mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f); } break; case MotionEvent.ACTION_MOVE: { final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId); if (index < 0) { Log.e(TAG, "Error processing scroll; pointer index for id " + mScrollPointerId + " not found. Did any MotionEvents get skipped?"); return false; } final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f); final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f); int dx = mLastTouchX - x; int dy = mLastTouchY - y; if (isFirst) {//暂时解决第次dy与后序符号相反导致的闪动问题 Log.i("pyt", "FIRST"); isFirst = false; resetScroll(e); return true; } if (!isSignOpposite(lastDy, dy)) {//解决手机触摸在屏幕上不松开一直抖动的问题 lastDy = dy; Log.i("pyt", "move lastY" + mLastTouchY + ",y=" + y + ",dy=" + dy); if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) { vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]); // Updated the nested offsets mNestedOffsets[0] += mScrollOffset[0]; mNestedOffsets[1] += mScrollOffset[1]; } mLastTouchX = x - mScrollOffset[0]; mLastTouchY = y - mScrollOffset[1]; } } break; case MotionEvent.ACTION_UP: { stopNestedScroll();// resetTouch(); isFirst = true; } break; case MotionEvent.ACTION_CANCEL: {// cancelTouch(); } break; } super.onTouchEvent(e); return true; } private void resetScroll(MotionEvent e) { lastDy = 0; mNestedOffsets[0] = mNestedOffsets[1] = 0; mScrollPointerId = MotionEventCompat.getPointerId(e, 0); mLastTouchX = (int) (e.getX() + 0.5f); mLastTouchY = (int) (e.getY() + 0.5f); int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE; nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL; startNestedScroll(nestedScrollAxis); } /** * 判断符号是否相反,可以改成异或 * * @param f * @param s * @return */ private boolean isSignOpposite(int f, int s) { if (f > 0 && s < 0 || f < 0 && s > 0) { return true; } return false; } //以下为接口实现-------------------------------------------------- @Override public void setNestedScrollingEnabled(boolean enabled) { mChildHelper.setNestedScrollingEnabled(enabled); } @Override public boolean isNestedScrollingEnabled() { return mChildHelper.isNestedScrollingEnabled(); } @Override public boolean startNestedScroll(int axes) { return mChildHelper.startNestedScroll(axes); } @Override public void stopNestedScroll() { mChildHelper.stopNestedScroll(); } @Override public boolean hasNestedScrollingParent() { return mChildHelper.hasNestedScrollingParent(); } @Override public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) { return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow); } @Override public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); } @Override public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); } @Override public boolean dispatchNestedPreFling(float velocityX, float velocityY) { return mChildHelper.dispatchNestedPreFling(velocityX, velocityY); }}
代码下载:https://github.com/pengyuntao/NestedListView/
- Android中NestedScrollingParent嵌套ListView
- 对scrollview嵌套listview说不(二)NestedScrollingParent +RecyleView
- Android 中ListView 嵌套 ListView
- Android listview 中嵌套 listview
- 仿NestedScrollingParent嵌套滑动
- Android-NestedScrollingParent, NestedScrollingChild父子View 间 的 嵌套滑动
- Android中ScrollView嵌套ListView
- Android ScrollView中嵌套ListView
- Android ListView 中嵌套EditText
- Android ScrollView中嵌套ListView
- Android Scroll 中嵌套ListView
- Android中ScrollView嵌套ListView
- android---ScrollView中嵌套ListView
- Android ScrollView中嵌套ListView
- Android如何在ListView中嵌套ListView
- Android如何在ListView中嵌套ListView
- Android中ExpandableListView中嵌套ListView
- Android中ExpandableListView中嵌套ListView
- Markdown笔记
- Spark性能优化指南——基础篇
- Unity3D Pattern not found 破解失败解决方法
- STM32的OSC和OSC32的差別
- C++中按行读取文本数据
- Android中NestedScrollingParent嵌套ListView
- POJ
- 解决ImportError: cannot import name webdriver
- 【C语言】关于宏定义中#和##符号的使用和宏定义展开问题
- libsvm 中文文本分类 java版本
- Python 批处理文件(全)
- poj 3190 Stall Reservations (贪心+优先队列)
- 好记性不如烂笔头
- 输出位数的填充C++