Android自定义View——教你制作ViewPager(一)

教你制作ViewPager(一)


事先说明:

本篇文章会介绍:自定义ViewPager的创建。

处理滑动监听,处理滑动冲突,增加ViewPager的指示器等将会在下篇文章中介绍。

文章也会涉及到手势识别器和Scroller滑动,详细可以查询相关文章。

基础概念:

常见的滑动冲突外部滑动方向和内部滑动方向不一致、外部滑动方向和内部滑动方向一致。

我们自定义的ViewPager如果在其中一页中存在ListView,那么就需要解决滑动冲突的问题。

由于系统自带ViewPager中,自己已经解决了滑动冲突。

欢迎关注我的CSDN博客,Hensen_的博客,http://blog.csdn.net/qq_30379689


步骤一:创建一个类,继承ViewGroup,由于ViewPager里面包含多个子View,所以继承这个类,实现onLayout方法

onLayout:这个方法是对我们该View的一个位置摆放,这里可以看到onLayout这(int l,int t,int r,int b)这四个参数,分别代表着这个ViewPager的左上右下的位置,由于你引用ViewPager是match_parent,所以l和t为0,r和b为宽和高的距离

public class MyViewPager extends ViewGroup{
    public MyViewPager(Context context) {
        super(context);
    }

    public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }
}
步骤二:我们在自己的Activity中引用自定义的这个组件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.handsome.app2.View.Custom.MyViewPager
        android:layout_width="match_parent"
        android:id="@+id/vp_my"
        android:layout_height="match_parent"/>
</RelativeLayout>
步骤三:复制几张图片作为演示,并为他们创建Id数组
private int[] image_id = {R.drawable.guide_map1,R.drawable.guide_map2,R.drawable.guide_map3,R.drawable.guide_map4};
步骤四:接着我们需要对图片进行初始化,并加入到ViewPager中,我们写个初始化方法,并将他们放在构造方法中

public MyViewPager(Context context) {
        super(context);
        initView();
    }

    public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public MyViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView(){
        for (int i=0;i<image_id.length;i++){
            ImageView iv = new ImageView(getContext());
            iv.setBackgroundResource(image_id[i]);
            this.addView(iv);
        }
    }
步骤五:这个时候,启动程序,你是看不到有图片出现的,因为你还没有对这几张图片进行位置的摆放,所以需要在onLayout中进行位置处理
@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for (int i = 0; i < image_id.length; i++) {
            this.getChildAt(i).layout(i * getWidth(), t, (i + 1) * getWidth(), b);
        }
    }
这样处理的好处就是将图片一字排开


步骤六:现在已经排好了图片,接着我们就来处理滑动事件了,我们通过一个手势识别器自动帮我们识别滑动事件

 private GestureDetector mDetector ;
 mDetector = new GestureDetector(new SimpleOnGestureListener(){
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                //scrollBy:相对滑动,相对我们当前的控件多少距离,就滑动多少距离
                //distanceX是我们手滑动的距离,即我们的手相对控件滑动了多少,所以X轴滑动这个距离,Y轴滑动0
                scrollBy((int)distanceX,0);
                return super.onScroll(e1, e2, distanceX, distanceY);
            }
        });
通过onTouchEvent委托给手势识别器,并且返回true,让这个控件消耗这个事件

@Override
    public boolean onTouchEvent(MotionEvent event) {
        mDetector.onTouchEvent(event);
        return true;
    }

这个时候我们就可以看下效果图:


步骤七:我们看到跟ViewPager还差一点,就是滑到第几张就自动复原和不能超出头和尾部的图片,这时就要处理滑动事件

