关于fragment内的监听事件

本文探讨在Android的Fragment中如何实现触摸监听,特别是左右滑动功能。作者遇到Fragment没有提供监听touch事件的问题,通过在FragmentActivity中设置ontouch接口并在Fragment中实现接口来解决,但在特定场景下会出现多个Fragment监听事件冲突。为解决这个问题,作者提出使用数组存储监听事件以及自定义FlingLinearLayout,重写onInterceptTouchEvent()方法,实现了更精确的滑动控制。然而,这种方法在包含ListView时可能导致滑动和点击事件的混淆,需要进一步优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用fragment+radiobutton实现导航标签页,遇到一个问题,fragment没有提供监听touch事件的方法,所以无法实现左右滑动的功能。在网上查询了好久,终于找到了解决方法。

方法一:

是在FragmentActivity中提供一个ontouch接口,然后在fragment中实现接口。具体代码参考:Android Fragment中监听事件http://blog.youkuaiyun.com/jdsjlzx/article/details/20695279。

修改注册方法registerMyOnTouchListener写在fragment的生命周期onResume中,注销方法 unregisterMyOnTouchListener写在onPause中。

但是这种方法也存在一些问题。


当代码结构如上图所示时,在第二层的fragment实现左右滑动切换第三层的fragment页面,当点击home键离开程序执行onPause方法,再回到程序时,第二层的三个fragment都会执行onResume方法。导致三个fragment都启动了监听事件,滑动第二层的fragment1时,fragment2和fragment3会一起滑动。


点击home键离开程序的生命周期:

返回程序的生命周期:


同时注册了三个监听事件,导致滑动一个fragment时,另外两个fragment也执行了滑动操作。


解决方法:

使用数组存储监听事件。


<span style="font-size:14px;">/** 
* 以下的几个方法用来,让fragment能够监听touch事件 
*/  
<span style="white-space: pre;">	</span>private MyOntouchListener[] mol = new MyOntouchListener[3];
  
<span style="white-space: pre;">	</span>@Override
<span style="white-space: pre;">	</span>public boolean onTouchEvent(MotionEvent event) {
<span style="white-space: pre;">		</span>// TODO Auto-generated method stub
<span style="white-space: pre;">		</span>//获取RadioGroup当前Tab的值-1得到对应数组里的touch监听事件。</span>
<span style="font-size:14px;"><span style="white-space: pre;">		</span>//getCurrentTab()方法为radiobutton+fragment时使用的适配器里自定义的方法,用于获取当前Tab
<span style="white-space: pre;">		</span>if (mol[tabAdapter.getCurrentTab() - 1] != null) {
<span style="white-space: pre;">			</span>MyOntouchListener listener = mol[tabAdapter.getCurrentTab() - 1];
<span style="white-space: pre;">			</span>listener.onTouchEvent(event);
<span style="white-space: pre;">		</span>}

<span style="white-space: pre;">		</span>return super.onTouchEvent(event);
<span style="white-space: pre;">	</span>}
  
<span style="white-space: pre;">	</span>public void registerListener(MyOntouchListener listener, int i) {

<span style="white-space: pre;">		</span>mol[i] = listener;
<span style="white-space: pre;">	</span>}


<span style="white-space: pre;">	</span>public void unRegisterListener(MyOntouchListener listener, int i) {

<span style="white-space: pre;">		</span>mol[i] = null;
<span style="white-space: pre;">	</span>}
  
<span style="white-space: pre;">	</span>public interface MyOntouchListener {
<span style="white-space: pre;">		</span>public void onTouchEvent(MotionEvent event);
<span style="white-space: pre;">	</span>}</span>

fragment内注册和注销代码:

		<span style="font-size:14px;">// 获取activity的onTouchEvent方法
		mGestureDetector = new GestureDetector(activity, this);
		listener = new FragmentTab.MyOntouchListener() {

			@Override
			public void onTouchEvent(MotionEvent event) {
				// TODO Auto-generated method stub
				mGestureDetector.onTouchEvent(event);
			}
		};</span>


<span style="font-size:14px;"><span style="font-size:18px;">	// 利用生命周期设置只有显示的fragment能注册滑动监听事件

	@Override
	public void onResume() {
		// TODO Auto-generated method stub
		((FragmentTab) activity).registerDispatch(dis, 0);
		((FragmentTab) activity).registerListener(listener, 0);
		super.onResume();


	}

	@Override
	public void onPause() {
		// TODO Auto-generated method stub
		((FragmentTab) activity).unRegisterDispatch(dis, 0);
		((FragmentTab) activity).unRegisterListener(listener, 0);
		super.onPause();

	}</span></span>


方法二:     

这种方法虽然实现了左右滑动,并且区别了fragment的监听事件。

但还是存在一些问题:

比如在包含listview时,滑动距离过小会导致左右滑动事件和点击事件一起触发,距离过大又会影响用户体验。

