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 实现,具体效果如下:
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 全面总结