100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 自定义控件(视图)2期笔记13:View的滑动冲突之 内部拦截法

自定义控件(视图)2期笔记13:View的滑动冲突之 内部拦截法

时间:2023-06-16 21:52:07

相关推荐

自定义控件(视图)2期笔记13:View的滑动冲突之 内部拦截法

1. 内部拦截法:

父容器不拦截事件,所有的事件全部都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器进行处理。

这种方法和Android中的事件分发机制不一样,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,使用起来较外部拦截法稍显负责一点。

我们需要重写子元素的dispatchTouchEvent方法。

这种方法的伪代码是:

1 @Override 2public boolean dispatchTouchEvent(MotionEvent event) { 3 int x = (int) event.getX(); 4 int y = (int) event.getY(); 5 6 switch (event.getAction()) { 7 case MotionEvent.ACTION_DOWN: { 8 parent.requestDisallowInterceptTouchEvent(true); 9 break;10 }11 case MotionEvent.ACTION_MOVE: {12 int deltaX = x - mLastX;13 int deltaY = y - mLastY;14 if (父容器需要此类点击事件) {15 parent.requestDisallowInterceptTouchEvent(false);16 }17 break;18 }19 case MotionEvent.ACTION_UP: {20 break;21 }22 default:23 break;24 }25 26 mLastX = x;27 mLastY = y;28 return super.dispatchTouchEvent(event);29}

上面重写的子元素的dispatchTouchEvent方法,这里同时需要重写父容器的onInterceptTouchEvent方法,为什么呢?

那是因为ACTION_DOWN事件并不受FLAG_DISALLOW_INTERCEPT这个标记位的控制,所以一旦父容器拦截ACTION_DOWN事件,那么所有的事件都无法传递到子元素之中,这样内部拦截法就无法起作用了。

父容器所做的修改如下:

1 @Override 2public boolean onInterceptTouchEvent(MotionEvent event) { 3 4 int action = event.getAction(); 5 if (action == MotionEvent.ACTION_DOWN) { 6 return false; 7 } else { 8 return true; 9 }10}

2. 下面通过一个Demo示例说明:

(1)首先我们创建一个Android工程,如下:

(2)首先我们来到主布局activity_main.xml,如下:

1 <com.himi.viewconflict1.ui.RevealLayout 2xmlns:android="/apk/res/android" 3xmlns:tools="/tools" 4android:layout_width="match_parent" 5android:layout_height="match_parent" 6android:orientation="vertical" 7android:padding="12dp" 8tools:context="${relativePackage}.${activityClass}" > 9 10<Button11 android:id="@+id/button1"12 style="@style/AppTheme.Button.Green"13 android:onClick="onButtonClick"14 android:text="滑动冲突场景1-内部拦截" />15 16 </com.himi.viewconflict1.ui.RevealLayout>

(3)接下来来到MainActivity,如下:

1 package com.himi.viewconflict; 2 3 import android.app.Activity; 4 import android.content.Intent; 5 import android.os.Bundle; 6 import android.view.View; 7 8 public class MainActivity extends Activity { 9 10@Override11protected void onCreate(Bundle savedInstanceState) {12 super.onCreate(savedInstanceState);13 setContentView(R.layout.activity_main);14}15 161718public void onButtonClick(View view) {19Intent intent = new Intent(this, DemoActivity_1.class);20startActivity(intent);21}22 }

(4)上面很自然地跳转到DemoActivity_2之中,如下:

