自定義View - 簡單的成績條

效果圖
這裏寫圖片描述

ScoreView的要求

  • 0-6060-7070-8080-9090-120 這幾段對應不同顏色
  • 處理padding , <0或>120的特殊值 , match_parent 和 wrap_content

具體步驟

1.在attrs.xml文件中定義屬性

<attr name="score" format="integer" />
<attr name="scoreColor" format="color" />
<attr name="baseLineColor" format="color" />
<declare-styleable name="ScoreView">    
    <attr name="score" />    
    <attr name="scoreColor" />    
    <attr name="baseLineColor" />
</declare-styleable>

2.在自定義View中獲取這些屬性

public ScoreView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    TypedArray ta = context.getTheme().
        obtainStyledAttributes(attrs, R.styleable.ScoreView, defStyleAttr, 0);
    int count = ta.getIndexCount();
    for (int i = 0; i < count; i++) {
        int attr = ta.getIndex(i);
        switch (attr) {
            case R.styleable.ScoreView_baseLineColor:
                baselineColor = ta.getColor(attr, Color.GRAY);
                break;
            case R.styleable.ScoreView_scoreColor:
                scoreColor = ta.getColor(attr, Color.RED);
                break;
            case R.styleable.ScoreView_score:
                //應該加上 StringToInteger 的檢測
                mScore = Integer.parseInt(ta.getString(attr));
                break;
            }
        }
    ta.recycle();
    mScore = Math.min(mScore, 120);
    mScore = Math.max(0, mScore);
    mScoreFraction = (float) (mScore / 10.0);
    paint = new Paint();
}

3.重寫onMeasure

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int mWidth = MeasureSpec.getSize(widthMeasureSpec);
    int mHeight = MeasureSpec.getSize(heightMeasureSpec);
    int mWidthMode = MeasureSpec.getMode(widthMeasureSpec);
    int mHeightMode = MeasureSpec.getMode(heightMeasureSpec);

    int lineWidth = 0;//基準線的長度
    Paint paint = new Paint();
    Rect boundScore = new Rect();
    paint.setTextSize(30);
    paint.getTextBounds("120", 0, "120".length(), boundScore); //所得分數對應的Rect
    Rect boundDial = new Rect();
    paint.setTextSize(20);
    paint.getTextBounds("120", 0, "120".length(), boundDial);//分數刻度對應的Rect

    if (mWidthMode == MeasureSpec.EXACTLY) {
        width = mWidth;
        lineWidth = width - boundScore.width() - getPaddingLeft() - getPaddingRight();
    } else {
        width = DensityUtils.getScreenWidth(ScoreApplication.getScoreApplicationContext()) / 2;
        lineWidth = width - (boundScore.width()) - getPaddingLeft() - getPaddingRight();
    }

    if (mHeightMode == MeasureSpec.EXACTLY) { //該情況不進行處理,即如果指定具體高度,不可過小
        height = mHeight;
    } else { 
        int baseHeight = DensityUtils.getScreenHeight(ScoreApplication.getScoreApplicationContext()) / 5;
        int minHeight = Math.max(boundScore.height(), 2 * stepY) + 4 * stepY;
        height = Math.min(baseHeight, minHeight);
        height += (getPaddingTop() + getPaddingBottom());
    }

    baseLineWidth = (int) (lineWidth * 0.9);
    baseLineStart = (int) (lineWidth * 0.05);
    //計算出 刻度線各里程碑對應的x座標,即DialStonesX[i]的值
    for (int i = 0; i < DialStones.length; i++) {
        DialStonesX[i] = (int) ((float) (DialStones[i] / 120.0) * baseLineWidth + baseLineStart);
    }
    setMeasuredDimension(width, height);
}

4.重寫onDraw

