Android 好用的万能指示器magicindicator

https://github.com/hackware1993/MagicIndicator

一、使用

1.导入依赖
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



    

    


3.通过代码找到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内容宽度 - 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 扩展
5.1 实现 IPagerTitleView 以自定义选项卡
  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实现 IPagerIndicator 自定义指标
 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 dataList) {
     }
 }
5.3使用 CommonPagerTitleView 加载自定义布局 xml

项目使用案列(kotlin)

字体选中加粗,指示器样式...

字体选中加粗

xml

    ....
 

  

   

 
....

 

kotlin:

 private fun initPagerView() {
        // FragmentPageResumeAdapter   3代表的是下面有三个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预加载
        binding.viewPager.offscreenPageLimit = 3
        val tabTitles = mutableListOf("综合", "最新", "最热")
        // CommonNavigator:通用的ViewPager指示器,包含PagerTitle和PagerIndicator
       //  CircleNavigator:圆圈式的指示器
        val commonNavigator = CommonNavigator(this)
        commonNavigator.isAdjustMode = false
        commonNavigator.adapter = object : CommonNavigatorAdapter() {
            //指示器标题设置
            override fun getTitleView(context: Context?, index: Int): IPagerTitleView {
                /**
                  * ClipPagerTitleView: 类似今日头条切换效果的指示器标题
                  * ColorTransitionPagerTitleView:两种颜色过渡的指示器标题
                  * CommonPagerTitleView:通用的指示器标题,子元素内容由外部提供,事件回传给外部
                  * DummyPagerTitleView:空指示器标题,用于只需要指示器而不需要title的需求
                  * SimplePagerTitleView:带文本的指示器标题
                  */
                val titleView = object : SimplePagerTitleView(context) {
                    // 指示器标题选择状态设置
                    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) // 没选中的话,字体为默认样式
                    }
                }
                titleView.apply {
                    normalColor = getColor(R.color.color_66) // 指示器标题默认颜色
                    selectedColor = getColor(R.color.app_color) // 指示器标题选红颜色
                    textSize = 13f // 指示器标题字体大小
                    text = tabTitles[index] // 指示器标题文本
                }
                // 指示器标题点击事件
                titleView.setOnClickListener {
                    binding.viewPager.currentItem = index
                }
                return titleView
            }

            // 数量
            override fun getCount(): Int = tabTitles.size

           // 指示器样式 
            override fun getIndicator(context: Context?): IPagerIndicator {
                return LinePagerIndicator(context).apply {
/*  
*  LinePagerIndicator:直线viewpager指示器,带颜色渐变
*  BezierPagerIndicator:贝塞尔曲线ViewPager指示器,带颜色渐变
*  TestPagerIndicator:用于测试的指示器,可用来检测自定义的IMeasurablePagerTitleView是否正确测量内容区域
*  TriangularPagerIndicator:带有小尖角的直线指示器
*  WrapPagerIndicator:包裹住内容区域的指示器,类似天天快报的切换效果,需要和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() // 指示器圆角
                    // 设置指示器开始和结束动画效果
                    startInterpolator = AccelerateInterpolator() 
                    endInterpolator = DecelerateInterpolator(2.0f)
                    yOffset = resources.getDimension(R.dimen.qb_px_7) // 指示器相对于底部的偏移量
                    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("动态${data?.momentsCount ?: "0"}", "收藏${data?.collectCount ?: "0"}")

        val commonNavigator = CommonNavigator(this)
        commonNavigator.isAdjustMode = true //自适应模式,适用于数目固定的、少量的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
                    }
                }

              // 核心代码:设置图片
                val badgePagerTitleView = BadgePagerTitleView(this)
                badgePagerTitleView.innerPagerTitleView = titleView
                if (index == 1) {
                  // 设置加载图片
                    val badgeImageView = LayoutInflater.from(this)
                                    .inflate(R.layout.view_collect_lock_view, null) as ImageView
                    badgePagerTitleView.badgeView = badgeImageView
                   // 设置X轴
                    badgePagerTitleView.xBadgeRule = BadgeRule(
                            BadgeAnchor.CONTENT_RIGHT, //角标的锚点
                            DensityUtil.dip2px(5f)
                        )
                   // 设置Y轴
                    badgePagerTitleView.yBadgeRule = BadgeRule(
                            BadgeAnchor.CONTENT_TOP, //角标的锚点
                            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.设置自定义指示器图片

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);
            }
        });
    }

最后,补充

1.通用的indicator,支持外面设置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内容宽度 - 2 * mXOffset
    public static final int MODE_EXACTLY = 2;

    private int mMode;  // 默认为MODE_MATCH_EDGE模式
    private Drawable mIndicatorDrawable;

    // 控制动画
    private Interpolator mStartInterpolator = new LinearInterpolator();
    private Interpolator mEndInterpolator = new LinearInterpolator();

    private float mDrawableHeight;
    private float mDrawableWidth;
    private float mYOffset;
    private float mXOffset;

    private List 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;
        }

        // 计算锚点位置
        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 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 非手指跟随的小圆点指示器

public class DotPagerIndicator extends View implements IPagerIndicator {
    private List 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 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();
    }
}

你可能感兴趣的:(Android 好用的万能指示器magicindicator)