android 滑动拦截,处理fragment的左右滑动事件意外拦截问题

请看load_data_waiting_layout.xml内容,RelativeLayout, ProgressBar, TextView等控件设置clickable为true之后就不再支持ProgramListFragment的左右滑动事件。如果不设置clickable属性或者设置为false之后就支持ProgramListFragment的左右滑动事件。

programListFragment.getView().setOnTouchListener(programTouchListener);

难道设置clickable属性为true之后,就不执行programListFragment.getView()的onTouch方法了吗<?xml version="1.0" encoding="utf-8"?>

android:id="@+id/layout_waiting"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:clickable="true"

android:background="#00ffff" >

android:id="@+id/waiting"

style="?android:attr/progressBarStyleLarge"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerInParent="true"

android:clickable="true"

android:background="#ff00ff" />

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_below="@id/waiting"

android:layout_centerHorizontal="true"

android:layout_marginTop="10dp"

android:text="@string/loading_data"

android:textSize="22sp"

android:clickable="true"

android:background="#0000ff" />

在xml文件设置clickable属性相当于在程序中调用setClickable方法。从android.view.View的setClickable方法实现进来分析clickable属性对touch事件的影响。

void setClickable(boolean clickable)public void setClickable(boolean clickable) {

setFlags(clickable ? CLICKABLE : 0, CLICKABLE);

}

void setFlags(int flags, int mask)/**

* Set flags controlling behavior of this view.

*

* @param flags Constant indicating the value which should be set

* @param mask Constant indicating the bit range that should be changed

*/

void setFlags(int flags, int mask) {

int old = mViewFlags;

mViewFlags = (mViewFlags & ~mask) | (flags & mask);

int changed = mViewFlags ^ old;

if (changed == 0) {

return;

}

int privateFlags = mPrivateFlags;

/* Check if the FOCUSABLE bit has changed */

if (((changed & FOCUSABLE_MASK) != 0) &&

((privateFlags & PFLAG_HAS_BOUNDS) !=0)) {

if (((old & FOCUSABLE_MASK) == FOCUSABLE)

&& ((privateFlags & PFLAG_FOCUSED) != 0)) {

/* Give up focus if we are no longer focusable */

clearFocus();

} else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE)

&& ((privateFlags & PFLAG_FOCUSED) == 0)) {

/*

* Tell the view system that we are now available to take focus

* if no one else already has it.

*/

if (mParent != null) mParent.focusableViewAvailable(this);

}

if (AccessibilityManager.getInstance(mContext).isEnabled()) {

notifyAccessibilityStateChanged();

}

}

if ((flags & VISIBILITY_MASK) == VISIBLE) {

if ((changed & VISIBILITY_MASK) != 0) {

/*

* If this view is becoming visible, invalidate it in case it changed while

* it was not visible. Marking it drawn ensures that the invalidation will

* go through.

*/

mPrivateFlags |= PFLAG_DRAWN;

invalidate(true);

needGlobalAttributesUpdate(true);

// a view becoming visible is worth notifying the parent

// about in case nothing has focus. even if this specific view

// isn't focusable, it may contain something that is, so let

// the root view try to give this focus if nothing else does.

if ((mParent != null) && (mBottom > mTop) && (mRight > mLeft)) {

mParent.focusableViewAvailable(this);

}

}

}

/* Check if the GONE bit has changed */

if ((changed & GONE) != 0) {

needGlobalAttributesUpdate(false);

requestLayout();

if (((mViewFlags & VISIBILITY_MASK) == GONE)) {

if (hasFocus()) clearFocus();

clearAccessibilityFocus();

destroyDrawingCache();

if (mParent instanceof View) {

// GONE views noop invalidation, so invalidate the parent

((View) mParent).invalidate(true);

}

// Mark the view drawn to ensure that it gets invalidated properly the next

// time it is visible and gets invalidated

mPrivateFlags |= PFLAG_DRAWN;

}

if (mAttachInfo != null) {

mAttachInfo.mViewVisibilityChanged = true;

}

}

/* Check if the VISIBLE bit has changed */

if ((changed & INVISIBLE) != 0) {

needGlobalAttributesUpdate(false);

/*

* If this view is becoming invisible, set the DRAWN flag so that

* the next invalidate() will not be skipped.

*/

mPrivateFlags |= PFLAG_DRAWN;

if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE) && hasFocus()) {

// root view becoming invisible shouldn't clear focus and accessibility focus

if (getRootView() != this) {

clearFocus();

clearAccessibilityFocus();

}

}

