android 之 indicatorView的实现分析

android 之indicatorView的实现分析

使用一个软件常注意到刚使用软件时会有一个导览页,通过滑动页面来显示不同的图,底部有用于指示在哪一页的圆点,这就是我们将要实现的indicator(指示器),接来我们介绍在android 中如何实现?

indicator的实现

首先,我们需要了解indicator的组成,一般来说,它是由几个圆点组成,翻到对应页时,圆点颜色改变,有些indicator圆点还有字母或者数字,如图:

在这里插入图片描述
在这里插入图片描述

接下来,我们需要知道如何绘出这些圆和圆内的数字。要确定一个圆,需要知道圆的半径和圆心位置x,y,半径是由我们自行配置的,所以我们需要确定每个圆圆心的位置,而y是固定的,所以我们需要知道每个圆x的值,如图,我们可以观察到它的规律如下:第一个圆的x 就等于圆的半径,从第二个圆开始,当前圆的圆心x 坐标为 上一个圆的x 坐标 + (radius * 2 + mSpace)。 其中mSpace 是圆之间的间距。
图源网络
(图源网络)

那么,接下来我们绘制这些圆,生成一个继承View的CircleIndicatorView,在这个View中定义相关的属性,如半径,圆点颜色等,我们需要定义一个indicator类,用来存放圆点数据:

public static class Indicator{
    public float cx; // 圆心x坐标
    public float cy; // 圆心y 坐标
}

接下来,计算每个圆的圆点圆心的位置,第一个圆心的x坐标为半径加上边界值,y的值取View的高度除二,取中间位置,上面已经介绍了它的计算规律,我们只需按规律算出相应的值保存在mIndicators(存放圆心的一个List)中:

private void measureIndicator(){
    mIndicators.clear();//mIndicators为存放每个点的圆心的List
    float cx = 0;
    for(int i=0;i<mCount;i++){
        Indicator indicator = new Indicator();
        if( i== 0){
            cx = mRadius + mStrokeWidth;//第一个点,mStrokeWidth为边界值
        }else{
            cx += (mRadius + mStrokeWidth) * 2 +mSpace;//第二、三......个
        }

        indicator.cx = cx;
        indicator.cy = getMeasuredHeight() / 2;//getMeasuredHeight() 获取View的实际大小

        mIndicators.add(indicator);
    }
}

有了圆心的数据,我们就可以绘制圆了,我们需要定义画圆心和字符的Paint类(mCirclePaint、mTextPaint),Paint可以用于画几何图形,文本和bitmap,setStyle用于设置样式,样式有三种:
Paint.Style.FILL:填充内部
Paint.Style.FILL_AND_STROKE :填充内部和描边
Paint.Style.STROKE :描边
Paint还有其他属性:
setAntiAlias(boolean a) //设置抗锯齿,如果不设置,加载位图的时候可能会出现锯齿状的边界,如果设置,边界就会变的稍微有点模糊,锯齿就看不到了。
setDither(boolean dither) //设置是否抖动,如果不设置感觉就会有一些僵硬的线条,如果设置图像就会看的更柔和一些

protected void onDraw(Canvas canvas) {

    for(int i=0;i<mIndicators.size();i++){

        Indicator indicator = mIndicators.get(i);
        float x = indicator.cx;
        float y = indicator.cy;

        if(mSelectPosition == i){//当前页
            mCirclePaint.setStyle(Paint.Style.FILL);//填充
            mCirclePaint.setColor(mSelectColor);//颜色可以再配置中自行设置
        }else{
            mCirclePaint.setColor(mDotNormalColor);
            if(mFillMode != FillMode.NONE){//圆内非空
                mCirclePaint.setStyle(Paint.Style.STROKE);//描边
            }else{
                mCirclePaint.setStyle(Paint.Style.FILL);

            }
        }
        canvas.drawCircle(x,y, mRadius, mCirclePaint);                 
}

如果圆内有字母数字我们还需要再进行绘制,首先需要判断FillMode的值是什么,若为字母或数字则可进一步绘制,用Canvas来绘制文本需要给文本定位,我们借助Rect来获得文本的宽、高,Paint.getTextBounds用于获取文本的宽高,然后我们计算文本的开始位置,使文本居中:

// 绘制小圆点中的内容
    if(mFillMode != FillMode.NONE){
        String text = "";
        if(mFillMode == FillMode.LETTER){//圆内为字母
            if(i >= 0 && i<LETTER.length){
                text = LETTER[i];
            }
        }else{
            text = String.valueOf(i+1);
        }
        Rect bound = new Rect();
        mTextPaint.getTextBounds(text,0,text.length(),bound);//用于获取文本的宽高
        int textWidth = bound.width();
        int textHeight = bound.height();

        float textStartX = x - textWidth / 2;
        float textStartY = y + textHeight / 2;
        canvas.drawText(text,textStartX,textStartY, mTextPaint);
    }

}

