先看一下效果圖:java
這裏我加了陰影效果,全部須要加入如下依賴:android
implementation 'com.google.android.material:material:1.0.0'
而後新建一個實現下載進度條的自定義View類,直接上代碼:canvas
1 public class FlikerProgressBar extends View implements Runnable{ 2 private PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP); 3 4 private int DEFAULT_HEIGHT_DP = 35; 5 6 private int borderWidth; 7 8 private float maxProgress = 100f; 9 10 private Paint textPaint; 11 12 private Paint bgPaint; 13 14 private Paint pgPaint; 15 16 private String progressText; 17 18 private Rect textRect; 19 20 private RectF bgRectf; 21 22 /** 23 * 左右來回移動的滑塊 24 */ 25 private Bitmap flikerBitmap; 26 27 /** 28 * 滑塊移動最左邊位置,做用是控制移動 29 */ 30 private float flickerLeft; 31 32 /** 33 * 進度條 bitmap ,包含滑塊 34 */ 35 private Bitmap pgBitmap; 36 37 private Canvas pgCanvas; 38 39 /** 40 * 當前進度 41 */ 42 private float progress; 43 44 private boolean isFinish; 45 46 private boolean isStop; 47 48 /** 49 * 下載中顏色 50 */ 51 private int loadingColor; 52 53 /** 54 * 暫停時顏色 55 */ 56 private int stopColor; 57 58 /** 59 * 進度文本、邊框、進度條顏色 60 */ 61 private int progressColor; 62 63 private int textSize; 64 65 private int radius; 66 67 private Thread thread; 68 69 BitmapShader bitmapShader; 70 71 public FlikerProgressBar(Context context) { 72 this(context, null, 0); 73 } 74 75 public FlikerProgressBar(Context context, AttributeSet attrs) { 76 this(context, attrs, 0); 77 } 78 79 public FlikerProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { 80 super(context, attrs, defStyleAttr); 81 initAttrs(attrs); 82 } 83 84 private void initAttrs(AttributeSet attrs) { 85 TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.FlikerProgressBar); 86 try { 87 textSize = (int) ta.getDimension(R.styleable.FlikerProgressBar_textSize, 12); 88 loadingColor = ta.getColor(R.styleable.FlikerProgressBar_loadingColor, Color.parseColor("#40c4ff")); 89 stopColor = ta.getColor(R.styleable.FlikerProgressBar_stopColor, Color.parseColor("#ff9800")); 90 radius = (int) ta.getDimension(R.styleable.FlikerProgressBar_radius, 0); 91 borderWidth = (int) ta.getDimension(R.styleable.FlikerProgressBar_borderWidth, 1); 92 } finally { 93 ta.recycle(); 94 } 95 } 96 97 private void init() { 98 bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); 99 bgPaint.setStyle(Paint.Style.STROKE); 100 bgPaint.setStrokeWidth(borderWidth); 101 102 pgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 103 pgPaint.setStyle(Paint.Style.FILL); 104 105 textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 106 textPaint.setTextSize(textSize); 107 108 textRect = new Rect(); 109 bgRectf = new RectF(borderWidth, borderWidth, getMeasuredWidth() - borderWidth, getMeasuredHeight() - borderWidth); 110 111 if(isStop){ 112 progressColor = stopColor; 113 } else{ 114 progressColor = loadingColor; 115 } 116 117 flikerBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.flicker); 118 flickerLeft = -flikerBitmap.getWidth(); 119 120 initPgBimap(); 121 } 122 123 private void initPgBimap() { 124 pgBitmap = Bitmap.createBitmap(getMeasuredWidth() - borderWidth, getMeasuredHeight() - borderWidth, Bitmap.Config.ARGB_8888); 125 pgCanvas = new Canvas(pgBitmap); 126 thread = new Thread(this); 127 thread.start(); 128 } 129 130 131 @Override 132 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 133 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 134 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); 135 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); 136 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); 137 int height = 0; 138 switch (heightSpecMode){ 139 case MeasureSpec.AT_MOST: 140 height = dp2px(DEFAULT_HEIGHT_DP); 141 break; 142 case MeasureSpec.EXACTLY: 143 case MeasureSpec.UNSPECIFIED: 144 height = heightSpecSize; 145 break; 146 } 147 setMeasuredDimension(widthSpecSize, height); 148 149 if(pgBitmap == null){ 150 init(); 151 } 152 153 } 154 155 @Override 156 protected void onDraw(Canvas canvas) { 157 super.onDraw(canvas); 158 159 //背景 160 drawBackGround(canvas); 161 162 //進度 163 drawProgress(canvas); 164 165 //進度text 166 drawProgressText(canvas); 167 168 //變色處理 169 drawColorProgressText(canvas); 170 } 171 172 /** 173 * 邊框 174 * @param canvas 175 */ 176 private void drawBackGround(Canvas canvas) { 177 bgPaint.setColor(progressColor); 178 //left、top、right、bottom不要貼着控件邊,不然border只有一半繪製在控件內,致使圓角處線條顯粗 179 canvas.drawRoundRect(bgRectf, radius, radius, bgPaint); 180 } 181 182 /** 183 * 進度 184 */ 185 private void drawProgress(Canvas canvas) { 186 pgPaint.setColor(progressColor); 187 188 float right = (progress / maxProgress) * getMeasuredWidth(); 189 pgCanvas.save(); 190 pgCanvas.clipRect(0, 0, right, getMeasuredHeight()); 191 pgCanvas.drawColor(progressColor); 192 pgCanvas.restore(); 193 194 if(!isStop){ 195 pgPaint.setXfermode(xfermode); 196 pgCanvas.drawBitmap(flikerBitmap, flickerLeft, 0, pgPaint); 197 pgPaint.setXfermode(null); 198 } 199 200 //控制顯示區域 201 bitmapShader = new BitmapShader(pgBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 202 pgPaint.setShader(bitmapShader); 203 canvas.drawRoundRect(bgRectf, radius, radius, pgPaint); 204 } 205 206 /** 207 * 進度提示文本 208 * @param canvas 209 */ 210 private void drawProgressText(Canvas canvas) { 211 textPaint.setColor(progressColor); 212 progressText = getProgressText(); 213 textPaint.getTextBounds(progressText, 0, progressText.length(), textRect); 214 int tWidth = textRect.width(); 215 int tHeight = textRect.height(); 216 float xCoordinate = (getMeasuredWidth() - tWidth) / 2; 217 float yCoordinate = (getMeasuredHeight() + tHeight) / 2; 218 canvas.drawText(progressText, xCoordinate, yCoordinate, textPaint); 219 } 220 221 /** 222 * 變色處理 223 * @param canvas 224 */ 225 private void drawColorProgressText(Canvas canvas) { 226 textPaint.setColor(Color.WHITE); 227 int tWidth = textRect.width(); 228 int tHeight = textRect.height(); 229 float xCoordinate = (getMeasuredWidth() - tWidth) / 2; 230 float yCoordinate = (getMeasuredHeight() + tHeight) / 2; 231 float progressWidth = (progress / maxProgress) * getMeasuredWidth(); 232 if(progressWidth > xCoordinate){ 233 canvas.save(); 234 float right = Math.min(progressWidth, xCoordinate + tWidth * 1.1f); 235 canvas.clipRect(xCoordinate, 0, right, getMeasuredHeight()); 236 canvas.drawText(progressText, xCoordinate, yCoordinate, textPaint); 237 canvas.restore(); 238 } 239 } 240 241 public void setProgress(float progress){ 242 if(!isStop){ 243 if(progress < maxProgress){ 244 this.progress = progress; 245 } else { 246 this.progress = maxProgress; 247 finishLoad(); 248 } 249 invalidate(); 250 } 251 } 252 253 public void setStop(boolean stop) { 254 isStop = stop; 255 if(isStop){ 256 progressColor = stopColor; 257 thread.interrupt(); 258 } else { 259 progressColor = loadingColor; 260 thread = new Thread(this); 261 thread.start(); 262 } 263 invalidate(); 264 } 265 266 public void finishLoad() { 267 isFinish = true; 268 setStop(true); 269 } 270 271 public void toggle(){ 272 if(!isFinish){ 273 if(isStop){ 274 setStop(false); 275 } else { 276 setStop(true); 277 } 278 } 279 } 280 281 @Override 282 public void run() { 283 int width = flikerBitmap.getWidth(); 284 try { 285 while (!isStop && !thread.isInterrupted()){ 286 flickerLeft += dp2px(5); 287 float progressWidth = (progress / maxProgress) * getMeasuredWidth(); 288 if(flickerLeft >= progressWidth){ 289 flickerLeft = -width; 290 } 291 postInvalidate(); 292 Thread.sleep(20); 293 } 294 }catch (InterruptedException e) { 295 e.printStackTrace(); 296 } 297 } 298 299 /** 300 * 重置 301 */ 302 public void reset(){ 303 setStop(true); 304 progress = 0; 305 isFinish = false; 306 isStop = false; 307 progressColor = loadingColor; 308 progressText = ""; 309 flickerLeft = -flikerBitmap.getWidth(); 310 initPgBimap(); 311 } 312 313 public float getProgress() { 314 return progress; 315 } 316 317 public boolean isStop() { 318 return isStop; 319 } 320 321 public boolean isFinish() { 322 return isFinish; 323 } 324 325 private String getProgressText() { 326 String text= ""; 327 if(!isFinish){ 328 if(!isStop){ 329 text = "下載中" + progress + "%"; 330 } else { 331 text = "繼續"; 332 } 333 } else{ 334 text = "下載完成"; 335 } 336 337 return text; 338 } 339 340 private int dp2px(int dp){ 341 float density = getContext().getResources().getDisplayMetrics().density; 342 return (int) (dp * density); 343 } 344 }
在MainActivity中使用,直接貼出代碼:app
1 public class MainActivity extends AppCompatActivity implements View.OnClickListener,Runnable{ 2 3 private FlikerProgressBar flikerProgressBar,roundProgressBar; 4 private Thread downLoadThread; 5 private Handler handler = new Handler(){ 6 @Override 7 public void handleMessage(@NonNull Message msg) { 8 super.handleMessage(msg); 9 flikerProgressBar.setProgress(msg.arg1); 10 roundProgressBar.setProgress(msg.arg1); 11 if(msg.arg1 == 100){ 12 flikerProgressBar.finishLoad(); 13 roundProgressBar.finishLoad(); 14 } 15 } 16 }; 17 18 @Override 19 protected void onCreate(Bundle savedInstanceState) { 20 super.onCreate(savedInstanceState); 21 setContentView(R.layout.activity_main); 22 initView(); 23 } 24 25 private void initView(){ 26 flikerProgressBar = (FlikerProgressBar)findViewById(R.id.fliker1); 27 roundProgressBar = (FlikerProgressBar)findViewById(R.id.fliker2); 28 flikerProgressBar.setOnClickListener(this); 29 roundProgressBar.setOnClickListener(this); 30 downLoad(); 31 } 32 33 private void downLoad(){ 34 downLoadThread = new Thread(this); 35 downLoadThread.start(); 36 } 37 38 public void reLoad(View view){ 39 downLoadThread.interrupt(); 40 //從新加載 41 flikerProgressBar.reset(); 42 roundProgressBar.reset(); 43 downLoad(); 44 } 45 46 @Override 47 public void onClick(View view) { 48 if(!flikerProgressBar.isFinish()){ 49 flikerProgressBar.toggle(); 50 roundProgressBar.toggle(); 51 if(flikerProgressBar.isStop()){ 52 downLoadThread.interrupt(); 53 } else { 54 downLoad(); 55 } 56 } 57 } 58 59 @Override 60 public void run() { 61 try { 62 while( ! downLoadThread.isInterrupted()){ 63 float progress = flikerProgressBar.getProgress(); 64 progress += 2; 65 Thread.sleep(200); 66 Message message = handler.obtainMessage(); 67 message.arg1 = (int) progress; 68 handler.sendMessage(message); 69 if(progress == 100){ 70 break; 71 } 72 } 73 }catch (InterruptedException e) { 74 e.printStackTrace(); 75 } 76 } 77 }
最後就是佈局文件的代碼:ide
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:orientation="vertical" 8 tools:context=".MainActivity"> 9 <androidx.cardview.widget.CardView 10 app:cardElevation="5dp" 11 android:layout_width="match_parent" 12 android:layout_height="wrap_content"> 13 <TextView 14 android:gravity="center" 15 android:textStyle="bold" 16 android:textSize="16sp" 17 android:text="下載進度條" 18 android:textColor="#FFF" 19 android:background="#01CCFF" 20 android:layout_width="match_parent" 21 android:layout_height="45dp" /> 22 </androidx.cardview.widget.CardView> 23 24 <LinearLayout 25 android:padding="20dp" 26 android:orientation="vertical" 27 android:layout_width="match_parent" 28 android:layout_height="match_parent"> 29 <androidx.cardview.widget.CardView 30 app:cardElevation="5dp" 31 android:layout_marginTop="30dp" 32 android:layout_width="match_parent" 33 android:layout_height="wrap_content"> 34 <com.example.download.FlikerProgressBar 35 android:id="@+id/fliker1" 36 android:layout_width="match_parent" 37 android:layout_height="wrap_content" 38 app:textSize="12sp" 39 app:loadingColor="#01CCFF" 40 app:stopColor="#ff9800"/> 41 </androidx.cardview.widget.CardView> 42 43 <androidx.cardview.widget.CardView 44 app:cardElevation="5dp" 45 app:cardCornerRadius="18dp" 46 android:layout_marginTop="30dp" 47 android:layout_width="match_parent" 48 android:layout_height="wrap_content"> 49 <com.example.download.FlikerProgressBar 50 android:id="@+id/fliker2" 51 android:layout_width="match_parent" 52 android:layout_height="wrap_content" 53 app:textSize="12sp" 54 app:loadingColor="#01CCFF" 55 app:stopColor="#ff9800" 56 app:radius="20dp"/> 57 </androidx.cardview.widget.CardView> 58 59 <Button 60 android:text="從新下載" 61 android:onClick="reLoad" 62 android:layout_marginTop="30dp" 63 android:layout_marginLeft="20dp" 64 android:layout_marginRight="20dp" 65 android:background="@drawable/button_five_theme_gray" 66 android:textColor="@drawable/button_five_theme_gray" 67 android:layout_width="match_parent" 68 android:layout_height="45dp" /> 69 </LinearLayout> 70 </LinearLayout>