ViewPager+Fragment+MagicIndicator实现多子界面带导航栏滑动

MagicIndicator

ViewPager 指示器框架 —— MagicIndicator
在这里插入图片描述

简单使用示例

添加依赖

dependencies {
    compile project(':magicindicator')
}

在布局文件中添加MagicIndicator

<?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="net.lucode.hackware.magicindicatordemo.MainActivity">

    <net.lucode.hackware.magicindicator.MagicIndicator
        android:id="@+id/magic_indicator"
        android:layout_width="match_parent"
        android:layout_height="@dimen/navigator_common_height"
        android:background="#d43d3d" />

    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@android:color/white" />

</LinearLayout>

在代码中简单设置

final MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
final CommonNavigator commonNavigator = new CommonNavigator(this);
commonNavigator.setAdapter(new CommonNavigatorAdapter() {

    @Override
    public int getCount() {
        return mDataList == null ? 0 : mDataList.size();
    }

    @Override
    public IPagerTitleView getItemView(Context context, final int index) {
        ClipPagerTitleView clipPagerTitleView = new ClipPagerTitleView(context);

        clipPagerTitleView.setText(mDataList.get(index));
        clipPagerTitleView.setTextColor(Color.parseColor("#f2c4c4"));
        clipPagerTitleView.setClipColor(Color.WHITE);

        clipPagerTitleView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPager.setCurrentItem(index);
            }
        });

        return clipPagerTitleView;
    }

    @Override
    public IPagerIndicator getIndicator(Context context) {
        return null;    // 没有指示器,因为title的指示作用已经很明显了
    }
});
magicIndicator.setNavigator(commonNavigator);

上面这代码明显没有和 ViewPager 相关联,因此滑动 ViewPager 时是看不到切换效果的,给 ViewPager 添加一个监听器:

mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        magicIndicator.onPageScrolled(position, positionOffset, positionOffsetPixels);
    }

    @Override
    public void onPageSelected(int position) {
        magicIndicator.onPageSelected(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        magicIndicator.onPageScrollStateChanged(state);
    }
});

导航栏

MagicIndicator 中目前内置了两个 IPagerNavigator (后续不断增多),分别是:

  • CommonNavigator
  • CircleNavigator

CommonNavigator 是一个通用的指示器,也就是指我们常见的横向的、带有很多子元素的的指示器。子元素中可带文本、图标以及你想要的任何View。

/**
 * 提供给外部的参数配置
 */
/****************************************************/
private boolean mAdjustMode;   // 自适应模式,为true表示title均分宽度,适用于数目固定的、少量的title
private boolean mEnablePivotScroll; // 启动中心点滚动
private float mScrollPivotX = 0.5f; // 滚动中心点 0.0f - 1.0f
private boolean mSmoothScroll = true;   // 是否平滑滚动,适用于 !mAdjustMode && !mFollowTouch
private boolean mFollowTouch = true;    // 是否手指跟随滚动
private int mRightPadding;
private int mLeftPadding;
/****************************************************/

mAdjustMode

自适应模式,默认为 false,适用于数目固定的、子元素较少的指示器,比如讯飞输入法的皮肤界面:

自适应模式下,每个子元素的宽度默认是均分的。
如果你不想均分,重写 CommonNavigatorAdapter 的 getTitleWeight 方法吧。同时,其余6个属性都不生效了。

在这里插入图片描述

mFollowTouch

手指跟随滚动,默认为true,此模式下,只要 ViewPager 滚动,子元素也会跟着滚动,否则要等手指抬起后(onPageSelected)才开始滚动,效果如小飞读报:

在这里插入图片描述

mScrollPivotX

滚动的中心点,默认为0.5f,也就是居中,就像刚才小飞读报的效果,如果你设置为0.15f,就是网易新闻的效果啦:

在这里插入图片描述

mEnablePivotScroll

是否开启中心点滚动,默认为 false,开启后,选中某一个子元素时,将会根据 mScrollPivotX 滚动到合适的位置,否则,只有当子元素不完全可见时,才会触发滚动,使得子元素完全可见。

