自定义ViewPager+RadioGroup联动效果的实现

package com.loaderman.myviewpager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
public class MainActivity extends AppCompatActivity {
    private MyViewPager mViewPager;
    private RadioGroup rgGroup;
    private int[] mImageIds = new int[]{R.drawable.a1, R.drawable.a2, R.drawable.a3, R.drawable
            .a4, R.drawable.a5, R.drawable.a6};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mViewPager = (MyViewPager) findViewById(R.id.viewpager);
        rgGroup = (RadioGroup) findViewById(R.id.rg_group);
        //给自定义Viewpager添加图片
        for (int id : mImageIds) {
            ImageView view = new ImageView(this);
            view.setBackgroundResource(id);
            mViewPager.addView(view);
        }
        //添加测试页面布局
        View view = View.inflate(this, R.layout.item_test, null);
        mViewPager.addView(view, 2);
        //动态添加RaidoButton
        for (int i = 0; i <= mImageIds.length; i++) {
            RadioButton rb = new RadioButton(this);
            rb.setId(i);//以当前位置为id
            rgGroup.addView(rb);
            if (i == 0) {
                rb.setChecked(true);
            }
        }
        //点击RadioButton, 切换页面
        rgGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                System.out.println("checkedId:" + checkedId);
                int pos = checkedId;
                mViewPager.setCurrentItem(pos);
            }
        });
        //切换页面, 更新RadioButton
        mViewPager.addOnPageChangeListener(new MyViewPager.OnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                int id = position;
                rgGroup.check(id);
            }
        });

    }
}

 

package com.loaderman.myviewpager;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
/**
 * 自定义ViewPager流程:
 * 1. 写一个类继承ViewGroup
 * 2. 在actvity中添加图片对象
 * 3. 重写onLayout, 保证子控件一字排开
 * 4. 滑动布局,切换页面, 手势识别器 onScroll: scrollBy, scrollTo
 * 5. 平滑滑动效果 Scroller滑动器
 * 6. 加测试页面ScrollView
 * 7. 重写onMeasure测量所有子控件
 * 8. 事件传递流程, 苹果例子
 * 9. 事件拦截流程
 * 10. 保证viewpager和scrollview分别处理相关事件
 * 11. 添加RadioButton
 * 12. 点击RadioButton切换页面
 * 13. 滑动页面, 切换RadioButton
 */
public class MyViewPager extends ViewGroup {
    private GestureDetector mDetector;
    private Scroller        mScroller;
    private int             startX;
    private int             startY;
    public MyViewPager(Context context) {
        this(context, null);
    }
    public MyViewPager(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }
    public MyViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    private void init() {
        mDetector = new GestureDetector(getContext(), new GestureDetector
                .SimpleOnGestureListener() {
            //触摸滑动的方法
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float
                    distanceY) {
                //distanceX: 水平滑动距离
                scrollBy((int) distanceX, 0);//基于当前位置进行滑动, 参1:水平滑动的偏移量, 相对位置
                return super.onScroll(e1, e2, distanceX, distanceY);
            }
        });
        //滑动器
        mScroller = new Scroller(getContext());
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);//设置ViewPager本身的尺寸
        //遍历所有子控件,设置每个控件的尺寸
        //解决测试页面显示白板的问题
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, heightMeasureSpec);
        }
        //widthMeasureSpec: 并不是真实的宽高信息, 它包含两部分, 1: 宽高模式信息; 2. 具体的尺寸
        // System.out.println("widthMeasureSpec:" + widthMeasureSpec);
        // System.out.println("heightMeasureSpec:" + heightMeasureSpec);
        //MeasureSpec.AT_MOST; 至多模式, 当前控件有多大就显示多大 wrap_content
        //MeasureSpec.EXACTLY; 确定模式, 宽高写死dp, match_parent(父控件多大,我就多大,所以也是确定的)
        //MeasureSpec.UNSPECIFIED; 未确定模式, ListView, ScrollView

        //        int mode = MeasureSpec.getMode(widthMeasureSpec);
        //        int size = MeasureSpec.getSize(widthMeasureSpec);
        //
        //        System.out.println("mode:" + mode);
        //        System.out.println("size:" + size);
    }
    //设置控件的位置
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //遍历所有子控件, 设置每个子控件位置
        //子控件一字排开
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.layout(getWidth() * i, 0, getWidth() * (i + 1), getHeight());
        }
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDetector.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                System.out.println("ViewPager 按下...");
                break;
            case MotionEvent.ACTION_MOVE:
                System.out.println("ViewPager 移动...");
                break;
            case MotionEvent.ACTION_UP:
                System.out.println("ViewPager 抬起...");
                //手指抬起
                //确定下一页的位置
                int scrollX = getScrollX();//获取当前移动后的x值
                // System.out.println("scrollX:" + scrollX);
                //计算当前页面位置
                int currPos = scrollX / getWidth();
                // System.out.println("currPos:" + currPos);
                int offset = scrollX % getWidth();//多划出的距离
                if (offset > getWidth() / 2) {
                    currPos++;
                }
                //避免越界 0->图片个数-1
                if (currPos < 0) {
                    currPos = 0;
                }
                if (currPos > getChildCount() - 1) {
                    currPos = getChildCount() - 1;
                
                //System.out.println("下一页位置:" + currPos);

                //跳到下一页位置
                // scrollTo(currPos * getWidth(), 0);//绝对位置,移动到确定位置
                //计算滑动距离
                //                int dx = currPos * getWidth() - getScrollX();//目标位置-当前位置=滑动距离
                //                //此方法不能产生滑动的动画效果, 会导致回调computeScroll方法, 需要在computeScroll方法中处理动画
                //                mScroller.startScroll(getScrollX(), 0, dx, 0, Math.abs(dx));//距离和时间要成正比
                //                invalidate();//要刷新界面
                setCurrentItem(currPos);
                break;
            default:
                break;
        }
        return true;
    }
    //此方法会回调多次, 每一次回调后修改页面位置, 连续在一起就形成动画
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {//判断有没有滑动结束
            int currX = mScroller.getCurrX();//获取当前应该滑动到的位置
            System.out.println("currX:" + currX);
            scrollTo(currX, 0);//滑动到特定位置
            invalidate();//要刷新界面
        }
    }
    //事件分发
    //dispatchTouchEvent->onInterceptTouchEvent-->onTouchEvent
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //如果上下滑动, 不需要中断事件
        //左右滑动, 才需要中断事件
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startX = (int) ev.getX();
                startY = (int) ev.getY();
                //由于按下之后, 返回false, 按下事件被子控件处理,导致ViewPager丢掉了按下事件,滑动时页面出现bug
                //补上按下事件
                mDetector.onTouchEvent(ev);
                break;
            case MotionEvent.ACTION_MOVE:
                int endX = (int) ev.getX();
                int endY = (int) ev.getY();
                int dx = endX - startX;
                int dy = endY - startY;
                if (Math.abs(dx) > Math.abs(dy)) {
                    //左右滑动
                    return true;//表示中断事件传递, 交给当前ViewPager处理, 子控件无法处理
                }
                break;
            default:
                break;
        }
        return false;//不中断事件, 交给子控件(ScrollView)处理
    }
    //设置当前页面
    public void setCurrentItem(int pos) {
        //计算滑动距离
        int dx = pos * getWidth() - getScrollX();//目标位置-当前位置=滑动距离
        //此方法不能产生滑动的动画效果, 会导致回调computeScroll方法, 需要在computeScroll方法中处理动画
        mScroller.startScroll(getScrollX(), 0, dx, 0, Math.abs(dx));//距离和时间要成正比
        invalidate();//要刷新界面
        //回调页面位置
        if (listener != null) {
            listener.onPageSelected(pos);
        }
    }
    private OnPageChangeListener listener;
    public void addOnPageChangeListener(OnPageChangeListener listener) {
        this.listener = listener;
    }
    public interface OnPageChangeListener {
        public void onPageSelected(int position);
    }
}

 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.loaderman.myviewpager.MainActivity">

    <RadioGroup
        android:id="@+id/rg_group"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
    </RadioGroup>

    <com.loaderman.myviewpager.MyViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</LinearLayout>

 item_test.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
<!--item_test-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试文本"
            android:textSize="30sp"/>
    </LinearLayout>
</ScrollView>

 效果:

posted on 2017-03-07 14:32  LoaderMan  阅读(320)  评论(0编辑  收藏  举报

导航