15.1 问题
应用程序视图中设计了嵌套的触摸交互,这些交互不能很好地作用于触摸层次结构 的标准流程,在此层次结构中,较高层的容器视图通过子视图进行窃取来直接处理触摸事件。
15.2 解决方案
(API Level 1)
ViewGroup是框架中所有布局和容器的基类,它为此提供了描述性命名方法requestDisallowTouchIntercept()。在任何容器视图上设置此标志会指示框架,在当前手势持续期间,我们更希望它们不会拦截进入其子视图的事件。
15.3 实现机制
为展示此方法的实际使用,我们创建了一个示例,其中两个互相竞争的可触摸视图位于同一位置。外部包含视图是ListView,它通过滚动内容响应指示垂直拖动的触摸事件。在ListView内部是作为头部添加的ViewPager,它响应水平拖动触摸事件以在页面之间轻扫。就其本质来说,该例带来了一个问题,水平轻扫在垂直方向上远距离变化的ViewPager的尝试会为了支持ListView滚动而被取消,因为ListView会监控和拦截这些事件。人们无法在垂直或水平运动过程中进行拖动,因此这就产生了可用性问题。
为建立此例,首先需要声明一个维度资源(参见以下代码),代码清单给出了完整的Activity。
res/values/dimens.xml
<?xml version="1.0" encoding="utf-8"?><resources> <dimen name="header_height">150dp</dimen></resources>
管理触摸拦截的Activity
public class DisallowActivity extends Activity implements ViewPager.OnPageChangeListener { private static final String[] ITEMS = { "Row One", "Row Two", "Row Three", "Row Four", "Row Five", "Row Six", "Row Seven", "Row Eight", "Row Nine", "Row Ten" }; private ViewPager mViewPager; private ListView mListView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create a header view of horizontal swiping items mViewPager = new ViewPager(this); // As a ListView header, ViewPager must be given a fixed height mViewPager.setLayoutParams(new ListView.LayoutParams( ListView.LayoutParams.MATCH_PARENT, getResources().getDimensionPixelSize(R.dimen.header_height)) ); // Listen for paging state changes to disable parent touches mViewPager.setOnPageChangeListener(this); mViewPager.setAdapter(new HeaderAdapter(this)); // Create a vertical scrolling list mListView = new ListView(this); // Add the pager as the list header mListView.addHeaderView(mViewPager); // Add list items mListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, ITEMS)); setContentView(mListView); } /* OnPageChangeListener Methods */ @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { } @Override public void onPageScrollStateChanged(int state) { // While the ViewPager is scrolling, disable the ScrollView touch // intercept so it cannot take over and try to vertical scroll. // This flag must be set for each gesture you want to override. boolean isScrolling = state != ViewPager.SCROLL_STATE_IDLE; mListView.requestDisallowInterceptTouchEvent(isScrolling); } private static class HeaderAdapter extends PagerAdapter { private Context mContext; public HeaderAdapter(Context context) { mContext = context; } @Override public int getCount() { return 5; } @Override public Object instantiateItem(ViewGroup container, int position) { // Create a new page view TextView tv = new TextView(mContext); tv.setText(String.format("Page %d", position + 1)); tv.setBackgroundColor((position % 2 == 0) ? Color.RED : Color.GREEN); tv.setGravity(Gravity.CENTER); tv.setTextColor(Color.BLACK); // Add as the view for this position, and return as the object for // this position container.addView(tv); return tv; } @Override public void destroyItem(ViewGroup container, int position, Object object) { View page = (View) object; container.removeView(page); } @Override public boolean isViewFromObject(View view, Object object) { return (view == object); } } }
在此Activity中,作为根视图的ListView包含一个基本适配器,用于显示字符串条目的静态列表。同样在onCreate()方法中,创建ViewPager实例并作为头部视图添加到列表中。我们将在本章后面更详细地讨论ViewPager的工作方式,此处只需要知道我们正在创建一个带有自定义PagerAdapter的简单ViewPager,它显示了一些彩色视图作为其页面,以供用户在这些页面直接轻扫。
创建ViewPager之后,构造并应用一组ListView.LayoutParams来控制ViewPager如何作为头部显示。必须执行该操作,因为ViewPager自身没有内在的内容大小,并且列表不能很好地作用于没有明确高度的视图。通过维度资源应用固定的高度,从而可以轻松获得适当缩放的dp值,该值与设备无关。这比完全通过Java代码全面构造dp值要简单很多。
此例的关键之处在于Activity实现的onPageChangeListener(该回调在后面会用于与ViewPager)。当用户与ViewPager交互并左右轻扫时,就会触发此回调。在onPageScrollStateChanged()方法内部,我们传递一个指示ViewPager是否空闲、
Activity正在滚动或在滚动后停到某个页面的值。这是控制父ListView的触摸拦截行为的最佳位置。当ViewPager的滚动状态不是空闲时,我们不希望ListView窃取Viewager正在使用的触摸事件,因此在requestDisallowTouchIntercept()中设置相应的标志。
连续触发该值还要另一个原因。在原始解决方案中提及,该标志对当前手势有效。这意味着每次新的ACTION_DOWN事件发生时,我们需要再次设置该标志。没有添加触摸侦听器来查找特定的事件,我们基于子视图的滚动行为连续设置该标志,这就获得了相同的效果。
作者:Jennyni1122
链接:https://www.jianshu.com/p/f10c0a19adcc