Android开发

Android 好用的万能指示器magicindicator

2021-11-08  本文已影响0人  遇见编程

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
<?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.通过代码找到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<PositionData> dataList) {
     }
 }
5.3使用 CommonPagerTitleView 加载自定义布局 xml

项目使用案列(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代表的是下面有三个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<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;
        }

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

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

上一篇下一篇

猜你喜欢

热点阅读