@Override
protected void onDraw(Canvas canvas) {
    paint.setStrokeWidth(mDialWidth);//paint線條寬度
    paint.setColor(baselineColor);
    paint.setTextSize(20);
    //繪製水平基線
    canvas.drawLine(baseLineStart, height / 2, baseLineWidth + baseLineStart, height / 2, paint);
    int baseX = baseLineStart + baseLineWidth / 2;
    int baseY = height / 2;
    int stepX = baseLineWidth / 12;
    //繪製第一個刻度 0
    canvas.drawLine(baseLineStart + mDialWidth / 2, baseY, baseLineStart + mDialWidth / 2, baseY + stepY, paint);
    canvas.drawText("0", baseLineStart, baseY + stepY * 3, paint);
    //繪製刻度 60 70 80 90 100 120
    for (int i = 0; i < 7; i++) {
        if (i == 5) continue;
        canvas.drawLine(baseX + i * stepX + mDialWidth / 2, baseY, baseX + i * stepX + mDialWidth / 2, baseY + stepY, paint);
        canvas.drawText(DialNumbers[i] + "", baseX + i * stepX - stepY, baseY + 3 * stepY, paint);
    }
    //繪製進度
    int destX = (int) (baseLineStart + stepX * mScoreFraction + mDialWidth);
    int indexBetweenDialStonesX = getIndexInDialStonesX(destX);
    //如果成績<=60
    if (indexBetweenDialStonesX == 0) {
        paint.setColor(getResources().getColor(progressRes[0]));
        canvas.drawRect(baseLineStart, baseY - stepY * 3, destX, baseY - stepY, paint);
    } else {
        //除去最後一部分,之前的部分可以通過 刻度里程碑的X座標確定起止X位置
        for (int i = 0; i < indexBetweenDialStonesX; i++) {
            paint.setColor(getResources().getColor(progressRes[i]));
            if (i == 0) {
                canvas.drawRect(baseLineStart, baseY - stepY * 3, DialStonesX[i], baseY - stepY, paint);
            } else {
                canvas.drawRect(DialStonesX[i - 1], baseY - stepY * 3, DialStonesX[i], baseY - stepY, paint);
            }
        }
        //繪製最後一段,起始點X座標爲對應刻度里程碑的X座標,終止點座標爲destX
        paint.setColor(getResources().getColor(progressRes[indexBetweenDialStonesX]));
        canvas.drawRect(DialStonesX[indexBetweenDialStonesX - 1], baseY - stepY * 3, destX, baseY - stepY, paint);
    }
    //繪製分數
    paint.setColor(scoreColor);
    paint.setTextSize(30);
    paint.setTypeface(Typeface.create(Typeface.DEFAULT_BOLD, Typeface.BOLD));
    canvas.drawText("" + mScore, (int) (baseLineStart + stepX * mScoreFraction + stepY), (int) (baseY - stepY * 1.3), paint);
    super.onDraw(canvas);
}
  • 根據進度條終點的X座標,計算對應於第幾個刻度里程碑
private int getIndexInDialStonesX(int dest) {
    for (int i = DialStonesX.length - 1; i >= 0; i--) {
        if (dest > DialStonesX[i] + mDialWidth) return i + 1;
    }
    return 0;
}

5.支持屬性動畫

public void setScore(int score) {
    mScore = Math.min(score,120);
    mScore = Math.max(mScore,0);
    mScoreFraction = (float) (mScore / 10.0);
    invalidate();
}

6.引用ScoreView並調用屬性動畫

 <org.issme.gh.scoreviewdemo.View.ScoreView
        android:id="@+id/acScore_scrollView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        hyzhang:baseLineColor="@color/baseLine"
        hyzhang:score="98"
        hyzhang:scoreColor="@color/score" />
  • 要加上 xmlns:hyzhang="http://schemas.android.com/apk/res-auto"
  • 在代碼使用屬性動畫
public void setScore(View view) {
    int random = new Random().nextInt(61) + 60;
    ObjectAnimator.ofInt(scoreView, "Score", random).setDuration(random * 10).start();
    }

歡迎指教, Demo下載請戳

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章