Android開發自定義控制元件實現一個足球積分榜RankBar
為了實現一個如下圖紅色方框所示的控制元件,系統自帶控制元件並不能滿足要求,因此需要繼承View重新畫一個這樣的控制元件
分析此控制元件發現分為3部分,中間的一列橫線和左右兩個標籤
中間的部分好繪製,通過迴圈呼叫canvas的drawLine方法即可
然後分析左右兩邊的兩個標籤,因為左右兩個是一樣的,因此只分析左邊的
外圍形狀是一個圓環被拉出來了一個三角形,這個三角形是等邊三角形,等邊三角形的上邊頂點設為tt,下邊頂點設為bb,右邊頂點設為rr
tt和bb與圓心相連構成一個角度,這個角度是30度
根據上邊已知條件,使用三角函式運算可以計算出等邊三角形的邊長
圓弧加上等邊三角形的兩條邊構成了一個閉合路徑,因此使用canvas的drawPath繪製路徑
路徑的起點設為等邊三角形的右頂點rr,根據三角函式關係可以計算出等邊三角形下頂點bb的座標
從bb開始計算圓弧路徑,起始角度為15度,掃過角度為360度減去30度
之後把路徑閉合即可,呼叫path的close()方法
圓弧位置的確定需要一個外接正方形,已知等邊三角形的三個頂點可以很方便求出圓的最右邊點的座標
以圓的最右邊座標計算外接正方形的左上頂點座標和右下頂點座標
最後繪製文字即可
測試效果如下:
測試程式碼:
final Random random=new Random(); final RankBar bar=(RankBar)findViewById(R.id.rank_bar); bar.setRanks(19,29); bar.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v){ bar.setRanks(random.nextInt(29)+1,random.nextInt(29)+1); } });完整程式碼如下:
public class RankBar extends View {
private Context context;
/*左右bar的顏色*/
private int mColorLeft, mColorRight;
/*左右bar的數值*/
private int mRankLeft, mRankRight;
private TypedValue typedValue;
/*畫圖形的畫筆*/
private Paint paintDraw = new Paint();
/*文字畫筆*/
private Paint paintText = new Paint();
private Path path = new Path();
/*等分*/
private final int HEIGHT_DEGREE = 40, WIDTH_DEGREES = 31;
public RankBar(Context context) {
super(context);
this.context=context;
init();
}
public RankBar(Context context, AttributeSet attrs) {
super(context, attrs);
this.context=context;
init();
}
public RankBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context=context;
init();
}
private void init() {
/*資料初始化,沒有設定資料時候的預設資料*/
mColorLeft = Color.rgb(95, 112, 72);
mColorRight = Color.rgb(69, 91, 136);
mRankLeft = 1;
mRankRight = 1;
typedValue=new TypedValue();
context.getTheme().resolveAttribute(R.attr.title_bar,typedValue,true);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float totalWidth = getWidth();
float totalHeight = getHeight();
paintDraw.reset();
paintDraw.setAntiAlias(true);
paintText.reset();
paintText.setAntiAlias(true);
/*把總寬度平均分為WIDTH_DEGREES份,中間短橫線的長度佔一份*/
float lineLength = totalWidth / WIDTH_DEGREES;
/*中間一列一共有30個短橫線刻度,記錄每個短橫線刻度的垂直方向的座標位置,方便繪製*/
float[] lineYs = new float[30];
for (int i = HEIGHT_DEGREE / 2 - 14; i <= HEIGHT_DEGREE / 2 + 15; i++) {
lineYs[i - (HEIGHT_DEGREE / 2 - 14)] = totalHeight * i / HEIGHT_DEGREE;
}
/*設定畫中間水平線的畫筆*/
paintDraw.setColor(getResources().getColor(typedValue.resourceId));
paintDraw.setAlpha(50);
/*設定畫筆的寬度*/
paintDraw.setStrokeWidth(totalHeight / HEIGHT_DEGREE /3);
for (int i = 0; i < lineYs.length; i++) {
/*迴圈繪製中間的短橫線刻度*/
canvas.drawLine(totalWidth / 2 - lineLength, lineYs[i], totalWidth / 2 + lineLength, lineYs[i], paintDraw);
}
/*氣泡的半徑*/
float radius = totalHeight *2.5f/ HEIGHT_DEGREE;
/*繪製左邊標籤*/
path.reset();
/*moveTo:Set the beginning of the next contour to the point (x,y).*/
/*計算等邊三角形右邊頂點的座標rr*/
float triangleRightVertexX=totalWidth / 2 - lineLength / 2;
float triangleRightVertexY=lineYs[mRankLeft - 1];
/*路徑起點移動到等邊三角形右邊頂點rr,以此頂點rr畫線*/
path.moveTo(triangleRightVertexX, triangleRightVertexY);
/*計算等邊三角形的邊長,等邊三角形上下兩個頂點和圓相交於tt,bb,tt、bb與圓心夾角30度*/
float triangleLength= radius*(float)Math.sin(15*2*Math.PI/360)*2;
/*計算bb座標,為繪製弧形的起點*/
float arcStartPointX=triangleRightVertexX-triangleLength*(float)Math.cos(30*2*Math.PI/360);
float arcStartPointY=triangleRightVertexY+triangleLength*(float)Math.sin(30*2*Math.PI/360);
/*畫等邊三角形的一條邊,rr-bb*/
path.lineTo(arcStartPointX,arcStartPointY);
/*arcTo 用於繪製弧線(實際是擷取圓或橢圓的一部分)。
mPath.arcTo(ovalRectF, startAngle, sweepAngle) , ovalRectF為橢圓的矩形,
startAngle 為開始角度,sweepAngle 為結束角度。*/
/*利用三角函式計算圓最右邊點的座標,利用該座標值和半徑構建圓的外接正方形然後畫圓*/
float circleRightPointX=arcStartPointX+(radius-radius*(float)Math.cos(15*2*Math.PI/360));
float circleRightPointY=triangleRightVertexY;
/*圓弧路徑,起始角度0度在3點鐘方向,因此弧形起始角度15度,掃過角度360-30度*/
path.arcTo(new RectF( circleRightPointX-2*radius,
circleRightPointY-radius,
circleRightPointX,
circleRightPointY+radius),
15, 360 - 30);
/*閉合路徑*/
path.close();
paintDraw.setStyle(Paint.Style.FILL);
paintDraw.setColor(mColorLeft);
/*將閉合路徑繪製在畫布上*/
canvas.drawPath(path, paintDraw);
/*繪製外邊白色邊框*/
paintDraw.setColor(Color.WHITE);
paintDraw.setAlpha(90);
paintDraw.setStyle(Paint.Style.STROKE);
paintDraw.setStrokeWidth(3);
canvas.drawPath(path, paintDraw);
/*準備繪製文字*/
paintText.setColor(Color.WHITE);
paintText.setTextSize(radius * 3 / 5);
/*根據資料位數來確定偏移量*/
float number_offset;
/*只有一位數字*/
if (mRankLeft <10) {
number_offset=radius/2;
/*兩位數字*/
} else {
number_offset=radius*3 / 4;
}
/*圓心x座標*/
float circleX=arcStartPointX-radius;
/*繪製#號*/
canvas.drawText("#", circleX - number_offset, lineYs[mRankLeft - 1] + radius / 4, paintText);
float offset = paintText.measureText("#");
/*繪製數字*/
paintText.setTextSize(radius);
canvas.drawText("" + mRankLeft, circleX - number_offset + offset, lineYs[mRankLeft - 1] + radius / 3, paintText);
/*繪製右邊部分*/
path.reset();
/*三角形左邊頂點座標*/
float triangleLeftVertexX=totalWidth / 2 + lineLength / 2;
float triangleLeftVertexY=lineYs[mRankRight - 1];
/*等邊三角形左邊頂點作為路徑起始點*/
path.moveTo(triangleLeftVertexX, triangleLeftVertexY);
/*等邊三角形上邊頂點和圓的焦點作為圓弧路徑的起點*/
arcStartPointX=triangleLeftVertexX+triangleLength*(float)Math.cos(30*2*Math.PI/360);
arcStartPointY=triangleLeftVertexY-triangleLength*(float)Math.sin(30*2*Math.PI/360);
path.lineTo(arcStartPointX, arcStartPointY);
/*利用三角函式計算圓最左邊點的座標,利用該座標值和半徑構建圓的外接正方形然後做圓弧路徑*/
float circleLeftPointX=arcStartPointX-(radius-radius*(float)Math.cos(15*2*Math.PI/360));
float circleLeftPointY=triangleLeftVertexY;
/*圓弧路徑*/
path.arcTo(new RectF( circleLeftPointX,
circleLeftPointY - radius,
circleLeftPointX+2*radius,
circleLeftPointY+radius),
180+15, 360 - 30);
/*閉合路徑*/
path.close();
/*繪製路徑*/
paintDraw.setStyle(Paint.Style.FILL);
paintDraw.setColor(mColorRight);
canvas.drawPath(path, paintDraw);
/*繪製路徑外圍白色邊框*/
paintDraw.setColor(Color.WHITE);
paintDraw.setAlpha(90);
paintDraw.setStyle(Paint.Style.STROKE);
paintDraw.setStrokeWidth(3);
canvas.drawPath(path, paintDraw);
/*開始繪製文字*/
paintText.setColor(Color.WHITE);
paintText.setTextSize(radius * 3 / 5);
if (mRankRight <10) {
number_offset=radius/2;
} else {
number_offset=radius*3 / 4;
}
circleX=circleLeftPointX+radius;
canvas.drawText("#", circleX - number_offset, lineYs[mRankRight - 1] + radius / 4, paintText);
offset = paintText.measureText("#");
paintText.setTextSize(radius);
canvas.drawText("" + mRankRight, circleX - number_offset + offset, lineYs[mRankRight - 1] + radius / 4, paintText);
}
public void setRanks(int rank1,int rank2) {
if (rank1<1||rank1>30||rank2<1||rank2>30)
throw new IllegalArgumentException("排名引數只能設定成1到30的整數");
this.mRankLeft =rank1;
this.mRankRight =rank2;
invalidate();
}
}
相關文章
- Android開發自定義控制元件實現一個餅狀圖Android控制元件
- Android開發自定義控制元件實現一個折線圖Android控制元件
- Android開發自定義控制元件實現一個球賽勝負數統計條Android控制元件
- Android自定義控制元件之實現一個球賽比分條Android控制元件
- Android開發自定義控制元件實現一個圓形進度條【帶數值和動畫】Android控制元件動畫
- VirtualView Android 實現詳解(三)—— 新增一個自定義控制元件ViewAndroid控制元件
- Android自定義控制元件之自定義ViewGroup實現標籤雲Android控制元件View
- Android 自定義dialog,實現右上角顯示一個控制元件按鈕Android控制元件
- Qt實現自定義控制元件QT控制元件
- Android開發之自定義View(一)AndroidView
- Android自定義控制元件——自定義屬性Android控制元件
- 自定義分頁控制元件控制元件
- Android 控制元件框架、View的分發機制和自定義ViewAndroid控制元件框架View
- Android自定義控制元件之自定義組合控制元件Android控制元件
- 自定義DropDownList控制元件的實現控制元件
- Android自定義控制元件實現一個帶文字與數字的圓形進度條Android控制元件
- Android 自定義 View 之 實現一個多功能的 IndicatorViewAndroidViewIndicator
- android自定義開關控制元件-SlideSwitchAndroid控制元件IDE
- Android自定義控制元件之自定義屬性Android控制元件
- Android開發之自定義隨機驗證碼控制元件Android隨機控制元件
- Android自定義拍照實現Android
- Android 實現自定義圓環Android
- 自定義TextBox控制元件的實現控制元件
- Android 自定義實現switch開關按鈕Android
- android:建立自定義控制元件Android控制元件
- Android右滑關閉Activity介面功能-自定義控制元件實現Android控制元件
- Android Paint應用之自定義View實現進度條控制元件AndroidAIView控制元件
- Android 自定義控制元件實現刮刮卡效果 真的就只是刮刮卡麼Android控制元件
- Android自定義日曆控制元件的實現過程詳解Android控制元件
- (Android自定義控制元件)Android自定義狀態提示圖表Android控制元件
- 從Android到ReactNative開發(三、自定義原生控制元件支援)AndroidReact控制元件
- Android自定義View--翻書控制元件(一)AndroidView控制元件
- 微信小程式Tree自定義控制元件實現微信小程式控制元件
- QT實現可拖動自定義控制元件QT控制元件
- Android開發之自定義SpinnerAndroid
- Android自定義組合控制元件之自定義屬性Android控制元件
- 如何開發FineReport的自定義控制元件?控制元件
- iOS開發自定義View佈局子控制元件iOSView控制元件