mSmoothScroll

是否平滑滚动,默认为 true,在 !mAdjustMode && !mFollowTouch 下生效。也就是当子元素不完全可见时,触发滚动使之完全可见时是否平滑滚动。

mLeftPadding,mRightPadding

CommonNavigator 内部的 HorizontalScrollView 的 左右padding,默认为0。今日头条的效果:
可以看到,添加频道按钮和搜索按钮是悬在指示器上边的,但是指示器中的最后一个元素却可以滚出来。

在这里插入图片描述

内建指示器

MagicIndicator 目前内建了好几种指示器,基本可以满足绝大部分需求,并且每一种指示器都支持通过 插值器(Interpolator) 来微调效果。
如果你想换一个效果,只需在 getItemView 里返回不同的指示器标题(IPagerTitleView),在 getIndicator 里返回不同的指示器(IPagerIndicator)
下面演示一下使用内建的指示器实现效果图中的小尖角式切换效果:

final MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
final CommonNavigator commonNavigator = new CommonNavigator(this);
commonNavigator.setAlwaysScrollToCenter(true);
commonNavigator.setAdapter(new CommonNavigatorAdapter() {

    @Override
    public int getCount() {
        return mDataList == null ? 0 : mDataList.size();
    }

    @Override
    public IPagerTitleView getItemView(Context context, final int index) {
        SimplePagerTitleView simplePagerTitleView = new SimplePagerTitleView(context);

        simplePagerTitleView.setText(mDataList.get(index));
        simplePagerTitleView.setNormalColor(Color.parseColor("#333333"));
        simplePagerTitleView.setSelectedColor(Color.parseColor("#e94220"));

        simplePagerTitleView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPager.setCurrentItem(index);
            }
        });

        return simplePagerTitleView;
    }

    @Override
    public IPagerIndicator getIndicator(Context context) {
        TriangularPagerIndicator indicator = new TriangularPagerIndicator(context);
        indicator.setLineColor(Color.parseColor("#e94220"));
        return indicator;
    }
});
magicIndicator.setNavigator(commonNavigator);

指示器标题

在getItemView()方法中,用CommonPagerTitleView自定义指示器标题的滑动效果。

final MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
CommonNavigator commonNavigator = new CommonNavigator(this);
commonNavigator.setAdapter(new CommonNavigatorAdapter() {

    @Override
    public int getCount() {
        return mDataList == null ? 0 : mDataList.size();
    }

    @Override
    public IPagerTitleView getItemView(Context context, final int index) {
        CommonPagerTitleView commonPagerTitleView = new CommonPagerTitleView(MainActivity.this);
        commonPagerTitleView.setContentView(R.layout.simple_pager_title_layout);

        // 初始化
        final ImageView titleImg = (ImageView) commonPagerTitleView.findViewById(R.id.title_img);
        titleImg.setImageResource(R.mipmap.ic_launcher);
        final TextView titleText = (TextView) commonPagerTitleView.findViewById(R.id.title_text);
        titleText.setText(mDataList.get(index));

        commonPagerTitleView.setOnPagerTitleChangeListener(new CommonPagerTitleView.OnPagerTitleChangeListener() {

            @Override
            public void onSelected(int index) {
                titleText.setTextColor(Color.RED);
            }

            @Override
            public void onDeselected(int index) {
                titleText.setTextColor(Color.BLACK);
            }

            @Override
            public void onLeave(int index, float leavePercent, boolean leftToRight) {
                titleImg.setScaleX(1.3f + (0.8f - 1.3f) * leavePercent);
                titleImg.setScaleY(1.3f + (0.8f - 1.3f) * leavePercent);
            }

            @Override
            public void onEnter(int index, float enterPercent, boolean leftToRight) {
                titleImg.setScaleX(0.8f + (1.3f - 0.8f) * enterPercent);
                titleImg.setScaleY(0.8f + (1.3f - 0.8f) * enterPercent);
            }
        });

        commonPagerTitleView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPager.setCurrentItem(index);
            }
        });

        return commonPagerTitleView;
    }

    @Override
    public IPagerIndicator getIndicator(Context context) {
        return null;
    }
});
magicIndicator.setNavigator(commonNavigator);

