android 自定義view實(shí)現(xiàn)彩虹進(jìn)度條功能

實(shí)現(xiàn)一個(gè)彩虹色進(jìn)度條功能,不說明具體用途大家應(yīng)該能猜到。想找別人造的輪子,但是沒有合適的,所以決定自己實(shí)現(xiàn)一個(gè)。
相關(guān)知識
android 自定義view
LinearGradient 線性漸變
實(shí)現(xiàn)步驟
自定義view
自定義一個(gè)TmcView類繼承View
重寫兩個(gè)構(gòu)造方法。構(gòu)造方法一共有4個(gè),這里邊重寫兩個(gè)
重寫ongSizeChanged方法,用來獲取控件寬、高,來計(jì)算內(nèi)部組件尺寸。
重寫onDraw方法,里邊要描畫背景drawBackground,分段數(shù)據(jù)drawSection,和seekbar圖片drawImage。
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import java.util.List;
public class TmcView extends View {
public TmcView(Context context) {
super(context);
}
public TmcView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(canvas);
drawSection(canvas);
drawImage(canvas);
}
/**
* 畫背景
* @param canvas 畫布
*/
private void drawBackground(Canvas canvas) {
}
/**
* 畫分段數(shù)據(jù)
* @param canvas 畫布
*/
private void drawSection(Canvas canvas) {
}
/**
* 畫圖片
* @param canvas 畫布
*/
private void drawImage(Canvas canvas) {
}
/**
* 更新view
* @param total 總長度
* @param rest 剩余長度
* @param sections 分段數(shù)據(jù)
*/
public void updateView(int total, int rest, List<SectionData> sections){
}實(shí)現(xiàn)幾個(gè)重構(gòu)方法
標(biāo)注:
整體寬度為圖片寬度44px
背景條寬度30px,外邊距7px,圓角15px
帶顏色的條寬度20xp,外邊距5px,圓角15px
自定義view的坐標(biāo)軸是以view的左上角位置為原點(diǎn),向右為x軸正方向,向下為y軸正方向

在視圖中用RectF創(chuàng)建背景,和顏色條,并在onSizeChanged中設(shè)置尺寸
public class TmcView extends View {
private final RectF backgroundRectF = new RectF();
private final RectF colorRectF = new RectF();
public TmcView(Context context) {
super(context);
}
public TmcView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//設(shè)置矩形 left top right bottom 左上右下點(diǎn)的值 按照標(biāo)注計(jì)算得出
backgroundRectF.set(7, 0, 37, h);
colorRectF.set(12, 5, 32, h-5);
}
...
}繪制背景條