还有一个细节,我们在点击相应的小圆点时会跳转到相应页面,我们来实现它:

@Override
public boolean onTouchEvent(MotionEvent event) {
    float xPoint = 0;
    float yPoint = 0;
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            xPoint = event.getX();
            yPoint = event.getY();
            handleActionDown(xPoint,yPoint);
            break;
    }
    return super.onTouchEvent(event);
}
private void handleActionDown(float xDis,float yDis){
    for(int i=0;i<mIndicators.size();i++){
        Indicator indicator = mIndicators.get(i);
        if(xDis < (indicator.cx + mRadius+mStrokeWidth)
                && xDis >=(indicator.cx - (mRadius + mStrokeWidth))
                && yDis >= (yDis - (indicator.cy+mStrokeWidth))
                && yDis <(indicator.cy+mRadius+mStrokeWidth)){
             // 找到了点击的Indicator
             // 是否允许切换ViewPager
             if(mIsEnableClickSwitch){
                 mViewPager.setCurrentItem(i,false);
             }

             // 回调
            if(mOnIndicatorClickListener!=null){
                mOnIndicatorClickListener.onSelected(i);
            }
            break;
        }
    }
}

我们需要与ViewPaper配合使用,所有还需要一个与ViewPaper关联的方法,并在其中设置ViewPaper监听该View:

public void setUpWithViewPager(ViewPager viewPager){
    releaseViewPager();
    if(viewPager == null){
        return;
    }
    mViewPager = viewPager;
    mViewPager.addOnPageChangeListener(this);
    int count = mViewPager.getAdapter().getCount();
    setCount(count);
}

基本的已实现,那么我们来应用它,在xml文件中,我们可以在这里设置很多属性,这些属性对应着attr.xml中的内容:

<resources>
    <declare-styleable name="CircleIndicatorView">
        <attr name="indicatorRadius" format="dimension"/>
        <attr name="indicatorBorderWidth" format="dimension"/>
        <attr name="indicatorSpace" format="dimension"/>
        <attr name="indicatorTextColor" format="color"/>
        <attr name="indicatorColor" format="color"/>
        <attr name="indicatorSelectColor" format="color"/>
        <attr name="enableIndicatorSwitch" format="boolean"/>
        <attr name="fill_mode">
            <enum name="letter" value="0"/>
            <enum name="number" value="1"/>
            <enum name="none" value="2"/>
        </attr>
    </declare-styleable>
</resources>
<com.zhouwei.indicatorview.CircleIndicatorView
    android:id="@+id/indicator_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_marginBottom="50dp"
    android:layout_centerHorizontal="true"
    app:indicatorSelectColor="#00A882"
    app:fill_mode="letter"
    app:indicatorBorderWidth="2dp"
    app:indicatorRadius="8dp"
    app:indicatorColor="@color/colorAccent"
    app:indicatorTextColor="@android:color/white"
    /> 

在activity中与ViewPaper关联:

mIndicatorView = (CircleIndicatorView) findViewById(R.id.indicator_view);
// 关联ViewPager
mIndicatorView.setUpWithViewPager(mViewPager);

实验效果:
在这里插入图片描述
到这里已经介绍完了,该控件已经有开源代码了,地址为:源码
我们可以在配置文件中设置即可直接使用:
在build.gradle(project)中添加:

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

在build.gradle(app)中dependencies添加:

implementation 'com.github.pinguo-zhouwei:CircleIndicatorView:v1.0.0'

sync一下即可直接使用

参考文章:
indicator的实现
一起看画布Android Canvas
Android文本测量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值