viewpager+fragment实现中间大两边小无限循环带指示器

  • 项目需要做一个得到APP好友邀请的效果,先上咱们做的效果,如果是你需要请往下看


    微信截图_20190718174532.png
  • 就这样的效果,原理是viewpager添加5个fragment,左边显示的其实是第三个,第四个显示的是第一个,原理网上很多,没有用viewpager返回数量无限大的那种,那种耗费内存。直接上代码吧
  • activity ,新建fragment就不说了,非常简单
public class TestViewPagerActivity extends BasesActivity {

    private ViewPager viewpager;
    private List list;
    private boolean mIsChanged = false;
    private int mCurrentPagePosition = FIRST_ITEM_INDEX;
    private static final int POINT_LENGTH = 3;
    private static final int FIRST_ITEM_INDEX = 1;
    private PointIndicator point_indicater;


    @Override
    public void initView() {
        AppTitleView appTitleView = getTitleView();
        appTitleView.initViewsVisible(true, true, false, false);
        appTitleView.setOnLeftButtonClickListener(this);
        appTitleView.setAppTitle("测试");

        viewpager = findViewById(R.id.viewpager);
        point_indicater = findViewById(R.id.point_indicater);


    }

    @Override
    public boolean isShowTitle() {
        return true;
    }


    @Override
    public void initData() {

        list = new ArrayList<>();
        list.add(new InviteThirdFragment());
        list.add(new InviteFirstFragment());
        list.add(new InviteSecondFragment());
        list.add(new InviteThirdFragment());
        list.add(new InviteFirstFragment());
        viewpager.setAdapter(new InviteFragmentAdapter(getSupportFragmentManager(), list));
        viewpager.setCurrentItem(1, false);
        viewpager.setOffscreenPageLimit(4);//记数从0开始!!! 设置预加载的个数
        viewpager.setPageTransformer(false, new DepthPageTransformer());
        viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageSelected(int pPosition) {
                mIsChanged = true;
                if (pPosition > POINT_LENGTH) {// 末位之后,跳转到首位(1)
                    mCurrentPagePosition = FIRST_ITEM_INDEX;
                } else if (pPosition < FIRST_ITEM_INDEX) {// 首位之前,跳转到末尾(N)
                    mCurrentPagePosition = POINT_LENGTH;
                } else {
                    mCurrentPagePosition = pPosition;
                }
            }


            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {
            }


            @Override
            public void onPageScrollStateChanged(int pState) {
                if (ViewPager.SCROLL_STATE_IDLE == pState) {
                    if (mIsChanged) {
                        mIsChanged = false;
                        viewpager.setCurrentItem(mCurrentPagePosition, false);
                    }
                }
            }
        });
        point_indicater.bindViewPager(viewpager);
    }

    @Override
    public int setViewLayout() {
        return R.layout.activity_testviewpager;
    }

    @Override
    public void onLeftButtonClick(View v) {
        finish();
    }


}
- 布局


android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:clipChildren="false"
android:orientation="vertical">



  • adapter

public class InviteFragmentAdapter extends FragmentPagerAdapter {
    private List list;

    public InviteFragmentAdapter(FragmentManager fm, List list) {
        super(fm);
        this.list = list;
    }

    @Override
    public Fragment getItem(int arg0) {
        // TODO Auto-generated method stub
        return list.get(arg0);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return list.size();
    }


}

  • 中间缩放的viewpager的方法
public class DepthPageTransformer implements ViewPager.PageTransformer {
//    private static final float MIN_SCALE = 0.70f;
//    private static final float MIN_ALPHA = 0.5f;
//
//    @Override
//    public void transformPage(View page, float position) {
//        if (position < -1 || position > 1) {
//            page.setAlpha(MIN_ALPHA);
//            page.setScaleX(MIN_SCALE);
//            page.setScaleY(MIN_SCALE);
//        } else if (position <= 1) { // [-1,1]
//            float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
//            if (position < 0) {
//                float scaleX = 1 + 0.1f * position;
//                page.setScaleX(scaleX);
//                page.setScaleY(scaleX);
//            } else {
//                float scaleX = 1 - 0.1f * position;
//                page.setScaleX(scaleX);
//                page.setScaleY(scaleX);
//            }
//            page.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
//        }
//    }