實(shí)現(xiàn)drawBackground方法,畫背景需要一根畫筆Paint 為了避免重復(fù)創(chuàng)建,聲明為成員變量
public class TmcView extends View {
/*背景畫筆*/
private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final RectF backgroundRectF = new RectF();
private final RectF colorRectF = new RectF();
public TmcView(Context context) {
super(context);
}
public TmcView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//設(shè)置矩形 left top right bottom 左上右下點(diǎn)的值 按照標(biāo)注計(jì)算得出
backgroundRectF.set(7, 0, 37, h);
colorRectF.set(12, 5, 32, h-5);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(canvas);
drawSection(canvas);
drawImage(canvas);
}
/**
* 畫背景
* @param canvas 畫布
*/
private void drawBackground(Canvas canvas) {
backPaint.setStyle(Paint.Style.FILL);
backPaint.setAntiAlias(true); //抗鋸齒
backPaint.setColor(Color.parseColor("#FFFFFF"));
//畫圓角矩形,15為圓角的角度
canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);
}
...
}布局中加入TmcView
<com.bigxuan.tesapp.view.TmcView
android:id="@+id/tmc"
android:layout_width="44px"
android:layout_height="690px"
android:layout_marginEnd="1230px"
android:layout_marginBottom="100px"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>繪制顏色條
![]()
實(shí)現(xiàn)drawSection方法,這里要用到線性漸變LinearGradient
LinearGradient 簡單說,指定每一段的顏色和位置百分比,就能實(shí)現(xiàn)每一段顯示不同顏色。
但它默認(rèn)是漸變色,要想不變就在每一段的開始和結(jié)束位置都設(shè)置相同的顏色。
再創(chuàng)建一個(gè)畫筆 Paint,畫顏色條
public class TmcView extends View {
private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint colorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final RectF backgroundRectF = new RectF();
private final RectF colorRectF = new RectF();
public TmcView(Context context) {
super(context);
}
public TmcView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//設(shè)置矩形 left top right bottom 左上右下點(diǎn)的值 按照標(biāo)注計(jì)算得出
backgroundRectF.set(7, 0, 37, h);
colorRectF.set(12, 5, 32, h-5);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(canvas);
drawSection(canvas);
drawImage(canvas);
}
/**
* 畫背景
* @param canvas 畫布
*/
private void drawBackground(Canvas canvas) {
backPaint.setStyle(Paint.Style.FILL);
backPaint.setAntiAlias(true); //抗鋸齒
backPaint.setColor(Color.parseColor("#FFFFFF"));
//畫圓角矩形,15為圓角的角度
canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);
}
/**
* 畫分段數(shù)據(jù)
* @param canvas 畫布
*/
private void drawSection(Canvas canvas) {
int[] colorArray = {
Color.parseColor("#FF0000"), Color.parseColor("#FF0000"),
Color.parseColor("#00FF00"), Color.parseColor("#00FF00"),
Color.parseColor("#0000FF"), Color.parseColor("#0000FF")
};
float[] positionArray = {
0f, 0.2f,
0.2f, 0.6f,
0.6f, 1f
};
colorPaint.setStyle(Paint.Style.FILL);
colorPaint.setAntiAlias(true);
//指定漸變色方向x軸方向不變,沿y方向漸變,漸變范圍為 顏色條高度
colorPaint.setShader(new LinearGradient(0, 0, 0, colorRectF.bottom, colorArray, positionArray, Shader.TileMode.REPEAT));
canvas.drawRoundRect(colorRectF,15, 15, colorPaint);
}
...
}繪制進(jìn)度圖片