效果如下:

在这里插入图片描述

监听ViewPager变化

MagicIndicator 同样考虑到了 ViewPager 内容变化的情况,当 ViewPager 内容发生变化时,除了调用 PagerAdapter.notifyDataSetChanged ,还记得先调用 IPagerNavigator.notifyDataSetChanged

// 导航栏datalist发生变化时
mDataList.clear();
mDataList.add("欢迎关注");
mDataList.add("我的博客");
mDataList.add("hackware.lucode.net");

// 调用 IPagerNavigator.notifyDataSetChanged
commonNavigator.notifyDataSetChanged();
// 调用 PagerAdapter.notifyDataSetChanged
mAdapter.notifyDataSetChanged();

扩展 MagicIndicator

继承 IPagerNavigator 打造任意的指示效果
继承 IPagerTitleView 打造任意效果的指示器标题
继承 IPagerIndicator 打造任意效果的指示器
使用 CommonPagerTitleView 加载自定义布局

扩展IPagerNavigator

实现 IPagerNavigator 接口,使用 Canvas 实现,具体效果如下:

扩展IPagerNavigator的demo项目

在这里插入图片描述

public class DummyCircleNavigator extends View implements IPagerNavigator {

    public DummyCircleNavigator(Context context) {
        super(context);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }

    // 被添加到 magicindicator 时调用
    @Override
    public void onAttachToMagicIndicator() {
    }

    // 从 magicindicator 上移除时调用
    @Override
    public void onDetachFromMagicIndicator() {
    }

    // 当指示数目改变时调用
    @Override
    public void notifyDataSetChanged() {
    }
}

根据用户设置的 mCircleSpacing,mRadius,mCircleCount,结合当前的宽度,我们可以计算出每一个圆的圆心位置:

private List<PointF> mCirclePoints = new ArrayList<PointF>();

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    prepareCirclePoints();
}

private void prepareCirclePoints() {
    mCirclePoints.clear();
    if (mCircleCount > 0) {
        int y = getHeight() / 2;
        int measureWidth = mCircleCount * mRadius * 2 + (mCircleCount - 1) * mCircleSpacing;
        int centerSpacing = mRadius * 2 + mCircleSpacing;
        int startX = (getWidth() - measureWidth) / 2 + mRadius;
        for (int i = 0; i < mCircleCount; i++) {
            PointF pointF = new PointF(startX, y);
            mCirclePoints.add(pointF);
            startX += centerSpacing;
        }
    }
}

绘制圆形

@Override
protected void onDraw(Canvas canvas) {
    drawDeselectedCircles(canvas);
    drawSelectedCircle(canvas);
}

private void drawDeselectedCircles(Canvas canvas) {
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(mStrokeWidth);
    mPaint.setColor(mCircleColor);
    for (int i = 0, j = mCirclePoints.size(); i < j; i++) {
        PointF pointF = mCirclePoints.get(i);
        canvas.drawCircle(pointF.x, pointF.y, mRadius, mPaint);
    }
}

private void drawSelectedCircle(Canvas canvas) {
    mPaint.setStyle(Paint.Style.FILL);
    if (mCirclePoints.size() > 0) {
        float selectedCircleX = mCirclePoints.get(mCurrentIndex).x;
        canvas.drawCircle(selectedCircleX, getHeight() / 2, mRadius, mPaint);
    }
}

给 mCurrentIndex 赋值,同时,mCircleCount 变化时需要重新计算圆心位置:

@Override
public void onPageSelected(int position) {
    mCurrentIndex = position;
    invalidate();
}

public void setCircleCount(int circleCount) {
    mCircleCount = circleCount;
}

