view滑动冲突的解决方案

在上一节,我们讲了view滑动冲突的解决方案,在这一节中,我们将要对此进行实践。我们来实现类似于在viewpage中嵌套listview的滑动效果,为了展示冲突效果我们写一个类似于viewpage的空间HorizontalScrollerViewEX,为了实现viewpage的效果,我们自定义一个类似于横向linnerlayout的控件,我们在他的内部添加很多个listview,这样它既可以横向滑动,又可以竖直滑动,我们来解决冲突问题。

下面是自定义view:

package com.example.animotiontest.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Scroller;

public class HorizontalScrollerViewEX extends LinearLayout {

	private static final String TAG = "HorizontalScrollerViewEX";
	private int mChildrenSize;
	private int mChildWith;
	private int mChildIndex;

	// 分别记录上次滑动的坐标
	private int mLastX = 0;
	private int mLastY = 0;
	// 分别记录上次滑动的坐标(onInterceptTouchEvent)
	private int mLastXIntercept = 0;
	private int mLastYIntercept = 0;

	private Scroller mScroller;
	private VelocityTracker mVelocityTracker;
	private Context context;

	public HorizontalScrollerViewEX(Context context) {
		this(context, null);
		this.context = context;
	}

	public HorizontalScrollerViewEX(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

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

	private void init() {
		mScroller = new Scroller(context);
		mVelocityTracker = VelocityTracker.obtain();
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {

		boolean intercepted = false;
		int x = (int) ev.getX();
		int y = (int) ev.getY();
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			intercepted = false;
			if (!mScroller.isFinished()) {
				mScroller.abortAnimation();
				intercepted = true;
			}

			break;
		case MotionEvent.ACTION_MOVE:
			int deltaX = x - mLastXIntercept;
			int deltaY = y - mLastYIntercept;
			// 取滑动距离的绝对值
			if (Math.abs(deltaX) > Math.abs(deltaY)) {
				intercepted = true;
			} else {
				intercepted = false;
			}
			break;
		case MotionEvent.ACTION_UP:
			intercepted = false;
		default:
			break;
		}

		Log.d(TAG, "intercepted=" + intercepted);
		mLastX = x;
		mLastY = y;
		mLastXIntercept = x;
		mLastYIntercept = y;
		return intercepted;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		int x = (int) event.getX();

		int y = (int) event.getY();
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (!mScroller.isFinished()) {
				mScroller.abortAnimation();
			}
			break;
		case MotionEvent.ACTION_MOVE:
			int deltax = x - mLastX;
			int deltay = y - mLastY;
			scrollBy(-deltax, 0);
			break;
		case MotionEvent.ACTION_UP:
			int scrollX = getScrollX();
			int scrollToChildIndex = scrollX / mChildWith;
			mVelocityTracker.computeCurrentVelocity(1000);
			float xVelocity = mVelocityTracker.getXVelocity();
			if (Math.abs(xVelocity) >= 50) {
				mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;

			} else {
				mChildIndex = (scrollX + mChildWith / 2) / mChildWith;
			}
			mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));
			int dx = mChildIndex * mChildWith - scrollX;
			smoothScrollTo(dx, 0);
			mVelocityTracker.clear();
		default:
			break;
		}
		mLastX = x;
		mLastY = y;
		return true;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int measuredWidth = 0;
		int measuredHeight = 0;
		final int childCount = getChildCount();
		measureChildren(widthMeasureSpec, heightMeasureSpec);
		int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
		int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
		int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
		int heightSpaceMode = MeasureSpec.getMode(heightMeasureSpec);
		if (childCount == 0) {
			setMeasuredDimension(0, 0);
		} else if (heightSpaceMode == MeasureSpec.AT_MOST) {
			final View childView = getChildAt(0);
			measuredHeight = childView.getMeasuredWidth();
			setMeasuredDimension(widthMeasureSpec,
					childView.getMeasuredHeight());

		} else if (widthSpecMode == MeasureSpec.AT_MOST) {
			final View childView = getChildAt(0);
			measuredWidth = childView.getMeasuredWidth() * childCount;
			setMeasuredDimension(measuredWidth, heightSpaceSize);
		} else {
			final View childView = getChildAt(0);
			measuredWidth = childView.getMeasuredWidth() * childCount;
			measuredHeight = childView.getMeasuredHeight();
			setMeasuredDimension(measuredWidth, measuredHeight);
		}
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		int childLeft = 0;
		final int childCount = getChildCount();
		mChildrenSize = childCount;
		for (int i = 0; i < childCount; i++) {
			final View childView = getChildAt(i);
			if (childView.getVisibility() != View.GONE) {
				final int childWidth = childView.getMeasuredWidth();
				mChildWith = childWidth;
				childView.layout(childLeft, 0, childLeft,
						childView.getMeasuredHeight());
				childLeft += childWidth;
			}
		}
	}

	/**
	 * 缓慢滑动到指定位置
	 * 
	 * @param destX
	 * @param destY
	 */
	private void smoothScrollTo(int destX, int destY) {
		mScroller.startScroll(getScrollX(), 0, destX, 500);
		invalidate();
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
			postInvalidate();
		}
		// TODO Auto-generated method stub
		super.computeScroll();
	}

	@Override
	protected void onDetachedFromWindow() {
		mVelocityTracker.recycle();
		super.onDetachedFromWindow();
	}
}

