• android 彩虹进度条自定义view实现


            实现一个彩虹色进度条功能,不说明具体用途大家应该能猜到。想找别人造的轮子,但是没有合适的,所以决定自己实现一个。

    相关知识

    android 自定义view

    LinearGradient 线性渐变

    实现步骤

    自定义view

    自定义一个TmcView类继承View

    重写两个构造方法。构造方法一共有4个,这里边重写两个

    重写ongSizeChanged方法,用来获取控件宽、高,来计算内部组件尺寸。

    重写onDraw方法,里边要描画背景drawBackground,分段数据drawSection,和seekbar图片drawImage。

    1. import android.content.Context;
    2. import android.graphics.Canvas;
    3. import android.util.AttributeSet;
    4. import android.view.View;
    5. import androidx.annotation.Nullable;
    6. import java.util.List;
    7. public class TmcView extends View {
    8. public TmcView(Context context) {
    9. super(context);
    10. }
    11. public TmcView(Context context, @Nullable AttributeSet attrs) {
    12. super(context, attrs);
    13. }
    14. @Override
    15. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    16. super.onSizeChanged(w, h, oldw, oldh);
    17. }
    18. @Override
    19. protected void onDraw(Canvas canvas) {
    20. super.onDraw(canvas);
    21. drawBackground(canvas);
    22. drawSection(canvas);
    23. drawImage(canvas);
    24. }
    25. /**
    26. * 画背景
    27. * @param canvas 画布
    28. */
    29. private void drawBackground(Canvas canvas) {
    30. }
    31. /**
    32. * 画分段数据
    33. * @param canvas 画布
    34. */
    35. private void drawSection(Canvas canvas) {
    36. }
    37. /**
    38. * 画图片
    39. * @param canvas 画布
    40. */
    41. private void drawImage(Canvas canvas) {
    42. }
    43. /**
    44. * 更新view
    45. * @param total 总长度
    46. * @param rest 剩余长度
    47. * @param sections 分段数据
    48. */
    49. public void updateView(int total, int rest, List sections){
    50. }

    实现几个重构方法

    标注:

    整体宽度为图片宽度44px

    背景条宽度30px,外边距7px,圆角15px

    带颜色的条宽度20xp,外边距5px,圆角15px

    自定义view的坐标轴是以view的左上角位置为原点,向右为x轴正方向,向下为y轴正方向

    在视图中用RectF创建背景,和颜色条,并在onSizeChanged中设置尺寸

    1. public class TmcView extends View {
    2. private final RectF backgroundRectF = new RectF();
    3. private final RectF colorRectF = new RectF();
    4. public TmcView(Context context) {
    5. super(context);
    6. }
    7. public TmcView(Context context, @Nullable AttributeSet attrs) {
    8. super(context, attrs);
    9. }
    10. @Override
    11. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    12. super.onSizeChanged(w, h, oldw, oldh);
    13. //设置矩形 left top right bottom 左上右下点的值 按照标注计算得出
    14. backgroundRectF.set(7, 0, 37, h);
    15. colorRectF.set(12, 5, 32, h-5);
    16. }
    17. ...
    18. }

    绘制背景条

    实现drawBackground方法,画背景需要一根画笔Paint 为了避免重复创建,声明为成员变量

    1. public class TmcView extends View {
    2. /*背景画笔*/
    3. private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    4. private final RectF backgroundRectF = new RectF();
    5. private final RectF colorRectF = new RectF();
    6. public TmcView(Context context) {
    7. super(context);
    8. }
    9. public TmcView(Context context, @Nullable AttributeSet attrs) {
    10. super(context, attrs);
    11. }
    12. @Override
    13. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    14. super.onSizeChanged(w, h, oldw, oldh);
    15. //设置矩形 left top right bottom 左上右下点的值 按照标注计算得出
    16. backgroundRectF.set(7, 0, 37, h);
    17. colorRectF.set(12, 5, 32, h-5);
    18. }
    19. @Override
    20. protected void onDraw(Canvas canvas) {
    21. super.onDraw(canvas);
    22. drawBackground(canvas);
    23. drawSection(canvas);
    24. drawImage(canvas);
    25. }
    26. /**
    27. * 画背景
    28. * @param canvas 画布
    29. */
    30. private void drawBackground(Canvas canvas) {
    31. backPaint.setStyle(Paint.Style.FILL);
    32. backPaint.setAntiAlias(true); //抗锯齿
    33. backPaint.setColor(Color.parseColor("#FFFFFF"));
    34. //画圆角矩形,15为圆角的角度
    35. canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);
    36. }
    37. ...
    38. }

      布局中加入TmcView

    1. <com.bigxuan.tesapp.view.TmcView
    2. android:id="@+id/tmc"
    3. android:layout_width="44px"
    4. android:layout_height="690px"
    5. android:layout_marginEnd="1230px"
    6. android:layout_marginBottom="100px"
    7. app:layout_constraintBottom_toBottomOf="parent"
    8. app:layout_constraintEnd_toEndOf="parent"
    9. />

    绘制颜色条

    实现drawSection方法,这里要用到线性渐变LinearGradient

    LinearGradient 简单说,指定每一段的颜色和位置百分比,就能实现每一段显示不同颜色。

    但它默认是渐变色,要想不变就在每一段的开始和结束位置都设置相同的颜色。

    再创建一个画笔 Paint,画颜色条

    1. public class TmcView extends View {
    2. private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    3. private final Paint colorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    4. private final RectF backgroundRectF = new RectF();
    5. private final RectF colorRectF = new RectF();
    6. public TmcView(Context context) {
    7. super(context);
    8. }
    9. public TmcView(Context context, @Nullable AttributeSet attrs) {
    10. super(context, attrs);
    11. }
    12. @Override
    13. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    14. super.onSizeChanged(w, h, oldw, oldh);
    15. //设置矩形 left top right bottom 左上右下点的值 按照标注计算得出
    16. backgroundRectF.set(7, 0, 37, h);
    17. colorRectF.set(12, 5, 32, h-5);
    18. }
    19. @Override
    20. protected void onDraw(Canvas canvas) {
    21. super.onDraw(canvas);
    22. drawBackground(canvas);
    23. drawSection(canvas);
    24. drawImage(canvas);
    25. }
    26. /**
    27. * 画背景
    28. * @param canvas 画布
    29. */
    30. private void drawBackground(Canvas canvas) {
    31. backPaint.setStyle(Paint.Style.FILL);
    32. backPaint.setAntiAlias(true); //抗锯齿
    33. backPaint.setColor(Color.parseColor("#FFFFFF"));
    34. //画圆角矩形,15为圆角的角度
    35. canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);
    36. }
    37. /**
    38. * 画分段数据
    39. * @param canvas 画布
    40. */
    41. private void drawSection(Canvas canvas) {
    42. int[] colorArray = {
    43. Color.parseColor("#FF0000"), Color.parseColor("#FF0000"),
    44. Color.parseColor("#00FF00"), Color.parseColor("#00FF00"),
    45. Color.parseColor("#0000FF"), Color.parseColor("#0000FF")
    46. };
    47. float[] positionArray = {
    48. 0f, 0.2f,
    49. 0.2f, 0.6f,
    50. 0.6f, 1f
    51. };
    52. colorPaint.setStyle(Paint.Style.FILL);
    53. colorPaint.setAntiAlias(true);
    54. //指定渐变色方向x轴方向不变,沿y方向渐变,渐变范围为 颜色条高度
    55. colorPaint.setShader(new LinearGradient(0, 0, 0, colorRectF.bottom, colorArray, positionArray, Shader.TileMode.REPEAT));
    56. canvas.drawRoundRect(colorRectF,15, 15, colorPaint);
    57. }
    58. ...
    59. }

    绘制进度图片

    app加入图片资源,根据资源id获取bitmap对象,绘制。

    需要注意的是,坐标轴的顶点在左上角,绘制图片时也是以图片左上顶点位置做定位,图片位置是view的高度减掉图片的高度,才能显示在正确位置。

    1. public class TmcView extends View {
    2. private Context context;
    3. private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    4. private final Paint colorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    5. private final RectF backgroundRectF = new RectF();
    6. private final RectF colorRectF = new RectF();
    7. /*图片资源id*/
    8. private int imgResId;
    9. /*图片位置*/
    10. private int imgPos;
    11. public TmcView(Context context) {
    12. super(context);
    13. }
    14. public TmcView(Context context, @Nullable AttributeSet attrs) {
    15. super(context, attrs);
    16. this.context = context;
    17. this.imgResId = R.drawable.icon_seekbar_day;
    18. }
    19. @Override
    20. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    21. super.onSizeChanged(w, h, oldw, oldh);
    22. //设置矩形 left top right bottom 左上右下点的值 按照标注计算得出
    23. backgroundRectF.set(7, 0, 37, h);
    24. colorRectF.set(12, 5, 32, h-5);
    25. imgPos = h - 44; // 设置一个初始位置
    26. }
    27. @Override
    28. protected void onDraw(Canvas canvas) {
    29. super.onDraw(canvas);
    30. drawBackground(canvas);
    31. drawSection(canvas);
    32. drawImage(canvas);
    33. }
    34. /**
    35. * 画背景
    36. * @param canvas 画布
    37. */
    38. private void drawBackground(Canvas canvas) {
    39. backPaint.setStyle(Paint.Style.FILL);
    40. backPaint.setAntiAlias(true); //抗锯齿
    41. backPaint.setColor(Color.parseColor("#FFFFFF"));
    42. //画圆角矩形,15为圆角的角度
    43. canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);
    44. }
    45. /**
    46. * 画分段数据
    47. * @param canvas 画布
    48. */
    49. private void drawSection(Canvas canvas) {
    50. int[] colorArray = {
    51. Color.parseColor("#FF0000"), Color.parseColor("#FF0000"),
    52. Color.parseColor("#00FF00"), Color.parseColor("#00FF00"),
    53. Color.parseColor("#0000FF"), Color.parseColor("#0000FF")
    54. };
    55. float[] positionArray = {
    56. 0f, 0.2f,
    57. 0.2f, 0.6f,
    58. 0.6f, 1f
    59. };
    60. colorPaint.setStyle(Paint.Style.FILL);
    61. colorPaint.setAntiAlias(true);
    62. //指定渐变色方向x轴方向不变,沿y方向渐变,渐变范围为 颜色条高度
    63. colorPaint.setShader(new LinearGradient(0, 0, 0, colorRectF.bottom, colorArray, positionArray, Shader.TileMode.REPEAT));
    64. canvas.drawRoundRect(colorRectF,15, 15, colorPaint);
    65. }
    66. /**
    67. * 画图片
    68. * @param canvas 画布
    69. */
    70. private void drawImage(Canvas canvas) {
    71. Bitmap bitmap = initBitmap(imgResId);
    72. canvas.save();
    73. canvas.translate(0, 0);
    74. canvas.drawBitmap(bitmap, 0, imgPos, null);
    75. canvas.restore();
    76. }
    77. /**
    78. * 通过资源id 创建bitmap
    79. * @param resId 资源id
    80. * @return
    81. */
    82. private Bitmap initBitmap(int resId) {
    83. Bitmap originalBmp = BitmapFactory.decodeResource(context.getResources(), resId);
    84. return Bitmap.createScaledBitmap(originalBmp, 44, 44, true);
    85. }
    86. ...
    87. }

    公共方法

    暴露方法用来更新进度updateView

    定义一个图片有效的运动距离为view高度减掉图片高度,函数参数为总距离和剩余距离,计算百分比后乘以有效运动距离得出图片描画位置。最后调用invalidate方法刷新view

    1. import android.content.Context;
    2. import android.graphics.Bitmap;
    3. import android.graphics.BitmapFactory;
    4. import android.graphics.Canvas;
    5. import android.graphics.Color;
    6. import android.graphics.LinearGradient;
    7. import android.graphics.Paint;
    8. import android.graphics.RectF;
    9. import android.graphics.Shader;
    10. import android.util.AttributeSet;
    11. import android.view.View;
    12. import androidx.annotation.Nullable;
    13. import com.navinfo.tesapp.R;
    14. import java.util.List;
    15. public class TmcView extends View {
    16. private Context context;
    17. private final Paint backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    18. private final Paint colorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    19. private final RectF backgroundRectF = new RectF();
    20. private final RectF colorRectF = new RectF();
    21. /*图片资源id*/
    22. private int imgResId;
    23. /*图片位置*/
    24. private int imgPos;
    25. /*图片有效的运动距离*/
    26. private int imgValidHeight;
    27. public TmcView(Context context) {
    28. super(context);
    29. }
    30. public TmcView(Context context, @Nullable AttributeSet attrs) {
    31. super(context, attrs);
    32. this.context = context;
    33. this.imgResId = R.drawable.icon_seekbar_day;
    34. }
    35. @Override
    36. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    37. super.onSizeChanged(w, h, oldw, oldh);
    38. //设置矩形 left top right bottom 左上右下点的值 按照标注计算得出
    39. backgroundRectF.set(7, 0, 37, h);
    40. colorRectF.set(12, 5, 32, h-5);
    41. imgValidHeight = h - 44;
    42. imgPos = h - 44; // 设置一个初始位置
    43. }
    44. @Override
    45. protected void onDraw(Canvas canvas) {
    46. super.onDraw(canvas);
    47. drawBackground(canvas);
    48. drawSection(canvas);
    49. drawImage(canvas);
    50. }
    51. /**
    52. * 画背景
    53. * @param canvas 画布
    54. */
    55. private void drawBackground(Canvas canvas) {
    56. backPaint.setStyle(Paint.Style.FILL);
    57. backPaint.setAntiAlias(true); //抗锯齿
    58. backPaint.setColor(Color.parseColor("#FFFFFF"));
    59. //画圆角矩形,15为圆角的角度
    60. canvas.drawRoundRect(backgroundRectF, 15, 15, backPaint);
    61. }
    62. /**
    63. * 画分段数据
    64. * @param canvas 画布
    65. */
    66. private void drawSection(Canvas canvas) {
    67. int[] colorArray = {
    68. Color.parseColor("#FF0000"), Color.parseColor("#FF0000"),
    69. Color.parseColor("#00FF00"), Color.parseColor("#00FF00"),
    70. Color.parseColor("#0000FF"), Color.parseColor("#0000FF")
    71. };
    72. float[] positionArray = {
    73. 0f, 0.2f,
    74. 0.2f, 0.6f,
    75. 0.6f, 1f
    76. };
    77. colorPaint.setStyle(Paint.Style.FILL);
    78. colorPaint.setAntiAlias(true);
    79. //指定渐变色方向x轴方向不变,沿y方向渐变,渐变范围为 颜色条高度
    80. colorPaint.setShader(new LinearGradient(0, 0, 0, colorRectF.bottom, colorArray, positionArray, Shader.TileMode.REPEAT));
    81. canvas.drawRoundRect(colorRectF,15, 15, colorPaint);
    82. }
    83. /**
    84. * 画图片
    85. * @param canvas 画布
    86. */
    87. private void drawImage(Canvas canvas) {
    88. Bitmap bitmap = initBitmap(imgResId);
    89. canvas.save();
    90. canvas.translate(0, 0);
    91. canvas.drawBitmap(bitmap, 0, imgPos, null);
    92. canvas.restore();
    93. }
    94. /**
    95. *
    96. * @param resId
    97. * @return
    98. */
    99. private Bitmap initBitmap(int resId) {
    100. Bitmap originalBmp = BitmapFactory.decodeResource(context.getResources(), resId);
    101. return Bitmap.createScaledBitmap(originalBmp, 44, 44, true);
    102. }
    103. /**
    104. * 更新view
    105. * @param total 总长度
    106. * @param rest 剩余长度
    107. * @param sections 分段数据
    108. */
    109. public void updateView(int total, int rest, List sections){
    110. float percent = (1f * rest) / total;
    111. //防止溢出
    112. if(percent < 0){
    113. return;
    114. }
    115. imgPos = (int)(percent * imgValidHeight);
    116. invalidate();
    117. }
    118. }

    updateView方法还有第三个参数,是用来传出颜色条不同段的颜色和百分比数据的。根据此数据来更新颜色条。这里需要根据业务不同自己实现,我这里就不写了。

    总结

            大家可能看出来了,这个视图是用来展示导航中不同路段交通情况和当前车辆进度用的。自定义view中可以在构造方法中获取一些自定义属性,像背景条和颜色条的边距、圆角这些都可以定义到xml中,因为只适配一种屏幕尺寸所以也没有做多尺寸适配。以前也没有做过,这次记录下来。

  • 相关阅读:
    操作系统笔记(王道考研) 第四章:文件管理
    算法:删除有序数组中的重复项---双指针[3]
    746. 使用最小花费爬楼梯 (Swift版本)
    Win10管理员权限怎么获取?Win10取得管理员权限的方法
    mongo实际业务场景实战
    电影评分数据分析案例-Spark SQL
    Linux由hello.c生成a.out整个过程
    全栈自动化测试之python基础类的自定义属性访问及动态属性设置
    python利用opencv进行相机标定获取参数,并根据畸变参数修正图像附有全部代码(流畅无痛版)
    【吴恩达机器学习-笔记整理】设计复杂的机器学习系统(执行的优先级,误差分析,不对称性误差评估,精确度和召回率)
  • 原文地址:https://blog.csdn.net/cyy298/article/details/139809185