@Override
public void notifyDataSetChanged() {
    prepareCirclePoints();
    invalidate();
}

扩展IPagerTitleView

我们来实现这种效果:在滑动一段距离后且手指未抬起时去改变颜色。

在这里插入图片描述

直接继承 TextView 并实现 IPagerTitleView,在 onEnter 回调中做判断,如果 enterPercent 大于设定的阈值,就将文字颜色设为选中颜色,否则,设为未选中颜色,代码如下:

public class ColorFlipPagerTitleView extends TextView implements IPagerTitleView {
    private int mNormalColor;
    private int mSelectedColor;
    private float mChangePercent = 0.45f;

    public ColorFlipPagerTitleView(Context context) {
        super(context);
        setGravity(Gravity.CENTER);
        int padding = UIUtil.dip2px(context, 10);
        setPadding(padding, 0, padding, 0);
        setSingleLine();
        setEllipsize(TextUtils.TruncateAt.END);
    }

    @Override
    public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
        if (leavePercent >= mChangePercent) {
            setTextColor(mNormalColor);
        } else {
            setTextColor(mSelectedColor);
        }
    }

    @Override
    public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
        if (enterPercent >= mChangePercent) {
            setTextColor(mSelectedColor);
        } else {
            setTextColor(mNormalColor);
        }
    }

    // 部分 setter、getter 略
}

还想提供内容的边界,那就继承 IMeasuablePagerTitleView 吧,并实现以下方法:

@Override
public int getContentLeft() {
    Rect bound = new Rect();
    getPaint().getTextBounds(getText().toString(), 0, getText().length(), bound);
    int contentWidth = bound.width();
    return getLeft() + getWidth() / 2 - contentWidth / 2;
}

@Override
public int getContentTop() {
    Paint.FontMetrics metrics = getPaint().getFontMetrics();
    float contentHeight = metrics.bottom - metrics.top;
    return (int) (getHeight() / 2 - contentHeight / 2);
}

@Override
public int getContentRight() {
    Rect bound = new Rect();
    getPaint().getTextBounds(getText().toString(), 0, getText().length(), bound);
    int contentWidth = bound.width();
    return getLeft() + getWidth() / 2 + contentWidth / 2;
}

@Override
public int getContentBottom() {
    Paint.FontMetrics metrics = getPaint().getFontMetrics();
    float contentHeight = metrics.bottom - metrics.top;
    return (int) (getHeight() / 2 + contentHeight / 2);
}

扩展 IPagerIndicator

目前内置的 IPagerIndicator 全是跟随手指滑动的,我们来打造一个简单的、不跟随的指示器。这个指示器会在被选中的 IPagerTitleView 下方显示一个小点。

在这里插入图片描述

public class DotPagerIndicator extends View implements IPagerIndicator {
    private List<PositionData> mDataList;
    private float mRadius;
    private float mYOffset;
    private float mCircleCenterX;
    private int mDotColor;
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public DotPagerIndicator(Context context) {
        super(context);
    }

    @Override
    public void onPageSelected(int position) {
        if (mDataList == null || mDataList.isEmpty()) {
            return;
        }
        PositionData data = mDataList.get(position);
        mCircleCenterX = data.mLeft + data.width() / 2;
        invalidate();
    }

    @Override
    public void onPositionDataProvide(List<PositionData> dataList) {
        mDataList = dataList;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(mDotColor);
        canvas.drawCircle(mCircleCenterX, getHeight() - mYOffset - mRadius, mRadius, mPaint);
    }

    // 一些 getter、setter 略
}

CommonPagerTitleView 加载自定义布局

CommonPagerTitleView 继承 FrameLayout 并实现了 IMeasurablePagerTitleView,它支持将自定义的布局文件设置进来,并且把 onEnter、onLeave . . . getContentLeft、getContentTop 等方法都回调出去

public class CommonPagerTitleView extends FrameLayout implements IMeasurablePagerTitleView {
    private OnPagerTitleChangeListener mOnPagerTitleChangeListener;
    private ContentPositionDataProvider mContentPositionDataProvider;

