美文网首页Android开发
Android 好用的万能指示器magicindicator

Android 好用的万能指示器magicindicator

作者: 遇见编程 | 来源:发表于2021-11-08 11:57 被阅读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();
        }
    }
    
    

    相关文章

      网友评论

        本文标题:Android 好用的万能指示器magicindicator

        本文链接:https://www.haomeiwen.com/subject/miruzltx.html