@Override
    public boolean onTouchEvent(MotionEvent event) {
        mDetector.onTouchEvent(event);
        //触摸事件处理
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                break;
            case MotionEvent.ACTION_MOVE:


                break;
            case MotionEvent.ACTION_UP:
                int scrollX = getScrollX();
                //你滑动的距离加上屏幕的一半,除以屏幕宽度,如果你滑动距离超过了屏幕的一半,这个pos就加1
                int pos = (scrollX + getWidth() / 2) / getWidth();
                //滑到最后一张的时候,不能出边界
                if (pos >= image_id.length) {
                    pos = image_id.length - 1;
                }
                //绝对滑动,直接滑到指定的x值
                scrollTo(pos * getWidth(), 0);
                break;
        }
        return true;
    }
效果图:


步骤八:基本效果已经出来了,就是没有很自然的滑动过去,那么这个时候就要用到scroller

private Scroller mScroller;

mScroller = new Scroller(getContext());
可以将ScrollTo替换掉了,让它自然滑动
//绝对滑动,直接滑到指定的x值
                //scrollTo(pos * getWidth(), 0);
                //自然滑动,从手滑到的地方开始,滑动距离是页面宽度减去滑到的距离,时间由路程的大小来决定
                mScroller.startScroll(scrollX, 0, pos * getWidth() - scrollX, 0, Math.abs(pos * getWidth()));
                invalidate();
                break;
使用invalidate这个方法会有执行一个回调方法computeScroll,我们来重写这个方法
@Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), 0);
            postInvalidate();
        }
    }
其实Scroller的原理就是用ScrollTo来一段一段的进行,最后看上去跟自然的一样,必须使用postInvalidate,这样才会一直回调computeScroll这个方法,直到滑动结束。基本上ViewPager的效果就出来了,看下效果图:
 
 
 
 
下一篇我们会介绍setCurrentItem,setOnPagerChangListener,指示器和滑动冲突的解决等问题
欢迎关注我的CSDN博客,Hensen_的博客,http://blog.csdn.net/qq_30379689 
以下是整个类的源码:
public class MyViewPager extends ViewGroup {

    private int[] image_id = {R.drawable.guide_map1, R.drawable.guide_map2, R.drawable.guide_map3, R.drawable.guide_map4};

    private GestureDetector mDetector;
    private Scroller mScroller;

    public MyViewPager(Context context) {
        super(context);
        initView();
    }

    public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public MyViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        for (int i = 0; i < image_id.length; i++) {
            ImageView iv = new ImageView(getContext());
            iv.setBackgroundResource(image_id[i]);
            this.addView(iv);
        }
        mDetector = new GestureDetector(new SimpleOnGestureListener() {
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                //scrollBy:相对滑动,相对我们当前的控件多少距离,就滑动多少距离
                //distanceX是我们手滑动的距离,即我们的手相对控件滑动了多少,所以X轴滑动这个距离,Y轴滑动0
                scrollBy((int) distanceX, 0);
                return super.onScroll(e1, e2, distanceX, distanceY);
            }
        });
        mScroller = new Scroller(getContext());
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for (int i = 0; i < image_id.length; i++) {
            this.getChildAt(i).layout(i * getWidth(), t, (i + 1) * getWidth(), b);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDetector.onTouchEvent(event);
        //触摸事件处理
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                break;
            case MotionEvent.ACTION_MOVE:


                break;
            case MotionEvent.ACTION_UP:
                int scrollX = getScrollX();
                //你滑动的距离加上屏幕的一半,除以屏幕宽度,如果你滑动距离超过了屏幕的一半,这个pos就加1
                int pos = (scrollX + getWidth() / 2) / getWidth();
                //滑到最后一张的时候,不能出边界
                if (pos >= image_id.length) {
                    pos = image_id.length - 1;
                }
                //绝对滑动,直接滑到指定的x值
                //scrollTo(pos * getWidth(), 0);
                //自然滑动,从手滑到的地方开始,滑动距离是页面宽度减去滑到的距离,时间由路程的大小来决定
                mScroller.startScroll(scrollX, 0, pos * getWidth() - scrollX, 0, Math.abs(pos * getWidth()));
                invalidate();
                break;
        }
        return true;
    }

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













你可能感兴趣的:(viewpager,控件,自定义view)