原因是左右滑动没有消费掉当前点击事件,而是继续传递给子控件,具体原因可能是onTouchEvent()用于处理事件,返回值决定当前控件是否消费(consume)了这个事件,也就是说在当前控件在处理完Touch事件后,是否还允许Touch事件继续向上(父控件)传递,一但返回True,则父控件不用操心自己来处理Touch事件。而左右滑动应该是先判断父控件是否滑动,不滑动则向下(子控件)传递touch事件。这时应该使用onInterceptTouchEvent()来处理。参考文章:Android-onInterceptTouchEvent()和onTouchEvent()总结http://blog.youkuaiyun.com/lvxiangan/article/details/9309927。

但是onInterceptTouchEvent()是ViewGroup的一个方法,只有包含子view的控件有这个方法,如Linearlayout,没有子view的控件没有这个方法,如TextView。

那么在fragment中没有onInterceptTouchEvent(),于是我自定义了一个FlingLinearLayout继承自Linearlayout。重写其中的onInterceptTouchEvent()方法,并自定义左右滑动方法Fling。具体FlingLinearLayout类的代码如下:

<span style="font-size:14px;">public class FlingLinearLayout extends LinearLayout {
	private int mLastMotionX;
	private int mLastMotionY;
	private RadioGroup rgs;
	private RadioButton rb;
<span style="white-space:pre">	</span>//获取对应的RadioGroup
	public void setRgs(RadioGroup rgs) {
		this.rgs = rgs;
	}

	public FlingLinearLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public FlingLinearLayout(Context context) {
		super(context);
	}

	public FlingLinearLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		int x = (int) ev.getRawX();
		int y = (int) ev.getRawY();
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			// 首先拦截down事件,记录x坐标
			mLastMotionX = x;
			mLastMotionY = y;
			break;
		case MotionEvent.ACTION_MOVE:
			// deltaX > 0 是向右运动,< 0是向左运动
			int deltaX = x - mLastMotionX;
			int deltaY = y - mLastMotionY;
			if (Fling(deltaX, deltaY)) {
				return true;
			}
			break;
		case MotionEvent.ACTION_UP:
		case MotionEvent.ACTION_CANCEL:
			break;
		default:
			break;
		}
		return false;
	}

</span>
<span style="font-size:14px;"><span style="white-space:pre">	</span>//左右滑动方法
	private boolean Fling(int deltaX, int deltaY) {
		for (int i = 0; i < rgs.getChildCount(); i++) {
			int radioButtonId = rgs.getChildAt(i).getId();
			if (rgs.getCheckedRadioButtonId() == radioButtonId) {
				int next = (i + 1) % rgs.getChildCount();
				if (next == 0) {
					next = i;
				}
				int pre;
				if (i == 0) {
					pre = 0;
				} else {
					pre = i - 1;
				}
				if (deltaX < -30 && Math.abs(deltaY) < 30) {
					rb = (RadioButton) findViewById(rgs.getChildAt(next)
							.getId());
					rb.setChecked(true);
					return true;
				} else if (deltaX > 30 && Math.abs(deltaY) < 30) {
					rb = (RadioButton) findViewById(rgs.getChildAt(pre).getId());
					rb.setChecked(true);
					return true;
				}
			}

		}
		return false;
	}
}</span>

应用中实现的代码:

	<span style="font-size:14px;">fll = (FlingLinearLayout) view.findViewById(R.id.<span style="font-family: verdana, Arial, Helvetica, sans-serif;">FlingLinearLayout</span>);
		pp_rgs = (RadioGroup) view.findViewById(R.id.pp_tabs_rg);
		fll.setRgs(pp_rgs);</span>

xml布局:

<span style="font-size:14px;"><?xml version="1.0" encoding="utf-8"?>
<com.example.utils.FlingLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/FlingLinearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    android:orientation="vertical" >

    <!-- 使用fragment实现tabhost功能 -->

    <HorizontalScrollView
        android:id="@+id/pp_radio_scroll"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:scrollbars="none" >

        <RadioGroup
            android:id="@+id/pp_tabs_rg"
            android:layout_width="fill_parent"
            android:layout_height="48dp"
            android:orientation="horizontal" >

            <RadioButton
                android:id="@+id/pp_tab_rb_all"
                android:layout_width="35dp"
                android:layout_height="36dp"
                android:layout_marginBottom="12dp"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="22dp"
                android:background="@drawable/pp_title_redline"
                android:button="@null"
                android:checked="true"
                android:gravity="center"
                android:singleLine="true"
                android:text="全部"
                android:textColor="@drawable/pp_fragment_text"
                android:textSize="14sp" />
        </RadioGroup>
    </HorizontalScrollView>

    <FrameLayout
        android:id="@+id/pp_tab_content"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:background="#f2f2f2" />

</com.example.utils.FlingLinearLayout></span>

最后问题完美解决了。而且不需要使用最上面说的使用接口获取activity的touch监听。只需要在xml文件中把RadioGroup放在FlingLinearLayout布局里面就可以了。具体要实现什么功能可以在FlingLinearLayout中自己写。

文章中也许有疏漏的地方,欢迎大家指出我的问题,或者大家有什么建议都可以和我交流,希望能在交流中共同进步。O(∩_∩)O~







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值