一步步教你寫StepView(流程指示器)

StepView

Step by step. Step indicator. Flow indicator离钝。

github地址:stepview

step-icon.png

snapshot

snapshot.png

like this

splash.png

also like this:VerticalStepView

vertical_stepview.gif

Yeah,also like this

vertical_snapshot.png

how to use

Add it in your root build.gradle at the end of repositories:

repositories {
        ...
        maven { url "https://jitpack.io" }
    }

Step 2. Add the dependency

dependencies {
        compile 'com.github.baoyachi:StepView:1.2'
}       

use HorizontalStepView

in xml

    <com.baoyachi.stepview.HorizontalStepView
            android:id="@+id/step_view0"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textColor="@android:color/white"
            android:textSize="14sp"
            />

use code

 HorizontalStepView setpview0 = (HorizontalStepView) mView.findViewById(R.id.step_view0);
        List<String> list0 = new ArrayList<>();
        list0.add("接單");
        list0.add("打包");
        list0.add("出發(fā)");
        list0.add("送單");
        list0.add("完成");
        list0.add("支付");
        setpview0.setStepsViewIndicatorComplectingPosition(2)//設(shè)置完成的步數(shù)
                .setStepViewTexts(list0)//總步驟
                .setStepsViewIndicatorCompletedLineColor(ContextCompat.getColor(getActivity(), android.R.color.white))//設(shè)置StepsViewIndicator完成線的顏色
                .setStepsViewIndicatorUnCompletedLineColor(ContextCompat.getColor(getActivity(), R.color.uncompleted_text_color))//設(shè)置StepsViewIndicator未完成線的顏色
                .setStepViewComplectedTextColor(ContextCompat.getColor(getActivity(), android.R.color.white))//設(shè)置StepsView text完成線的顏色
                .setStepViewUnComplectedTextColor(ContextCompat.getColor(getActivity(), R.color.uncompleted_text_color))//設(shè)置StepsView text未完成線的顏色
                .setStepsViewIndicatorCompleteIcon(ContextCompat.getDrawable(getActivity(), R.drawable.complted))//設(shè)置StepsViewIndicator CompleteIcon
                .setStepsViewIndicatorDefaultIcon(ContextCompat.getDrawable(getActivity(), R.drawable.default_icon))//設(shè)置StepsViewIndicator DefaultIcon
                .setStepsViewIndicatorAttentionIcon(ContextCompat.getDrawable(getActivity(), R.drawable.attention));//設(shè)置StepsViewIndicator AttentionIcon

use VerticalStepView

in xml

    <com.baoyachi.stepview.VerticalStepView
            android:id="@+id/step_view0"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="vertical setpview"
            />

in code


mSetpview0 = (VerticalStepView) mView.findViewById(R.id.step_view0);

        List<String> list0 = new ArrayList<>();
        list0.add("接已提交定案娃圆,等待系統(tǒng)確認(rèn)");
        list0.add("您的商品需要從外地調(diào)撥,我們會盡快處理滓走,請耐心等待");
        list0.add("您的訂單已經(jīng)進(jìn)入亞洲第一倉儲中心1號庫準(zhǔn)備出庫");
        list0.add("您的訂單預(yù)計6月23日送達(dá)您的手中,618期間促銷火爆帽馋,可能影響送貨時間搅方,請您諒解,我們會第一時間送到您的手中");
        list0.add("您的訂單已打印完畢");
        list0.add("您的訂單已揀貨完成");
        list0.add("掃描員已經(jīng)掃描");
        list0.add("打包成功");
        list0.add("配送員【包牙齒】已出發(fā)绽族,聯(lián)系電話【130-0000-0000】姨涡,感謝您的耐心等待,參加評價還能贏取好多禮物哦");
        list0.add("感謝你在京東購物吧慢,歡迎你下次光臨绣溜!");
        mSetpview0.setStepsViewIndicatorComplectingPosition(list0.size() - 2)//設(shè)置完成的步數(shù)
                .setStepViewTexts(list0)//總步驟
                .setStepsViewIndicatorCompletedLineColor(ContextCompat.getColor(getActivity(), android.R.color.white))//設(shè)置StepsViewIndicator完成線的顏色
                .setStepsViewIndicatorUnCompletedLineColor(ContextCompat.getColor(getActivity(), R.color.uncompleted_text_color))//設(shè)置StepsViewIndicator未完成線的顏色
                .setStepViewComplectedTextColor(ContextCompat.getColor(getActivity(), android.R.color.white))//設(shè)置StepsView text完成線的顏色
                .setStepViewUnComplectedTextColor(ContextCompat.getColor(getActivity(), R.color.uncompleted_text_color))//設(shè)置StepsView text未完成線的顏色
                .setStepsViewIndicatorCompleteIcon(ContextCompat.getDrawable(getActivity(), R.drawable.complted))//設(shè)置StepsViewIndicator CompleteIcon
                .setStepsViewIndicatorDefaultIcon(ContextCompat.getDrawable(getActivity(), R.drawable.default_icon))//設(shè)置StepsViewIndicator DefaultIcon
                .setStepsViewIndicatorAttentionIcon(ContextCompat.getDrawable(getActivity(), R.drawable.attention));//設(shè)置StepsViewIndicator AttentionIcon

Use So Simple!!!

See Detail

you can see thereExample

Introduction

