溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android自定義View實(shí)現(xiàn)多節(jié)點(diǎn)進(jìn)度條功能的方法

發(fā)布時(shí)間:2020-07-28 16:05:13 來(lái)源:億速云 閱讀:361 作者:小豬 欄目:移動(dòng)開發(fā)

這篇文章主要講解了Android自定義View實(shí)現(xiàn)多節(jié)點(diǎn)進(jìn)度條功能的方法,內(nèi)容清晰明了,對(duì)此有興趣的小伙伴可以學(xué)習(xí)一下,相信大家閱讀完之后會(huì)有幫助。

前言

最近項(xiàng)目有一個(gè)節(jié)點(diǎn)進(jìn)度條的小需求,完成后,想分享出來(lái)希望可以幫到有需要的同學(xué)。

真機(jī)效果圖

Android自定義View實(shí)現(xiàn)多節(jié)點(diǎn)進(jìn)度條功能的方法

Android自定義View實(shí)現(xiàn)多節(jié)點(diǎn)進(jìn)度條功能的方法

自定義View完整代碼

開箱即用~,注釋已經(jīng)炒雞詳細(xì)了

/**
 * @description: 節(jié)點(diǎn)進(jìn)度條
 * @author: DMingO
 * @date: 2020/4/15
 */
public class PointProcessBar extends View {
  /**
   * 未選中時(shí)的連線畫筆
   */
  private Paint mLinePaint;
  /**
   * 選中時(shí)的連線畫筆
   */
  private Paint mLineSelectedPaint;
  /**
   * 未選中時(shí)的文字畫筆
   */
  private Paint mTextPaint;
  /**
   * 選中時(shí)的文字畫筆
   */
  private Paint mTextSelPaint;
  /**
   * 未選中時(shí)的實(shí)心圓畫筆
   */
  private Paint mCirclePaint;
  /**
   * 選中時(shí)的內(nèi)部實(shí)心圓畫筆
   */
  private Paint mCircleSelPaint;
  /**
   * 選中時(shí)的邊框圓畫筆
   */
  private Paint mCircleStrokeSelPaint;
  /**
   * 未選中時(shí)的線,節(jié)點(diǎn)圓的顏色
   */
  private int mColorUnselected = Color.parseColor("#1ca8b0d9");
  /**
   * 選中時(shí)的顏色
   */
  private int mColorSelected = Color.parseColor("#61A4E4");
  /**
   * 未選中的文字顏色
   */
  private int mColorTextUnselected = Color.parseColor("#5c030f09");
  /**
   * 繪制的節(jié)點(diǎn)個(gè)數(shù),由底部節(jié)點(diǎn)標(biāo)題數(shù)量控制
   */
  int circleCount ;
  /**
   * 連線的高度
   */
  float mLineHeight = 7f;
  //圓的直徑
  float mCircleHeight = 50f;
  float mCircleSelStroke = 8f;
  float mCircleFillRadius = 15f;
  //文字大小
  float mTextSize = 35f;
  //文字離頂部的距離
  float mMarginTop = 40f;
  /**
   * 首個(gè)圓向中心偏移的距離
   */
  float marginLeft = 30f;
  /**
   * 最后一個(gè)圓向中心偏移的距離
   */
  float marginRight = marginLeft;
  /**
   * 每個(gè)節(jié)點(diǎn)相隔的距離
   */
  float divideWidth;
  int defaultHeight;
  /**
   * 節(jié)點(diǎn)底部的文字列表
   */
  List<String> textList = new ArrayList<>();
  /**
   * 文字同寬高的矩形,用來(lái)測(cè)量文字
   */
  List<Rect> mBounds;
  /**
   * 存儲(chǔ)每個(gè)圓心在同一直線上的節(jié)點(diǎn)圓的 x 坐標(biāo)值
   */
  List<Float> circleLineJunctions = new ArrayList<>();
  /**
   * 選中項(xiàng)集合
   */
  Set<Integer> selectedIndexSet = new HashSet<>();
  public PointProcessBar(Context context) {
    super(context);
  }
  public PointProcessBar(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    initPaint();
  }
  public PointProcessBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }
  public PointProcessBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  }
  /**
   * 初始化畫筆屬性
   */
  private void initPaint(){
    mLinePaint = new Paint();
    mLineSelectedPaint = new Paint();
    mCirclePaint = new Paint();
    mTextPaint = new Paint();
    mCircleStrokeSelPaint = new Paint();
    mTextSelPaint=new Paint();
    mCircleSelPaint = new Paint();
    mLinePaint.setColor(mColorDef);
    //設(shè)置填充
    mLinePaint.setStyle(Paint.Style.FILL);
    //筆寬像素
    mLinePaint.setStrokeWidth(mLineHeight);
    //鋸齒不顯示
    mLinePaint.setAntiAlias(true);
    mLineSelectedPaint.setColor(mColorSelected);
    mLineSelectedPaint.setStyle(Paint.Style.FILL);
    mLineSelectedPaint.setStrokeWidth(mLineHeight);
    mLineSelectedPaint.setAntiAlias(true);
    mCirclePaint.setColor(mColorDef);
    //設(shè)置填充
    mCirclePaint.setStyle(Paint.Style.FILL);
    //筆寬像素
    mCirclePaint.setStrokeWidth(1);
    //鋸齒不顯示
    mCirclePaint.setAntiAlias(true);
    //選中時(shí)外框空心圓圈畫筆
    mCircleStrokeSelPaint.setColor(mColorSelected);
    mCircleStrokeSelPaint.setStyle(Paint.Style.STROKE);
    mCircleStrokeSelPaint.setStrokeWidth(mCircleSelStroke);
    mCircleStrokeSelPaint.setAntiAlias(true);
    //選中時(shí)的內(nèi)部填充圓畫筆
    mCircleSelPaint.setStyle(Paint.Style.FILL);
    mCircleSelPaint.setStrokeWidth(1);
    mCircleSelPaint.setAntiAlias(true);
    mCircleSelPaint.setColor(mColorSelected);
    //普通狀態(tài)的文本 畫筆
    mTextPaint.setTextSize(mTextSize);
    mTextPaint.setColor(mColorTextDef);
    mTextPaint.setAntiAlias(true);
    mTextPaint.setTextAlign(Paint.Align.CENTER);
    //選中后的文本畫筆
    mTextSelPaint.setTextSize(mTextSize);
    mTextSelPaint.setColor(mColorSelected);
    mTextSelPaint.setAntiAlias(true);
    mTextSelPaint.setTextAlign(Paint.Align.CENTER);
  }
  /**
   * 測(cè)量文字的長(zhǎng)寬,將文字視為rect矩形
   */
  private void measureText(){
    mBounds = new ArrayList<>();
    for(String name : textList){
      Rect mBound = new Rect();
      mTextPaint.getTextBounds(name, 0, name.length(), mBound);
      mBounds.add(mBound);
    }
  }

  /**
   * 測(cè)量view的高度
   */
  private void measureHeight(){
    if (mBounds!=null && mBounds.size()!=0) {
      defaultHeight = (int) (mCircleHeight + mMarginTop + mCircleSelStroke + mBounds.get(0).height()/2);
    } else {
      defaultHeight = (int) (mCircleHeight + mMarginTop+mCircleSelStroke);
    }
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    //寬高都設(shè)置為wrap_content
    if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){
      //寬設(shè)置為wrap_content
      setMeasuredDimension(widthSpecSize,defaultHeight);
    }else if(widthSpecMode == MeasureSpec.AT_MOST){
      setMeasuredDimension(widthSpecSize,heightSpecSize);
    }else if(heightSpecMode == MeasureSpec.AT_MOST){
      //高設(shè)置為wrap_content
      setMeasuredDimension(widthSpecSize, defaultHeight);
    }else{
      //寬高都設(shè)置為match_parent或具體的dp值
      setMeasuredDimension(widthSpecSize, heightSpecSize);
    }
  }
  @Override
  protected void onDraw(Canvas canvas) {
    //若未設(shè)置節(jié)點(diǎn)標(biāo)題或者選中項(xiàng)的列表,則取消繪制
    if (textList == null || textList.isEmpty() ||
        selectedIndexSet == null || selectedIndexSet.isEmpty() ||
        mBounds == null || mBounds.isEmpty()) {
      return;
    }
    //畫灰色圓圈的個(gè)數(shù)
    circleCount=textList.size();
    //每個(gè)圓相隔的距離(重要),可以通過(guò)這個(gè)調(diào)節(jié)節(jié)點(diǎn)間距
    divideWidth = (getWidth() - mCircleHeight ) / (circleCount - 1);
    //繪制文字和圓形
    for (int i=0; i < circleCount ;i++){
      float cx;
      float cy;
      float textX;
      if (i==0){
        //第一個(gè)節(jié)點(diǎn),圓心需要向右偏移
        cx = mCircleHeight / 2 + i * divideWidth + marginLeft;
        cy = mCircleHeight / 2 + mCircleSelStroke;
        textX = cx;
        circleLineJunctions.add(cx + mCircleHeight / 2);
      }else if (i==textList.size()-1){
        //最后一個(gè)節(jié)點(diǎn),圓心需要向左偏移
        cx = mCircleHeight / 2 + i * divideWidth - marginRight;
        cy = mCircleHeight / 2 + mCircleSelStroke;
        textX = cx;
        circleLineJunctions.add(cx - mCircleHeight / 2);
      }else {
        //中間部分的節(jié)點(diǎn)
        cx = mCircleHeight / 2 + i * divideWidth;
        cy = mCircleHeight / 2+mCircleSelStroke;
        textX = cx;
        circleLineJunctions.add(cx - mCircleHeight / 2);
        circleLineJunctions.add(cx + mCircleHeight / 2);
      }
      if (getSelectedIndexSet().contains(i)){
        //若當(dāng)前位置節(jié)點(diǎn)被包含在選中項(xiàng)Set中,判定此節(jié)點(diǎn)被選中
        canvas.drawCircle(cx , cy, mCircleHeight / 2, mCircleStrokeSelPaint);
        canvas.drawCircle(cx, cy, mCircleFillRadius, mCircleSelPaint);
        canvas.drawText(textList.get(i), textX, (float) (mCircleHeight + mMarginTop +mCircleSelStroke+mBounds.get(i).height()/2.0), mTextSelPaint);
      }else {
        //若當(dāng)前位置節(jié)點(diǎn)沒(méi)有被包含在選中項(xiàng)Set中,判定此節(jié)點(diǎn)沒(méi)有被選中
        canvas.drawCircle(cx , cy, mCircleHeight / 2, mCirclePaint);
        canvas.drawText(textList.get(i), textX, (float) (mCircleHeight + mMarginTop +mCircleSelStroke+mBounds.get(i).height()/2.0), mTextPaint);
      }
    }
    for(int i = 1 , j = 1 ; j <= circleLineJunctions.size() && ! circleLineJunctions.isEmpty() ; ++i , j=j+2){
      if(getSelectedIndexSet().contains(i)){
        canvas.drawLine(circleLineJunctions.get(j-1),mCircleHeight/2+mCircleSelStroke,
            circleLineJunctions.get(j) ,mCircleHeight/2+mCircleSelStroke,mLineSelectedPaint);
      }else {
        canvas.drawLine(circleLineJunctions.get(j-1),mCircleHeight/2+mCircleSelStroke,
            circleLineJunctions.get(j) ,mCircleHeight/2+mCircleSelStroke,mLinePaint);
      }
    }
  }
  /**
   * 供外部調(diào)用,顯示控件
   * @param titles 底部標(biāo)題內(nèi)容列表
   * @param indexSet 選中項(xiàng)Set
   */
  public void show(List<String> titles , Set<Integer> indexSet){
    if(titles != null && ! titles.isEmpty()){
      this.textList = titles;
    }
    if(indexSet != null && ! indexSet.isEmpty()){
      this.selectedIndexSet = indexSet;
    }
    measureText();
    measureHeight();
    //繪制
    invalidate();
  }
  /**
   * 更新底部節(jié)點(diǎn)標(biāo)題內(nèi)容
   * @param textList 節(jié)點(diǎn)標(biāo)題內(nèi)容列表
   */
  public void refreshTextList(List<String> textList) {
    this.textList = textList;
    measureText();
    measureHeight();
    invalidate();
  }
  /**
   * 獲取節(jié)點(diǎn)選中狀態(tài)
   * @return 節(jié)點(diǎn)選中狀態(tài)列表
   */
  public Set<Integer> getSelectedIndexSet() {
    return selectedIndexSet;
  }
  /**
   * 更新選中項(xiàng)
   * @param set 選中項(xiàng)Set
   */
  public void refreshSelectedIndexSet(Set<Integer> set) {
    this.selectedIndexSet = set;
    invalidate();
  }
}

