让listview在scrollview中自由滑动
2016-04-07 12:57
169 查看
总有人我listview嵌套scrollview怎么弄。一问就是半天,太耗时,所以写个博客也算是自己总结一下。
让listview在scrollview中自由滑动。
当listview滑动到顶端或底端的时候,让scrollview开始滑动
直接上图看效果好了:
代码也很简单 直接上代码
上面代码直接用就可以啦。
那么下面说下好多人遇到过的疑惑
为什么这么写就能避免嵌套问题呢
下面代码是网上流传最多的
这样写呢有个弊端,就是 在scrollview里面展示了 listview的全部内容。有100条就显示了100条。
Integer.MAX_VALUE >> 2这个呢就是一个值。让你的listview最大值是多大。你可以写成65535,或者其他数值。为什么用 MeasureSpec.AT_MOST呢?
看下super里面
再看下measureHeightOfChildren()的内容
所以我们赋值的Integer.MAX_VALUE >> 2只是一个参考值,你想写多少就是多少。
但是那样写有个弊端就是在xml里面写的高度 就没用了。所以我们就这么写了
这样的话就是如果 xml里面是wrap_content的话 就显示所有item的高度。如果是设置了 高度值 比如是400dp。那么listview显示的高度就是400dp。
为什么那么些 请戳这尼自定义view,viewgroup的onMeasure 方法
那么问题又来了。
如果显示400dp的高度。listview就没法全部显示了。
所以就不能让scrollview拦截事件,要让该事件传递到listview,并且处理。 一行代码就搞定
剩下的就只是当listview滑动到顶端和底端 listview就拦截该事件了。代码里已经体现的很简单了。
尊重原创:http://blog.csdn.net/wanghao200906/article/details/51084975
代码在这里
最近太忙了。发个福利安慰一下自己
目标
scrollview嵌套listview,可以自由的定义listview的大小,而不是展示全部listview。让listview在scrollview中自由滑动。
当listview滑动到顶端或底端的时候,让scrollview开始滑动
直接上图看效果好了:
代码也很简单 直接上代码
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimary" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="wang.com.scrollholdlistview.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/activity_vertical_margin" android:text="Hello World!" android:textSize="30sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/activity_vertical_margin" android:text="Hello World!" android:textSize="30sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/activity_vertical_margin" android:text="Hello World!" android:textSize="30sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/activity_vertical_margin" android:text="Hello World!" android:textSize="30sp" /> <wang.com.scrollholdlistview.MyListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="400dp" android:background="@color/colorAccent"></wang.com.scrollholdlistview.MyListView> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/activity_vertical_margin" android:text="Hello World!" android:textSize="30sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/activity_vertical_margin" android:text="Hello World!" android:textSize="30sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/activity_vertical_margin" android:text="Hello World!" android:textSize="30sp" /> <wang.com.scrollholdlistview.MyListView android:id="@+id/lv1" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorAccent"> </wang.com.scrollholdlistview.MyListView> </LinearLayout> </ScrollView>
package wang.com.scrollholdlistview; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.ListView; /** * 创建日期: 16/4/3 下午9:55 * 作者:wanghao * 描述: */ public class MyListView extends ListView { private static final String TAG = "MyListView"; public MyListView(Context context) { this(context, null); } public MyListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int defaultsize=measureHight(Integer.MAX_VALUE >> 2, heightMeasureSpec); int expandSpec = MeasureSpec.makeMeasureSpec(defaultsize, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } private int measureHight(int size, int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { Log.i(TAG, "exactly" ); result = specSize; } else { result = size;//最小值是200px ,自己设定 if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } Log.i(TAG, "specMode:"+specMode+"--result:"+result ); } return result; } /** * 改listview滑到底端了 * * @return */ public boolean isBottom() { int firstVisibleItem = getFirstVisiblePosition();//屏幕上显示的第一条是list中的第几条 int childcount = getChildCount();//屏幕上显示多少条item int totalItemCount = getCount();//一共有多少条 if ((firstVisibleItem + childcount) >=totalItemCount) { return true; } return false; } /** * 改listview在顶端 * * @return */ public boolean isTop() { int firstVisibleItem = getFirstVisiblePosition(); if (firstVisibleItem ==0) { return true; } return false; } float down = 0; float y; @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: down = event.getRawY(); getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: y = event.getRawY(); if (isTop()) { if (y - down > 1) { // 到顶端,向下滑动 把事件教给父类 getParent().requestDisallowInterceptTouchEvent(false); } else { // 到顶端,向上滑动 把事件拦截 由自己处理 getParent().requestDisallowInterceptTouchEvent(true); } } if (isBottom()) { if (y - down > 1) { // 到底端,向下滑动 把事件拦截 由自己处理 getParent().requestDisallowInterceptTouchEvent(true); } else { // 到底端,向上滑动 把事件教给父类 getParent().requestDisallowInterceptTouchEvent(false); } } break; default: break; } return super.dispatchTouchEvent(event); } }
上面代码直接用就可以啦。
那么下面说下好多人遇到过的疑惑
为什么这么写就能避免嵌套问题呢
下面代码是网上流传最多的
@Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec( Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); }
这样写呢有个弊端,就是 在scrollview里面展示了 listview的全部内容。有100条就显示了100条。
Integer.MAX_VALUE >> 2这个呢就是一个值。让你的listview最大值是多大。你可以写成65535,或者其他数值。为什么用 MeasureSpec.AT_MOST呢?
看下super里面
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Sets up mListPadding super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); ... if (heightMode == MeasureSpec.AT_MOST) { // TODO: after first layout we should maybe start at the first visible position, not 0 heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1); } setMeasuredDimension(widthSize, heightSize); mWidthMeasureSpec = widthMeasureSpec; }
再看下measureHeightOfChildren()的内容
final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition, int maxHeight, int disallowPartialChildPosition) { final ListAdapter adapter = mAdapter; if (adapter == null) { return mListPadding.top + mListPadding.bottom; } // Include the padding of the list int returnedHeight = mListPadding.top + mListPadding.bottom; final int dividerHeight = ((mDividerHeight > 0) && mDivider != null) ? mDividerHeight : 0; // The previous height value that was less than maxHeight and contained // no partial children int prevHeightWithoutPartialChild = 0; int i; View child; // mItemCount - 1 since endPosition parameter is inclusive endPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition;//获取有多少个item final AbsListView.RecycleBin recycleBin = mRecycler; final boolean recyle = recycleOnMeasure(); final boolean[] isScrap = mIsScrap; //遍历所有item并且 获取到 所有item的整体高度 for (i = startPosition; i <= endPosition; ++i) { child = obtainView(i, isScrap); measureScrapChild(child, i, widthMeasureSpec, maxHeight); if (i > 0) { // Count the divider for all but one child returnedHeight += dividerHeight; } // Recycle the view before we possibly return from the method if (recyle && recycleBin.shouldRecycleViewType( ((LayoutParams) child.getLayoutParams()).viewType)) { recycleBin.addScrapView(child, -1); } returnedHeight += child.getMeasuredHeight(); //如果item的高度大于咱们赋值的高度也就是Integer.MAX_VALUE >> 2 if (returnedHeight >= maxHeight) { // We went over, figure out which height to return. If returnedHeight > maxHeight, // then the i'th position did not fit completely. return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1) && (i > disallowPartialChildPosition) // We've past the min pos && (prevHeightWithoutPartialChild > 0) // We have a prev height && (returnedHeight != maxHeight) // i'th child did not fit completely ? prevHeightWithoutPartialChild 如果是大于就用自己item整体的高度作为listview的高度 : maxHeight; 如果是等于那么久用 我们赋值的高度 } //如果item的高度 不大于等于 默认值 那么久显示他自己的高度了 if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) { prevHeightWithoutPartialChild = returnedHeight; } } // At this point, we went through the range of children, and they each // completely fit, so return the returnedHeight return returnedHeight; }
所以我们赋值的Integer.MAX_VALUE >> 2只是一个参考值,你想写多少就是多少。
但是那样写有个弊端就是在xml里面写的高度 就没用了。所以我们就这么写了
@Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int defaultsize=measureHight(Integer.MAX_VALUE >> 2, heightMeasureSpec); int expandSpec = MeasureSpec.makeMeasureSpec(defaultsize, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } private int measureHight(int size, int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { Log.i(TAG, "exactly" ); result = specSize; } else { result = size;//最小值是200px ,自己设定 if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } Log.i(TAG, "specMode:"+specMode+"--result:"+result ); } return result; }
这样的话就是如果 xml里面是wrap_content的话 就显示所有item的高度。如果是设置了 高度值 比如是400dp。那么listview显示的高度就是400dp。
为什么那么些 请戳这尼自定义view,viewgroup的onMeasure 方法
那么问题又来了。
如果显示400dp的高度。listview就没法全部显示了。
所以就不能让scrollview拦截事件,要让该事件传递到listview,并且处理。 一行代码就搞定
getParent().requestDisallowInterceptTouchEvent(true); true:就是拦截此事件 false:不拦截此事件,让父类处理
剩下的就只是当listview滑动到顶端和底端 listview就拦截该事件了。代码里已经体现的很简单了。
尊重原创:http://blog.csdn.net/wanghao200906/article/details/51084975
代码在这里
最近太忙了。发个福利安慰一下自己
相关文章推荐
- 使用栈实现DFS(非递归DFS)
- 【笔记】 《js权威指南》- 第15章 脚本化文档 - 15.3 - 15.4
- 动态规划经典问题Java实现
- mysql常用的一些命令,用于查看数据库、表、字段编码
- .NET 通过代码创建XML文档
- ASP.NET-页面传值
- java String 中 intern方法的概念
- 短信接口被***
- 软件工程需求分析
- 欢迎使用CSDN-markdown编辑器
- 初学Java的类和对象
- java服务器应答例子(用telnet方法测试)
- IOS delegate 实例
- wstring
- (十)【续】网络框架篇(RxJava+OkHttp+Gson)
- 转-思维要裂变要敢闯想
- LeetCode 249. Group Shifted Strings
- 团队成员及项目介绍
- POJ 2752
- 抽象工厂模式