ScrollView,ListView,GrideView,RecyclerView,ViewPager等多种view嵌套问题
2017-02-22 14:43
573 查看
在开发中各种滑动布局的view如ScrollView,ListView等非常常用,但是你也会发现各种奇怪问题产生。网上的解决方法有很多种,但是杂而不全,根据个人经验现在列出常见问题以及代码最少最简单的解决方法,首先你要了解事件的分发机制及事件冲突,这里不再赘述。(其他继承自AbsListView的类也适用,包括ExpandableListView、GridView等等)
ScrollView嵌套ListView冲突问题的最优解决方案
ScrollView嵌套ListVew或者GridView等很常用,下面来说解决版本 问题一 : 嵌套在 ScrollView的 ListVew数据显示不全,我遇到的是最多只显示两条已有的数据。 解决办法:重写 ListVew或者 GridView,网上还有很多若干解决办法,但是都不好用或者很复杂。 @Override /** 只重写该方法,达到使ListView适应ScrollView的效果 */ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } 这个方法有一个同样的毛病,就是默认显示的首项是ListView,需要手动把ScrollView滚动至最顶端 [/code] 问题二 、打开套有 ListVew的 ScrollView的页面布局 默认 起始位置不是最顶部。 解决办法有两种都挺好用: 一是把套在里面的Gridview 或者 ListVew 不让获取焦点即可。 gridview.setFocusable(false); listview.setFocusable(false); 注意:在xml布局里面设置android:focusable=“false”不生效 方法二:网上还查到说可以设置myScrollView.smoothScrollTo(0,0); |
看错误的布局代码:
<ScrollView
android:id="@+id/act_solution_1_sv"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="\nListView上方数据\n" />
<ListView
android:id="@+id/act_solution_1_lv"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ListView>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="\nListView下方数据\n" />
</LinearLayout>
</ScrollView>
ScrollView中只能放一个控件,一般都放LinearLayout,orientation属性值为vertical。
在LinearLayout中放需要呈现的内容。ListView也在其中,ListView的高度设为适应自身内容(wrap_content
原因就是scroll事件的消费处理以及ListView控件的高度设定问题
[/code]
ViewPager和ScrollView嵌套滚动问题解决方案
嵌套是ViewPager-->ScrollView-->ViewPager.
public class HorizontalInnerViewPager extends ViewPager {
/** 触摸时按下的点 **/
PointF downP = new PointF();
/** 触摸时当前的点 **/
PointF curP = new PointF();
/** 自定义手势**/
private GestureDetector mGestureDetector;
public HorizontalInnerViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
mGestureDetector = new GestureDetector(context, new XScrollDetector());
}
public HorizontalInnerViewPager(Context context) {
super(context);
mGestureDetector = new GestureDetector(context, new XScrollDetector());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev);//default
//当拦截触摸事件到达此位置的时候,返回true,
//说明将onTouch拦截在此控件,进而执行此控件的onTouchEvent
//return true;
//接近水平滑动时子控件处理该事件,否则交给父控件处理
//return mGestureDetector.onTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
//每次进行onTouch事件都记录当前的按下的坐标
curP.x = ev.getX();
curP.y = ev.getY();
if(ev.getAction() == MotionEvent.ACTION_DOWN){
//记录按下时候的坐标
//切记不可用 downP = curP ,这样在改变curP的时候,downP也会改变
downP.x = ev.getX();
downP.y = ev.getY();
//此句代码是为了通知他的父ViewPager现在进行的是本控件的操作,不要对我的操作进行干扰
getParent().requestDisallowInterceptTouchEvent(true);
}
if(ev.getAction() == MotionEvent.ACTION_MOVE){
float distanceX = curP.x - downP.x;
float distanceY = curP.y - downP.y;
//接近水平滑动,ViewPager控件捕获手势,水平滚动
if(Math.abs(distanceX) > Math.abs(distanceY)){
//此句代码是为了通知他的父ViewPager现在进行的是本控件的操作,不要对我的操作进行干扰
getParent().requestDisallowInterceptTouchEvent(true);
}else{
//接近垂直滑动,交给父控件处理
getParent().requestDisallowInterceptTouchEvent(false);
}
}
return super.onTouchEvent(ev);
}
private class XScrollDetector extends GestureDetector.SimpleOnGestureListener{
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//return super.onScroll(e1, e2, distanceX, distanceY);
//接近水平滑动时子控件处理该事件,否则交给父控件处理
return (Math.abs(distanceX) > Math.abs(distanceY));
}
}
}
[/code]
ScrollView里嵌套ScrollView
两个相同方向的ScrollView是不能嵌套的,所以尽量避免这种情况; 如果不能避免,则看下面
目前做的这个只支持两个ScrollView嵌套,两个以上还有待改进,能套两个就已经能满足很多需求了目前为纵向scrollview的支持.直接看代码:
public class InnerScrollView extends ScrollView {
/**
*/
public ScrollView parentScrollView;
public InnerScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
private int lastScrollDelta = 0;
public void resume() {
overScrollBy(0, -lastScrollDelta, 0, getScrollY(), 0, getScrollRange(), 0, 0, true);
lastScrollDelta = 0;
}
int mTop = 10;
/**
* 将targetView滚到最顶端
*/
public void scrollTo(View targetView) {
int oldScrollY = getScrollY();
int top = targetView.getTop() - mTop;
int delatY = top - oldScrollY;
lastScrollDelta = delatY;
overScrollBy(0, delatY, 0, getScrollY(), 0, getScrollRange(), 0, 0, true);
}
private int getScrollRange() {
int scrollRange = 0;
if (getChildCount() > 0) {
View child = getChildAt(0);
scrollRange = Math.max(0, child.getHeight() - (getHeight()));
}
return scrollRange;
}
int currentY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (parentScrollView == null) {
return super.onInterceptTouchEvent(ev);
} else {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// 将父scrollview的滚动事件拦截
currentY = (int)ev.getY();
setParentScrollAble(false);
return super.onInterceptTouchEvent(ev);
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
// 把滚动事件恢复给父Scrollview
setParentScrollAble(true);
} else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
}
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
View child = getChildAt(0);
if (parentScrollView != null) {
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
int height = child.getMeasuredHeight();
height = height - getMeasuredHeight();
// System.out.println("height=" + height);
int scrollY = getScrollY();
// System.out.println("scrollY" + scrollY);
int y = (int)ev.getY();
// 手指向下滑动
if (currentY < y) {
if (scrollY <= 0) {
// 如果向下滑动到头,就把滚动交给父Scrollview
setParentScrollAble(true);
return false;
} else {
setParentScrollAble(false);
}
} else if (currentY > y) {
if (scrollY >= height) {
// 如果向上滑动到头,就把滚动交给父Scrollview
setParentScrollAble(true);
return false;
} else {
setParentScrollAble(false);
}
}
currentY = y;
}
}
return super.onTouchEvent(ev);
}
/**
* 是否把滚动事件交给父scrollview
*
* @param flag
*/
private void setParentScrollAble(boolean flag) {
parentScrollView.requestDisallowInterceptTouchEvent(!flag);
}
}
[/code]
RecyclerView嵌套滚动问题
在 Android 应用中,大部分情况下都会使用一个垂直滚动的 View 来显示内容(比如 ListView、RecyclerView 等)。但是有时候你还希望垂直滚动的View 里面的内容可以水平滚动。如果直接在垂直滚动的 View 里面使用水平滚动的 View,则滚动操作并不是很流畅。 比如下图中的示例: 上图中的布局为一个 RecyclerView 使用的是垂直滚动的 LinearLayoutManager 布局管理器,而里面每个 Item 为另外一个 RecyclerView 使用的是水平滚动的 LinearLayoutManager。而在 Android系统的事件分发 中,即使最上层的 |
recycleview跟scrollview嵌套问题
scrollview 嵌套recyclerview 时,recyclerview不显示,这就需要我们自己计算recyclerview的高度,比如:ViewGroup.LayoutParams mParams = recyclerView.getLayoutParams(); mParams.height = (CommonUtils.getScreenWidthPX(getActivity()) * 480 / 720 + CommonUtils.dipToPixels(40)) * num + CommonUtils.dipToPixels(8); mParams.width = CommonUtils.getScreenWidthPX(getActivity()); recyclerView.setLayoutParams(mParams); [/code] 这中方法适合item高度比较好计算的情形,但要遇到里面的item高度不一定这就需要我们重写recyclerview的高度了,以前嵌套listview的时候我们只需重写listview 然后重写 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } [/code] 但是这种方法在recyclerview重写不管用。 我们此时要重写的的是LinearLayoutManager或GridLayoutManager public class FullyLinearLayoutManager extends LinearLayoutManager { private static final String TAG = FullyLinearLayoutManager.class.getSimpleName(); public FullyLinearLayoutManager(Context context) { super(context); } public FullyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } private int[] mMeasuredDimension = new int[2]; @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); Log.i(TAG, "onMeasure called. \nwidthMode " + widthMode + " \nheightMode " + heightSpec + " \nwidthSize " + widthSize + " \nheightSize " + heightSize + " \ngetItemCount() " + getItemCount()); int width = 0; int height = 0; for (int i = 0; i < getItemCount(); i++) { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); if (getOrientation() == HORIZONTAL) { width = width + mMeasuredDimension[0]; if (i == 0) { height = mMeasuredDimension[1]; } } else { height = height + mMeasuredDimension[1]; if (i == 0) { width = mMeasuredDimension[0]; } } } switch (widthMode) { case View.MeasureSpec.EXACTLY: width = widthSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } switch (heightMode) { case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } setMeasuredDimension(width, height); } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { try { View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); view.measure(childWidthSpec, childHeightSpec); measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; recycler.recycleView(view); } } catch (Exception e) { e.printStackTrace(); } finally { } } } [/code] public class FullyGridLayoutManager extends GridLayoutManager { private int mwidth = 0; private int mheight = 0; public FullyGridLayoutManager(Context context, int spanCount) { super(context, spanCount); } public FullyGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) { super(context, spanCount, orientation, reverseLayout); } private int[] mMeasuredDimension = new int[2]; public int getMwidth() { return mwidth; } public void setMwidth(int mwidth) { this.mwidth = mwidth; } public int getMheight() { return mheight; } public void setMheight(int mheight) { this.mheight = mheight; } @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); int width = 0; int height = 0; int count = getItemCount(); int span = getSpanCount(); for (int i = 0; i < count; i++) { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); if (getOrientation() == HORIZONTAL) { if (i % span == 0) { width = width + mMeasuredDimension[0]; } if (i == 0) { height = mMeasuredDimension[1]; } } else { if (i % span == 0) { height = height + mMeasuredDimension[1]; } if (i == 0) { width = mMeasuredDimension[0]; } } } switch (widthMode) { case View.MeasureSpec.EXACTLY: width = widthSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } switch (heightMode) { case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } setMheight(height); setMwidth(width); setMeasuredDimension(width, height); } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { if (position < getItemCount()) { try { View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); view.measure(childWidthSpec, childHeightSpec); measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; recycler.recycleView(view); } } catch (Exception e) { e.printStackTrace(); } } } } [/code] 重写完之后,用就好说了,在adapter的onBindview和平常一样用就可以了 final FullyGridLayoutManager manager = new FullyGridLayoutManager(context.getActivity(), 3); manager.setOrientation(GridLayoutManager.VERTICAL); manager.setSmoothScrollbarEnabled(true); viewHolder.recyclerView.setLayoutManager(manager); [/code] 或者再activity中设置一样的 此种方法在4.x系统上好用,能显示滑动也流畅,但是在5.x上虽然显示正常,但是滑动的时候好像被粘住了,没有惯性效果。。。。然后郁闷了一下午。。。。 最后解决方法是重写最外层的Scrollview |
**
* 屏蔽 滑动事件
* Created by fc on 2015/7/16.
*/
public class MyScrollview extends ScrollView {
private int downX;
private int downY;
private int mTouchSlop;
public MyScrollview(Context context) {
super(context);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public MyScrollview(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public MyScrollview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downX = (int) e.getRawX();
downY = (int) e.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) e.getRawY();
if (Math.abs(moveY - downY) > mTouchSlop) {
return true;
}
}
return super.onInterceptTouchEvent(e);
}
}
[/code]
相关文章推荐
- ScrollView或者ListView中嵌套ViewPager,展示不出来问题。
- 《转》ScrollView中嵌套ListView和Viewpager问题
- ScrollView或者ListView中嵌套ViewPager,展示不出来问题。
- ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
- Android在ScrollView中嵌套ViewPager以及ListView的可能出现的问题以及解决方案
- ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
- android中viewpager,scrollview、listview的嵌套问题
- 解决ScrollView嵌套viewPager中嵌套listView滑动事件冲突问题(水平方向)
- Android ScrollView嵌套Viewpager嵌套ListView切换时到顶部或位置改变问题解决
- ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
- 关于ScrollView中嵌套ListView和Viewpager问题
- 一个 ScrollView 里面包含 viewpager 嵌套 listview 或 RecyclerView 极少代码实现的流畅滑动效果 处理一个两层滑动 view 的自定义布局,以最少的代码实现,
- listview ,scrollview,viewpager,嵌套问题
- ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
- Viewpager嵌套ScrollView和ListView嵌套ScrollView引起的冲突问题
- ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
- 解决ListView嵌套ViewPager滑动不了的问题
- 完美解决ScrollView嵌套ViewPager滑动失效和无法正常滑动冲突问题
- 解决小米系统下ViewPager、ScrollView内嵌套WebView时,Touch事件不响应的问题
- ScrollView中嵌套的viewPager,viepager高度设置为wrap_content时内容不显示问题以及与scrollview冲突的解决方案