一、使用
1.導(dǎo)入依賴
repositories {
...
maven {
url "https://jitpack.io"
}
}
dependencies {
...
implementation 'com.github.hackware1993:MagicIndicator:1.6.0' // for support lib
implementation 'com.github.hackware1993:MagicIndicator:1.7.0' // for androidx
}
2.將 MagicIndicator 添加到您的布局 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="net.lucode.hackware.magicindicatordemo.MainActivity">
<net.lucode.hackware.magicindicator.MagicIndicator
android:id="@+id/magic_indicator"
android:layout_width="match_parent"
android:layout_height="40dp" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</LinearLayout>
3.通過(guò)代碼找到MagicIndicator颠印,初始化
MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
CommonNavigator commonNavigator = new CommonNavigator(this);
commonNavigator.setAdapter(new CommonNavigatorAdapter() {
@Override
public int getCount() {
return mTitleDataList == null ? 0 : mTitleDataList.size();
}
@Override
public IPagerTitleView getTitleView(Context context, final int index) {
ColorTransitionPagerTitleView colorTransitionPagerTitleView = new ColorTransitionPagerTitleView(context);
colorTransitionPagerTitleView.setNormalColor(Color.GRAY);
colorTransitionPagerTitleView.setSelectedColor(Color.BLACK);
colorTransitionPagerTitleView.setText(mTitleDataList.get(index));
colorTransitionPagerTitleView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mViewPager.setCurrentItem(index);
}
});
return colorTransitionPagerTitleView;
}
@Override
public IPagerIndicator getIndicator(Context context) {
// public static final int MODE_MATCH_EDGE = 0; // 直線寬度 == title寬度 - 2 * mXOffset
// public static final int MODE_WRAP_CONTENT = 1; // 直線寬度 == title內(nèi)容寬度 - 2 * mXOffset
// public static final int MODE_EXACTLY = 2; // 直線寬度 == mLineWidth
LinePagerIndicator indicator = new LinePagerIndicator(context);
indicator.setMode(LinePagerIndicator.MODE_WRAP_CONTENT);
return indicator;
}
});
magicIndicator.setNavigator(commonNavigator);
4.使用 ViewPager
ViewPagerHelper.bind(magicIndicator, mViewPager);
or work with Fragment Container(switch Fragment by hide()勉失、show()):
mFramentContainerHelper = new FragmentContainerHelper(magicIndicator);
// ...
mFragmentContainerHelper.handlePageSelected(pageIndex); // invoke when switch Fragment
5. MagicIndicator 擴(kuò)展
5.1 實(shí)現(xiàn) IPagerTitleView 以自定義選項(xiàng)卡
public class MyPagerTitleView extends View implements IPagerTitleView {
public MyPagerTitleView(Context context) {
super(context);
}
@Override
public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
}
@Override
public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
}
@Override
public void onSelected(int index, int totalCount) {
}
@Override
public void onDeselected(int index, int totalCount) {
}
}
5.2實(shí)現(xiàn) IPagerIndicator 自定義指標(biāo)
public class MyPagerIndicator extends View implements IPagerIndicator {
public MyPagerIndicator(Context context) {
super(context);
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
public void onPositionDataProvide(List<PositionData> dataList) {
}
}
5.3使用 CommonPagerTitleView 加載自定義布局 xml
項(xiàng)目使用案列(kotlin)
字體選中加粗,指示器樣式...
字體選中加粗
xml
....
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/qb_px_50"
android:background="@drawable/social_circle_tab_bg">
<net.lucode.hackware.magicindicator.MagicIndicator
android:id="@+id/magicIndicator"
android:layout_width="wrap_content"
android:layout_height="@dimen/qb_px_40"
android:layout_marginStart="@dimen/qb_px_20" />
<TextView
android:id="@+id/tvSelection"
android:layout_width="wrap_content"
android:layout_height="@dimen/qb_px_40"
android:layout_alignParentEnd="true"
android:drawableEnd="@mipmap/iocn_triangle"
android:drawablePadding="@dimen/qb_px_5"
android:gravity="center_vertical"
android:paddingStart="@dimen/qb_px_20"
android:paddingEnd="@dimen/qb_px_20"
android:text="全部"
android:textColor="@color/color_FFAA00"
android:textSize="13dp"
tools:ignore="UseCompatTextViewDrawableXml" />
</RelativeLayout>
....
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="-10dp"
android:background="@color/color_F7F7F7"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
kotlin:
private fun initPagerView() {
// FragmentPageResumeAdapter 3代表的是下面有三個(gè)fragment
val pagerAdapter = object : FragmentPageResumeAdapter(supportFragmentManager, 3) {
override fun getFragment(position: Int): Fragment {
return SocialCircleDetailsViewPagerFragment.newInstance(
id,
(position + 1).toString()
)
}
}
// 綁定pagerAdapter
binding.viewPager.adapter = pagerAdapter
// viewpager預(yù)加載
binding.viewPager.offscreenPageLimit = 3
val tabTitles = mutableListOf("綜合", "最新", "最熱")
// CommonNavigator:通用的ViewPager指示器,包含PagerTitle和PagerIndicator
// CircleNavigator:圓圈式的指示器
val commonNavigator = CommonNavigator(this)
commonNavigator.isAdjustMode = false
commonNavigator.adapter = object : CommonNavigatorAdapter() {
//指示器標(biāo)題設(shè)置
override fun getTitleView(context: Context?, index: Int): IPagerTitleView {
/**
* ClipPagerTitleView: 類似今日頭條切換效果的指示器標(biāo)題
* ColorTransitionPagerTitleView:兩種顏色過(guò)渡的指示器標(biāo)題
* CommonPagerTitleView:通用的指示器標(biāo)題,子元素內(nèi)容由外部提供窟社,事件回傳給外部
* DummyPagerTitleView:空指示器標(biāo)題痊末,用于只需要指示器而不需要title的需求
* SimplePagerTitleView:帶文本的指示器標(biāo)題
*/
val titleView = object : SimplePagerTitleView(context) {
// 指示器標(biāo)題選擇狀態(tài)設(shè)置
override fun onSelected(index: Int, totalCount: Int) {
super.onSelected(index, totalCount)
setTypeface(Typeface.DEFAULT, Typeface.BOLD) // 選中的話,字體加粗
}
override fun onDeselected(index: Int, totalCount: Int) {
super.onDeselected(index, totalCount)
setTypeface(Typeface.DEFAULT, Typeface.NORMAL) // 沒(méi)選中的話内狸,字體為默認(rèn)樣式
}
}
titleView.apply {
normalColor = getColor(R.color.color_66) // 指示器標(biāo)題默認(rèn)顏色
selectedColor = getColor(R.color.app_color) // 指示器標(biāo)題選紅顏色
textSize = 13f // 指示器標(biāo)題字體大小
text = tabTitles[index] // 指示器標(biāo)題文本
}
// 指示器標(biāo)題點(diǎn)擊事件
titleView.setOnClickListener {
binding.viewPager.currentItem = index
}
return titleView
}
// 數(shù)量
override fun getCount(): Int = tabTitles.size
// 指示器樣式
override fun getIndicator(context: Context?): IPagerIndicator {
return LinePagerIndicator(context).apply {
/*
* LinePagerIndicator:直線viewpager指示器检眯,帶顏色漸變
* BezierPagerIndicator:貝塞爾曲線ViewPager指示器,帶顏色漸變
* TestPagerIndicator:用于測(cè)試的指示器昆淡,可用來(lái)檢測(cè)自定義的IMeasurablePagerTitleView是否正確測(cè)量?jī)?nèi)容區(qū)域
* TriangularPagerIndicator:帶有小尖角的直線指示器
* WrapPagerIndicator:包裹住內(nèi)容區(qū)域的指示器锰瘸,類似天天快報(bào)的切換效果,需要和IMeasurablePagerTitleView配合使用
*/
mode = LinePagerIndicator.MODE_EXACTLY // 直線寬度 == mLineWidth
lineHeight = this.resources.getDimension(R.dimen.qb_px_1) // 指示器高度
lineWidth = this.resources.getDimension(R.dimen.qb_px_10) // 指示器寬度
roundRadius = UIUtil.dip2px(context, 1.0).toFloat() // 指示器圓角
// 設(shè)置指示器開始和結(jié)束動(dòng)畫效果
startInterpolator = AccelerateInterpolator()
endInterpolator = DecelerateInterpolator(2.0f)
yOffset = resources.getDimension(R.dimen.qb_px_7) // 指示器相對(duì)于底部的偏移量
setColors(getColor(this,R.color.color_FFAA00 ) ) // 指示器顏色
}
}
}
binding.magicIndicator.navigator = commonNavigator
ViewPagerHelper.bind(binding.magicIndicator, binding.viewPager)
}
2.指示器文本添加圖片
val pagerAdapter = object : FragmentPageResumeAdapter(supportFragmentManager, 2) {
override fun getFragment(position: Int): Fragment {
return when (position) {
0 -> UserDynamicFragment.newInstance(userId)
else -> UserCollectionFragment.newInstance(userId, data?.collectSecStatus)
}
}
}
binding.viewPager.adapter = pagerAdapter
// title 列表
val tabTitles = listOf("動(dòng)態(tài)${data?.momentsCount ?: "0"}", "收藏${data?.collectCount ?: "0"}")
val commonNavigator = CommonNavigator(this)
commonNavigator.isAdjustMode = true //自適應(yīng)模式昂灵,適用于數(shù)目固定的避凝、少量的title
commonNavigator.adapter = object : CommonNavigatorAdapter() {
@SuppressLint("InflateParams")
override fun getTitleView(context: Context?, index: Int): IPagerTitleView {
val titleView = ColorTransitionPagerTitleView(this).apply {
normalColor = getColor(this, R.color.color_666666)
selectedColor = getColor(this, R.color.color_FFAA00)
textSize = 14f
text = tabTitles[index]
setOnClickListener {
binding.viewPager.currentItem = index
}
}
// 核心代碼:設(shè)置圖片
val badgePagerTitleView = BadgePagerTitleView(this)
badgePagerTitleView.innerPagerTitleView = titleView
if (index == 1) {
// 設(shè)置加載圖片
val badgeImageView = LayoutInflater.from(this)
.inflate(R.layout.view_collect_lock_view, null) as ImageView
badgePagerTitleView.badgeView = badgeImageView
// 設(shè)置X軸
badgePagerTitleView.xBadgeRule = BadgeRule(
BadgeAnchor.CONTENT_RIGHT, //角標(biāo)的錨點(diǎn)
DensityUtil.dip2px(5f)
)
// 設(shè)置Y軸
badgePagerTitleView.yBadgeRule = BadgeRule(
BadgeAnchor.CONTENT_TOP, //角標(biāo)的錨點(diǎn)
DensityUtil.dip2px(5f)
)
}
badgePagerTitleView.isAutoCancelBadge = false
return badgePagerTitleView
}
override fun getCount(): Int = tabTitles.size
override fun getIndicator(context: Context?): IPagerIndicator {
return LinePagerIndicator(context).apply {
setColors(getColor(this,R.color.color_FFBB33) )
}
}
}
binding.magicIndicator.navigator = commonNavigator
ViewPagerHelper.bind(binding.magicIndicator, binding.viewPager)
}
3.設(shè)置自定義指示器圖片
private void initMagicIndicator4() {
MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator4);
CommonNavigator commonNavigator = new CommonNavigator(this);
commonNavigator.setAdapter(new CommonNavigatorAdapter() {
@Override
public int getCount() {
return mDataList.size();
}
@Override
public IPagerTitleView getTitleView(Context context, final int index) {
SimplePagerTitleView simplePagerTitleView = new ColorTransitionPagerTitleView(context);
simplePagerTitleView.setNormalColor(Color.GRAY);
simplePagerTitleView.setSelectedColor(Color.WHITE);
simplePagerTitleView.setText(mDataList.get(index));
simplePagerTitleView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mViewPager.setCurrentItem(index);
}
});
return simplePagerTitleView;
}
@SuppressLint("UseCompatLoadingForDrawables")
@Override
public IPagerIndicator getIndicator(Context context) {
CommonPagerIndicator indicator = new CommonPagerIndicator(context);
indicator.setDrawableHeight(UIUtil.dip2px(context,6));
indicator.setDrawableWidth(UIUtil.dip2px(context,15));
indicator.setIndicatorDrawable(getResources().getDrawable(R.drawable.icon_indicator));
indicator.setMode(LinePagerIndicator.MODE_EXACTLY);
indicator.setStartInterpolator(new AccelerateInterpolator());
indicator.setEndInterpolator(new DecelerateInterpolator(1.6f));
indicator.setYOffset(UIUtil.dip2px(context,5));
return indicator;
}
});
magicIndicator.setNavigator(commonNavigator);
LinearLayout titleContainer = commonNavigator.getTitleContainer(); // must after setNavigator
titleContainer.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
titleContainer.setDividerDrawable(new ColorDrawable() {
@Override
public int getIntrinsicWidth() {
return UIUtil.dip2px(FixedTabExampleActivity.this, 15);
}
});
final FragmentContainerHelper fragmentContainerHelper = new FragmentContainerHelper(magicIndicator);
fragmentContainerHelper.setInterpolator(new OvershootInterpolator(2.0f));
fragmentContainerHelper.setDuration(300);
mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
fragmentContainerHelper.handlePageSelected(position);
}
});
}
最后,補(bǔ)充
1.通用的indicator眨补,支持外面設(shè)置Drawable
public class CommonPagerIndicator extends View implements IPagerIndicator {
public static final int MODE_MATCH_EDGE = 0; // drawable寬度 == title寬度 - 2 * mXOffset
public static final int MODE_WRAP_CONTENT = 1; // drawable寬度 == title內(nèi)容寬度 - 2 * mXOffset
public static final int MODE_EXACTLY = 2;
private int mMode; // 默認(rèn)為MODE_MATCH_EDGE模式
private Drawable mIndicatorDrawable;
// 控制動(dòng)畫
private Interpolator mStartInterpolator = new LinearInterpolator();
private Interpolator mEndInterpolator = new LinearInterpolator();
private float mDrawableHeight;
private float mDrawableWidth;
private float mYOffset;
private float mXOffset;
private List<PositionData> mPositionDataList;
private Rect mDrawableRect = new Rect();
public CommonPagerIndicator(Context context) {
super(context);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mIndicatorDrawable == null) {
return;
}
if (mPositionDataList == null || mPositionDataList.isEmpty()) {
return;
}
// 計(jì)算錨點(diǎn)位置
PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position);
PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1);
float leftX;
float nextLeftX;
float rightX;
float nextRightX;
if (mMode == MODE_MATCH_EDGE) {
leftX = current.mLeft + mXOffset;
nextLeftX = next.mLeft + mXOffset;
rightX = current.mRight - mXOffset;
nextRightX = next.mRight - mXOffset;
mDrawableRect.top = (int) mYOffset;
mDrawableRect.bottom = (int) (getHeight() - mYOffset);
} else if (mMode == MODE_WRAP_CONTENT) {
leftX = current.mContentLeft + mXOffset;
nextLeftX = next.mContentLeft + mXOffset;
rightX = current.mContentRight - mXOffset;
nextRightX = next.mContentRight - mXOffset;
mDrawableRect.top = (int) (current.mContentTop - mYOffset);
mDrawableRect.bottom = (int) (current.mContentBottom + mYOffset);
} else { // MODE_EXACTLY
leftX = current.mLeft + (current.width() - mDrawableWidth) / 2;
nextLeftX = next.mLeft + (next.width() - mDrawableWidth) / 2;
rightX = current.mLeft + (current.width() + mDrawableWidth) / 2;
nextRightX = next.mLeft + (next.width() + mDrawableWidth) / 2;
mDrawableRect.top = (int) (getHeight() - mDrawableHeight - mYOffset);
mDrawableRect.bottom = (int) (getHeight() - mYOffset);
}
mDrawableRect.left = (int) (leftX + (nextLeftX - leftX) * mStartInterpolator.getInterpolation(positionOffset));
mDrawableRect.right = (int) (rightX + (nextRightX - rightX) * mEndInterpolator.getInterpolation(positionOffset));
mIndicatorDrawable.setBounds(mDrawableRect);
invalidate();
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
protected void onDraw(Canvas canvas) {
if (mIndicatorDrawable != null) {
mIndicatorDrawable.draw(canvas);
}
}
@Override
public void onPositionDataProvide(List<PositionData> dataList) {
mPositionDataList = dataList;
}
public Drawable getIndicatorDrawable() {
return mIndicatorDrawable;
}
public void setIndicatorDrawable(Drawable indicatorDrawable) {
mIndicatorDrawable = indicatorDrawable;
}
public Interpolator getStartInterpolator() {
return mStartInterpolator;
}
public void setStartInterpolator(Interpolator startInterpolator) {
mStartInterpolator = startInterpolator;
}
public Interpolator getEndInterpolator() {
return mEndInterpolator;
}
public void setEndInterpolator(Interpolator endInterpolator) {
mEndInterpolator = endInterpolator;
}
public int getMode() {
return mMode;
}
public void setMode(int mode) {
if (mode == MODE_EXACTLY || mode == MODE_MATCH_EDGE || mode == MODE_WRAP_CONTENT) {
mMode = mode;
} else {
throw new IllegalArgumentException("mode " + mode + " not supported.");
}
}
public float getDrawableHeight() {
return mDrawableHeight;
}
public void setDrawableHeight(float drawableHeight) {
mDrawableHeight = drawableHeight;
}
public float getDrawableWidth() {
return mDrawableWidth;
}
public void setDrawableWidth(float drawableWidth) {
mDrawableWidth = drawableWidth;
}
public float getYOffset() {
return mYOffset;
}
public void setYOffset(float yOffset) {
mYOffset = yOffset;
}
public float getXOffset() {
return mXOffset;
}
public void setXOffset(float xOffset) {
mXOffset = xOffset;
}
}
2 非手指跟隨的小圓點(diǎn)指示器
public class DotPagerIndicator extends View implements IPagerIndicator {
private List<PositionData> mDataList;
private float mRadius;
private float mYOffset;
private int mDotColor;
private float mCircleCenterX;
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public DotPagerIndicator(Context context) {
super(context);
mRadius = UIUtil.dip2px(context, 3);
mYOffset = UIUtil.dip2px(context, 3);
mDotColor = Color.WHITE;
}
@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) {
super.onDraw(canvas);
mPaint.setColor(mDotColor);
canvas.drawCircle(mCircleCenterX, getHeight() - mYOffset - mRadius, mRadius, mPaint);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
public float getRadius() {
return mRadius;
}
public void setRadius(float radius) {
mRadius = radius;
invalidate();
}
public float getYOffset() {
return mYOffset;
}
public void setYOffset(float yOffset) {
mYOffset = yOffset;
invalidate();
}
public int getDotColor() {
return mDotColor;
}
public void setDotColor(int dotColor) {
mDotColor = dotColor;
invalidate();
}
}