    private static final float MIN_SCALE = 0.9f;

    @Override
    public void transformPage(View view, float position) {
        /**
         * 过滤那些 <-1 或 >1 的值,使它区于【-1,1】之间
         */
        if (position < -1) {
            position = -1;
        } else if (position > 1) {
            position = 1;
        }
        /**
         * 判断是前一页 1 + position ,右滑 pos -> -1 变 0
         * 判断是后一页 1 - position ,左滑 pos -> 1 变 0
         */
        float tempScale = position < 0 ? 1 + position : 1 - position; // [0,1]
        float scaleValue = MIN_SCALE + tempScale * 0.1f; // [0,1]
        view.setScaleX(scaleValue);
        view.setScaleY(scaleValue);
    }
}

  • 自定义指示器PointIndicator
/**
 * 圆点指示器
 */
public class PointIndicator extends View implements OnPageScrollListener {

    private Context mContext;

    private int mNormalColor;
    private int mSelectColor;
    private int mPointSize;
    private int mPointSpace;

    private Paint mNormalPaint;
    private Paint mSelectPaint;

    private int mCount;
    private int enterPosition;
    private int leavePosition;
    private float percent;

    public PointIndicator(Context context) {
        this(context, null);
    }

    public PointIndicator(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PointIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(context, attrs);
        initPaint();
    }

    private void initPaint() {
        mNormalPaint = new Paint();
        mNormalPaint.setColor(mNormalColor);
        mNormalPaint.setAntiAlias(true);

        mSelectPaint = new Paint();
        mSelectPaint.setColor(mSelectColor);
        mSelectPaint.setAntiAlias(true);

        mCount = 3;
    }

    private void initAttrs(Context context, AttributeSet attrs) {
        mContext = context;

        mNormalColor = 0x66cccccc;
        mSelectColor = 0xfffdd63b;
        mPointSize = dp2px(3f);
        mPointSpace = dp2px(3f);

        // 自定义属性
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PointIndicator);
        mNormalColor = ta.getColor(R.styleable.PointIndicator_normal_color, mNormalColor);
        mSelectColor = ta.getColor(R.styleable.PointIndicator_select_color, mSelectColor);
        mPointSize = (int) ta.getDimension(R.styleable.PointIndicator_point_size, mPointSize);
        mPointSpace = (int) ta.getDimension(R.styleable.PointIndicator_point_space, mPointSpace);
        ta.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }

    private int measureWidth(int measureSpec) {
        int size = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (specMode) {
            case MeasureSpec.EXACTLY:
                size = specSize;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.UNSPECIFIED:
                size = mCount * mPointSize + (mCount - 1) * mPointSpace;
                break;
        }
        return size;
    }

    private int measureHeight(int measureSpec) {
        int size = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (specMode) {
            case MeasureSpec.EXACTLY:
                size = specSize;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.UNSPECIFIED:
                size = mPointSize;
                break;
        }
        return size;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 绘制normalPoint
        drawNormalPoint(canvas);
        // 绘制selectPoint
        drawSelectPoint(canvas);

    }

    private void drawSelectPoint(Canvas canvas) {
        float cx;
        if (enterPosition > leavePosition) {
            cx = (leavePosition + 0.5f) * mPointSize
                    + leavePosition * mPointSpace
                    + (mPointSize + mPointSpace) * percent;
        } else {
            cx = (leavePosition + 0.5f) * mPointSize
                    + leavePosition * mPointSpace
                    - (mPointSize + mPointSpace) * percent;
        }
        float cy = getHeight() / 2;
        float radius = mPointSize / 2f;
        canvas.drawCircle(cx, cy, radius, mSelectPaint);
    }

    private void drawNormalPoint(Canvas canvas) {
        for (int i = 0; i < mCount; i++) {
            float cx = mPointSize / 2f + (mPointSize + mPointSpace) * i;
            float cy = getHeight() / 2;
            float radius = mPointSize / 2f;
            canvas.drawCircle(cx, cy, radius, mNormalPaint);
        }
    }

    public void bindViewPager(ViewPager viewPager) {
        if (viewPager != null) {
            if (viewPager.getAdapter() != null) {
                mCount = viewPager.getAdapter().getCount()-2;
                new ViewPagerHelper().bindScrollListener(viewPager, this);
                requestLayout(); // 绑定ViewPager后指示器重新布局,因为指示器的数量和宽度可能有变化
            }
        }
    }

    @Override
    public void onPageScroll(int enterPosition, int leavePosition, float percent) {
        this.enterPosition = enterPosition;
        this.leavePosition = leavePosition;
        this.percent = percent;
        postInvalidate();
    }

    @Override
    public void onPageSelected(int position) {

    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }

    private int dp2px(float dpValue) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dpValue,
                mContext.getResources().getDisplayMetrics());
    }

}

  • 所用到的辅助类