注意點(diǎn)

  • 控件的節(jié)點(diǎn)總個(gè)數(shù)是與傳入的節(jié)點(diǎn)底部標(biāo)題列表中元素個(gè)數(shù)控制(相同)的,簡(jiǎn)而言之就是傳入的標(biāo)題列表中有多少個(gè)標(biāo)題,節(jié)點(diǎn)就會(huì)繪制多少個(gè)
  • 控件通過(guò)show方法進(jìn)行View的初始化和顯示內(nèi)容,傳入節(jié)點(diǎn)標(biāo)題列表和節(jié)點(diǎn)選中項(xiàng)集合,控制View的選中狀態(tài)和顯示的內(nèi)容
  • 控件初始化顯示后,可以通過(guò)refreshTextList(),refreshSelectedIndexSet() 更新標(biāo)題和選中項(xiàng)
  • 具體不同的顏色,大小可以具體在View中調(diào)整

總結(jié)

可以看到效果不復(fù)雜,因此自定義View的代碼行數(shù)不多,也很容易看懂,直接拿走代碼即可在項(xiàng)目中食用啦。

由于不同項(xiàng)目設(shè)計(jì)稿會(huì)有不同,這里也僅僅給有需要的同學(xué)一個(gè)思路,可以改造具體實(shí)現(xiàn)代碼~

看完上述內(nèi)容,是不是對(duì)Android自定義View實(shí)現(xiàn)多節(jié)點(diǎn)進(jìn)度條功能的方法有進(jìn)一步的了解,如果還想學(xué)習(xí)更多內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI