Android开发-自定义View-AndroidStudio(十三)仿ViewPager(3)

来源:互联网 发布:关键词排列组合软件 编辑:程序博客网 时间:2024/05/02 02:19
转载请注明出处:http://blog.csdn.net/iwanghang/article/details/53839229
觉得博文有用,请点赞,请评论,请关注,谢谢!~


项目源码下载:http://download.csdn.net/detail/iwanghang/9721146


老规矩,先上GIF动态图,看个效果,如果符合你的项目或者确定你要了解的内容,再往下看吧:
跟前一篇博文相比:
1、添加了RadioButton。
2、实现了RadioButton的选中时,设置ViewPager下标。
3、实现了滑动页面ViewPager下标改变时,RadioButton的选中状态改变。
4、实现了添加页面后,将页面的孩子控件显示出来。
5、遗留一个问题,看GIF动态图可以发现,我们后面解决。

※博文结尾有解决的GIF动图和对应代码。

MainActivity.java:
package com.iwanghang.mygesturedetector;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.ImageView;import android.widget.RadioButton;import android.widget.RadioGroup;public class MainActivity extends AppCompatActivity {    private MyViewPager myViewPager;    private RadioGroup rg_main;    private int[] ids = {R.drawable.a,R.drawable.b,R.drawable.c,            R.drawable.d,R.drawable.e,R.drawable.f};    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        getSupportActionBar().hide();        myViewPager = (MyViewPager) findViewById(R.id.myViewPager);        rg_main = (RadioGroup) findViewById(R.id.rg_main);        // 添加页面        for (int i = 0; i <ids.length ; i++) {            ImageView iv = new ImageView(this);            iv.setBackgroundResource(ids[i]);            //iv.setImageResource(ids[i]);            myViewPager.addView(iv);        }        // 添加测试页面        View testView = View.inflate(this,R.layout.test,null);        myViewPager.addView(testView);        // 添加RadioButton        for (int i = 0; i < myViewPager.getChildCount(); i++) {            RadioButton button = new RadioButton(this); // 实例化RadioButton            button.setId(i); // 给每个RadioButton设置一个id            if(i==0){ // 设置第一个id选中状态                button.setChecked(true);            }            rg_main.addView(button); // 添加到RadioGroup集合中        }        // 设置RadioGroup选中状态的变化 -> 这里是根据RadioButton设置ViewPager        // 还需要根据ViewPager的滑动 设置 RadioButton的选中状态        rg_main.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(RadioGroup group, int checkedId) {                myViewPager.scrollToPager(checkedId); // 根据下标位置定位到具体的某个页面            }        });        // 设置监听页面的改变        MyOnPagerChangeListener mOnPagerChangeListener = new MyOnPagerChangeListener();        myViewPager.setOnPagerChangeListener(mOnPagerChangeListener);    }    class MyOnPagerChangeListener implements MyViewPager.OnPagerChangeListener {        @Override        public void onScrollToPager(int position) {            rg_main.check(position); // RadioGroup的check方法,设置选中状态        }    }//     // 监听页面的改变//    public interface OnPagerChangeListener{//        // 当页面改变的时候回调这个方法//        void onScrollToPager(int position); // 回传页面下标//    }//    private OnPagerChangeListener mOnPagerChangeListener;//    public void  setOnPagerChangeListener(OnPagerChangeListener listener){//        mOnPagerChangeListener = listener;//    }}
MyViewPager.java:
package com.iwanghang.mygesturedetector;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 */public class MyViewPager extends ViewGroup{    /**     * 手势识别     * 1、定义出来     * 2、实例化 -> 把想要的方法给重写     * 3、在OnTouchEvent()把时间传递给手势识别器     */    private GestureDetector detector; // 1、定义出来    /**     * 当前页面的下标位置     */    private int currentIndex;    //private MyScroller myScroller; // 自己写的回弹效果    private Scroller myScroller; // 原生的回弹效果    public MyViewPager(Context context, AttributeSet attrs) {        super(context, attrs);        initView(context); // 2、实例化    }    private void initView(final Context context) {        //myScroller = new MyScroller(); // 自己写的回弹效果        myScroller = new Scroller(context); // 原生的回弹效果        // 2、实例化        detector = new GestureDetector(context,new GestureDetector.SimpleOnGestureListener(){            @Override            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {                //scrollBy((int)distanceX, (int)distanceY); // 这里为了演示,可以上下左右滑动                scrollBy((int)distanceX, getScrollY()); // Y轴保持在创建时的起始值,我们一般这么用                //Toast.makeText(context, ""+e1+"|"+e2+"|"+distanceX+"|"+distanceY, Toast.LENGTH_SHORT).show();                return true;            }        });    }    @Override    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {        // 遍历孩子,给每个孩子指定在屏幕的坐标位置        for (int n = 0; n <getChildCount() ; n++) {            View childView = getChildAt(n);            childView.layout(n*getWidth(),0,(n+1)*getWidth(),getHeight());        }    }    private float startX;    @Override    public boolean onTouchEvent(MotionEvent event) {        super.onTouchEvent(event);        // 把时间传递给手势识别器        detector.onTouchEvent(event);        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                // 记录坐标                startX = event.getX();                break;            case MotionEvent.ACTION_MOVE:                break;            case MotionEvent.ACTION_UP:                // 新的坐标                float endX = event.getX();                // 下标位置                int tempIndex = currentIndex;                // 计算偏移量                if ((startX-endX)>getWidth()/2){                    // 显示下一个页面                    tempIndex++;                }else if ((endX-startX)>getWidth()/2){                    // 显示上一个页面                    tempIndex--;                }                scrollToPager(tempIndex);                break;        }        return true;    }    /**     * pager计算     */    public void  scrollToPager(int tempIndex){        if (tempIndex < 0){            tempIndex = 0;        }        if (tempIndex > getChildCount()-1){            tempIndex = getChildCount()-1;        }        // 当前页面的下标位置        currentIndex = tempIndex;        // 回传前判断        if(mOnPagerChangeListener != null){            mOnPagerChangeListener.onScrollToPager(currentIndex);        }        int distanceX = currentIndex*getWidth() - getScrollX();        //scrollTo(currentIndex*getWidth(), getScrollY());        //myScroller.startScroll(getScrollX(),getScrollY(),distanceX,0);        myScroller.startScroll(getScrollX(),getScrollY(),distanceX,0,Math.abs(distanceX));        invalidate();// 强制绘制,导致下面computeScroll执行    }    @Override    public void computeScroll() {        //super.computeScroll();        if(myScroller.computeScrollOffset()){            //得到移动这个一小段对应的坐标            float currX = myScroller.getCurrX();            scrollTo((int) currX,0);            invalidate();        }    }    // 回传页面下标    public interface OnPagerChangeListener {        void onScrollToPager(int position);    }    private OnPagerChangeListener mOnPagerChangeListener;    /**     * 设置页面改变的监听     */    public void setOnPagerChangeListener(OnPagerChangeListener listener) {        mOnPagerChangeListener = listener;    }    // 显示页面的孩子    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        for (int i = 0; i < getChildCount(); i++) {            View child = getChildAt(i);            child.measure(widthMeasureSpec,heightMeasureSpec);        }    }}
MyScroller.java:(这个不看也可以)
package com.iwanghang.mygesturedetector;import android.os.SystemClock;/** * 自己写的回弹效果 */public class MyScroller {    /**     * X轴的起始坐标     */    private float startY;    /**     * Y轴的起始坐标     */    private float startX;    /**     * 在X轴要移动的距离     */    private int distanceX;    /**     * 在Y轴要移动的距离     */    private int distanceY;    /**     开始的时间     */    private long startTime;    /**     * 总时间     */    private long totalTime = 500;    /**     * 是否移动完成     * false没有移动完成     * true:移动结束     */    private boolean isFinish;    private float currX;    /**     * 得到坐标     */    public float getCurrX() {        return currX;    }    public void startScroll(float startX, float startY, int distanceX, int distanceY) {        this.startY = startY;        this.startX = startX;        this.distanceX = distanceX;        this.distanceY = distanceY;        this.startTime = SystemClock.uptimeMillis();//系统开机时间        this.isFinish = false;    }    /**     * 速度     求移动一小段的距离     求移动一小段对应的坐标     求移动一小段对应的时间     true:正在移动     false:移动结束     * @return     */    public boolean computeScrollOffset(){        if(isFinish){            return  false;        }        //这一小段的结束时间        long endTime = SystemClock.uptimeMillis();        //这一小段所花的时间        long passTime = endTime - startTime;        if(passTime < totalTime){            // 还没有移动结束            // 计算平均速度            //float voleCity = distanceX/totalTime;            // 移动这个一小段对应的距离            float distanceSamllX = passTime* distanceX/totalTime;            // 移动这一小段后对应x轴上的坐标            currX = startX + distanceSamllX;        }else{            // 移动结束            isFinish = true;            currX = startX + distanceX;        }        return true;    }}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.iwanghang.mygesturedetector.MainActivity">    <RadioGroup        android:id="@+id/rg_main"        android:gravity="center_horizontal"        android:orientation="horizontal"        android:layout_width="match_parent"        android:layout_height="wrap_content"/>    <com.iwanghang.mygesturedetector.MyViewPager        android:layout_below="@id/rg_main"        android:id="@+id/myViewPager"        android:layout_width="match_parent"        android:layout_height="match_parent"/>    </RelativeLayout>
test.xml:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical" android:layout_width="match_parent"    android:layout_height="match_parent">    <TextView        android:textSize="20sp"        android:text="这里只是一个TextVew        只有MyViewPager重写onMeasure把每个孩子都画出来的时候,这些文字才显示        这里可以左右滑动"        android:layout_width="wrap_content"        android:layout_height="wrap_content" />    <ScrollView        android:id="@+id/scrollView"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_gravity="center_horizontal">        <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="这里是ScrollView包裹LinearLayout包裹的TextView"                android:textAppearance="?android:attr/textAppearanceLarge" />            <TextView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="这里不可以左右滑动"                android:textAppearance="?android:attr/textAppearanceLarge" />        </LinearLayout>    </ScrollView></LinearLayout>

--------------------我是分割线--------------------
--------------------第一次改进--------------------

在MyViewPager加入onInterceptTouchEvent直接拦截事件分发:
    // 在ViewGroup中,孩子的事件分发,返回true,拦截事件分发,会触发onTouchEvent()    // 在ViewGroup中,孩子的事件分发,返回false,不拦截事件分发,事件继续传递    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        return true;    }

--------------------我是分割线--------------------
--------------------第二次改进--------------------

继续改进onInterceptTouchEvent:
    //private float startX;    private float endX;    private float endY;    private float downX;    private float downY;    // 在ViewGroup中,孩子的事件分发,返回true,拦截事件分发,会触发onTouchEvent()    // 在ViewGroup中,孩子的事件分发,返回false,不拦截事件分发,事件继续传递    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        // 把事件传递给手势识别器        detector.onTouchEvent(ev);        boolean result = false; // 定义一个布尔值,让事件默认情况下继续传递        switch (ev.getAction()){            case MotionEvent.ACTION_DOWN:                // 1、记录坐标                downX = ev.getX();                downY = ev.getY();                break;            case MotionEvent.ACTION_MOVE:                // 2、记录结束值                endX = ev.getX();                endY = ev.getY();                // 3、计算绝对值 distance距离                float distanceX = Math.abs(endX -downX);                float distanceY = Math.abs(endY -downY);                if (distanceX>distanceY && distanceX>10){                    result = true;                }                break;        }        return result;    }


项目源码下载:http://download.csdn.net/detail/iwanghang/9721146


转载请注明出处:http://blog.csdn.net/iwanghang/article/details/53839229



欢迎移动开发爱好者交流
沈阳或周边城市公司有意开发Android,请与我联系
联系方式

微信:iwanghang
QQ:413711276
邮箱:iwanghang@qq.com



项目源码下载:http://download.csdn.net/detail/iwanghang/9721146


觉得博文有用,请点赞,请评论,请关注,谢谢!~

1 0