自定义控件(29)---onTouchEvent与Scroller

来源:互联网 发布:httppost json参数 编辑:程序博客网 时间:2024/05/17 03:59

转自 http://blog.csdn.net/yanzhenjie1003/article/details/53046027

我们定义一个ScrollLayout,然后继承自LinearLayout,在xml中引用,然后在ScrollLayout中放一个TextView,并让内容居中:

<?xml version="1.0" encoding="utf-8"?><com.yanzhenjie.defineview.widget.ScrollLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:gravity="center">    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="按住我拖动试试" /></com.yanzhenjie.defineview.widget.ScrollLayout>

布局就是这样的,根据上面的分析我们实现ScrollLayout的具体代码,请看:

// 手指最后在View中的坐标。private int mLastX;private int mLastY;// 手指按下时View的相对坐标。private int mDownViewX;private int mDownViewY;@Overridepublic boolean onTouchEvent(MotionEvent event) {    // 第一步,记录手指在view的坐标。    int x = (int) event.getRawX();    int y = (int) event.getRawY();    int action = event.getAction();    switch (action) {        case MotionEvent.ACTION_DOWN: {            // 记录View相对于初始位置的滚动坐标。            mDownViewX = getScrollX();            mDownViewY = getScrollY();            // 更新手指此时的坐标。            mLastX = x;            mLastY = y;            return true;        }        case MotionEvent.ACTION_MOVE: {            // 计算手指此时的坐标和上次的坐标滑动的距离。            int dy = y - mLastY;            int dx = x - mLastX;            // 更新手指此时的坐标。            mLastX = x;            mLastY = y;            // 滑动相对距离。            scrollBy(-dx, -dy);            return true;        }        case MotionEvent.ACTION_UP:        case MotionEvent.ACTION_CANCEL: {            scrollTo(mDownViewX, mDownViewY);            return true;        }    }    return super.onTouchEvent(event);}

这里写图片描述

Scroller
Scroller是手指滑动中比较重要的一个辅助类,可以辅助我们完成一些动画参数的计算等,下面把它的几个重要的方法做个简单解释。

Scroller#startScroll(int startX, int startY, int dx, int dy)Scroller#startScroll(int startX, int startY, int dx, int dy, int duration)这俩方法几乎是一样的,用来标记一个View想要从哪里移动到哪里。startX,x方向从哪里开始移动。startY,y方向从哪里开始移动。dx,x方向移动多远。dy,y方向移动多远。duration,这个移动操作需要多少时间执行完,默认是250毫秒。
private Scroller mScroller;private int mLastX;private int mLastY; public ScrollLayout(Context context) {    this(context, null, 0);}public ScrollLayout(Context context, AttributeSet attrs) {    this(context, attrs, 0);}public ScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    mScroller = new Scroller(context);}@Overridepublic boolean onTouchEvent(MotionEvent event) {    int x = (int) event.getRawX();    int y = (int) event.getRawY();    int action = event.getAction();    switch (action) {        case MotionEvent.ACTION_DOWN: {            if (!mScroller.isFinished()) { // 如果上次的调用没有执行完就取消。                mScroller.abortAnimation();            }            mLastX = x;            mLastY = y;            return true;        }        case MotionEvent.ACTION_MOVE: {            int dy = y - mLastY;            int dx = x - mLastX;            mLastX = x;            mLastY = y;            scrollBy(-dx, -dy);            return true;        }        case MotionEvent.ACTION_UP:        case MotionEvent.ACTION_CANCEL: {            // XY都从滑动的距离回去,最后一个参数是多少毫秒内执行完这个动作。            mScroller.startScroll(getScrollX(), getScrollY(), -getScrollX(), -getScrollY(), 1000);            invalidate();            return true;        }    }    return super.onTouchEvent(event);}/** * 这个方法在调用了invalidate()后被回调。 */@Overridepublic void computeScroll() {    if (mScroller.computeScrollOffset()) { // 计算新位置,并判断上一个滚动是否完成。        scrollTo(mScroller.getCurrX(), mScroller.getCurrY());        invalidate();// 再次调用computeScroll。    }}

这里写图片描述


类似ViewPager的翻页效果
content_scroll_pager.xml

<?xml version="1.0" encoding="utf-8"?><com.safly.ui.ScrollPager xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/content_scroll_pager"    android:layout_width="match_parent"    android:layout_height="match_parent">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@android:color/holo_blue_light"        android:gravity="center"        android:minHeight="200dp"        android:orientation="vertical">        <TextView            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:text="第一页" />    </LinearLayout>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@android:color/holo_green_dark"        android:gravity="center"        android:minHeight="200dp"        android:orientation="vertical">        <TextView            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:text="第二页" />    </LinearLayout>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@android:color/holo_orange_dark"        android:gravity="center"        android:minHeight="200dp"        android:orientation="vertical">        <TextView            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:text="第三页" />    </LinearLayout></com.safly.ui.ScrollPager>
/* * Copyright © Yan Zhenjie. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.safly.ui;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.Scroller;/** * Created by Yan Zhenjie on 2016/11/5. */public class ScrollPager extends ViewGroup {    private Scroller mScroller;    // 手指每次移动时需要更新xy,记录上次手指所处的坐标。    private float mLastX;    public ScrollPager(Context context) {        this(context, null, 0);    }    public ScrollPager(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public ScrollPager(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        mScroller = new Scroller(context);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        float x = event.getRawX();        int action = event.getAction();        switch (action) {            case MotionEvent.ACTION_DOWN:                if (!mScroller.isFinished()) { // 如果上次的调用没有执行完就取消。                    mScroller.abortAnimation();                }                mLastX = x;                return true;            case MotionEvent.ACTION_MOVE:                int dxMove = (int) (mLastX - x);                scrollBy(dxMove, 0);                mLastX = x;                return true;            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_CANCEL: {                // 当手指抬起时,第几屏占的比例大就去第几屏。(这里在除的时候+view一半宽是因为滑动到0.6的时候,不到1,结果就是0                // 其实按照惯性应该是1,所以我们给它补上一般的屏,这样相当于4设5入。)                int sonIndex = (getScrollX() + getWidth() / 2) / getWidth();                // 如果滑动页面超过当前页面数,那么把屏index定为最大页面数的index。                int childCount = getChildCount();                if (sonIndex >= childCount)                    sonIndex = childCount - 1;                // 现在滑动的相对距离。                int dx = sonIndex * getWidth() - getScrollX();                // Y方向不变,X方向到目的地。                mScroller.startScroll(getScrollX(), 0, dx, 0, 500);                invalidate();                break;            }        }        return super.onTouchEvent(event);    }    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());            invalidate();        }    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int childCount = getChildCount();        // 在Layout 子view之前测量子view大小,在onLayout的时候才能调用getMeasuredWidth()和getMeasuredHeight()。        for (int i = 0; i < childCount; i++) {            View childView = getChildAt(i);            measureChild(childView, widthMeasureSpec, heightMeasureSpec);        }    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        if (changed) {            int childCount = getChildCount();            for (int i = 0; i < childCount; i++) {                View childView = getChildAt(i);                int childW = childView.getMeasuredWidth();                // 把所有子view放在水平方向,依次排开。                // left:  0, w, 2w, 3w..                // top:   0...                // right: w, 2w, 3w...                // topL   h...                childView.layout(i * childW, 0, childW * i + childW, childView.getMeasuredHeight());            }        }    }}

这里写图片描述

0 0
原创粉丝点击