app加入圖片資源,根據(jù)資源id獲取bitmap對象,繪制。
需要注意的是,坐標(biāo)軸的頂點(diǎn)在左上角,繪制圖片時(shí)也是以圖片左上頂點(diǎn)位置做定位,圖片位置是view的高度減掉圖片的高度,才能顯示在正確位置。
public class TmcView extends View {
private Context context;
private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint colorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final RectF backgroundRectF = new RectF();
private final RectF colorRectF = new RectF();
/*圖片資源id*/
private int imgResId;
/*圖片位置*/
private int imgPos;
public TmcView(Context context) {
super(context);
}
public TmcView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
this.imgResId = R.drawable.icon_seekbar_day;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//設(shè)置矩形 left top right bottom 左上右下點(diǎn)的值 按照標(biāo)注計(jì)算得出
backgroundRectF.set(7, 0, 37, h);
colorRectF.set(12, 5, 32, h-5);
imgPos = h - 44; // 設(shè)置一個(gè)初始位置
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(canvas);
drawSection(canvas);
drawImage(canvas);
}
/**
* 畫背景
* @param canvas 畫布
*/
private void drawBackground(Canvas canvas) {
backPaint.setStyle(Paint.Style.FILL);
backPaint.setAntiAlias(true); //抗鋸齒
backPaint.setColor(Color.parseColor("#FFFFFF"));
//畫圓角矩形,15為圓角的角度
canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);
}
/**
* 畫分段數(shù)據(jù)
* @param canvas 畫布
*/
private void drawSection(Canvas canvas) {
int[] colorArray = {
Color.parseColor("#FF0000"), Color.parseColor("#FF0000"),
Color.parseColor("#00FF00"), Color.parseColor("#00FF00"),
Color.parseColor("#0000FF"), Color.parseColor("#0000FF")
};
float[] positionArray = {
0f, 0.2f,
0.2f, 0.6f,
0.6f, 1f
};
colorPaint.setStyle(Paint.Style.FILL);
colorPaint.setAntiAlias(true);
//指定漸變色方向x軸方向不變,沿y方向漸變,漸變范圍為 顏色條高度
colorPaint.setShader(new LinearGradient(0, 0, 0, colorRectF.bottom, colorArray, positionArray, Shader.TileMode.REPEAT));
canvas.drawRoundRect(colorRectF,15, 15, colorPaint);
}
/**
* 畫圖片
* @param canvas 畫布
*/
private void drawImage(Canvas canvas) {
Bitmap bitmap = initBitmap(imgResId);
canvas.save();
canvas.translate(0, 0);
canvas.drawBitmap(bitmap, 0, imgPos, null);
canvas.restore();
}
/**
* 通過資源id 創(chuàng)建bitmap
* @param resId 資源id
* @return
*/
private Bitmap initBitmap(int resId) {
Bitmap originalBmp = BitmapFactory.decodeResource(context.getResources(), resId);
return Bitmap.createScaledBitmap(originalBmp, 44, 44, true);
}
...
}公共方法
暴露方法用來更新進(jìn)度updateView
定義一個(gè)圖片有效的運(yùn)動距離為view高度減掉圖片高度,函數(shù)參數(shù)為總距離和剩余距離,計(jì)算百分比后乘以有效運(yùn)動距離得出圖片描畫位置。最后調(diào)用invalidate方法刷新view
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import com.navinfo.tesapp.R;
import java.util.List;
public class TmcView extends View {
private Context context;
private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint colorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final RectF backgroundRectF = new RectF();
private final RectF colorRectF = new RectF();
/*圖片資源id*/
private int imgResId;
/*圖片位置*/
private int imgPos;
/*圖片有效的運(yùn)動距離*/
private int imgValidHeight;
public TmcView(Context context) {
super(context);
}
public TmcView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
this.imgResId = R.drawable.icon_seekbar_day;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//設(shè)置矩形 left top right bottom 左上右下點(diǎn)的值 按照標(biāo)注計(jì)算得出
backgroundRectF.set(7, 0, 37, h);
colorRectF.set(12, 5, 32, h-5);
imgValidHeight = h - 44;
imgPos = h - 44; // 設(shè)置一個(gè)初始位置
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(canvas);
drawSection(canvas);
drawImage(canvas);
}
/**
* 畫背景
* @param canvas 畫布
*/
private void drawBackground(Canvas canvas) {
backPaint.setStyle(Paint.Style.FILL);
backPaint.setAntiAlias(true); //抗鋸齒
backPaint.setColor(Color.parseColor("#FFFFFF"));
//畫圓角矩形,15為圓角的角度
canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);
}
/**
* 畫分段數(shù)據(jù)
* @param canvas 畫布
*/
private void drawSection(Canvas canvas) {
int[] colorArray = {
Color.parseColor("#FF0000"), Color.parseColor("#FF0000"),
Color.parseColor("#00FF00"), Color.parseColor("#00FF00"),
Color.parseColor("#0000FF"), Color.parseColor("#0000FF")
};
float[] positionArray = {
0f, 0.2f,
0.2f, 0.6f,
0.6f, 1f
};
colorPaint.setStyle(Paint.Style.FILL);
colorPaint.setAntiAlias(true);
//指定漸變色方向x軸方向不變,沿y方向漸變,漸變范圍為 顏色條高度
colorPaint.setShader(new LinearGradient(0, 0, 0, colorRectF.bottom, colorArray, positionArray, Shader.TileMode.REPEAT));
canvas.drawRoundRect(colorRectF,15, 15, colorPaint);
}
/**
* 畫圖片
* @param canvas 畫布
*/
private void drawImage(Canvas canvas) {
Bitmap bitmap = initBitmap(imgResId);
canvas.save();
canvas.translate(0, 0);
canvas.drawBitmap(bitmap, 0, imgPos, null);
canvas.restore();
}
/**
*
* @param resId
* @return
*/
private Bitmap initBitmap(int resId) {
Bitmap originalBmp = BitmapFactory.decodeResource(context.getResources(), resId);
return Bitmap.createScaledBitmap(originalBmp, 44, 44, true);
}
/**
* 更新view
* @param total 總長度
* @param rest 剩余長度
* @param sections 分段數(shù)據(jù)
*/
public void updateView(int total, int rest, List<SectionData> sections){
float percent = (1f * rest) / total;
//防止溢出
if(percent < 0){
return;
}
imgPos = (int)(percent * imgValidHeight);
invalidate();
}
}updateView方法還有第三個(gè)參數(shù),是用來傳出顏色條不同段的顏色和百分比數(shù)據(jù)的。根據(jù)此數(shù)據(jù)來更新顏色條。這里需要根據(jù)業(yè)務(wù)不同自己實(shí)現(xiàn),我這里就不寫了。
總結(jié)
大家可能看出來了,這個(gè)視圖是用來展示導(dǎo)航中不同路段交通情況和當(dāng)前車輛進(jìn)度用的。自定義view中可以在構(gòu)造方法中獲取一些自定義屬性,像背景條和顏色條的邊距、圓角這些都可以定義到xml中,因?yàn)橹贿m配一種屏幕尺寸所以也沒有做多尺寸適配。以前也沒有做過,這次記錄下來。
- Android 自定義View實(shí)現(xiàn)多節(jié)點(diǎn)進(jìn)度條功能
- Android自定義View實(shí)現(xiàn)水平帶數(shù)字百分比進(jìn)度條
- Android自定義View實(shí)現(xiàn)音頻播放圓形進(jìn)度條
- Android自定義View實(shí)現(xiàn)漸變色進(jìn)度條
- Android自定義View實(shí)現(xiàn)加載進(jìn)度條效果
- Android自定義帶進(jìn)度條WebView仿微信加載過程
- Android view自定義實(shí)現(xiàn)動態(tài)進(jìn)度條
- Android 自定義view和屬性動畫實(shí)現(xiàn)充電進(jìn)度條效果
相關(guān)文章
Android中替換WebView加載網(wǎng)頁失敗時(shí)的頁面
這篇文章主要介紹了Android中替換WebView加載網(wǎng)頁失敗時(shí)的頁面,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-01-01
Android PopupMenu彈出菜單的實(shí)現(xiàn)
這篇文章主要介紹了 Android PopupMenu彈出菜單的實(shí)現(xiàn)的相關(guān)資料,希望通過本文能幫助到大家,實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-10-10
TabLayout+ViewPager實(shí)現(xiàn)切頁的示例代碼
這篇文章主要介紹了TabLayout+ViewPager實(shí)現(xiàn)切頁的示例代碼,可實(shí)現(xiàn)左右滑動切換視圖界面和點(diǎn)擊切換,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2019-01-01
Android Studio生成函數(shù)注釋的實(shí)現(xiàn)方法
這篇文章主要介紹了Android Studio生成函數(shù)注釋的實(shí)現(xiàn)方法的相關(guān)資料,希望通過本文大家能夠?qū)崿F(xiàn)這樣的功能,需要的朋友可以參考下2017-09-09
Android靜默安裝實(shí)現(xiàn)方案 仿360手機(jī)助手秒裝和智能安裝功能
這篇文章主要介紹了Android靜默安裝實(shí)現(xiàn)方案,仿360手機(jī)助手秒裝和智能安裝功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
Android實(shí)時(shí)獲取攝像頭畫面?zhèn)鬏斨罰C端思路詳解
這篇文章主要介紹了Android實(shí)時(shí)獲取攝像頭畫面?zhèn)鬏斨罰C端思路詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
android基礎(chǔ)總結(jié)篇之九:Intent應(yīng)用詳解
這篇文章主要介紹了android基礎(chǔ)總結(jié)篇之九:Intent應(yīng)用詳解,有需要的可以了解一下。2016-11-11
直接應(yīng)用項(xiàng)目中的Android圖片緩存技術(shù)
這篇文章主要為大家詳細(xì)介紹了直接應(yīng)用項(xiàng)目中的Android圖片緩存技術(shù),簡單、方便、高效,感興趣的小伙伴們可以參考一下2016-04-04

