复杂自定义控件---自定义ViewPager的实现

来源:互联网 发布:sql server参考文献 编辑:程序博客网 时间:2024/05/07 22:19

效果图


核心方法

1、三个构造方法(一个参数, 两个参数, 三个参数)

2、onMesure 测量控件

4、onLayout 分配控件布局

5、computeScroll()  计算滑动

6、onDraw   绘制控件

7、onTouchEvent() 中断事件传递

8、dispatchTouchEvent 分发事件


实现步骤:

1   初始化显示的数据

        //为MyViewPager添加图片        for(int i=0; i<imgs.length; i++) {            ImageView imageView = new ImageView(getApplicationContext());            imageView.setBackgroundResource(imgs[i]);            mMyViewPager.addView(imageView);        }        View view = View.inflate(getApplicationContext(), R.layout.ll_view, null);        mMyViewPager.addView(view, 2);

2 测量控件  (注:由于控件的嵌套复杂性不同,导致系统测量的次数不一样,嵌套布局越多测量                           越复杂,所以在使用布局时尽量避免嵌套的层次)

<strong><span style="color:#ff0000;">//  onMeasure  会在onLayout 之前调用    // 要求父容器一定要测量子容器 ,如果不测量 子容器 子容器宽和高 都是0   子容器由于挂载到父容器可以正常显示,但是 孙子就不能显示    // 父容器先知道自己大小(match_parent) 子容器先知道大小(wrap_content)    //widthMeasureSpec不仅表示控件的宽,里面还带有控件的属性的基本信息</span></strong>    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        System.out.println("onMeasure");        System.out.println(widthMeasureSpec);        MeasureSpec.getMode(widthMeasureSpec); //获取控件的模型        MeasureSpec.getSize(widthMeasureSpec); // 得到控件真正的尺寸        System.out.println(heightMeasureSpec);        for(int i=0; i< getChildCount(); i++) {            View view = getChildAt(i);            view.measure(widthMeasureSpec, heightMeasureSpec);// 对每个孩子都测量        }    }

3  分配控件显示的位置
 //  分配孩子位置     在onDraw方法之前调用    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        for(int i=0; i<getChildCount(); i++) {            View view = getChildAt(i);            view.layout(0 + getWidth() * i, 0, getWidth() + getWidth() * i, getHeight());        }    }

4   让控件随着手指的移动而移动
//手势识别监听器    private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener {        //滑动事件        //distanceX x轴滑动的距离        //distanceY y轴滑动的距离        @Override        public boolean onScroll(MotionEvent e1, MotionEvent e2,                                float distanceX, float distanceY) {            scrollBy((int)distanceX, 0); //让viewGroup 移动多少距离            //scrollBy  会自动调用invalidate() 该方法            //invalidate();   自动调用onDraw            return super.onScroll(e1, e2, distanceX, distanceY);        }    }

 private void initView() {        mGestureDetector = new GestureDetector(getContext(), new MySimpleOnGestureListener());    }