if (mAttachInfo != null) {

mAttachInfo.mViewVisibilityChanged = true;

}

}

if ((changed & VISIBILITY_MASK) != 0) {

if (mParent instanceof ViewGroup) {

((ViewGroup) mParent).onChildVisibilityChanged(this,

(changed & VISIBILITY_MASK), (flags & VISIBILITY_MASK));

((View) mParent).invalidate(true);

} else if (mParent != null) {

mParent.invalidateChild(this, null);

}

dispatchVisibilityChanged(this, (flags & VISIBILITY_MASK));

}

if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {

destroyDrawingCache();

}

if ((changed & DRAWING_CACHE_ENABLED) != 0) {

destroyDrawingCache();

mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;

invalidateParentCaches();

}

if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0) {

destroyDrawingCache();

mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;

}

if ((changed & DRAW_MASK) != 0) {

if ((mViewFlags & WILL_NOT_DRAW) != 0) {

if (mBackground != null) {

mPrivateFlags &= ~PFLAG_SKIP_DRAW;

mPrivateFlags |= PFLAG_ONLY_DRAWS_BACKGROUND;

} else {

mPrivateFlags |= PFLAG_SKIP_DRAW;

}

} else {

mPrivateFlags &= ~PFLAG_SKIP_DRAW;

}

requestLayout();

invalidate(true);

}

if ((changed & KEEP_SCREEN_ON) != 0) {

if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {

mParent.recomputeViewAttributes(this);

}

}

if (AccessibilityManager.getInstance(mContext).isEnabled()

&& ((changed & FOCUSABLE) != 0 || (changed & CLICKABLE) != 0

|| (changed & LONG_CLICKABLE) != 0 || (changed & ENABLED) != 0)) {

notifyAccessibilityStateChanged();

}

}

扩展TextView,重写onTouchEvent方法,返回false。

public class MyTextView extends TextView {

public MyTextView(Context context) {

super(context);

}

public MyTextView(Context context, AttributeSet attrs) {

super(context, attrs);

}

public MyTextView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

}

@Override

public boolean onTouchEvent(MotionEvent event) {

super.onTouchEvent(event);

return false;

}

}

调试结果:返回false时,点击TextView无效。

跟踪touch事件的执行11-08 15:22:57.524: D/MyLinearLayout(22210): ++onInterceptTouchEvent++

11-08 15:22:57.524: D/MyLinearLayout(22210): result:false

11-08 15:22:57.524: D/MyRelativeLayout(22210): ++onInterceptTouchEvent++

11-08 15:22:57.524: D/MyRelativeLayout(22210): result:false

11-08 15:22:57.524: D/MyLinearLayout(22210): ++onInterceptTouchEvent++

11-08 15:22:57.534: D/MyLinearLayout(22210): result:false

11-08 15:22:57.534: D/MyTextView(22210): ++onTouchEvent++

11-08 15:22:57.534: D/MyTextView(22210): result:true

上面日志显示:MyText在ViewTree中处于第4级,第1,2,3级分别是MyLinearLayout, MyRelativeLayout,MyLinearLayout.当发生touch事件时,依次调用第1,2,3级的onInterceptTouchEvent方法处理。onInterceptTouchEvent返回false,就继续调用下级View的onTouchEvent或者下级ViewGoup的onInterceptTouchEvent来处理。

11-08 15:52:56.374: D/MyLinearLayout(23972): ++onInterceptTouchEvent++

11-08 15:52:56.374: D/MyLinearLayout(23972): result:false

11-08 15:52:56.374: D/MyRelativeLayout(23972): ++onInterceptTouchEvent++

11-08 15:52:56.374: D/MyRelativeLayout(23972): result:false

11-08 15:52:56.374: D/MyLinearLayout(23972): ++onInterceptTouchEvent++

11-08 15:52:56.374: D/MyLinearLayout(23972): result:false

11-08 15:52:56.374: D/MyTextView(23972): ++onTouchEvent++

11-08 15:52:56.374: D/MyTextView(23972): result:true

11-08 15:52:56.374: D/MyTextView(23972): ++dispatchTouchEvent++

11-08 15:52:56.374: D/MyTextView(23972): result:true

11-08 15:52:56.374: D/MyLinearLayout(23972): ++dispatchTouchEvent++

11-08 15:52:56.374: D/MyLinearLayout(23972): result:true