現(xiàn)在,絕大部分公司對于一個項目訂單流程控制不外乎從訂單的初始狀態(tài)到訂單完成的狀態(tài),總結(jié)起來就是關(guān)于一個物體(訂單/工單)狀態(tài)變化的事物屬性娄蔼。所以就有了下面類似圖片的效果怖喻,從訂單的初始狀態(tài)到訂單完成。一個好的流程展示岁诉,對于工作效率的提高尤其的關(guān)鍵锚沸。該庫代碼看起來簡潔,加上了詳細(xì)的備注涕癣,看起來不會那么枯燥哗蜈,也方便定制。那么廢話不多說坠韩,直接進(jìn)入正題距潘。

preview

可能當(dāng)前的StepView展示的點是動態(tài)的,有時候是兩個只搁,有時候是三個音比,有時候是四個甚至五個六個,之前有想過展示成下面的效果氢惋。

snapshot_before.png

但是當(dāng)stepview的個數(shù)為2時效果太丑洞翩,一個距最左邊,一個距左右邊焰望。能不忍骚亿,改吧,改了變成下面的效果

snapshot_after.png

上下對比下是不是好看了很多呢熊赖。

What can be learned about this page

好了来屠,我們通過這篇文章可以學(xué)習(xí)哪些知識呢?

  • 自定義控件的加強(qiáng)
  • Canvas,Paint的使用
  • 如何繪制矩形
  • 如何繪制虛線
  • 如何繪制圓
  • 加上小小的數(shù)學(xué)計算俱笛,哈哈捆姜,開個小玩笑,學(xué)過數(shù)學(xué)嫂粟,應(yīng)該不成問題娇未!

start-pre

寫這個開源項目前的熱身

  • 首先我們來畫一個矩形
public class RectView extends View
    {

        public RectView(Context context)
        {
            super(context);
        }

        @Override
        protected void onDraw(Canvas canvas)
        {

            super.onDraw(canvas);
            setBackgroundResource(R.drawable.default_bg);//設(shè)置背景色

            //----繪制矩形------------
            Paint paint = new Paint();// 定義畫筆
            paint.setStyle(Paint.Style.FILL);//設(shè)置實心
            paint.setAntiAlias(true);// 消除鋸齒
            paint.setColor(Color.WHITE);//設(shè)置畫筆顏色
            paint.setStrokeWidth(40);// 設(shè)置paint的外框?qū)挾?            canvas.drawRect(200, 200, 800, 220, paint);//繪制矩形
            //----繪制矩形------------
          }
      }
}

如下圖:繪制矩形,因為繪制矩形需要知道左上又下四個點的坐標(biāo)星虹。
我們設(shè)置好四個點的坐標(biāo)零抬,繪制如下圖

draw_rect.png

接上面繪制圓

            //----繪制圓---------------------
            canvas.drawCircle(350, 350, 100, paint);
            //----繪制圓---------------------

因為繪制圓需要圓點和半徑,定義好后宽涌,展示如下:

draw_circle.png

接著繪制虛線

            //----繪制虛線---------------------
            Paint pathPaint = new Paint();
            pathPaint.setAntiAlias(true);
            pathPaint.setColor(Color.WHITE);
            pathPaint.setStyle(Paint.Style.STROKE);
            pathPaint.setStrokeWidth(2);
            DashPathEffect mEffects = new DashPathEffect(new float[]{8, 8, 8, 8}, 1);
            Path path = new Path();
            path.moveTo(200, 600);
            path.lineTo(800, 600);
            pathPaint.setPathEffect(mEffects);
            canvas.drawPath(path, pathPaint);
            //----繪制虛線---------------------
        }
    }

這里用到一個新控件

DashPathEffect:DashPathEffect是PathEffect類的一個子類,可以使paint畫出類似虛線的樣子,并且可以任意指定虛實的排列方式.
好了平夜,我們通過canvas.drawPath(path, pathPaint)繪制如下圖

draw_effect.png

難點說完了,我們就開始我們的項目吧卸亮。

構(gòu)成

我們拿這個局部圖來做‘栗子’:

因此整個這一張圖是一個組合控件忽妒,

snapshot.png

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <com.baoyachi.stepview.StepsViewIndicator
        android:id="@+id/steps_indicator"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"/>

    <RelativeLayout
        android:id="@+id/rl_text_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
</LinearLayout>

上面是我們自定義的StepsViewIndicator,

draw_stepview.png

下面是我們展示的文字,因為是多個文字組合兼贸,我們下面應(yīng)該是一個ViewGroup容器

draw_stepview_text.png

分析:

  • 如何做到居中顯示

    1.首先得到該控件的寬度段直。

    2.其次得到當(dāng)前控件距離最左邊值。然后計算當(dāng)前有幾個圓溶诞,幾個條線鸯檬?這個是外部傳遞進(jìn)來的,告訴該控件當(dāng)前共有幾個步驟螺垢,并且當(dāng)前正在執(zhí)行到哪一步喧务?

    當(dāng)前控件距離最左邊值,定義為:paddingLeft枉圃;
    當(dāng)前控件的寬度:getWidth()功茴;
    圓的半徑:mCircleRadius;
    兩條線之前的padding值:mLinePadding孽亲;

這么說可能不怎么明白坎穿,舉個‘栗子’。

1.假設(shè)當(dāng)前共有一步: 接單墨林。
意味當(dāng)前有一個圓赁酝,沒有連線,得出paddingLeft

float paddingLeft= (getWidth() - 1*(mCircleRadius*2)-0*mLinePadding)/2旭等;

