近段时间,自己在做之前项目经验的一些总结,也刚好今天有个刚进入Android开发没多久的伙计问我塔项目中遇到的滑动冲突的问题,就是scrollView嵌套的listview。今天帮他解决问题的同时也记录下自己之前项目中遇到的冲突的解决方案。
首先我们来看下滑动冲突的原因:借助任玉刚刚哥的《Android开发艺术探索》这本书,可以参考155到157页,同时也可以看下140页到154页的view的分发事件。看过之后就很清楚啦。view的滑动冲突无非就是在手指在滑动时拥有两个可以同时滑动的view控件。常见的场景通常有以下三种情况:
1)、外部滑动方向和内部滑动方向不一致;
2)、外部滑动方向和内部滑动方向一直;
3)、以上两者的情况的嵌套;
场景一这种场景:也是我们最常见的,比如说我们做应用时经常使用的fragment+viewpager组合而成的底部菜单页面切换的效果,他们滑动的方向时不一致的。fragment中的scrollview、listview等本来是应该有滑动冲突的,但是google已经帮我们解决啦这样的冲突在viewpager中。但是如果是scrollview嵌套listview就需呀处理啦,不处理的话就会造成内位两层只能有一层可以滑动。出啦这种,像外部上下滑动、内部左右滑动都属于这种。
场景二:当内外两层在同一个方向滑动的时候,这种是存在逻辑问题,当手指开始滑动时,系统无法知道是到底用户想要那个滑动。要么就是只有一层能够滑动,要不就是内外两层滑动的都非常的卡,我在项目中就是遇到这样的问题(内外俩层同时上下滑动)。
场景三:场景三时前两种场景的组合嵌套。这种情况更加的复杂,但是仔细分析下来又可以看出来,他们又是几种简单冲突分析的组合叠加。所以需要分别处理内中外存在的冲突即可。
其次、懂得造成冲突的原因,我们就来看冲突的解决办法。根据以上三种滑动冲突的场景,我们仅需要修改滑动规则即可。
1)、外部拦截法:
在控件的onInterceptTouchEvent方法中根据手勢的上滑下滑或者左滑右滑,设置是否拦截滑动事件
2)、内部拦截法:
是指父容器不拦截任何事件,所有事件都传递给子元素。需要此事件就消耗掉,否则就直接传递给父容器处理。此种方法需要配置requestDisallowInterceptTouchEvent方法才能正常工作。需要在父容器中的dispatchTouchEvent和自容器的onInterceptTouchEvent 两个方法中共同做逻辑判断才可以。
最后、除啦上述的滑动冲突以外,还有就是同有滑动事件造成的内部嵌套仅显示一行item的问题。
关于这个问题解决如下:
1)、先看布局xml设置中的父布局的高度属性设置的是否是match_parent属性或者按照百分比显示。是的话可以需改成具体的数值。
2)、如果方法一不能解决问题,就重新自定义一个继承listview的控件,并重写onMeasure()方法,修改显示内容的测量模式,在嵌套使用listview时的默认测量模式是MeasureSpec.UNSPECIFIED,这种模式测量时仅能加载布局的部分高度。具体代码如下:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // MeasureSpec的前两位是模式,所以需要右移两位。 heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
3)、除啦以上方法外还可以在Activity中修改listview的高度,自己遍历计算listview控件要显示的数据的高度,如下:
public void setListViewHeightBased2ChildItem(ListView listView) { ListAdapter listAdapter = listView.getAdapter(); if (null == listAdapter) return; int listViewTotalHeight = 0; for (int i = 0, lent = listAdapter.getCount(); i < lent; i++) { // listAdapter.getCount();获取返回数据项的数目 View itemView = listAdapter.getView(i, null, listView); itemView.measure(0, 0);//计算子项view的宽高 listViewTotalHeight += itemView.getMeasuredHeight();//统计所有子项的总高度 } ViewGroup.LayoutParams layoutParams = listView.getLayoutParams(); layoutParams.height = listViewTotalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); listView.setLayoutParams(layoutParams); listView.invalidate(); } 但是以上两种方法都存在缺陷:首次进入listview动态加载 数据时,scrollview会跳转到listview加载的第一个数据的位置; 解决方法:1)、重新调整scrollview的位置:例如
//解决缺陷 scrollView.post(new Runnable() { @Override public void run() { scrollView.scrollTo(0, 0); } }); 或者直接先设置listview失去焦点,listview.setFocusable(false);不影响listview的item的点击事件。 以上除啦解决scrollview嵌套listview解决嵌套的graidview滑动冲突、数据显示不全也是同样适用的。