android 自定義view實(shí)現(xiàn)彩虹進(jìn)度條功能
實(shí)現(xiàn)一個(gè)彩虹色進(jìn)度條功能,不說明具體用途大家應(yīng)該能猜到。想找別人造的輪子,但是沒有合適的,所以決定自己實(shí)現(xiàn)一個(gè)。
相關(guān)知識(shí)
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 總長(zhǎng)度 * @param rest 剩余長(zhǎng)度 * @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 簡(jiǎn)單說,指定每一段的顏色和位置百分比,就能實(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對(duì)象,繪制。
需要注意的是,坐標(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)動(dòng)距離為view高度減掉圖片高度,函數(shù)參數(shù)為總距離和剩余距離,計(jì)算百分比后乘以有效運(yùn)動(dòng)距離得出圖片描畫位置。最后調(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)動(dòng)距離*/ 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 總長(zhǎng)度 * @param rest 剩余長(zhǎng)度 * @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)動(dòng)態(tài)進(jìn)度條
- Android 自定義view和屬性動(dòng)畫實(shí)現(xiàn)充電進(jìn)度條效果
相關(guān)文章
Android中替換WebView加載網(wǎng)頁失敗時(shí)的頁面
這篇文章主要介紹了Android中替換WebView加載網(wǎng)頁失敗時(shí)的頁面,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-01-01Android PopupMenu彈出菜單的實(shí)現(xiàn)
這篇文章主要介紹了 Android PopupMenu彈出菜單的實(shí)現(xiàn)的相關(guān)資料,希望通過本文能幫助到大家,實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-10-10TabLayout+ViewPager實(shí)現(xiàn)切頁的示例代碼
這篇文章主要介紹了TabLayout+ViewPager實(shí)現(xiàn)切頁的示例代碼,可實(shí)現(xiàn)左右滑動(dòng)切換視圖界面和點(diǎn)擊切換,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2019-01-01Android Studio生成函數(shù)注釋的實(shí)現(xiàn)方法
這篇文章主要介紹了Android Studio生成函數(shù)注釋的實(shí)現(xiàn)方法的相關(guān)資料,希望通過本文大家能夠?qū)崿F(xiàn)這樣的功能,需要的朋友可以參考下2017-09-09Android靜默安裝實(shí)現(xiàn)方案 仿360手機(jī)助手秒裝和智能安裝功能
這篇文章主要介紹了Android靜默安裝實(shí)現(xiàn)方案,仿360手機(jī)助手秒裝和智能安裝功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Android實(shí)時(shí)獲取攝像頭畫面?zhèn)鬏斨罰C端思路詳解
這篇文章主要介紹了Android實(shí)時(shí)獲取攝像頭畫面?zhèn)鬏斨罰C端思路詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07android基礎(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ù),簡(jiǎn)單、方便、高效,感興趣的小伙伴們可以參考一下2016-04-04