    public CommonPagerTitleView(Context context) {
        super(context);
    }

    public void setContentView(int layoutId) {
        View child = LayoutInflater.from(getContext()).inflate(layoutId, null);
        setContentView(child, null);
    }

    @Override
    public void onSelected(int index, int totalCount) {
        if (mOnPagerTitleChangeListener != null) {
            mOnPagerTitleChangeListener.onSelected(index, totalCount);
        }
    }

    // 省略一部分方法

    public interface OnPagerTitleChangeListener {
        void onSelected(int index, int totalCount);

        void onDeselected(int index, int totalCount);

        void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight);

        void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight);
    }

    public interface ContentPositionDataProvider {
        int getContentLeft();

        int getContentTop();

        int getContentRight();

        int getContentBottom();
    }
}

定义布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:paddingLeft="10dp"
    android:paddingRight="10dp">

    <ImageView
        android:id="@+id/title_img"
        android:layout_width="20dp"
        android:layout_height="20dp" />

    <TextView
        android:id="@+id/title_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp" />

</LinearLayout>

布局文件设置到 CommonPagerTitleView 并进行初始化:

@Override
public IPagerTitleView getTitleView(Context context, final int index) {
    CommonPagerTitleView commonPagerTitleView = new CommonPagerTitleView(MainActivity.this);
    commonPagerTitleView.setContentView(R.layout.simple_pager_title_layout);

    // 初始化
    final ImageView titleImg = (ImageView) commonPagerTitleView.findViewById(R.id.title_img);
    titleImg.setImageResource(R.mipmap.ic_launcher);
    final TextView titleText = (TextView) commonPagerTitleView.findViewById(R.id.title_text);
    titleText.setText(mDataList.get(index));

    commonPagerTitleView.setOnPagerTitleChangeListener(new CommonPagerTitleView.OnPagerTitleChangeListener() {

        @Override
        public void onSelected(int index, int totalCount) {
            titleText.setTextColor(Color.RED);
        }

        @Override
        public void onDeselected(int index, int totalCount) {
            titleText.setTextColor(Color.BLACK);
        }

        @Override
        public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
            titleImg.setScaleX(1.3f + (0.8f - 1.3f) * leavePercent);
            titleImg.setScaleY(1.3f + (0.8f - 1.3f) * leavePercent);
        }

        @Override
        public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
            titleImg.setScaleX(0.8f + (1.3f - 0.8f) * enterPercent);
            titleImg.setScaleY(0.8f + (1.3f - 0.8f) * enterPercent);
        }
    });

    return commonPagerTitleView;
}

效果如下:

在这里插入图片描述

ViewPager

简单示例

新建展示布局

新建一个Layout布局,用来容纳三个子界面。这里面要引入viewPager组件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/new_broad_list_bg"
    android:clickable="true">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navigationIcon="@drawable/ic_menu"
        app:title="智能互动"
        app:titleTextColor="@color/c_ffffff" />

    <net.lucode.hackware.magicindicator.MagicIndicator
        android:id="@+id/magic_indicator"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_45"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/toolbar" />

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_0"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/magic_indicator" />

</androidx.constraintlayout.widget.ConstraintLayout>

新建子布局

新建在以上view pager中展示的子界面
信息采集子界面,布局界面如下:

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="信息采集"
        android:id="@+id/textView"
        android:textSize="18dp" />
</LinearLayout>


信息采集子界面的加载代码:

public class DataCollectFragment extends BaseFragment implements View.OnClickListener {

    @Override
    protected int setContentView() {
        return R.layout.fragment_data_collect;
    }
    ...

服务器监测子界面

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="服务器监测"
        android:id="@+id/textView"
        android:textSize="18dp" />
</LinearLayout>

服务器监测子界面的加载代码:

public class ServerMonitorFragment extends BaseFragment implements View.OnClickListener {