从上面的代码来看,我们是使用外部拦截法实现了冲突事件的解决,在滑动过程中,当水平距离大时,就判断为水平滑动,为了能够水平滑动,所以让父容器拦截事件,而竖直距离大时父容器就不拦截事件,于是事件就传递给了listview,如此listview也实现了上下滑动,所以就解决了滑动冲突问题。

我们继续来看看activity中代码的实现

package com.example.animotiontest.activity;

import java.util.ArrayList;

import com.example.animotiontest.R;
import com.example.animotiontest.uitls.MyUtils;
import com.example.animotiontest.view.HorizontalScrollerViewEX;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class DemoActivity1 extends Activity {

	private static final String TAG = "DemoActivity1";
	private HorizontalScrollerViewEX mListContainer;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_demo1);
		Log.d(TAG, "oncreate");
		initView();
	}

	private void initView() {
		LayoutInflater inflater = getLayoutInflater();
		mListContainer = (HorizontalScrollerViewEX) findViewById(R.id.container);
		final int screenWith = MyUtils.getScreenMetrics(this).widthPixels;
		final int screenheight = MyUtils.getScreenMetrics(this).heightPixels;
		for (int i = 0; i < 3; i++) {
			ViewGroup layout = (ViewGroup) inflater.inflate(
					R.layout.content_layout, mListContainer, false);
			layout.getLayoutParams().width = screenWith;
			TextView textView = (TextView) layout.findViewById(R.id.title);
			textView.setText("page" + (i + 1));
			layout.setBackgroundColor(Color
					.rgb(255 / (i + 1), 255 / (i + 1), 0));
			createList(layout);
			mListContainer.addView(layout);
		}
	}

	private void createList(ViewGroup layout) {
		ListView listView = (ListView) layout.findViewById(R.id.list);
		ArrayList<String> datas = new ArrayList<String>();
		for (int i = 0; i < 50; i++) {
			datas.add("name" + i);
		}

		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
				R.layout.content_list_item, R.id.name, datas);
		listView.setAdapter(adapter);
		listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				// TODO Auto-generated method stub
				Toast.makeText(DemoActivity1.this, "click item",
						Toast.LENGTH_SHORT).show();

			}
		});
	}
}



上面的代码很简单,就是把三个listview添加到我们自定义的HorizontalScrollerViewEX布局里边,在这里HorizontalScrollerViewEX是父容器,而listview是子view;我们上边采用的室外不拦截法实现的滑动区分;
下面来看看获取屏幕高和宽的工具类
public static DisplayMetrics getScreenMetrics(Context context) {
		WindowManager wm = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		DisplayMetrics dm = new DisplayMetrics();
		wm.getDefaultDisplay().getMetrics(dm);
		return dm;
	}

如果采用内部拦截法也是可以的,按照前面对内部拦截法的分析,我们只需要修改listview的dispatchtouchevent方法中的父容器的拦截

package com.example.animotiontest.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ListView;

public class ListViewEx extends ListView {

	private static final String TAG = "ListViewEx";

	private HorizontalScrollerViewEX2 mHorizontalScrollViewEx2;

	// 分别记录上次滑动的坐标
	private int mLastX = 0;
	private int mLastY = 0;

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

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

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

	public void setHorizontalScrollViewEx2(
			HorizontalScrollerViewEX2 horizontalScrollViewEx2) {
		mHorizontalScrollViewEx2 = horizontalScrollViewEx2;
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		int x = (int) ev.getX();
		int y = (int) ev.getY();

		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(true);

			break;
		case MotionEvent.ACTION_MOVE:
			int deltaX = x - mLastX;
			int deltaY = y - mLastY;
			if (Math.abs(deltaX) > Math.abs(deltaY)) {
				mHorizontalScrollViewEx2
						.requestDisallowInterceptTouchEvent(false);
			}
			break;
		case MotionEvent.ACTION_UP:
			break;
		default:
			break;
		}
		mLastX = x;
		mLastY = y;
		return super.dispatchTouchEvent(ev);
	}
}

