滑动过渡之Scroller

来源:互联网 发布:怎样收藏淘宝店铺 编辑:程序博客网 时间:2024/05/22 00:24

Scroller:手指滑动中比较重要的一个辅助类,辅助完成一些动画参数的计算等。看到Scroller你可能感到陌生,但我们每个人都用过它,因为viewpage,listvew等控件源码都会用到它。而本文主要实现一个简单的viewpage效果。听说先上效果图不被打:
这里写图片描述
首先介绍scroller的几个重要方法:
scrollTo(x,y) : 让view 滑动到初始位置(x,y)处。(字面意思To: 就是到的意思,到了就不会再滚动)

scrollBy(): 让view 滑动到相对于view当前位置(x,y)处。(字面意思By: 就是步的意思,一步一步的滚动)

getScrollX() : 获取view 相对于初始位置的 x 方向的滚动偏移量。

getScrollY() : 获取view 相对于初始位置的 y 方向的滚动偏移量。
下面看张动态图你就会明白scrollTo 和scrollBy的区别:
这里写图片描述
好了,下边开始上代码

 <FrameLayout     android:layout_width="match_parent"     android:layout_height="200dp">  <lzh.myview.view.ScrollerView      android:id="@+id/slv"      android:layout_width="match_parent"      android:layout_height="200dp">  </lzh.myview.view.ScrollerView>  <LinearLayout      android:id="@+id/ll_point"      android:layout_width="wrap_content"      android:orientation="horizontal"      android:gravity="center"      android:layout_gravity="center_horizontal|bottom"      android:layout_marginBottom="10dp"      android:layout_height="wrap_content"></LinearLayout> </FrameLayout>

ScrollerView为我们实现翻页效果的自定义view,LinearLayout为指示器小圆圈的布局。

public class ScrollerView extends ViewGroup {   private Scroller scroller;  // 用于滚动操作的实例   private int mixTouchSlip; // 判断是否滑动的临界值    private  float xDown;   // 手指按下时,X 的屏幕坐标    private float  xMove;    // 移动时,X的屏幕坐标    private float  xLastMove ; // 上一次 move事件的屏幕坐标    private int leftBorder;    //界面可滚动的左边界    private int rightBorder;//    界面可滚动的右边界    private  int count ;  // 图片的数量    private  int position ;  // 当前选中的位置    ViewGroup.LayoutParams  params;    private Context mContext;   private OnPayListener listener;    public ScrollerView(Context context, AttributeSet attrs) {        super(context, attrs);        mContext = context;        scroller = new Scroller(context);        mixTouchSlip =ViewConfiguration.get(context).getScaledPagingTouchSlop();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int childCount  = getChildCount();        for (int i = 0; i < childCount; i++) {            View childView = getChildAt(i);            // 为每一个子控件测量大小            measureChild(childView, widthMeasureSpec, heightMeasureSpec);        }    }    @Override    protected void onLayout(boolean b, int l, int i1, int i2, int i3) {        if (b) {            int childCount = getChildCount();            for (int i = 0; i < childCount; i++) {                View childView = getChildAt(i);                // 子控件进行布局                childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());            }            // 初始化左右边界值            leftBorder = getChildAt(0).getLeft()-100;            rightBorder = getChildAt(getChildCount() - 1).getRight()+100;        }    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                xDown = ev.getRawX();                xLastMove = xDown;                break;            case MotionEvent.ACTION_MOVE:                xMove = ev.getRawX();                float diff = Math.abs(xMove - xDown);                xLastMove = xMove;                // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件                if (diff > mixTouchSlip) {                    return true;                }                break;        }        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {             case MotionEvent.ACTION_DOWN:                return  true;            case MotionEvent.ACTION_MOVE:                xMove = event.getRawX();                int scrolledX = (int) (xLastMove - xMove);             // getScrollX()                if (getScrollX() + scrolledX < leftBorder) {                    scrollTo(leftBorder, 0);                    return true;                } else if (getScrollX() + getWidth() + scrolledX > rightBorder) {                    scrollTo(rightBorder - getWidth(), 0);                    return true;                }                scrollBy(scrolledX, 0);                xLastMove = xMove;                break;            case MotionEvent.ACTION_UP:                // 当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面                int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();                position = targetIndex;                int dx = targetIndex * getWidth() - getScrollX();                // 调用startScroll()方法来初始化滚动数据                // startScroll()方法接收四个参数,第一个参数是滚动开始时X的坐标,第二个参数是滚动开始时Y的坐标,                // 第三个参数是横向滚动的距离,正值表示向左滚动,第四个参数是纵向滚动的距离,正值表示向上滚动。紧接着调用invalidate()方法来刷新界面。                scroller.startScroll(getScrollX(), 0, dx, 0);                invalidate();                if (listener!=null) listener.onGetPoint(position);                break;        }        return super.onTouchEvent(event);    }    @Override    public void computeScroll() {        // 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑        if (scroller.computeScrollOffset()) {            scrollTo(scroller.getCurrX(), scroller.getCurrY());            invalidate();        }    }    public  void  setCount( int mcount){        count  = mcount;        if (params==null) params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);        for (int i = 0;i< count;i++){            ImageView image = new ImageView(mContext);            image.setScaleType(ImageView.ScaleType.FIT_XY);            image.setImageResource(R.drawable.meizhi);            image.setLayoutParams(params);            addView(image);        }        invalidate();    }    public interface OnPayListener {        void onGetPoint(int pas);    }    public  void  setListener(OnPayListener mlistener){        this.listener = mlistener;    }}

首先我们自定义viwe继承的是ViewGroup,在这里我运用setCount()方法指定图片数量,并用代码动态添加imageview,在onMeasure 里为每一个子控件测量大小,然后在onLayout 为每个子空间进行布局,沿X轴水平摆放,然后调用onInterceptTouchEvent();根据滑动距离是否满足条件进行事件拦截,然后在onTouchEvent()中进行事件消费,实现移动效果,这里只得注意的是: 在 onTouchEvent 的 MotionEvent.ACTION_DOWN 中 要 return true; 因为如果不消费onInterceptTouchEvent 传下来的down事件,默认也不会响应move 和 up 事件,所以onTouchEvent down的时候 return true。OnPayListener 接口回调实现每当我进行滑动我都可以拿到当前滚动到哪个界面的position,然后进行设置指示器。

   LinearLayout.LayoutParams  lparams = new LinearLayout.LayoutParams(20,20);        lparams.leftMargin = 10;        for (int i= 0 ;i< n;i++){            TextView textView = new TextView(this);            textView.setLayoutParams(lparams);            textView.setBackgroundResource(R.drawable.point);            linearLayout.addView(textView);        }        linearLayout.getChildAt(0).setSelected(true);        slv.setListener(new ScrollerView.OnPayListener() {            @Override            public void onGetPoint(int pas) {                for (int i = 0; i<n;i++){                    if (i==pas){                        linearLayout.getChildAt(pas).setSelected(true);                    }else {                        linearLayout.getChildAt(i).setSelected(false);                    }                }            }        });

在activity里为linearLayout动态添加控件,并设置小圆圈的背景颜色。

<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:state_selected="true" android:drawable="@drawable/pink"></item><item android:state_selected="false" android:drawable="@drawable/white"></item></selector><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="@color/colorAccent"></solid></shape><shape xmlns:android="http://schemas.android.com/apk/res/android"    android:shape="oval">    <solid android:color="@color/white"></solid></shape>
原创粉丝点击