2.假設(shè)當(dāng)前共有二步:接單->打包
意味當(dāng)前有兩個圓,之前有一條連線衡载,得出paddingLeft

float paddingLeft= (getWidth() - 2*(mCircleRadius*2)-1*mLinePadding)/2搔耕;

3.假設(shè)當(dāng)前共有三步:接單->打包->出發(fā)
意味當(dāng)前有三個圓,之前有二條連線,得出paddingLeft

float paddingLeft= (getWidth() - 3(mCircleRadius2)-2*mLinePadding)/2弃榨;

4.假設(shè)當(dāng)前共有四步:接單->打包->出發(fā)->送單
意味當(dāng)前有四個圓菩收,之前有三條連線,得出paddingLeft

float paddingLeft= (getWidth() - 4*(mCircleRadius*2)-3*mLinePadding)/2鲸睛;

5.假設(shè)當(dāng)前共有五步:接單->打包->出發(fā)->送單->完成

意味著我當(dāng)前有五個圓娜饵,五個圓中間有四條線相連接,這樣我們可以得到五個圓的直徑+四條線的長度官辈,這樣我們就可以得到最左邊的paddingLeft的值:

float paddingLeft= getWidth() - 5*(mCircleRadius*2)-4*mLinePadding箱舞;

綜上所述:
得出一個計算公式:其中mStepNum:當(dāng)前控件所展示的步數(shù)。

float paddingLeft= (getWidth() - 5*(mCircleRadius*2)-4*mLinePadding)/2拳亿;

所以晴股,paddingLeft就可以動態(tài)展示出來,這樣我們就可以知道所有步數(shù)對應(yīng)圓的中心點的位置了:其中mComplectedXPosition為裝所有圓中心點的集合肺魁。

  for(int i = 0; i < mStepNum; i++)
  {
      //先計算全部最左邊的padding值(getWidth()-(圓形直徑+兩圓之間距離)*2)
      float paddingLeft = (getWidth() - mStepNum * mCircleRadius * 2 - (mStepNum - 1) * mLinePadding) / 2;
      mComplectedXPosition.add(paddingLeft + mCircleRadius + i * mCircleRadius * 2 + i * mLinePadding);
  }

Global graph 我們先來看下面這張動態(tài)全局預(yù)覽圖.

我們來看下全局預(yù)覽圖电湘,當(dāng)前StepView的展示是根據(jù)StepView的位置動態(tài)調(diào)整的。

splash.png

是不是有了上面的小知識點的引導(dǎo)鹅经,這張圖看起后寂呛,是不是思路就清晰許多了呢!是的,StepView本身就是有圖片矩形和虛線繪制而成瘾晃,下面我們進(jìn)行拆分贷痪。
一步步解析。

第一步 init() 初始化

因為該view分為完成塊和未完成塊酗捌,因此呢诬,init()主要是在做一些初始化的信息設(shè)置

    private void init()
    {
         mPath = new Path();
        mEffects = new DashPathEffect(new float[]{8, 8, 8, 8}, 1);

        mComplectedXPosition = new ArrayList<>();//初始化
         //設(shè)置未完成mUnCompletedPaint的初始化設(shè)置
        mUnCompletedPaint = new Paint();
        mCompletedPaint = new Paint();
        mUnCompletedPaint.setAntiAlias(true);
        mUnCompletedPaint.setColor(mUnCompletedLineColor);
        mUnCompletedPaint.setStyle(Paint.Style.STROKE);
        mUnCompletedPaint.setStrokeWidth(2);

         //設(shè)置完成mCompletedPaint的初始化設(shè)置
        mCompletedPaint.setAntiAlias(true);
        mCompletedPaint.setColor(mCompletedLineColor);
        mCompletedPaint.setStyle(Paint.Style.STROKE);
        mCompletedPaint.setStrokeWidth(2);
        mUnCompletedPaint.setPathEffect(mEffects);
        mCompletedPaint.setStyle(Paint.Style.FILL);
        .
        .
        .
    }
  • 定義已完成成線(矩形)mCompletedLineHeight的高度:

    mCompletedLineHeight = 0.05f * defaultStepIndicatorNum;

  • 定義圓的半徑:
    mCircleRadius = 0.28f * defaultStepIndicatorNum;

  • 定義線與線之前的間距
    mLinePadding = 0.85f * defaultStepIndicatorNum;//線與線之間的間距
    不要問我這線是怎么算出來的,我也是慢慢調(diào)出來的E昼汀I辛!

  • 定義三個狀態(tài)的圖片

      mCompleteIcon = ContextCompat.getDrawable(getContext(), R.drawable.complted);//已經(jīng)完成的icon
      mAttentionIcon = ContextCompat.getDrawable(getContext(), R.drawable.attention);//正在進(jìn)行的icon
      mDefaultIcon = ContextCompat.getDrawable(getContext(), R.drawable.default_icon);//未完成的icon
    

第二步

  • onMeasure()
    哈哈哪廓,好熟悉的方法狗唉,這里的作用為了測量自身寬和高
        @Override
        protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            int width = defaultStepIndicatorNum * 2;
            if(MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec))
            {
                width = MeasureSpec.getSize(widthMeasureSpec);
            }
            int height = defaultStepIndicatorNum;
            if(MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec))
            {
                height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
            }
            setMeasuredDimension(width, height);
        }

第三步 onSizeChanged(int w, int h, int oldw, int oldh)

