Android滑动冲突解决的实现
在之前的章节Anddroid滑动冲突中我们分析了Android的滑动冲突的解决办法,本节贴出实现的代码供参考,代码中注释的比较详细,就不再过多介绍。
在这段代码中,我们自定义了一个可以水平滚动的ViewGroup,然后在该ViewGroup中放入listview以制造滑动冲突,自定义控件如下代码所示:
package com.example.zhangyi.hdct;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
/**
* Created by ZhangYi on 2016/1/28.
*/
public class HorizontalView extends ViewGroup{
private Scroller mScroller;//滑动
private VelocityTracker mVelocityTracker;//速度追踪
private int mLastInterceptX = 0;
private int mLastInterceptY = 0;
private int mLastTouchX = 0;
private int mLastTouchY = 0;
private int mChildWidth; //记录child的宽度
private int mChildSize; //记录child的个数
private int mChildIndex; //记录当前chld的偏移量
public HorizontalView(Context context) {
super(context);
init();
}
public HorizontalView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
if(mScroller == null){
mScroller = new Scroller(getContext());
mVelocityTracker = VelocityTracker.obtain();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
measureChildren(widthMeasureSpec,heightMeasureSpec);
int childCount = getChildCount();
if(childCount == 0){
setMeasuredDimension(0,0);
}else if(heightSpecMode == MeasureSpec.AT_MOST && widthSpecMode == MeasureSpec.AT_MOST){
//即如果宽高都是wrap_content,那么测量的宽应该是单个子view的宽度乘以个数,高为单个view的高度
View view = getChildAt(0);
heightSpecSize = view.getMeasuredHeight();
widthMeasureSpec = view.getMeasuredWidth()*childCount;//假定所有子view的宽度相同
setMeasuredDimension(widthSpecSize,heightSpecSize);
}else if(heightSpecMode ==MeasureSpec.AT_MOST){
//高是wrap_content,那么高度应该是单个子view的高度,宽度为屏幕宽度
View view = getChildAt(0);
heightSpecSize = view.getMeasuredHeight();
setMeasuredDimension(widthSpecSize,heightSpecSize);
}else if(widthSpecMode == MeasureSpec.AT_MOST){
//宽是wrap_content,那么高度应该是屏幕高度,宽度为子view的宽度乘以个数
View view = getChildAt(0);
widthSpecSize = view.getMeasuredWidth()*childCount;
setMeasuredDimension(widthSpecSize,heightSpecSize);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//放置元素
int childLeft = 0;
int childCount = getChildCount();
mChildSize = childCount;
for (int i = 0 ; i < childCount ; i ++ ){
View view = getChildAt(i);
int childWidth = view.getMeasuredWidth();
mChildWidth = childWidth;
view.layout(childLeft,0,childLeft+childWidth,view.getMeasuredHeight());
childLeft += childWidth;
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercept = false;
int x = (int) ev.getX();//获取当前手指位置的X坐标
int y = (int) ev.getY();//获取当前手指位置的Y坐标
switch(ev.getAction()){
case MotionEvent.ACTION_DOWN:
//当点击事件发生时,首先判断之前的滑动过程是否结束
intercept = false;
if(!mScroller.isFinished()){
mScroller.abortAnimation();
intercept = true;
}
break;
case MotionEvent.ACTION_MOVE:
//判断水平滑动和垂直方向滑动的距离,如果水平距离大于垂直距离,进行拦截
int deltaX = x - mLastInterceptX;
int deltaY = y - mLastInterceptY;
if(Math.abs(deltaX) > Math.abs(deltaY)){
//拦截,然后会调用OnTouchEvent
intercept = true;
}else{
intercept = false;
}
break;
case MotionEvent.ACTION_UP:
intercept = false;
break;
}
mLastInterceptX = x;
mLastInterceptY = y;
return intercept;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
/**
* 对水平滑动时间进行处理
* 考虑到两种情况:
* 1.水平滑动速度比较快,则直接滑动到第二屏
* 2.水平滑动距离大于屏幕宽度一半,则也直接滑动到第二屏,否则还是当前屏
*/
mVelocityTracker.addMovement(event);
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();//滑动的距离,相对于原始位置
mVelocityTracker.computeCurrentVelocity(1000);
float xVelocity = mVelocityTracker.getXVelocity();
//如果水平速度大于50,则滑动到下一页或者上一页
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,mChildSize-1));
int dx = mChildIndex*mChildWidth-scrollX;
smoothScrollBy(dx,0);
mVelocityTracker.clear();
break;
}
mLastTouchX = x;
mLastTouchY = y;
return true;
}
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());
postInvalidateDelayed(20);
}
}
@Override
protected void onDetachedFromWindow() {
mVelocityTracker.recycle();
super.onDetachedFromWindow();
}
}
然后我们在Activity的布局中使用它,布局文件如下所示:
<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="horizontal"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<com.example.zhangyi.hdct.HorizontalView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/darker_gray">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/testInfo"></ListView>
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/testInfo"></ListView>
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/testInfo"></ListView>
</com.example.zhangyi.hdct.HorizontalView>
</LinearLayout>
其中的 android:entries=”@array/testInfo” 中的testInfo为我们在/res/values/arrays.xml中定义的数组:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="testInfo">
<item>hello world</item>
<item>hello world</item>
<item>hello world</item>
<item>hello world</item>
<item>hello world</item>
<item>hello world</item>
<item>hello world</item>
<item>hello world</item>
<item>hello world</item>
<item>hello world</item>
<item>hello world</item>
<item>hello world</item>
<item>hello world</item>
</string-array>
</resources>