View 的事件体系

一、View的基础知识

1)view的位置参数
    1.view的位置主要有四个属性:top.left.right.bottom;(是相对父控件来说的,及相对坐标)。android 3.0增加x,y,(view左上角的坐标)translationX,translationY(左上角相对于父容器的偏移量);及x = left + translationX, y = top + translationY;
    2.在平移的时候,top和left还是原始左上角的位置信息,发生改变的是x, y, translationX, translationY。

2)MotionEvent 和 TouchSlop
    1.典型的事件有:
    ACTION_DOWN-----手指刚接触屏幕;
    ACTION_MOVE-----手指在屏幕上移动;
    ACTION_UP-----松开是一瞬间;

    2.由MotionEvent对象我们可以获取发生的x和y坐标;系统提供了      getX/getY(返回的是相对于当前的view左上角的坐标),getRawX/getRawY(返回的是相对于手机屏幕左上角的坐标)。

    3.TouchSlop是系统所能识别出的被认为是滑动的最小距离。通过ViewConfiguration.get(getContext()).getScaledTouchSlop()获取该常量;

3)VelocityTracker,GestureDetector,Scroller
    1.velcityTracker用于追踪手指在滑动中的速度(包括水平和竖直方向的速度)。
    首先在view的onTouchEvent中追踪当前单击事件的速度及:

VelocityTracker velocityTracker = VelocityTracker.obtain( );
velocityTracker.addmMovement(event);

    获取当前速度:
    //在获取速度之前,必须先计算速度
    velocityTracker.computeCurrentVelocity(1000); 
    int xVelocity = velocityTracker.getXVelocity( );
    int yVelocity = velocityTracker.getYVelocity( );

当不需要的时候,调用clear来重置并回收内存:
velocityTracker.clear(); velocityTracker.recycle();

2.GestureDetector:手势检测,用于检测用户的单机.滑动.长按.双击等行为.

3.Scroller:用于实现View的弹性滑动;需要和computeScroll配合使用才能共同实现弹性滑动。

二.View 的滑动
1)使用scrollTo/scrollBy,scrollBy实际上也是调用了scrollTo方法。

2).使用动画
主要是操作view的translationX和translationY属性来进行平移的。当有单机事件,由于平移了位置,使得当前位置的控件响应不了单机事件,原始位置能响应该事件。属性动画可以解决该问题。android 3.0以下的版本要用其他方法处理。

3).改变布局参数 及改变Layoutparams。

三种滑动的比较:

1.scrollTo/scrollBy:操作简单,适合对view内容的滑动;
2.动画:操作简单,主要使用于没有交互的view和实现复杂的动画效果;
3.改变布局参数:操作稍微复杂,适用于有交互的view。

三.view的事件分发机制
点击事件的分发过程由三个很主要的方法共同实现:
dispatchTouchEvent(MotionEvent ev) :用于进行事件的分发。返回结果受当前view的onTouchEvent()和下级view的dispatchTouchEvent方法的影响;

onInterceptTouchEvent(MotionEvent event)在上述方法内部调用;返回是否拦截当前事件;

onTouchEvent(MotionEvent event) 在dispathTouchEvent方法中调用,用来处理点击事件。

三者的关系:

public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean consume = false;
    if(onInterceptTouchEvent(ev)){
        consume = onTouchEvent(ev);
    }else {
        consume = child.dispatchTouchEvent(ev);
}   
    return consume;
}

四.View的滑动冲突
存在如下的滑动冲突的情况;
1)外部滑动方向和内部滑动方向不一致;
2)外部滑动方向和内部滑动方向一致;
3)如上二者的嵌套;

解决滑动冲突

1)外部拦截法
点击事件都先经过父容器的拦截处理;重写父容器的onInterceptTouchEvent方法。

2)内部拦截法
点击事件都传递给子元素,子元素重写dispatchTouchEvent方法;需要配合requestDisallowInterceptTouchEvent方法才能正常工作。较为复杂,推荐使用外部拦截法。

实例(使用外部拦截法来处理第一种情况)

自定义一个view如下:

package com.example.horizontalscrollviewex;

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;

public class HorizontalScrollViewEx extends ViewGroup {
    private Scroller mScroller;
    private VelocityTracker mVelocityTracker;

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

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

    public HorizontalScrollViewEx(Context context) {
        this(context, null);
    }

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

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

    @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();
                mChildrenSize = childWidth;
                childView.layout(childLeft, 0, childLeft + childWidth, childView.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }

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

    @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 widthSpaceMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightSpaceMode = MeasureSpec.getMode(heightMeasureSpec);

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

    @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;
            break;
        }
        default:
            break;
        }
         mLastX = x;
            mLastY = y;
            mLastXIntercept = x;
            mLastYIntercept = y;

        return intercepted;
    }

    @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;
    }

    private void smoothScrollBy(int dx, int i) {
         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();
    }
}

主程序:

package com.example.horizontalscrollviewex;

import java.util.ArrayList;
import com.example.horizontalscrollviewex.R;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
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 MainActivity extends Activity {
    private HorizontalScrollViewEx mListContainer;

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

    private void initView() {
        LayoutInflater inflater = getLayoutInflater();
        mListContainer = (HorizontalScrollViewEx) findViewById(R.id.container);
        final int screenWidth = getScreenMetrics(this).widthPixels;
        final int screenHeight = getScreenMetrics(this).heightPixels;
        for (int i = 0; i < 3; i++) {
            ViewGroup layout = (ViewGroup) inflater.inflate(
                    R.layout.content_layout, mListContainer, false);
            layout.getLayoutParams().width = screenWidth;
            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, 1, datas);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                Toast.makeText(MainActivity.this, "click item",
                        Toast.LENGTH_SHORT).show();

            }

        });
    }

     public static DisplayMetrics getScreenMetrics(Context context) {
            WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            DisplayMetrics dm = new DisplayMetrics();
            wm.getDefaultDisplay().getMetrics(dm);
            return dm;
        }
}

源码:https://github.com/yang-mr/manage (HorizontalScrollViewEx)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值