    @Override
    protected int setContentView() {
        return R.layout.fragment_server_monitor;
    }
    ...

新建PagerAdapter

编写adapter类,继承FragmentPagerAdapter类:

public class ServerPagerAdapter extends FragmentPagerAdapter {

    private List<Fragment> fragmentList;

    public ServerPagerAdapter(FragmentManager fm,List<Fragment> fragmentList) {
        super(fm);
        this.fragmentList = fragmentList;
    }

    @Override
    public Fragment getItem(int position) {
        return fragmentList.get(position);
    }


    @Override
    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        super.setPrimaryItem(container, position, object);
    }

    @Override
    public int getCount() {
        return fragmentList.size();
    }


}

view pager & magicIndicator 初始化

public class ServerFragment extends BaseFragment {

    @Nullable
    @BindView(R.id.toolbar)
    Toolbar toolbar;
    @BindView(R.id.magic_indicator)
    MagicIndicator magicIndicator;
    @BindView(R.id.view_pager)
    ViewPager viewPager;
    private List<String> navStrList = Arrays.asList(new String[]{
            "信息采集", "服务器监测"});
    private List<Fragment> serverFragmentList = new ArrayList<>();
    private ServerPagerAdapter serverPagerAdapter;

    final static String TAG = ServerFragment.class.getSimpleName();


    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        initViewPager();
        initMagicIndicator();

    }

    private void initViewPager() {
        serverFragmentList.add(new DataCollectFragment());
        serverFragmentList.add(new ServerMonitorFragment());
        serverPagerAdapter = new ServerPagerAdapter(getChildFragmentManager(), serverFragmentList);
        viewPager.setAdapter(serverPagerAdapter);
    }

// magicIndicator初始化,和view pager的绑定
private void initMagicIndicator() {
        magicIndicator.setBackgroundResource(R.color.colorPrimary);
        CommonNavigator commonNavigator = new CommonNavigator(mContext);
        commonNavigator.setAdjustMode(true);
        commonNavigator.setAdapter(new CommonNavigatorAdapter() {
            @Override
            public int getCount() {
                return navStrList == null ? 0 : navStrList.size();
            }

            @Override
            public IPagerTitleView getTitleView(Context context, int index) {
                SimplePagerTitleView pagerTitleView = new SimplePagerTitleView(mContext);
                pagerTitleView.setText(navStrList.get(index));
                pagerTitleView.setTextSize(16);
                pagerTitleView.setNormalColor(Color.parseColor("#295B69"));
                pagerTitleView.setSelectedColor(Color.parseColor("#8A9CA3"));
                pagerTitleView.setOnClickListener(v -> {
                    viewPager.setCurrentItem(index);
                });
                return pagerTitleView;
            }

            @Override
            public IPagerIndicator getIndicator(Context context) {
                LinePagerIndicator linePagerIndicator = new LinePagerIndicator(mContext);
                linePagerIndicator.setStartInterpolator(new AccelerateInterpolator());
                linePagerIndicator.setEndInterpolator(new DecelerateInterpolator(1.6f));
                linePagerIndicator.setYOffset(UIUtil.dip2px(mContext, 39));
                linePagerIndicator.setLineHeight(UIUtil.dip2px(mContext, 3));
                linePagerIndicator.setColors(Color.parseColor("#7DA0AA"));
                return linePagerIndicator;
            }

            @Override
            public float getTitleWeight(Context context, int index) {
                return 2.0f;
            }
        });
        magicIndicator.setNavigator(commonNavigator);
        ViewPagerHelper.bind(magicIndicator, viewPager);

    }
    ...

翻页动画

ViewPager有个方法叫做:
setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) 用于设置ViewPager切换时的动画效果。

position说明:
当前显示页为0,前一页为-1,后一页为1,滑动过程中数值不断变大或变小,所以为float类型。

DepthPageTransformer