修改后的
HorizontalScrollerViewEX2 为
package com.example.animotiontest.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.MeasureSpec;
import android.widget.Scroller;

public class HorizontalScrollerViewEX2 extends ViewGroup {
	private static final String TAG = "HorizontalScrollViewEx2";

	private int mChildrenSize;
	private int mChildWidth;
	private int mChildIndex;

	// 分别记录上次华东的坐标
	private int mLastX = 0;
	private int mLastY = 0;
	// 分别记录上次滑动的坐标(onInterceptTouchEvent)
	private int mLastXIntercept = 0;
	private int mLastYIntercept = 0;

	private Scroller mScroller;
	private VelocityTracker mVelocityTracker;
	private Context context;

	public HorizontalScrollerViewEX2(Context context) {

		this(context, null);
	}

	public HorizontalScrollerViewEX2(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public HorizontalScrollerViewEX2(Context context, AttributeSet attrs,
			int defStyle) {
		super(context, attrs, defStyle);
		this.context = context;
		init();

	}

	private void init() {
		mScroller = new Scroller(context);
		mVelocityTracker = VelocityTracker.obtain();
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		int x = (int) ev.getX();
		int y = (int) ev.getY();
		int action = ev.getAction();
		if (action == MotionEvent.ACTION_DOWN) {
			mLastX = x;
			mLastY = y;
			if (!mScroller.isFinished()) {
				mScroller.abortAnimation();
				return true;
			}
			return false;
		} else {
			return true;
		}

	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		Log.d(TAG, "onTouchEvent action:" + event.getAction());
		mVelocityTracker.addMovement(event);
		int x = (int) event.getX();
		int y = (int) event.getY();

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (!mScroller.isFinished()) {
				mScroller.abortAnimation();
			}
			break;
		case MotionEvent.ACTION_MOVE:
			int deltaX = x - mLastX;
			int deltaY = y - mLastY;
			scrollBy(-deltaX, 0);
			break;
		case MotionEvent.ACTION_UP:
			int scrollX = getScrollX();
			int scrollToChildIndex = scrollX / mChildWidth;
			Log.d(TAG, "current index:" + scrollToChildIndex);
			mVelocityTracker.computeCurrentVelocity(1000);
			float xVelocity = mVelocityTracker.getXVelocity();
			if (Math.abs(xVelocity) >= 50) {
				mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;
			} else {
				mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;
			}
			mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));
			int dx = mChildIndex * mChildWidth - scrollX;
			smoothScrollBy(dx, 0);
			mVelocityTracker.clear();
			Log.d(TAG, "index:" + scrollToChildIndex + " dx:" + dx);
			break;
		default:
			break;
		}
		mLastX = x;
		mLastY = y;
		return true;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int measuredWidth = 0;
		int measuredHeight = 0;
		final int childCount = getChildCount();
		measureChildren(widthMeasureSpec, heightMeasureSpec);

		int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
		int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
		int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
		int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
		if (childCount == 0) {
			setMeasuredDimension(0, 0);
		} else if (heightSpecMode == MeasureSpec.AT_MOST) {
			final View childView = getChildAt(0);
			measuredHeight = childView.getMeasuredHeight();
			setMeasuredDimension(widthSpaceSize, childView.getMeasuredHeight());
		} else if (widthSpecMode == MeasureSpec.AT_MOST) {
			final View childView = getChildAt(0);
			measuredWidth = childView.getMeasuredWidth() * childCount;
			setMeasuredDimension(measuredWidth, heightSpaceSize);
		} else {
			final View childView = getChildAt(0);
			measuredWidth = childView.getMeasuredWidth() * childCount;
			measuredHeight = childView.getMeasuredHeight();
			setMeasuredDimension(measuredWidth, measuredHeight);
		}
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		Log.d(TAG, "width:" + getWidth());
		int childLeft = 0;
		final int childCount = getChildCount();
		mChildrenSize = childCount;

		for (int i = 0; i < childCount; i++) {
			final View childView = getChildAt(i);
			if (childView.getVisibility() != View.GONE) {
				final int childWidth = childView.getMeasuredWidth();
				mChildWidth = childWidth;
				childView.layout(childLeft, 0, childLeft + childWidth,
						childView.getMeasuredHeight());
				childLeft += childWidth;
			}
		}

	}

	private void smoothScrollBy(int dx, int dy) {
		mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
		invalidate();
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
			postInvalidate();
		}
	}

	@Override
	protected void onDetachedFromWindow() {
		mVelocityTracker.recycle();
		super.onDetachedFromWindow();
	}
}


以上就是内部拦截的实现 其中	mScroller.abortAnimation();不是必须的,这里是为了优化滑动体验问题。从上可以看出来内部拦截发烧为复杂一些,推荐使用外部拦截法。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值