動態(tài)計算當(dāng)前view的位置,這里的計算paddingLeft和mComplectedXPosition的方法已經(jīng)在前面詳細(xì)說明

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);
        //獲取中間的高度
        mCenterY = 0.5f * getHeight();
        //獲取左上方Y(jié)的位置,獲取該點的意義是為了方便畫矩形左上的Y位置
        mLeftY = mCenterY - (mCompletedLineHeight / 2);
        //獲取右下方Y(jié)的位置涡真,獲取該點的意義是為了方便畫矩形右下的Y位置
        mRightY = mCenterY + mCompletedLineHeight / 2;

        for(int i = 0; i < mStepNum; i++)
        {
            //先計算全部最左邊的padding值(getWidth()-(圓形直徑+兩圓之間距離)*2)
            float paddingLeft = (getWidth() - mStepNum * mCircleRadius * 2 - (mStepNum - 1) * mLinePadding) / 2;
            mComplectedXPosition.add(paddingLeft + mCircleRadius + i * mCircleRadius * 2 + i * mLinePadding);
        }
        //當(dāng)位置發(fā)生改變時的回調(diào)監(jiān)聽
        mOnDrawListener.ondrawIndicator();
    }

這里onSizeChanged()方法里取mLeftY分俯,mRightY是為了方便之后畫矩形。

第四步 protected synchronized void onDraw(Canvas canvas)

重重之重的方法來了哆料,就會繪制該view所調(diào)用的方法缸剪,我們來看代碼


    @Override
    protected synchronized void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        mUnCompletedPaint.setColor(mUnCompletedLineColor);
        mCompletedPaint.setColor(mCompletedLineColor);

        //-----------------------畫線---------------------------------------------------------------
        for(int i = 0; i < mComplectedXPosition.size() - 1; i++)
        {
            //前一個ComplectedXPosition
            final float preComplectedXPosition = mComplectedXPosition.get(i);
            //后一個ComplectedXPosition
            final float afterComplectedXPosition = mComplectedXPosition.get(i + 1);

            if(i < mComplectingPosition)//判斷在完成之前的所有點
            {
                //判斷在完成之前的所有點,畫完成的線东亦,這里是矩形
                canvas.drawRect(preComplectedXPosition + mCircleRadius - 10, mLeftY, afterComplectedXPosition - mCircleRadius + 10, mRightY, mCompletedPaint);
            } else
            {
                mPath.moveTo(preComplectedXPosition + mCircleRadius, mCenterY);
                mPath.lineTo(afterComplectedXPosition - mCircleRadius, mCenterY);
                canvas.drawPath(mPath, mUnCompletedPaint);
            }
        }
        //-----------------------畫線---------------------------------------------------------------


        //-----------------------畫圖標(biāo)--------------------------------------------------------------
        for(int i = 0; i < mComplectedXPosition.size(); i++)
        {
            final float currentComplectedXPosition = mComplectedXPosition.get(i);
            Rect rect = new Rect((int) (currentComplectedXPosition - mCircleRadius), (int) (mCenterY - mCircleRadius), (int) (currentComplectedXPosition + mCircleRadius), (int) (mCenterY + mCircleRadius));
            if(i < mComplectingPosition)
            {
                mCompleteIcon.setBounds(rect);
                mCompleteIcon.draw(canvas);
            } else if(i == mComplectingPosition && mComplectedXPosition.size() != 1)
            {
                mCompletedPaint.setColor(Color.WHITE);
                canvas.drawCircle(currentComplectedXPosition, mCenterY, mCircleRadius * 1.1f, mCompletedPaint);
                mAttentionIcon.setBounds(rect);
                mAttentionIcon.draw(canvas);
            } else
            {
                mDefaultIcon.setBounds(rect);
                mDefaultIcon.draw(canvas);
            }
        }
        //-----------------------畫圖標(biāo)--------------------------------------------------------------
    }

我們來看杏节,在onDraw()方法里,主要是做了兩部分事情

  • 畫線
    這里我們拿到前一個圓的位置preComplectedXPosition和后一個圓心的位置afterComplectedXPosition,然后進(jìn)行判斷奋渔,小于當(dāng)前正在進(jìn)行的點镊逝,畫完成的線,我們這里用繪制矩形樣式表示嫉鲸。大于當(dāng)前正在進(jìn)行的點撑蒜,畫虛線。

    //-----------------------畫線---------------------------------------------------------------
        for(int i = 0; i < mComplectedXPosition.size() - 1; i++)
        {
            //前一個ComplectedXPosition
            final float preComplectedXPosition = mComplectedXPosition.get(i);
            //后一個ComplectedXPosition
            final float afterComplectedXPosition = mComplectedXPosition.get(i + 1);

            if(i < mComplectingPosition)//判斷在完成之前的所有點
            {
                //判斷在完成之前的所有點玄渗,畫完成的線座菠,這里是矩形
                canvas.drawRect(preComplectedXPosition + mCircleRadius - 10, mLeftY, afterComplectedXPosition - mCircleRadius + 10, mRightY, mCompletedPaint);
            } else
            {
                mPath.moveTo(preComplectedXPosition + mCircleRadius, mCenterY);
                mPath.lineTo(afterComplectedXPosition - mCircleRadius, mCenterY);
                canvas.drawPath(mPath, mUnCompletedPaint);
            }
        }

