自定义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>
效果:
最后,关注【码上加油站】微信公众号后,有疑惑有问题想加油的小伙伴可以码上加入社群,让我们一起码上加油吧!!!