1 package com.himi.viewconflict1; 2 3 import java.util.ArrayList; 4 5 import com.himi.viewconflict1.ui.HorizontalScrollViewEx2; 6 import com.himi.viewconflict1.ui.ListViewEx; 7 import com.himi.viewconflict1.utils.MyUtils; 8 9 import android.app.Activity;10 import android.graphics.Color;11 import android.os.Bundle;12 import android.util.Log;13 import android.view.LayoutInflater;14 import android.view.MotionEvent;15 import android.view.View;16 import android.view.ViewGroup;17 import android.widget.AdapterView;18 import android.widget.AdapterView.OnItemClickListener;19 import android.widget.ArrayAdapter;20 import android.widget.TextView;21 import android.widget.Toast;22 23 public class DemoActivity_2 extends Activity {24private static final String TAG = "DemoActivity_2";25 26private HorizontalScrollViewEx2 mListContainer;27 28@Override29protected void onCreate(Bundle savedInstanceState) {30 super.onCreate(savedInstanceState);31 setContentView(R.layout.demo_2);32 Log.d(TAG, "onCreate");33 initView();34}35 36private void initView() {37 LayoutInflater inflater = getLayoutInflater();38 mListContainer = (HorizontalScrollViewEx2) findViewById(R.id.container);39 final int screenWidth = MyUtils.getScreenMetrics(this).widthPixels;40 final int screenHeight = MyUtils.getScreenMetrics(this).heightPixels;41 for (int i = 0; i < 3; i++) {42 ViewGroup layout = (ViewGroup) inflater.inflate(43 R.layout.content_layout2, mListContainer, false);44 layout.getLayoutParams().width = screenWidth;45 TextView textView = (TextView) layout.findViewById(R.id.title);46 textView.setText("page " + (i + 1));47 layout.setBackgroundColor(Color48 .rgb(255 / (i + 1), 255 / (i + 1), 0));49 createList(layout);50 mListContainer.addView(layout);51 }52}53 54private void createList(ViewGroup layout) {55 ListViewEx listView = (ListViewEx) layout.findViewById(R.id.list);56 ArrayList<String> datas = new ArrayList<String>();57 for (int i = 0; i < 50; i++) {58 datas.add("name " + i);59 }60 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,61 R.layout.content_list_item, R.id.name, datas);62 listView.setAdapter(adapter);63 listView.setHorizontalScrollViewEx2(mListContainer);64 listView.setOnItemClickListener(new OnItemClickListener() {65 @Override66 public void onItemClick(AdapterView<?> parent, View view,67 int position, long id) {68 Toast.makeText(DemoActivity_2.this, "click item "+position,69Toast.LENGTH_SHORT).show();70 71 }72 });73}74 75@Override76public boolean dispatchTouchEvent(MotionEvent ev) {77 Log.d(TAG, "dispatchTouchEvent action:" + ev.getAction());78 return super.dispatchTouchEvent(ev);79}8081@Override82public boolean onTouchEvent(MotionEvent event) {83 Log.d(TAG, "onTouchEvent action:" + event.getAction());84 return super.onTouchEvent(event);85}86 }

上面的DemoActivity_2主布局demo_2.xml,如下:

1 <LinearLayout xmlns:android="/apk/res/android" 2xmlns:tools="/tools" 3android:layout_width="match_parent" 4android:layout_height="match_parent" 5android:background="#ffffff" 6android:orientation="vertical" > 7 8<com.himi.viewconflict1.ui.HorizontalScrollViewEx2 9 android:id="@+id/container"10 android:layout_width="wrap_content"11 android:layout_height="match_parent" />12 13 14 </LinearLayout>

上面使用到HorizontalScrollViewEx2是自定义控件(继承自ViewGroup),如下:

HorizontalScrollViewEx2是父容器,这里需要重写它的onInterceptTouchEvent方法,让父容器不拦截ACTION_DOWN事件。