這里細(xì)心的小伙伴可能有注意到,為什么在畫線的的時候捻爷,為什么canvas.drawRect的時候辈灼,左右會分別-10,+10

    canvas.drawRect(preComplectedXPosition + mCircleRadius - 10, mLeftY, afterComplectedXPosition - mCircleRadius + 10, mRightY, mCompletedPaint);

那是因為當(dāng)畫矩形時也榄,線和圓之間的連接像是有縫隙巡莹,于是我就讓矩形在往左邊多畫了10單位,在右邊多畫了10單位甜紫,這樣他們看起來像是無縫連接一樣降宅。哈哈,但是這間距不能太大囚霸。

  • 畫圓
    這里判斷小于正在進(jìn)行中的為則表示已經(jīng)完成腰根。并且剛好等于正在進(jìn)行中的且不等于一表示正在進(jìn)行中,大于則表示該步驟為完成,分別繪制圖標(biāo)拓型。

    //-----------------------畫圖標(biāo)--------------------------------------------------------------
        for(int i = 0; i < mComplectedXPosition.size(); i++)
        {
            final float currentComplectedXPosition = mComplectedXPosition.get(i);
            Rect rect = new Rect((int) (currentComplectedXPosition - mCircleRadius), (int) (mCenterY - mCircleRadius), (int) (currentComplectedXPosition + mCircleRadius), (int) (mCenterY + mCircleRadius));
            if(i < mComplectingPosition)
            {
                mCompleteIcon.setBounds(rect);
                mCompleteIcon.draw(canvas);
            } else if(i == mComplectingPosition && mComplectedXPosition.size() != 1)
            {
                mCompletedPaint.setColor(Color.WHITE);
                canvas.drawCircle(currentComplectedXPosition, mCenterY, mCircleRadius * 1.1f, mCompletedPaint);
                mAttentionIcon.setBounds(rect);
                mAttentionIcon.draw(canvas);
            } else
            {
                mDefaultIcon.setBounds(rect);
                mDefaultIcon.draw(canvas);
            }
        }
        //-----------------------畫圖標(biāo)--------------------------------------------------------------

StepsViewIndicator的其他方法

     /**
     * 得到所有圓點所在的位置
     *
     * @return
     */
    public List<Float> getComplectedXPosition()
    {
        return mComplectedXPosition;
    }

    /**
     * 設(shè)置流程步數(shù)
     *
     * @param stepNum 流程步數(shù)
     */
    public void setStepNum(int stepNum)
    {
        this.mStepNum = stepNum;
        invalidate();
    }

    /**
     * 設(shè)置正在進(jìn)行position
     *
     * @param complectingPosition
     */
    public void setComplectingPosition(int complectingPosition)
    {
        this.mComplectingPosition = complectingPosition;
        invalidate();
    }

    /**
     * 設(shè)置未完成線的顏色
     *
     * @param unCompletedLineColor
     */
    public void setUnCompletedLineColor(int unCompletedLineColor)
    {
        this.mUnCompletedLineColor = unCompletedLineColor;
    }

    /**
     * 設(shè)置已完成線的顏色
     *
     * @param completedLineColor
     */
    public void setCompletedLineColor(int completedLineColor)
    {
        this.mCompletedLineColor = completedLineColor;
    }

    /**
     * 設(shè)置默認(rèn)圖片
     *
     * @param defaultIcon
     */
    public void setDefaultIcon(Drawable defaultIcon)
    {
        this.mDefaultIcon = defaultIcon;
    }

    /**
     * 設(shè)置已完成圖片
     * @param completeIcon
     */
    public void setCompleteIcon(Drawable completeIcon)
    {
        this.mCompleteIcon = completeIcon;
    }

    /**
     * 設(shè)置正在進(jìn)行中的圖片
     * @param attentionIcon
     */
    public void setAttentionIcon(Drawable attentionIcon)
    {
        this.mAttentionIcon = attentionIcon;
    }

StepsViewIndicator (全部代碼)

public class StepsViewIndicator extends View
{

