关闭

滑动冲突之外部拦截法

标签: androidUI事件滑动冲突View
357人阅读 评论(0) 收藏 举报
分类:

  前一篇文章介绍了Android的事件分发机制,这一篇文章主要说一下View的滑动冲突和使用外部拦截法来解决滑动冲突问题。

  外部拦截就是点击事件先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要就不拦截。

 外部拦截法需要重写父容器的onInterceptTouchEvent()方法。

 这种方法的伪代码如下:

public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted=false;
int x= (int) event.getX();
int y= (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
intercepted=false;//必须不能拦截,否则后续的ACTION_MOME和ACTION_UP事件都会拦截。
break;
case MotionEvent.ACTION_MOVE:
if (父容器需要当前点击事件){
intercepted=true;
}else {
intercepted=false;
}
break;
case MotionEvent.ACTION_UP:
intercepted=false;
break;
default:
break;
}
mLastXIntercept=x;
mLastXIntercept=y;
return intercepted;
}

针对不同的滑动冲突事件,只要修改父容器需要当前点击事件这个条件即可。

下面实现一个类似于水平LinearLayout的东西,它可以水平滑动,我们在他内部添加若干个ListView,这样它本身可以水平滑动,

它内部的ListView可以竖直滑动,这样就出现了滑动冲突。

1.Activity代码如下:创建了3个ListView并把3个Listview添加到了自定义的HorizontalScollViewEx中,HorizontalScollViewEx是父容器,可以实现竖屏滑动。

package com.hybunion.myview;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.hybunion.myview.ui.HorizontalScrollViewEx;
import com.hybunion.myview.utils.MyUtils;
import java.util.ArrayList;

public class ExterScrollActivity extends AppCompatActivity {

private static final String TAG = "ExterScrollActivity";
private HorizontalScrollViewEx mListContainer;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_exter_scroll);
initView();
}

private void initView(){
LayoutInflater inflater=getLayoutInflater();
mListContainer=(HorizontalScrollViewEx) findViewById(R.id.container);
final int screenWidth= MyUtils.getScreenMetrics(this).widthPixels;
final int screenHeight=MyUtils.getScreenMetrics(this).heightPixels;
for (int i=0;i<3;i++){//依次添加View到layout
ViewGroup layout= (ViewGroup) inflater.inflate(R.layout.content_layout,mListContainer,false);
layout.getLayoutParams().width=screenWidth;//设置layout的宽度为屏幕的宽度
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);//添加子View
}
}
private void createList(ViewGroup layout){
ListView listView=(ListView) layout.findViewById(R.id.list);//初始化ListView
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 AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(ExterScrollActivity.this, "click item",
Toast.LENGTH_SHORT).show();
}
});

}

}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#ffffff"
tools:context="com.hybunion.myview.ExterScrollActivity">

<com.hybunion.myview.ui.HorizontalScrollViewEx
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="match_parent">

</com.hybunion.myview.ui.HorizontalScrollViewEx>

</LinearLayout>
content_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:text="TextView"/>

<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff4f7f9"
android:cacheColorHint="#00000000"
android:divider="#dddbdb"
android:dividerHeight="1.0px"
android:listSelector="@android:color/transparent" />

</LinearLayout>

2.HorizontalScollViewEx主要重写了onInterceptTouchEvent()方法,只需要修改如容器需要拦截事件的条件即可。在这里是根据滑动的水平距离比垂直距离大,父容器拦截水平滑动事件,竖直滑动事件才能传递到子View中。

package com.hybunion.myview.ui;
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.widget.Scroller;

/**
* Created by Hbj on 2016/8/18.
*/
public class HorizontalScrollViewEx extends ViewGroup {

private static final String TAG = "HorizontalScrollViewEx";

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;//Scroller
private VelocityTracker mVelocityTracker;//速度追踪

public HorizontalScrollViewEx(Context context) {
super(context);
init();
}

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

public HorizontalScrollViewEx(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

/**
* 初始化
*/
private void init() {
mScroller = new Scroller(getContext());
mVelocityTracker = VelocityTracker.obtain();
}

/**
* 外部拦截法,重写父容器的onInterceptTouchEvent方法,在这里需要拦截时做拦截
* 因为父容器是水平滑动,子View是竖直滑动,所以父容器要拦截水平滑动,子View就
* 可以正常接收竖直滑动信息
*
* @param event
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {

boolean intercepted = false;
int x = (int) event.getX();
int y = (int) event.getY();

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
intercepted = false;//ACTION_DOWN事件不能拦截,一旦拦截就无法再传递给子元素了
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
intercepted = true;
}
break;
case MotionEvent.ACTION_HOVER_MOVE:
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
if (Math.abs(deltaX) > Math.abs(deltaY)) {//拦截水平滑动事件,竖直滑动事件就能传递到子View
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
default:
break;
}
Log.d(TAG, "intercepted=" + intercepted);
mLastX = x;
mLastY = y;
mLastXIntercept = x;
mLastYIntercept = y;

return intercepted;
}

/**
* 父容器拦截了水平方向的滑动,要在这里进行处理
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
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;
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();
break;
default:
break;
}
mLastX = x;
mLastY = y;
return true;
}

/**
* 重写onMeasure()方法对子View进行测量
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int measuredWidth = 0;
int measuredHeight = 0;
final int childCount = getChildCount();//获得子View的个数
measureChildren(widthMeasureSpec, heightMeasureSpec);//测量每个子View
/****测量每个子View,和上面的方法等价***/
/** for (int i=0;i<childCount;i++){ 和上面的方法等价
View childView=getChildAt(i);
measureChild(childView,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 (heightMeasureSpec == MeasureSpec.AT_MOST) {
final View childView = getChildAt(0);
measuredHeight = childView.getMeasuredHeight();
setMeasuredDimension(widthSpaceSize, measuredHeight);
} else if (widthMeasureSpec == 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);
}
}

/**
* 自定义ViewGroup必须重写onLayout()方法实现将子View放到合适的位置
*
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@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();
mChildWidth = childWidth;
childView.layout(childLeft, 0, childLeft + mChildWidth, childView.getMeasuredHeight());
childLeft += childWidth;
}
}
}

/**
* 从某个位置开始滚动
* @param dx
* @param dy
*/
private void smoothScrollBy(int dx,int dy){
mScroller.startScroll(getScrollX(),0,dx,0,500);//仅仅调用startScroll是无法让View滑动的
invalidate();//invalidate()的onDraw()方法会调用computeScroll()方法实现View的重绘
}

/**
* 完成滑动
*/
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
}
}

@Override
protected void onDetachedFromWindow() {
mVelocityTracker.recycle();
super.onDetachedFromWindow();
}
}
效果图如下所示:
                            





 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:6764次
    • 积分:276
    • 等级:
    • 排名:千里之外
    • 原创:22篇
    • 转载:1篇
    • 译文:0篇
    • 评论:1条