public class DepthPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.75f;
    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();
        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setAlpha(0);
        } else if (position <= 0) { // [-1,0]
            // Use the default slide transition when moving to the left page
            view.setAlpha(1);
            view.setTranslationX(0);
            view.setScaleX(1);
            view.setScaleY(1);
        } else if (position <= 1) { // (0,1]
            // Fade the page out.
            view.setAlpha(1 - position);
            // Counteract the default slide transition
            view.setTranslationX(pageWidth * -position);
            // Scale the page down (between MIN_SCALE and 1)
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0);
        }
    }
}

// 调用
vp.setPageTransformer(false,new DepthPageTransformer());

效果如下:

在这里插入图片描述

ZoomOutPageTransformer

public class ZoomOutPageTransformer implements ViewPager.PageTransformer
{
    private static final float MIN_SCALE = 0.85f;
    private static final float MIN_ALPHA = 0.5f;
    @SuppressLint("NewApi")
    public void transformPage(View view, float position)
    {
        int pageWidth = view.getWidth();
        int pageHeight = view.getHeight();
        Log.e("TAG", view + " , " + position + "");
        if (position < -1)
        { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setAlpha(0);
        } else if (position <= 1) //a页滑动至b页 ; a页从 0.0 -1 ;b页从1 ~ 0.0
        { // [-1,1]
            // Modify the default slide transition to shrink the page as well
            float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
            float vertMargin = pageHeight * (1 - scaleFactor) / 2;
            float horzMargin = pageWidth * (1 - scaleFactor) / 2;
            if (position < 0)
            {
                view.setTranslationX(horzMargin - vertMargin / 2);
            } else
            {
                view.setTranslationX(-horzMargin + vertMargin / 2);
            }
            // Scale the page down (between MIN_SCALE and 1)
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
            // Fade the page relative to its size.
            view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE)
                    / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
        } else
        { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0);
        }
    }
}

效果如下:

在这里插入图片描述

开源框架ViewPagerTransforms

Github地址:ViewPagerTransforms
效果如下:

在这里插入图片描述

翻页监听

// 调用方法,在监听器中重写以下三个方法即可对翻页不同状态监听
addOnPageChangeListener()
ViewPager.OnPageChangeListener()

/**
* 页面滑动状态停止前一直调用
* position:当前点击滑动页面的位置
* positionOffset:当前页面偏移的百分比
* positionOffsetPixels:当前页面偏移的像素位置
*/
onPageScrolled(int position, float positionOffset, int positionOffsetPixels)

/**
* 滑动后显示的页面和滑动前不同,调用
* position:选中显示页面的位置
*/
onPageSelected(int position)

/**
* 页面状态改变时调用
* state:当前页面的状态
* SCROLL_STATE_IDLE:空闲状态
* SCROLL_STATE_DRAGGING:滑动状态
* SCROLL_STATE_SETTLING:滑动后滑翔的状态
*/
onPageScrollStateChanged(int state)

简单使用示例:

vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        Log.e("vp","滑动中=====position:"+ position + "   positionOffset:"+ positionOffset + "   positionOffsetPixels:"+positionOffsetPixels);
    }

    @Override
    public void onPageSelected(int position) {
        Log.e("vp","显示页改变=====postion:"+ position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        switch (state) {
            case ViewPager.SCROLL_STATE_IDLE:
                Log.e("vp","状态改变=====SCROLL_STATE_IDLE====静止状态");
                break;
            case ViewPager.SCROLL_STATE_DRAGGING:
                Log.e("vp","状态改变=====SCROLL_STATE_DRAGGING==滑动状态");
                break;
            case ViewPager.SCROLL_STATE_SETTLING:
                Log.e("vp","状态改变=====SCROLL_STATE_SETTLING==滑翔状态");
                break;
        }
    }
});

实现轮播图效果

轮播图开源控件:banner

参考文章:
MagicIndicato系列之一 —— 使用MagicIndicator打造千变万化的ViewPager指示器
MagicIndicator系列之二 —— MagicIndicator使用指南
MagicIndicator系列之三 —— MagicIndicator原理浅析及扩展MagicIndicator的4种方式
ViewPager+Fragment实现多个子界面滑动
ViewPager 全面总结

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值