11-08 15:52:56.374: D/MyRelativeLayout(23972): ++dispatchTouchEvent++

11-08 15:52:56.374: D/MyRelativeLayout(23972): result:true

11-08 15:52:56.374: D/MyLinearLayout(23972): ++dispatchTouchEvent++

11-08 15:52:56.374: D/MyLinearLayout(23972): result:true

上面日志显示:先是依次调用第1,2,3级的onInterceptTouchEvent方法,以及第4级的onTouchEvent方法;然后是依次调用第4,3,2,1级的dispatchTouchEvent方法。

调用toString()方法标识当前View

11-08 16:08:38.424: D/WatchTvFragment(25580): programListFragment.getView():android.support.v4.app.NoSaveStateFrameLayout{421799f0 V.E..... ........ 427,0-986,703 #7f070077 app:id/program_list_fragment}

11-08 16:09:18.994: D/MyLinearLayout(25580): ++onInterceptTouchEvent++

11-08 16:09:18.994: D/MyLinearLayout(25580): com.tvie.ivideo.pad.live.MyLinearLayout{42188478 V.E..... ........ 0,0-559,703}

11-08 16:09:18.994: D/MyLinearLayout(25580): result:false

11-08 16:09:18.994: D/MyRelativeLayout(25580): ++onInterceptTouchEvent++

11-08 16:09:18.994: D/MyRelativeLayout(25580): com.tvie.ivideo.pad.live.MyRelativeLayout{4216cfd0 V.E..... ........ 0,0-559,43}

11-08 16:09:18.994: D/MyRelativeLayout(25580): result:false

11-08 16:09:18.994: D/MyLinearLayout(25580): ++onInterceptTouchEvent++

11-08 16:09:18.994: D/MyLinearLayout(25580): com.tvie.ivideo.pad.live.MyLinearLayout{4216d1f8 V.E..... ........ 0,0-524,43}

11-08 16:09:18.994: D/MyLinearLayout(25580): result:false

11-08 16:09:18.994: D/MyTextView(25580): ++onTouchEvent++

11-08 16:09:18.994: D/MyTextView(25580): com.tvie.ivideo.pad.live.MyTextView{4216e750 V.ED..C. ...P.... 321,7-412,36 #7f07014b app:id/tomorrow}

11-08 16:09:18.994: D/MyTextView(25580): result:true

11-08 16:09:18.994: D/MyTextView(25580): ++dispatchTouchEvent++

11-08 16:09:18.994: D/MyTextView(25580): com.tvie.ivideo.pad.live.MyTextView{4216e750 V.ED..C. ...P.... 321,7-412,36 #7f07014b app:id/tomorrow}

11-08 16:09:18.994: D/MyTextView(25580): result:true

11-08 16:09:18.994: D/MyLinearLayout(25580): ++dispatchTouchEvent++

11-08 16:09:18.994: D/MyLinearLayout(25580): com.tvie.ivideo.pad.live.MyLinearLayout{4216d1f8 V.E..... ........ 0,0-524,43}

11-08 16:09:18.994: D/MyLinearLayout(25580): result:true

11-08 16:09:18.994: D/MyRelativeLayout(25580): ++dispatchTouchEvent++

11-08 16:09:18.994: D/MyRelativeLayout(25580): com.tvie.ivideo.pad.live.MyRelativeLayout{4216cfd0 V.E..... ........ 0,0-559,43}

11-08 16:09:18.994: D/MyRelativeLayout(25580): result:true

11-08 16:09:18.994: D/MyLinearLayout(25580): ++dispatchTouchEvent++

11-08 16:09:18.994: D/MyLinearLayout(25580): com.tvie.ivideo.pad.live.MyLinearLayout{42188478 V.E..... ........ 0,0-559,703}

11-08 16:09:18.994: D/MyLinearLayout(25580): result:true

上面日志显示:

programListFragment.getView()和R.layout.watchtv_program_list的root layout不是同一个view。

11-08 17:02:35.134: D/MyLinearLayout(25580): ++onInterceptTouchEvent++

11-08 17:02:35.134: D/MyLinearLayout(25580): result:false

11-08 17:02:35.134: D/MyRelativeLayout(25580): ++onInterceptTouchEvent++

11-08 17:02:35.134: D/MyRelativeLayout(25580): result:false

11-08 17:02:35.134: D/MyLinearLayout(25580): ++onInterceptTouchEvent++

11-08 17:02:35.134: D/MyLinearLayout(25580): result:false

11-08 17:02:35.134: D/MyLinearLayout(25580): ++onTouchEvent++

11-08 17:02:35.134: D/MyLinearLayout(25580): result:false

11-08 17:02:35.134: D/MyLinearLayout(25580): ++dispatchTouchEvent++

11-08 17:02:35.134: D/MyLinearLayout(25580): result:false

11-08 17:02:35.134: D/MyRelativeLayout(25580): ++onTouchEvent++

11-08 17:02:35.134: D/MyRelativeLayout(25580): result:false

11-08 17:02:35.134: D/MyRelativeLayout(25580): ++dispatchTouchEvent++

11-08 17:02:35.134: D/MyRelativeLayout(25580): result:false

11-08 17:02:35.134: D/MyLinearLayout(25580): ++onTouchEvent++

11-08 17:02:35.134: D/MyLinearLayout(25580): result:false

11-08 17:02:35.134: D/MyLinearLayout(25580): ++dispatchTouchEvent++

11-08 17:02:35.134: D/MyLinearLayout(25580): result:false

11-08 17:02:35.134: D/WatchTvFragment(25580): ++programTouchListener.onTouch++

当touch事件发生在MyTextView外面的MyLinearLayout时,可以让fragment左右滑动起来。查看日志:MyTextView外部的MyLinearLayout在dispatchTouchEvent方法中返回false。前面,MyTextView在dispatchTouchEvent方法中返回true,导致外面的几个xxxLayout都返回true。

在MyLinearLayout.dispatchTouchEvent方法中指定返回false,那么下级MyTextView的click事件就无效。说明dispatchTouchEvent返回值起着很重要的作用。

11-08 17:27:55.144: D/MyLinearLayout(32017): ++onInterceptTouchEvent++

11-08 17:27:55.144: D/MyLinearLayout(32017): result:false

11-08 17:27:55.144: D/MyRelativeLayout(32017): ++onInterceptTouchEvent++

11-08 17:27:55.144: D/MyRelativeLayout(32017): result:false

11-08 17:27:55.144: D/MyLinearLayout(32017): ++onInterceptTouchEvent++

11-08 17:27:55.144: D/MyLinearLayout(32017): result:false

11-08 17:27:55.144: D/MyTextView(32017): ++onTouchEvent++

11-08 17:27:55.144: D/MyTextView(32017): result:true

11-08 17:27:55.144: D/MyTextView(32017): ++dispatchTouchEvent++

11-08 17:27:55.144: D/MyTextView(32017): result:false

11-08 17:27:55.154: D/MyLinearLayout(32017): ++onTouchEvent++

11-08 17:27:55.154: D/MyLinearLayout(32017): result:false

11-08 17:27:55.154: D/MyLinearLayout(32017): ++dispatchTouchEvent++

11-08 17:27:55.154: D/MyLinearLayout(32017): result:false

11-08 17:27:55.154: D/MyRelativeLayout(32017): ++onTouchEvent++

11-08 17:27:55.154: D/MyRelativeLayout(32017): result:false

11-08 17:27:55.154: D/MyRelativeLayout(32017): ++dispatchTouchEvent++

11-08 17:27:55.154: D/MyRelativeLayout(32017): result:false

11-08 17:27:55.154: D/MyLinearLayout(32017): ++onTouchEvent++

11-08 17:27:55.154: D/MyLinearLayout(32017): result:false

11-08 17:27:55.154: D/MyLinearLayout(32017): ++dispatchTouchEvent++

11-08 17:27:55.154: D/MyLinearLayout(32017): result:false

11-08 17:27:55.154: D/WatchTvFragment(32017): ++programTouchListener.onTouch++

日志第8行:第4级MyTextView的dispatchTouchEvent方法返回false。接着调用第3,2,1级的onTouchEvent, dispatchTouchEvent方法。前面的日志中,dispatchTouchEvent方法返回true,后面的onTouchEvent方法就不再调用。

void android.view.View.setOnClickListener(OnClickListener l)

Register a callback to be invoked when this view is clicked. If this view is not clickable, it becomes clickable.

Parameters:

lThe callback that will run

public void setOnClickListener(OnClickListener l) {

if (!isClickable()) {

setClickable(true);

}

getListenerInfo().mOnClickListener = l;

}

调用setOnClickListener时,如果clickable为false,会被设定为true。

参考链接:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值