    //定義默認(rèn)的高度
    private int defaultStepIndicatorNum = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics());

    private float mCompletedLineHeight;//完成線的高度
    private float mCircleRadius;//圓的半徑

    private Drawable mCompleteIcon;//完成的默認(rèn)圖片
    private Drawable mAttentionIcon;//正在進(jìn)行的默認(rèn)圖片
    private Drawable mDefaultIcon;//默認(rèn)的背景圖
    private float mCenterY;//該view的中間位置
    private float mLeftY;//左上方的Y位置
    private float mRightY;//右下方的位置

    private int mStepNum = 0;//當(dāng)前有幾部流程
    private float mLinePadding;

    private List<Float> mComplectedXPosition;//定義完成時當(dāng)前view在左邊的位置
    private Paint mUnCompletedPaint;//未完成Paint
    private Paint mCompletedPaint;//完成paint
    private int mUnCompletedLineColor = ContextCompat.getColor(getContext(), R.color.uncompleted_color);//定義默認(rèn)未完成線的顏色
    private int mCompletedLineColor = Color.WHITE;//定義默認(rèn)完成線的顏色
    private PathEffect mEffects;

    private int mComplectingPosition;//正在進(jìn)行position
    private Path mPath;

    private OnDrawIndicatorListener mOnDrawListener;

    /**
     * 設(shè)置監(jiān)聽
     *
     * @param onDrawListener
     */
    public void setOnDrawListener(OnDrawIndicatorListener onDrawListener)
    {
        mOnDrawListener = onDrawListener;
    }

    /**
     * get小圓的半徑
     *
     * @return
     */
    public float getCircleRadius()
    {
        return mCircleRadius;
    }

    public StepsViewIndicator(Context context)
    {
        this(context, null);
    }

    public StepsViewIndicator(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }

    public StepsViewIndicator(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        init();
    }

    /**
     * init
     */
    private void init()
    {
        mPath = new Path();
        mEffects = new DashPathEffect(new float[]{8, 8, 8, 8}, 1);

        mComplectedXPosition = new ArrayList<>();//初始化

        mUnCompletedPaint = new Paint();
        mCompletedPaint = new Paint();
        mUnCompletedPaint.setAntiAlias(true);
        mUnCompletedPaint.setColor(mUnCompletedLineColor);
        mUnCompletedPaint.setStyle(Paint.Style.STROKE);
        mUnCompletedPaint.setStrokeWidth(2);

        mCompletedPaint.setAntiAlias(true);
        mCompletedPaint.setColor(mCompletedLineColor);
        mCompletedPaint.setStyle(Paint.Style.STROKE);
        mCompletedPaint.setStrokeWidth(2);

        mUnCompletedPaint.setPathEffect(mEffects);
        mCompletedPaint.setStyle(Paint.Style.FILL);

        //已經(jīng)完成線的寬高
        mCompletedLineHeight = 0.05f * defaultStepIndicatorNum;
        //圓的半徑
        mCircleRadius = 0.28f * defaultStepIndicatorNum;
        //線與線之間的間距
        mLinePadding = 0.85f * defaultStepIndicatorNum;

        mCompleteIcon = ContextCompat.getDrawable(getContext(), R.drawable.complted);//已經(jīng)完成的icon
        mAttentionIcon = ContextCompat.getDrawable(getContext(), R.drawable.attention);//正在進(jìn)行的icon
        mDefaultIcon = ContextCompat.getDrawable(getContext(), R.drawable.default_icon);//未完成的icon
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        int width = defaultStepIndicatorNum * 2;
        if(MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec))
        {
            width = MeasureSpec.getSize(widthMeasureSpec);
        }
        int height = defaultStepIndicatorNum;
        if(MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec))
        {
            height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);
        //獲取中間的高度
        mCenterY = 0.5f * getHeight();
        //獲取左上方Y(jié)的位置额嘿,獲取該點的意義是為了方便畫矩形左上的Y位置
        mLeftY = mCenterY - (mCompletedLineHeight / 2);
        //獲取右下方Y(jié)的位置,獲取該點的意義是為了方便畫矩形右下的Y位置
        mRightY = mCenterY + mCompletedLineHeight / 2;

        for(int i = 0; i < mStepNum; i++)
        {
            //先計算全部最左邊的padding值(getWidth()-(圓形直徑+兩圓之間距離)*2)
            float paddingLeft = (getWidth() - mStepNum * mCircleRadius * 2 - (mStepNum - 1) * mLinePadding) / 2;
            mComplectedXPosition.add(paddingLeft + mCircleRadius + i * mCircleRadius * 2 + i * mLinePadding);
        }

        mOnDrawListener.ondrawIndicator();
    }

    @Override
    protected synchronized void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        mUnCompletedPaint.setColor(mUnCompletedLineColor);
        mCompletedPaint.setColor(mCompletedLineColor);

        //-----------------------畫線---------------------------------------------------------------
        for(int i = 0; i < mComplectedXPosition.size() - 1; i++)
        {
            //前一個ComplectedXPosition
            final float preComplectedXPosition = mComplectedXPosition.get(i);
            //后一個ComplectedXPosition
            final float afterComplectedXPosition = mComplectedXPosition.get(i + 1);

            if(i < mComplectingPosition)//判斷在完成之前的所有點
            {
                //判斷在完成之前的所有點劣挫,畫完成的線册养,這里是矩形
                canvas.drawRect(preComplectedXPosition + mCircleRadius - 10, mLeftY, afterComplectedXPosition - mCircleRadius + 10, mRightY, mCompletedPaint);
            } else
            {
                mPath.moveTo(preComplectedXPosition + mCircleRadius, mCenterY);
                mPath.lineTo(afterComplectedXPosition - mCircleRadius, mCenterY);
                canvas.drawPath(mPath, mUnCompletedPaint);
            }
        }
        //-----------------------畫線---------------------------------------------------------------


        //-----------------------畫圖標(biāo)--------------------------------------------------------------
        for(int i = 0; i < mComplectedXPosition.size(); i++)
        {
            final float currentComplectedXPosition = mComplectedXPosition.get(i);
            Rect rect = new Rect((int) (currentComplectedXPosition - mCircleRadius), (int) (mCenterY - mCircleRadius), (int) (currentComplectedXPosition + mCircleRadius), (int) (mCenterY + mCircleRadius));
            if(i < mComplectingPosition)
            {
                mCompleteIcon.setBounds(rect);
                mCompleteIcon.draw(canvas);
            } else if(i == mComplectingPosition && mComplectedXPosition.size() != 1)
            {
                mCompletedPaint.setColor(Color.WHITE);
                canvas.drawCircle(currentComplectedXPosition, mCenterY, mCircleRadius * 1.1f, mCompletedPaint);
                mAttentionIcon.setBounds(rect);
                mAttentionIcon.draw(canvas);
            } else
            {
                mDefaultIcon.setBounds(rect);
                mDefaultIcon.draw(canvas);
            }
        }
        //-----------------------畫圖標(biāo)--------------------------------------------------------------
    }

    /**
     * 得到所有圓點所在的位置
     *
     * @return
     */
    public List<Float> getComplectedXPosition()
    {
        return mComplectedXPosition;
    }

    /**
     * 設(shè)置流程步數(shù)
     *
     * @param stepNum 流程步數(shù)
     */
    public void setStepNum(int stepNum)
    {
        this.mStepNum = stepNum;
        invalidate();
    }

    /**
     * 設(shè)置正在進(jìn)行position
     *
     * @param complectingPosition
     */
    public void setComplectingPosition(int complectingPosition)
    {
        this.mComplectingPosition = complectingPosition;
        invalidate();
    }

    /**
     * 設(shè)置未完成線的顏色
     *
     * @param unCompletedLineColor
     */
    public void setUnCompletedLineColor(int unCompletedLineColor)
    {
        this.mUnCompletedLineColor = unCompletedLineColor;
    }

    /**
     * 設(shè)置已完成線的顏色
     *
     * @param completedLineColor
     */
    public void setCompletedLineColor(int completedLineColor)
    {
        this.mCompletedLineColor = completedLineColor;
    }

    /**
     * 設(shè)置默認(rèn)圖片
     *
     * @param defaultIcon
     */
    public void setDefaultIcon(Drawable defaultIcon)
    {
        this.mDefaultIcon = defaultIcon;
    }

    /**
     * 設(shè)置已完成圖片
     * @param completeIcon
     */
    public void setCompleteIcon(Drawable completeIcon)
    {
        this.mCompleteIcon = completeIcon;
    }

    /**
     * 設(shè)置正在進(jìn)行中的圖片
     * @param attentionIcon
     */
    public void setAttentionIcon(Drawable attentionIcon)
    {
        this.mAttentionIcon = attentionIcon;
    }


    /**
     * 設(shè)置對view監(jiān)聽
     */
    public interface OnDrawIndicatorListener
    {
        void ondrawIndicator();
    }
}

