自定义控件之滑动

View滑动的方法是现实绚丽的自定义控件的基础

一般来说实现滑动的方法有三种:

  1. 通过View本身提供的scrollTo/scrollBy方法来实现
  2. 通过动画方法
  3. 通过更改View的LayoutParams,实现重新布局来实现。

一、通过View本身提供的scrollTo/scrollBy方法来实现

scrollTo/scrollBy改变的是控件的内容的而不是控件在布局中的位置。例如设置一个长宽高都是100dp的TextView,效果如下:

scrollTo使用方法是:

/**
 * x:是在x轴上的偏移量
 * y:是在y轴上的偏移量
 * 例如从(0,0)偏移到(20,20),那么x=0-20=-20,  y=0-20=-20
 * 所以,如果从上向下,从左向右,那么偏移量x,y为负值,反之为正值。
 */
tv.scrollTo(int x,int y);

scrollBy可以多次偏移,再次偏移的时候就以上次偏移的位置作为起始点。具体效果如上图。

tv.scrollBy(int x,int y);

二、通过动画方式

动画的部分下次会详细描述,这次略过。

三、通过更改View的LayoutParams,实现重新布局来实现。

制作一个翻页的界面,在翻页的时候小圆点跟着变化,效果如下:

查看主要代码:

xml布局:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_test3"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="cn.centran.zx_view_custom.Test3Activity">

    <android.support.v4.view.ViewPager
        android:id="@+id/vp_guide"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="30dp" >

        <LinearLayout
            android:id="@+id/ll_point_group"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >
        </LinearLayout>

        <View
            android:id="@+id/view_red_point"
            android:layout_width="10dp"
            android:layout_height="10dp"
            android:background="@drawable/shape_point_selected" />
    </RelativeLayout>


</RelativeLayout>

重要代码片段:

/**
 * 加载小圆点
 */
for(int i=0;i<imageId.length;i++){
    View point = new View(this);
    //设置圆点背景
    point.setBackgroundResource(R.drawable.shape_point_normal);

    //设置圆点宽高
    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(30,30);
    point.setLayoutParams(params);

    if(i!=0){
        params.leftMargin = 30;
    }
    llPointGroup.addView(point);

}


/**
     * 获取两个圆点之间的距离
     */
    //获取视图树
    final ViewTreeObserver viewTreeObserver = llPointGroup.getViewTreeObserver();
    //添加对试图树中layout过程的监控
    viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
        @Override
        public void onGlobalLayout() {
            System.out.println("layout");
            llPointGroup.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            pointWidth = llPointGroup.getChildAt(1).getLeft() - llPointGroup.getChildAt(0).getLeft();
        }
    });


/**
     * 监控ViewPager的滑动
     */
    mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            System.out.println("position::"+position+"   positionOffset::"+positionOffset+"  positionOffsetPixels::"+positionOffsetPixels);
            int offset = (int) (pointWidth * positionOffset+pointWidth*position);
            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(mRedPoint.getLayoutParams());
            params.leftMargin = offset;
            mRedPoint.setLayoutParams(params);
        }

        @Override
        public void onPageSelected(int position) {

        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });

VelocityTracker介绍:
VelocityTracker是速度跟踪器,用于跟踪手指在滑动过程中的速度,包括竖直速度和水平速度。

//获取速度跟踪器的对象
velocityTracker = VelocityTracker.obtain();


@Override
public boolean onTouchEvent(MotionEvent event) {
    //添加速度跟踪器监控的事件
    velocityTracker.addMovement(event);
    switch (event.getAction()){

        case MotionEvent.ACTION_MOVE:
            /**
             * 设置速度跟踪器的时间段
             * 1000:表示跟踪1s中滑过的像素
             */
            velocityTracker.computeCurrentVelocity(1000);
            xVelocity = velocityTracker.getXVelocity();
            yVelocity = velocityTracker.getYVelocity();

            break;
        case MotionEvent.ACTION_UP:
            Toast.makeText(Test4Activity.this, "水平速度::"+xVelocity+"   竖直速度::"+yVelocity, Toast.LENGTH_SHORT).show();
            break;

        default:break;
    }

    return super.onTouchEvent(event);
}



/**
     * 当不使用的时候,重置以及释放内存
     */
    velocityTracker.clear();
    velocityTracker.recycle();

因为: 速度 = (终点位置 - 起点位置) / 时间段 , 所以速度有正负值,向下/向右滑动,水平/竖直的速度为正值,反之为负值。

GestureDector详解:

手势检测器,虽然android中有onTouch()方法,但是这个方法太简单了,如果需要处理一些复杂的手势,那么就需要用到gestureDector。

GestureDector这个类提供了两个接口和一个类。

  1. OnGestureListener接口
  2. OnDoubleTapListener接口
  3. SimpleOnGestureListener,此类继承了上面两个接口的所有方法,需要哪个方法就实现哪个方法。

    GestureDetector mGestureDetector = new GestureDetector(mGestureListener);
    
     private GestureDetector.OnGestureListener mGestureListener = new GestureDetector.OnGestureListener(){
        ......
    }
    
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return mGestureDetector.onTouchEvent(event);
    }
    

弹性滑动:

上面介绍的滑动方法都是生硬的滑动,这次介绍弹性滑动,弹性滑动差不多有三种方法:

  1. 使用Scroller
  2. 通过动画
  3. 通过延时策略

一、使用Scroller

scroller需要与computeScroll配合使用:

Scroller mScroller = new Scroller(context);

public void smoothScrollTo(int destX,int destY,int durationTime){
    int scrollX = getScrollX();
    int scrollY = getScrollY();
    mScroller.startScroll(scrollX,scrollX,destX,destY,durationTime);
    invalidate();
}

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

当调用smoothScrollTo方法的时候,invalidate()方法会导致View重绘。当View重绘后会在draw方法中调用computeScroll,而computeScroll又会去从Scroller中获取当前的scrollX和scrollY;然后又通过scrollTo实现滑动。接着又调用postInvalidate方法来进行绘制,这次绘制又会导致computeScroll方法的调用(注意此时获取的当前的scrollX和scrollY是上次滑动后的位置),如此反复,直至整个滑动过程结束。

二、通过动画方式

动画的部分下次会详细描述,这次略过。

三、通过延时策略方式

private static final int MESSAGE_TAG = 1;
private int mCount = 0;
private int TOTAL_COUNT = 30;
private int DELAY_TIME = 33;
public Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);

        if(msg.what == MESSAGE_TAG){
            mCount++;
            if(mCount<TOTAL_COUNT){
                int scrollX = -300/TOTAL_COUNT;
                btn_moving.scrollBy(scrollX,0);
                mHandler.sendEmptyMessageDelayed(MESSAGE_TAG,DELAY_TIME);
            }
        }
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值