/**
 * ViewPage的页面滚动监听器
 */
public interface OnPageScrollListener {
    /**
     * 页面滚动时调用
     *
     * @param enterPosition 进入页面的位置
     * @param leavePosition 离开的页面的位置
     * @param percent       滑动百分比
     */
    void onPageScroll(int enterPosition, int leavePosition, float percent);

    /**
     * 页面选中时调用
     *
     * @param position 选中页面的位置
     */
    void onPageSelected(int position);

    /**
     * 页面滚动状态变化时调用
     *
     * @param state 页面的滚动状态
     */
    void onPageScrollStateChanged(int state);
}
/**
 * Created by XiaoJianjun on 2017/1/8.
 * ViewPager辅助类
 */
public class ViewPagerHelper implements ViewPager.OnPageChangeListener {

    private double mLastPositionOffsetSum;  // 上一次滑动总的偏移量
    private OnPageScrollListener mOnPageScrollListener;

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        // 当前总的偏移量
        position = position - 1;
        float currentPositionOffsetSum = position + positionOffset;
        // 上次滑动的总偏移量大于此次滑动的总偏移量,页面从右向左进入(手指从右向左滑动)
        boolean rightToLeft = mLastPositionOffsetSum <= currentPositionOffsetSum;
        // 页面正在被拖动或惯性滑动
        if (currentPositionOffsetSum == mLastPositionOffsetSum) return;
        int enterPosition;
        int leavePosition;
        float percent;
        if (rightToLeft) {  // 从右向左滑
            enterPosition = (positionOffset == 0.0f) ? position : position + 1;
            leavePosition = enterPosition - 1;
            percent = (positionOffset == 0.0f) ? 1.0f : positionOffset;
            if (enterPosition == 3) {
                return;
            }
        } else {            // 从左向右滑
            enterPosition = position;
            leavePosition = position + 1;
            percent = 1 - positionOffset;
            if (enterPosition == -1) {
                return;
            }
        }

        Logger.d("enterPosition=" + enterPosition + "   leavePosition=" + leavePosition + "      percent=" + percent);
        if (mOnPageScrollListener != null) {
            mOnPageScrollListener.onPageScroll(enterPosition, leavePosition, percent);
        }
        mLastPositionOffsetSum = currentPositionOffsetSum;
    }

    @Override
    public void onPageSelected(int position) {
        if (mOnPageScrollListener != null) {
            mOnPageScrollListener.onPageSelected(position);
        }
    }

    /**
     * @param state 当前滑动状态
     *              ViewPager.SCROLL_STATE_IDLE     页面处于闲置、稳定状态,即没被拖动也没惯性滑动
     *              ViewPager.SCROLL_STATE_DRAGGING 页面正在被用户拖动,即手指正在拖动状态
     *              Viewpager.SCROLL_STATE_SETTLING 页面处于即将到达最终状态的过程,即手指松开后惯性滑动状态
     */
    @Override
    public void onPageScrollStateChanged(int state) {
        if (mOnPageScrollListener != null) {
            mOnPageScrollListener.onPageScrollStateChanged(state);
        }
    }

    public void bindScrollListener(ViewPager viewPager, OnPageScrollListener onPageScrollListener) {
        mOnPageScrollListener = onPageScrollListener;
        viewPager.addOnPageChangeListener(this);
    }
}

  • 就这几个类,原理网上一大堆就不细述了,需要注意的事设置adapter的顺序,先设置adapter在设置属性才行

你可能感兴趣的:(viewpager+fragment实现中间大两边小无限循环带指示器)