StepView

好了StepsViewIndicator是不是已經(jīng)完全熟悉了呢,下面我們看StepView.StepView其實組合控件压固,設(shè)置其子控件的屬性球拦,包括顏色,position的狀態(tài)...其中有一個方法我們來看下帐我,就是對于設(shè)置TextView的。


    @Override
    public void ondrawIndicator()
    {
        List<Float> complectedXPosition = mStepsViewIndicator.getComplectedXPosition();
        if(mTexts != null)
        {
            for(int i = 0; i < mTexts.size(); i++)
            {
                TextView textView = new TextView(getContext());
                textView.setText(mTexts.get(i));
                textView.setX(complectedXPosition.get(i) - mStepsViewIndicator.getCircleRadius() - 10);//這里的-10是將文字進(jìn)行調(diào)整居中拦键,稍后再動態(tài)修改
                textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));

                if(i <= mComplectingPosition)
                {
                    textView.setTypeface(null, Typeface.BOLD);
                    textView.setTextColor(mComplectedTextColor);
                } else
                {
                    textView.setTextColor(mUnComplectedTextColor);
                }

                mTextContainer.addView(textView);
            }
        }
    }

這里的ondrawIndicator()方法是在StepsViewIndicator的onSizeChanged()引發(fā)的回調(diào)碳柱,來設(shè)置TextView

StepView全部代碼

public class StepView extends LinearLayout implements StepsViewIndicator.OnDrawIndicatorListener
{

    private RelativeLayout mTextContainer;
    private StepsViewIndicator mStepsViewIndicator;
    private List<String> mTexts;
    private int mComplectingPosition;
    private int mUnComplectedTextColor = ContextCompat.getColor(getContext(), R.color.uncompleted_text_color);//定義默認(rèn)未完成文字的顏色;
    private int mComplectedTextColor = ContextCompat.getColor(getContext(), android.R.color.white);//定義默認(rèn)完成文字的顏色;


    public StepView(Context context)
    {
        this(context, null);
    }

    public StepView(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }

    public StepView(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init()
    {
        View rootView = LayoutInflater.from(getContext()).inflate(R.layout.widget_stepsview, this);
        mStepsViewIndicator = (StepsViewIndicator) rootView.findViewById(R.id.steps_indicator);
        mStepsViewIndicator.setOnDrawListener(this);
        mTextContainer = (RelativeLayout) rootView.findViewById(R.id.rl_text_container);
        mTextContainer.removeAllViews();
    }

    /**
     * 設(shè)置顯示的文字
     *
     * @param texts
     * @return
     */
    public StepView setStepViewTexts(List<String> texts)
    {
        mTexts = texts;
        mStepsViewIndicator.setStepNum(mTexts.size());
        return this;
    }

    /**
     * 設(shè)置正在進(jìn)行的position
     *
     * @param complectingPosition
     * @return
     */
    public StepView setStepsViewIndicatorComplectingPosition(int complectingPosition)
    {
        mComplectingPosition = complectingPosition;
        mStepsViewIndicator.setComplectingPosition(complectingPosition);
        return this;
    }

    /**
     * 設(shè)置未完成文字的顏色
     *
     * @param unComplectedTextColor
     * @return
     */
    public StepView setStepViewUnComplectedTextColor(int unComplectedTextColor)
    {
        mUnComplectedTextColor = unComplectedTextColor;
        return this;
    }

    /**
     * 設(shè)置完成文字的顏色
     *
     * @param complectedTextColor
     * @return
     */
    public StepView setStepViewComplectedTextColor(int complectedTextColor)
    {
        this.mComplectedTextColor = complectedTextColor;
        return this;
    }

    /**
     * 設(shè)置StepsViewIndicator未完成線的顏色
     *
     * @param unCompletedLineColor
     * @return
     */
    public StepView setStepsViewIndicatorUnCompletedLineColor(int unCompletedLineColor)
    {
        mStepsViewIndicator.setUnCompletedLineColor(unCompletedLineColor);
        return this;
    }

    /**
     * 設(shè)置StepsViewIndicator完成線的顏色
     *
     * @param completedLineColor
     * @return
     */
    public StepView setStepsViewIndicatorCompletedLineColor(int completedLineColor)
    {
        mStepsViewIndicator.setCompletedLineColor(completedLineColor);
        return this;
    }

    /**
     * 設(shè)置StepsViewIndicator默認(rèn)圖片
     *
     * @param defaultIcon
     */
    public StepView setStepsViewIndicatorDefaultIcon(Drawable defaultIcon)
    {
        mStepsViewIndicator.setDefaultIcon(defaultIcon);
        return this;
    }

    /**
     * 設(shè)置StepsViewIndicator已完成圖片
     *
     * @param completeIcon
     */
    public StepView setStepsViewIndicatorCompleteIcon(Drawable completeIcon)
    {
        mStepsViewIndicator.setCompleteIcon(completeIcon);
        return this;
    }

    /**
     * 設(shè)置StepsViewIndicator正在進(jìn)行中的圖片
     *
     * @param attentionIcon
     */
    public StepView setStepsViewIndicatorAttentionIcon(Drawable attentionIcon)
    {
        mStepsViewIndicator.setAttentionIcon(attentionIcon);
        return this;
    }


    @Override
    public void ondrawIndicator()
    {
        List<Float> complectedXPosition = mStepsViewIndicator.getComplectedXPosition();
        if(mTexts != null)
        {
            for(int i = 0; i < mTexts.size(); i++)
            {
                TextView textView = new TextView(getContext());
                textView.setText(mTexts.get(i));
                textView.setX(complectedXPosition.get(i) - mStepsViewIndicator.getCircleRadius() - 10);//這里的-10是將文字進(jìn)行調(diào)整居中梅掠,稍后再動態(tài)修改
                textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));

                if(i <= mComplectingPosition)
                {
                    textView.setTypeface(null, Typeface.BOLD);
                    textView.setTextColor(mComplectedTextColor);
                } else
                {
                    textView.setTextColor(mUnComplectedTextColor);
                }

                mTextContainer.addView(textView);
            }
        }
    }
}

好了,其實到了這里店归,基本上也要接近尾聲了.有了前面的只是積累阎抒,寫VerticalStepView是不是就思路更加明確了呢?其實這個控件看起來‘很復(fù)雜’消痛,翻了翻代碼且叁,看了也沒有多大難度,小伙伴們秩伞,只要靜下心來逞带,花點時間研究下,發(fā)現(xiàn)纱新,也就是那么回事展氓,只要努力,就會有收貨怒炸。

關(guān)于VerticalStepView

這里就不在啰嗦的闡述VerticalStepView的原理啦带饱,有了上面的HorizontalStepsViewIndicator的講解,在來看看VerticalStepView應(yīng)該是非常輕松的吧阅羹,好了勺疼,有想法的小伙伴何不嘗試自己來寫這個VerticalStepView,相信你可以的D笥恪V绰!

參考鏈接

Android-StepsView

Thanks

  • 感謝 iconfont 提供的圖片資源
  • 感謝 anton46 提供的源碼作參考

圖片來源

End

喜歡這個控件的小伙伴可以點擊star哦导梆!

下一篇文章WrapTextView

點擊查看WrapTextView

art.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末轨淌,一起剝皮案震驚了整個濱河市迂烁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌递鹉,老刑警劉巖盟步,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異躏结,居然都是意外死亡却盘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門媳拴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來黄橘,“玉大人,你說我怎么就攤上這事屈溉∪兀” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵子巾,是天一觀的道長帆赢。 經(jīng)常有香客問我,道長砰左,這世上最難降的妖魔是什么匿醒? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮缠导,結(jié)果婚禮上廉羔,老公的妹妹穿的比我還像新娘。我一直安慰自己僻造,他們只是感情好憋他,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著髓削,像睡著了一般竹挡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上立膛,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天揪罕,我揣著相機(jī)與錄音,去河邊找鬼宝泵。 笑死好啰,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的儿奶。 我是一名探鬼主播框往,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼闯捎!你這毒婦竟也來了椰弊?” 一聲冷哼從身側(cè)響起许溅,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秉版,沒想到半個月后贤重,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡沐飘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年游桩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耐朴。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖盹憎,靈堂內(nèi)的尸體忽然破棺而出筛峭,到底是詐尸還是另有隱情,我是刑警寧澤陪每,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布影晓,位于F島的核電站,受9級特大地震影響檩禾,放射性物質(zhì)發(fā)生泄漏挂签。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一盼产、第九天 我趴在偏房一處隱蔽的房頂上張望饵婆。 院中可真熱鬧,春花似錦戏售、人聲如沸侨核。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搓译。三九已至,卻和暖如春锋喜,著一層夾襖步出監(jiān)牢的瞬間些己,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工嘿般, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留段标,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓博个,卻偏偏與公主長得像怀樟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盆佣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容