1 package com.himi.viewconflict1.ui; 2 3 import android.content.Context; 4 import android.util.AttributeSet; 5 import android.util.Log; 6 import android.view.MotionEvent; 7 import android.view.VelocityTracker; 8 import android.view.View; 9 import android.view.ViewGroup; 10 import android.widget.Scroller; 11 12 public class HorizontalScrollViewEx2 extends ViewGroup { 13private static final String TAG = "HorizontalScrollViewEx2"; 14 15private int mChildrenSize; 16private int mChildWidth; 17private int mChildIndex; 18// 分别记录上次滑动的坐标 19private int mLastX = 0; 20private int mLastY = 0; 21 22// 分别记录上次滑动的坐标(onInterceptTouchEvent) 23private int mLastXIntercept = 0; 24private int mLastYIntercept = 0; 25 26private Scroller mScroller; 27private VelocityTracker mVelocityTracker; 28 29public HorizontalScrollViewEx2(Context context) { 30 super(context); 31 init(); 32} 33 34public HorizontalScrollViewEx2(Context context, AttributeSet attrs) { 35 super(context, attrs); 36 init(); 37} 38 39public HorizontalScrollViewEx2(Context context, AttributeSet attrs, 40 int defStyle) { 41 super(context, attrs, defStyle); 42 init(); 43} 44 45private void init() { 46 mScroller = new Scroller(getContext()); 47 mVelocityTracker = VelocityTracker.obtain(); 48} 49 50@Override 51public boolean onInterceptTouchEvent(MotionEvent event) { 52 int x = (int) event.getX(); 53 int y = (int) event.getY(); 54 int action = event.getAction(); 55 if (action == MotionEvent.ACTION_DOWN) { 56 mLastX = x; 57 mLastY = y; 58 if (!mScroller.isFinished()) { 59 mScroller.abortAnimation(); 60 return true; 61 } 62 return false; 63 } else { 64 return true; 65 } 66} 67 68@Override 69public boolean onTouchEvent(MotionEvent event) { 70 Log.d(TAG, "onTouchEvent action:" + event.getAction()); 71 mVelocityTracker.addMovement(event); 72 int x = (int) event.getX(); 73 int y = (int) event.getY(); 74 switch (event.getAction()) { 75 case MotionEvent.ACTION_DOWN: { 76 if (!mScroller.isFinished()) { 77 mScroller.abortAnimation(); 78 } 79 break; 80 } 81 case MotionEvent.ACTION_MOVE: { 82 int deltaX = x - mLastX; 83 int deltaY = y - mLastY; 84 Log.d(TAG, "move, deltaX:" + deltaX + " deltaY:" + deltaY); 85 scrollBy(-deltaX, 0); 86 break; 87 } 88 case MotionEvent.ACTION_UP: { 89 int scrollX = getScrollX(); 90 int scrollToChildIndex = scrollX / mChildWidth; 91 Log.d(TAG, "current index:" + scrollToChildIndex); 92 puteCurrentVelocity(1000); 93 float xVelocity = mVelocityTracker.getXVelocity(); 94 if (Math.abs(xVelocity) >= 50) { 95 mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1; 96 } else { 97 mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth; 98 } 99 mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));100 int dx = mChildIndex * mChildWidth - scrollX;101 smoothScrollBy(dx, 0);102 mVelocityTracker.clear();103 Log.d(TAG, "index:" + scrollToChildIndex + " dx:" + dx);104 break;105 }106 default:107 break;108 }109 110 mLastX = x;111 mLastY = y;112 return true;113}114 115@Override116protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {117 super.onMeasure(widthMeasureSpec, heightMeasureSpec);118 int measuredWidth = 0;119 int measuredHeight = 0;120 final int childCount = getChildCount();121 measureChildren(widthMeasureSpec, heightMeasureSpec);122 123 int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);124 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);125 int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);126 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);127 if (childCount == 0) {128 setMeasuredDimension(0, 0);129 } else if (heightSpecMode == MeasureSpec.AT_MOST) {130 final View childView = getChildAt(0);131 measuredHeight = childView.getMeasuredHeight();132 setMeasuredDimension(widthSpaceSize, childView.getMeasuredHeight());133 } else if (widthSpecMode == MeasureSpec.AT_MOST) {134 final View childView = getChildAt(0);135 measuredWidth = childView.getMeasuredWidth() * childCount;136 setMeasuredDimension(measuredWidth, heightSpaceSize);137 } else {138 final View childView = getChildAt(0);139 measuredWidth = childView.getMeasuredWidth() * childCount;140 measuredHeight = childView.getMeasuredHeight();141 setMeasuredDimension(measuredWidth, measuredHeight);142 }143}144 145@Override146protected void onLayout(boolean changed, int l, int t, int r, int b) {147 Log.d(TAG, "width:" + getWidth());148 int childLeft = 0;149 final int childCount = getChildCount();150 mChildrenSize = childCount;151 152 for (int i = 0; i < childCount; i++) {153 final View childView = getChildAt(i);154 if (childView.getVisibility() != View.GONE) {155 final int childWidth = childView.getMeasuredWidth();156 mChildWidth = childWidth;157 childView.layout(childLeft, 0, childLeft + childWidth,158childView.getMeasuredHeight());159 childLeft += childWidth;160 }161 }162}163 164private void smoothScrollBy(int dx, int dy) {165 mScroller.startScroll(getScrollX(), 0, dx, 0, 500);166 invalidate();167}168 169@Override170public void computeScroll() {171 if (puteScrollOffset()) {172 scrollTo(mScroller.getCurrX(), mScroller.getCurrY());173 postInvalidate();174 }175}176 177@Override178protected void onDetachedFromWindow() {179 mVelocityTracker.recycle();180 super.onDetachedFromWindow();181}182 }

(5)来到主布局之中,在HorizontalScrollViewEx2之中包含一个子布局content_layout2.xml,如下:

1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="/apk/res/android" 3android:layout_width="match_parent" 4android:layout_height="match_parent" 5android:orientation="vertical" > 6 7<TextView 8 android:id="@+id/title" 9 android:layout_width="wrap_content"10 android:layout_height="wrap_content"11 android:layout_marginTop="5dp"12 android:layout_marginBottom="5dp"13 android:text="TextView" />14 15<com.himi.viewconflict1.ui.ListViewEx16 android:id="@+id/list"17 android:layout_width="match_parent"18 android:layout_height="match_parent"19 android:background="#fff4f7f9"20 android:cacheColorHint="#00000000"21 android:divider="#dddbdb"22 android:dividerHeight="1.0px"23 android:listSelector="@android:color/transparent" />24 25 </LinearLayout>

上面的ListViewEx是自定义的控件(继承自ListView),在ListViewEx里面实现了内部拦截法的逻辑,如下:

1 package com.himi.viewconflict1.ui; 2 3 import android.content.Context; 4 import android.util.AttributeSet; 5 import android.util.Log; 6 import android.view.MotionEvent; 7 import android.widget.ListView; 8 9 public class ListViewEx extends ListView {10private static final String TAG = "ListViewEx";11 12private HorizontalScrollViewEx2 mHorizontalScrollViewEx2;13 14// 分别记录上次滑动的坐标15private int mLastX = 0;16private int mLastY = 0;17 18public ListViewEx(Context context) {19 super(context);20}21 22public ListViewEx(Context context, AttributeSet attrs) {23 super(context, attrs);24}25 26public ListViewEx(Context context, AttributeSet attrs, int defStyle) {27 super(context, attrs, defStyle);28}29 30public void setHorizontalScrollViewEx2(31 HorizontalScrollViewEx2 horizontalScrollViewEx2) {32 mHorizontalScrollViewEx2 = horizontalScrollViewEx2;33}34 35@Override36public boolean dispatchTouchEvent(MotionEvent event) {37 int x = (int) event.getX();38 int y = (int) event.getY();39 40 switch (event.getAction()) {41 case MotionEvent.ACTION_DOWN: {42 mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(true);43 break;44 }45 case MotionEvent.ACTION_MOVE: {46 int deltaX = x - mLastX;47 int deltaY = y - mLastY;48 Log.d(TAG, "dx:" + deltaX + " dy:" + deltaY);49 if (Math.abs(deltaX) > Math.abs(deltaY)) {50 mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(false);51 }52 break;53 }54 case MotionEvent.ACTION_UP: {55 break;56 }57 default:58 break;59 }60 61 mLastX = x;62 mLastY = y;63 return super.dispatchTouchEvent(event);64}65 66 }

void requestDisallowInterceptTouchEvent(booleandisallowIntercept):

这个方法的入参一个boolean变量,用来表示是否需要调用onInterceptTouchEvent来判断是否拦截.

该标记如果为True,就如它的字面意思一样---不允许调用onInterceptTouchEvent(),结果就是,所有的父类方法都不会进行拦截,而把事件传递给子View. 该方法属于ViewGroup ,并且是个递归方法,也就是说一旦调用后,所有父类的disallowIntercept都会设置成True。即当前View的所有父类View,都不会调用自身的onInterceptTouchEvent()进行拦截。

该标记如果为False,就如它的字面意思一样---允许调用onInterceptTouchEvent(),结果就是,父类可以拦截事件。

接下来,来到Listview的Item布局content_list_item.xml,如下:

1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="/apk/res/android" 3android:layout_width="match_parent" 4android:layout_height="50dp" 5android:gravity="center_vertical" 6android:orientation="vertical" > 7 8<TextView 9 android:id="@+id/name"10 android:layout_width="wrap_content"11 android:layout_height="wrap_content"12 android:text="TextView" />13 14 </LinearLayout>

(6)最终项目如下:

(7)部署程序到手机上,如下:

3. 示例源码下载

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。