最近项目有个需求, 需要一个展示下载状态的按钮, 类似这样:
这个效果有多种方法都可以实现, 最初的思路是在自定义view里封装一个ProgressBar
/Button
, 然后根据状态设置两个控件Visibility
, 后来想了想还是放弃了ProgressBar
, 直接使用了ClipDrawable
作为 BackgroundDrawable
实现的, 感觉更方便一些.
Github 上存有完整代码: https://github.com/YouCii/LearnApp/blob/master/app/src/main/java/com/youcii/mvplearn/widget/DownLoadButton.kt
转载请标明出处, 原文地址:
https://blog.csdn.net/j550341130/article/details/83993676
1. ClipDrawable
这是一个按比例裁剪的Drawable
, 可以使用它实现按百分比进度填充红色的部分. Clip的布局 bg_download_button_clip
具体写法如下:
<clip
xmlns:android="http://schemas.android.com/apk/res/android"
android:clipOrientation="horizontal"
android:drawable="@drawable/bg_download_button_downloading"
android:gravity="left"/>
其中 android:gravity
是用来设置裁剪起始位置, 有多种属性可以设置, 具体可以搜一下.
内部的 bg_download_button_downloading
就是一个完整填充的红色背景
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="100px"/>
<solid android:color="#D8413A"/>
shape>
在代码中获取方法与普通Drawable
一样
private var solidDrawable: Drawable? = resources.getDrawable(R.drawable.bg_download_button_clip)
然后通过设置solidDrawable
的level
来实现进度, level
最大是10000, 所以设置进度时需要这样做
solidDrawable?.level = currentPercent * 100
注意, ClipDrawable
只是一个按某规则进行裁剪的Drawable
, 最外部是没有边框的, 想要一个固定存在的边框的话仅用ClipDrawable
就不够了, 需要叠加一个普通的边框Drawable
, 这就需要用到LayerDrawable
了.
2. LayerDrawable
LayerDrawable
可以把多个Drawable
叠加到一起, 在这里需要使用它拼合 进度填充ClipDrawable
+ 普通边框效果Drawable
, 实现最终效果.
普通边框效果的布局如下:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="100px"/>
<solid android:color="#FFFFFF"/>
<stroke
android:width="2px"
android:color="#D8413A"/>
shape>
拼接需要在代码中实现:
private var layerDrawable: Drawable? = LayerDrawable(arrayOf(background, solidDrawable))
其中background
就是边框Drawable
, 实例化LayerDrawable
时传入需要叠加的Drawable
数组即可
内部使用的Drawable
布局可以看下Github的Demo:
https://github.com/YouCii/LearnApp/blob/master/app/src/main/java/com/youcii/mvplearn/widget/DownLoadButton.kt
package com.youcii.mvplearn.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.util.AttributeSet
import android.view.Gravity
import android.view.View
import android.widget.Button
import com.youcii.mvplearn.R
/**
* Created by jdw on 2018/11/2.
*
* 状态转换按钮, 带有边框填充效果的下载进度
*/
class DownLoadButton @JvmOverloads constructor(context:Context,attrs:AttributeSet?=null,defStyleAttr:Int=0)
: Button(context, attrs, defStyleAttr) {
/**
* 当前状态
*/
private var currentState = 0
/**
* 当前下载进度
* 百分比
*/
private var currentPercent = 0
private var onDownLoadButtonClickListener: OnDownLoadButtonClickListener? = null
init {
gravity = Gravity.CENTER
currentState = STATE_NO_DOWNLOAD
setOnClickListener { v -> onDownLoadButtonClickListener?.onClick(v, currentState) }
}
/**
* 设置当前状态
*/
fun setState(state: Int) {
this.currentState = state
postInvalidate()
}
/**
* 设置下载进度
*/
fun setDownLoadProgress(percent: Int) {
this.currentPercent = percent
postInvalidate()
}
private var solidDrawable: Drawable? = null
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
when (currentState) {
STATE_NO_DOWNLOAD -> {
currentPercent = 0
text = "下载"
setTextColor(Color.parseColor("#D8413A"))
setBackgroundResource(R.drawable.bg_download_button_download)
}
STATE_DOWNLOADING -> {
if (text != "下载中") {
text = "下载中"
setTextColor(Color.parseColor("#000000"))
// 动态填充的Drawable
solidDrawable = resources.getDrawable(R.drawable.bg_download_button_clip)
// 原background是显示的边框, 合并后显示
background = LayerDrawable(arrayOf(background, solidDrawable))
}
// 利用ClipDrawable的裁剪效果实现进度展示
solidDrawable?.level = currentPercent * 100
}
STATE_COMPLETE -> {
text = "立即使用"
setTextColor(Color.parseColor("#FFFFFF"))
setBackgroundResource(R.drawable.bg_download_button_to_use)
}
STATE_USED -> {
text = "使用中"
setTextColor(Color.parseColor("#000000"))
setBackgroundResource(R.drawable.bg_download_button_using)
}
}
}
fun setOnDownLoadButtonClickListener(onDownLoadButtonClickListener: OnDownLoadButtonClickListener) {
this.onDownLoadButtonClickListener = onDownLoadButtonClickListener
}
interface OnDownLoadButtonClickListener {
fun onClick(view: View, currentState: Int)
}
companion object {
// 未下载
val STATE_NO_DOWNLOAD = 0
// 下载中
val STATE_DOWNLOADING = 1
// 下载完成
val STATE_COMPLETE = 2
// 已使用
val STATE_USED = 3
}
}
下载进度动画效果的实现 除了 ClipDrawable
/ Progress + Button
之外, 当时还尝试了一种Drawable.setBounds(int left, int top, int right, int bottom)
, 这四个参数指的是drawable
被canvas
绘制时要放在多大的矩形区域内, 也就是说Drawable
是被完整绘制出来的, 只不过是被压缩或者拉伸了. 对于我们需要的这种圆角背景, 进度非常小的时候就会出现圆角消失的情况, 所以不采用. 具体代码如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// ...
case STATE_DOWNLOADING:
//计算当前进度所需宽度
int downLoadedWidth = (int) (getMeasuredWidth() * ((double) curPrecent / 100));
Rect rect = new Rect(0, 0, downLoadedWidth, getMeasuredHeight());
downLoadBackground.setBounds(rect); // 带边框, 满红色填充, 类似于下载完成样式的Drawable
downLoadBackground.draw(canvas);
break;
// ...
}