滑动过渡之Scroller

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>
地址:

csdn:http://download.csdn.net/download/qq_38367802/10172775
github:https://github.com/liuzhenhang/myview

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值