@Override    public boolean onTouchEvent(MotionEvent event) {        mGestureDetector.onTouchEvent(event); //把手势识别器注册到触摸事件中        switch(event.getAction()) {            case MotionEvent.ACTION_DOWN:                // 当手指按下的时候 记录开始的坐标                startX = (int) event.getX();                break;            case MotionEvent.ACTION_UP:                // 当手指抬起的时候 记录结束的坐标                int endX = (int) event.getX();                if((startX - endX) > getWidth() / 2) {                    //进入下一个界面                    index++;                } else if((endX - startX) > getWidth() / 2) {                    // 进入上一个界面                    index--;                }                moveToIndex();                break;            default:                break;        }        //返回处理了触摸事件        return true;    }
5 自动跳转界面(根据手势滑动的距离,确定页面跳转)
 private void moveToIndex() {        if(index < 0) {            index = 0;        }        if(index == getChildCount()) {            index = getChildCount() -1;        }        if(mOnpageChangedListener != null) {            mOnpageChangedListener.onChange(index);        }        mScroller = new Scroller(getContext());        mScroller.startScroll(getScrollX(), getScrollY(), (int)(getWidth() * index - getScrollX()), 0);        invalidate();    }
6 移动(手指抬起时确定要跳转的页面后,慢慢的实现页面移动到指定的位置)
 private void moveToIndex() {        if(index < 0) {            index = 0;        }        if(index == getChildCount()) {            index = getChildCount() -1;        }        if(mOnpageChangedListener != null) {            mOnpageChangedListener.onChange(index);        }        mScroller = new Scroller(getContext());        mScroller.startScroll(getScrollX(), getScrollY(), (int)(getWidth() * index - getScrollX()), 0);        invalidate();    }    //计算移动   每次刷新界面 该方法都会被调用    //scroller.computeScrollOffset()   返回值是true 情况下  代表动作没有结束    @Override    public void computeScroll() {        if(mScroller != null) {            if(mScroller.computeScrollOffset()) {                scrollTo(mScroller.getCurrX(), 0);  //scrollTo这个方法一执行 会调用invalidate();,异步执行                invalidate();            }        }        super.computeScroll();    }


7 中断事件(当左右移动的控件里嵌套了上下移动的空间--ScrollView 应该判断,当前的手势响应是否被中断,通过判断,当前使用者的意图,确定是左走滑动,还是要上下滑动,来决定是否中断手势事件的向下传递)
//  中断事件传递    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        switch(ev.getAction()) {            case MotionEvent.ACTION_DOWN:                mGestureDetector.onTouchEvent(ev); // 避免了中断事件 导致没有处理按下的操作                startX2 = (int) ev.getX();                startY2 = (int) ev.getY();                break;            case MotionEvent.ACTION_MOVE: // 手指移动的事件                int endX2 = (int) ev.getX();                int endY2 = (int) ev.getY();                int dx = endX2 - startX2;  // x轴的偏移量                int dy = endY2 - startY2;  //y轴的偏移量                if(Math.abs(dx) > Math.abs(dy)) { //如果为左右移动则中断手势事件响应的传递                    return true;                }                break;            default:                break;        }        // 如果是上下滑动 屏幕的时候 不中断事件        //  return false;        //如果是左右滑动 中断事件        // return true;        //交给父类判断(即交给ViewGroup判断)父类的该方法返回值为false 不中断事件        return super.onInterceptTouchEvent(ev);    }
android中Touch事件处理流程图:




Touch事件传递机制流程图:

8 回调方法(当跳转到ViewPager中的某一页时,会自动触发某个事件实现接口回调)
// 定义一个公开接口,设置回调方法    public interface OnPageChangedListener {        void onChange(int index);    }    private OnPageChangedListener mOnpageChangedListener;    //定义一个公开的注册页面改变的方法    public void setOnpageChangedListener(OnPageChangedListener listener) {        mOnpageChangedListener = listener;    }
private void moveToIndex() {        if(index < 0) {            index = 0;        }        if(index == getChildCount()) {            index = getChildCount() -1;        }        if(mOnpageChangedListener != null) {            mOnpageChangedListener.onChange(index);        }        mScroller = new Scroller(getContext());        mScroller.startScroll(getScrollX(), getScrollY(), (int)(getWidth() * index - getScrollX()), 0);        invalidate();    }


完整代码:
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:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context="com.example.mhy.zidingyiviewpager.MainActivity">    <RadioGroup        android:id="@+id/mRadioGroup"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal"        >        </RadioGroup>    <com.example.mhy.zidingyiviewpager.MyViewPager        android:id="@+id/mViewPager"        android:layout_width="match_parent"        android:layout_height="match_parent"        >    </com.example.mhy.zidingyiviewpager.MyViewPager></LinearLayout>

ll_view.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="wrap_content" >     <LinearLayout                android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical" >        <Button            android:id="@+id/button1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Button" />        <Button            android:id="@+id/button2"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Button" />        <EditText            android:id="@+id/editText1"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:ems="10"            android:inputType="textPersonName" >            <requestFocus />        </EditText>        <TextView            android:id="@+id/textView1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Medium Text"            android:textAppearance="?android:attr/textAppearanceMedium" />        <TextView            android:id="@+id/textView2"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Large Text"            android:textAppearance="?android:attr/textAppearanceLarge" />        <TextView            android:id="@+id/textView3"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Large Text"            android:textAppearance="?android:attr/textAppearanceLarge" />        <TextView            android:id="@+id/textView4"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Large Text"            android:textAppearance="?android:attr/textAppearanceLarge" />        <Button            android:id="@+id/button1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Button" />        <Button            android:id="@+id/button2"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Button" />        <EditText            android:id="@+id/editText1"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:ems="10"            android:inputType="textPersonName" >            <requestFocus />        </EditText>        <TextView            android:id="@+id/textView1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Medium Text"            android:textAppearance="?android:attr/textAppearanceMedium" />        <TextView            android:id="@+id/textView2"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Large Text"            android:textAppearance="?android:attr/textAppearanceLarge" />        <TextView            android:id="@+id/textView3"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Large Text"            android:textAppearance="?android:attr/textAppearanceLarge" />        <TextView            android:id="@+id/textView4"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Large Text"            android:textAppearance="?android:attr/textAppearanceLarge" />        <Button            android:id="@+id/button1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Button" />        <Button            android:id="@+id/button2"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Button" />        <EditText            android:id="@+id/editText1"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:ems="10"            android:inputType="textPersonName" >            <requestFocus />        </EditText>        <TextView            android:id="@+id/textView1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Medium Text"            android:textAppearance="?android:attr/textAppearanceMedium" />        <TextView            android:id="@+id/textView2"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Large Text"            android:textAppearance="?android:attr/textAppearanceLarge" />        <TextView            android:id="@+id/textView3"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Large Text"            android:textAppearance="?android:attr/textAppearanceLarge" />        <TextView            android:id="@+id/textView4"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Large Text"            android:textAppearance="?android:attr/textAppearanceLarge" />    </LinearLayout></ScrollView>

MyViewPager.java
package com.example.mhy.zidingyiviewpager;import android.content.Context;import android.graphics.Canvas;import android.util.AttributeSet;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.Scroller;/** * Created by mhy on 2016/6/15. */public class MyViewPager extends ViewGroup {    private GestureDetector mGestureDetector;    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 class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener {        //滑动事件        //distanceX x轴滑动的距离        //distanceY y轴滑动的距离        @Override        public boolean onScroll(MotionEvent e1, MotionEvent e2,                                float distanceX, float distanceY) {            scrollBy((int)distanceX, 0); //让viewGroup 移动多少距离            //scrollBy  会自动调用invalidate() 该方法            //invalidate();   自动调用onDraw            return super.onScroll(e1, e2, distanceX, distanceY);        }    }    private void initView() {        mGestureDetector = new GestureDetector(getContext(), new MySimpleOnGestureListener());    }    // 定义一个公开接口,设置回调方法    public interface OnPageChangedListener {        void onChange(int index);    }    private OnPageChangedListener mOnpageChangedListener;    //定义一个公开的注册页面改变的方法    public void setOnpageChangedListener(OnPageChangedListener listener) {        mOnpageChangedListener = listener;    }    //  中断事件传递    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        switch(ev.getAction()) {            case MotionEvent.ACTION_DOWN:                mGestureDetector.onTouchEvent(ev); // 避免了中断事件 导致没有处理按下的操作                startX2 = (int) ev.getX();                startY2 = (int) ev.getY();                break;            case MotionEvent.ACTION_MOVE: // 手指移动的事件                int endX2 = (int) ev.getX();                int endY2 = (int) ev.getY();                int dx = endX2 - startX2;  // x轴的偏移量                int dy = endY2 - startY2;  //y轴的偏移量                if(Math.abs(dx) > Math.abs(dy)) { //如果为左右移动则中断手势事件响应的传递                    return true;                }                break;            default:                break;        }        // 如果是上下滑动 屏幕的时候 不中断事件        //  return false;        //如果是左右滑动 中断事件        // return true;        //交给父类判断(即交给ViewGroup判断)父类的该方法返回值为false 不中断事件        return super.onInterceptTouchEvent(ev);    }    private int startX2;    private int startY2;    private int index = 0; // 当前显示的位置    private int startX;    @Override    public boolean onTouchEvent(MotionEvent event) {        mGestureDetector.onTouchEvent(event); //把手势识别器注册到触摸事件中        switch(event.getAction()) {            case MotionEvent.ACTION_DOWN:                // 当手指按下的时候 记录开始的坐标                startX = (int) event.getX();                break;            case MotionEvent.ACTION_UP:                // 当手指抬起的时候 记录结束的坐标                int endX = (int) event.getX();                if((startX - endX) > getWidth() / 2) {                    //进入下一个界面                    index++;                } else if((endX - startX) > getWidth() / 2) {                    // 进入上一个界面                    index--;                }                moveToIndex();                break;            default:                break;        }        //返回处理了触摸事件        return true;    }    //外界通过指定索引将页面切换到指定的位置    public void moveToIndex(int index) {        this.index = index;        moveToIndex();    }    private void moveToIndex() {        if(index < 0) {            index = 0;        }        if(index == getChildCount()) {            index = getChildCount() -1;        }        if(mOnpageChangedListener != null) {            mOnpageChangedListener.onChange(index);        }        mScroller = new Scroller(getContext());        mScroller.startScroll(getScrollX(), getScrollY(), (int)(getWidth() * index - getScrollX()), 0);        invalidate();    }    //计算移动   每次刷新界面 该方法都会被调用    //scroller.computeScrollOffset()   返回值是true 情况下  代表动作没有结束    @Override    public void computeScroll() {        if(mScroller != null) {            if(mScroller.computeScrollOffset()) {                scrollTo(mScroller.getCurrX(), 0);  //scrollTo这个方法一执行 会调用invalidate();,异步执行                invalidate();            }        }        super.computeScroll();    }    //  onMeasure  会在onLayout 之前调用    // 要求父容器一定要测量子容器 ,如果不测量 子容器 子容器宽和高 都是0   子容器由于挂载到父容器可以正常显示,但是 孙子就不能显示    // 父容器先知道自己大小(match_parent) 子容器先知道大小(wrap_content)    //widthMeasureSpec不仅表示控件的宽,里面还带有控件的属性的基本信息    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        System.out.println("onMeasure");        System.out.println(widthMeasureSpec);        MeasureSpec.getMode(widthMeasureSpec); //获取控件的模型        MeasureSpec.getSize(widthMeasureSpec); // 得到控件真正的尺寸        System.out.println(heightMeasureSpec);        for(int i=0; i< getChildCount(); i++) {            View view = getChildAt(i);            view.measure(widthMeasureSpec, heightMeasureSpec);// 对每个孩子都测量        }    }    //  分配孩子位置     在onDraw方法之前调用    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        for(int i=0; i<getChildCount(); i++) {            View view = getChildAt(i);            view.layout(0 + getWidth() * i, 0, getWidth() + getWidth() * i, getHeight());        }    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);    }}
MainActivity.java
package com.example.mhy.zidingyiviewpager;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 mMyViewPager;    private RadioGroup mRadioGroup;    private int[] imgs = new int[] { R.mipmap.a1, R.mipmap.a2, R.mipmap.a3,            R.mipmap.a4, R.mipmap.a5, R.mipmap.a6};    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mMyViewPager = (MyViewPager) findViewById(R.id.mViewPager);        mRadioGroup = (RadioGroup) findViewById(R.id.mRadioGroup);        //为MyViewPager添加图片        for(int i=0; i<imgs.length; i++) {            ImageView imageView = new ImageView(getApplicationContext());            imageView.setBackgroundResource(imgs[i]);            mMyViewPager.addView(imageView);        }        View view = View.inflate(getApplicationContext(), R.layout.ll_view, null);        mMyViewPager.addView(view, 2);        System.out.println("mMyViewPager.getChildCount() " + mMyViewPager.getChildCount());        for(int i=0; i<mMyViewPager.getChildCount(); i++) {            RadioButton radioButton = new RadioButton(getApplicationContext());            radioButton.setId(i);            mRadioGroup.addView(radioButton);            if(i == 0) {                radioButton.setChecked(true);            }        }        //监听页面切换事件,使对应的单选按钮做相应的改变        mMyViewPager.setOnpageChangedListener(new MyViewPager.OnPageChangedListener() {            @Override            public void onChange(int index) {                RadioButton radioButton = (RadioButton) mRadioGroup.getChildAt(index);                radioButton.setChecked(true);            }        });        //监听单选按钮更改的事件,使ViewPager页面更随做相应的切换        mRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(RadioGroup group, int checkedId) {                mMyViewPager.moveToIndex(checkedId);            }        });    }}





0 0
原创粉丝点击