嗨 大家好,我是不服不行 。
今天为大家带来一个手机软件中所使用到的这个,这个一个常客。不管是下载什么,还是加载什么。都可以用它。
实现这个效果可以使用组合控件的方式
可以将进度条看为3个部分 左侧表示已经下载的横线,中间的进度信息和右侧的表示未下载的横线。
于是对应的布局代码便当如此:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical" >
<View
android:id="@+id/view_part_completed"
android:layout_width="0dip"
android:layout_height="1dip"
android:layout_weight="0" />
<TextView
android:id="@+id/tx_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="2dip"
android:paddingRight="2dip" />
<View
android:id="@+id/view_part_waiting"
android:layout_width="0dip"
android:layout_height="1dip"
android:layout_weight="100" />
</LinearLayout>
由代码可见实现原理是使用 layout_weight 属性。动态的分别设置完成部分(view_part_completed) 和 等待部分(view_part_waiting) 的权重值来达到中间信息不断移动一个效果。 核心代码:
public class ProgressLineView extends LinearLayout {
private View viewPartCompleted;
private View viewPartWaiting;
private TextView txProgress;
private LinearLayout.LayoutParams completedLp;
private LinearLayout.LayoutParams waitingLp;
public ProgressLineView(Context context) {
this(context, null);
}
public ProgressLineView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ProgressLineView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
LayoutInflater.from(context).inflate(R.layout.view_progress_line, this);
viewPartCompleted = findViewById(R.id.view_part_completed);
viewPartWaiting = findViewById(R.id.view_part_waiting);
txProgress = (TextView) findViewById(R.id.tx_progress);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ProgressLineView);
int lineWidth = (int) ta.getDimension(R.styleable.ProgressLineView_lineWidth, 1);
int progressTextSize = (int) ta.getDimension(R.styleable.ProgressLineView_progressTextSize, 12);
int progressColor = ta.getColor(R.styleable.ProgressLineView_progressColor, 0xff515151);
int completedColor = ta.getColor(R.styleable.ProgressLineView_completedColor, 0xff085cb2);
int waitingColor = ta.getColor(R.styleable.ProgressLineView_waitingColor, 0xffa7f0f9);
ta.recycle();
completedLp = (LayoutParams) viewPartCompleted.getLayoutParams();
completedLp.height = lineWidth;
waitingLp = (LayoutParams) viewPartWaiting.getLayoutParams();
waitingLp.height = lineWidth;
txProgress.setTextSize(progressTextSize);
txProgress.setTextColor(progressColor);
viewPartCompleted.setBackgroundColor(completedColor);
viewPartWaiting.setBackgroundColor(waitingColor);
updateProgress(0);
}
public void updateProgress(int progress) {
if (progress < 0 || progress > 100) {
return; // error progress
}
txProgress.setText(progress + "%");
completedLp.weight = progress;
viewPartCompleted.setLayoutParams(completedLp);
waitingLp.weight = 100 - progress;
viewPartWaiting.setLayoutParams(waitingLp);
}
}
代码非常简单,主要内容为:
1 提供5个可设置属性。
进度线的厚度(lineWidth)
下载信息文字的大小和颜色(progressTextSize&Color)
已完成部分的颜色(completedColor)
未完成部分的颜色(waitingColor)
注:TypedArray的使用所需要的attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ProgressLineView">
<attr name="lineWidth" format="dimension"></attr>
<attr name="completedColor" format="color" />
<attr name="waitingColor" format="color" />
<attr name="progressColor" format="color" />
<attr name="progressTextSize" format="dimension" />
</declare-styleable>
</resources>
2 对外公开的接口 updateProgress(),只需要传入当前的进度即可达到图中效果。
如何使用:
在布局文件添加如下内容:
<com.example.view.ProgressLineView
xmlns:app="http://schemas.android.com/apk/res/com.example.aa"
android:id="@+id/plv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:completedColor="@android:color/holo_blue_light"
app:lineWidth="1dip"
app:progressColor="@android:color/holo_blue_light"
app:progressTextSize="12sp"
app:waitingColor="@android:color/darker_gray" />
请注意修改包名
最后只需在代码中不停的调用 ProgressLineView对象的 updateProgress()方法即可。
以上内容非本文章重点。因为还有更好的办法达到同样的效果。那么我们进入本文正题。
将ProgressView 的继承父类改为View:
public class ProgressLineView extends View {
private Paint paint = new Paint();
private int mWidth;
private int mHeight;
private int progress;
private int progressPadding;
private int lineWidth;
private int progressTextSize;
private int progressColor;
private int completedColor;
private int waitingColor;
public ProgressLineView(Context context) {
this(context, null);
}
public ProgressLineView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ProgressLineView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
progressPadding = 5;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ProgressLineView);
lineWidth = (int) ta.getDimension(R.styleable.ProgressLineView_lineWidth, 1);
progressTextSize = (int) ta.getDimension(R.styleable.ProgressLineView_progressTextSize, 12);
progressColor = ta.getColor(R.styleable.ProgressLineView_progressColor, 0xff515151);
completedColor = ta.getColor(R.styleable.ProgressLineView_completedColor, 0xff085cb2);
waitingColor = ta.getColor(R.styleable.ProgressLineView_waitingColor, 0xffa7f0f9);
ta.recycle();
paint.setStrokeWidth(lineWidth);
paint.setTextSize(progressTextSize);
}
public void updateProgress(int progress) {
this.progress = progress;
invalidate();
}
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
mWidth = right - left;
mHeight = bottom - top;
}
}
@Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final String progressInfo = progress + "%";
float[] textWidths = new float[progressInfo.length()];
paint.getTextWidths(progressInfo, textWidths);
float textTotalWidth = calcTotal(textWidths);
float cellWidth = (mWidth - (textTotalWidth + progressPadding * 2)) / 100;
paint.setColor(completedColor);
canvas.drawLine(0f, mHeight / 2f, cellWidth * progress, mHeight / 2f, paint);
paint.setColor(progressColor);
canvas.drawText(progressInfo, cellWidth * progress + progressPadding, (mHeight + progressTextSize) / 2, paint);
paint.setColor(waitingColor);
canvas.drawLine(mWidth - cellWidth * (100 - progress), mHeight / 2f, mWidth, mHeight / 2f, paint);
}
private float calcTotal(float[] arrFloat) {
float result = 0f;
for (float f : arrFloat) {
result += f;
}
return result;
}
}
核心内容为onDraw()方法内部逻辑。
mWidth 为该视图宽度[相当于 compeleted(完成部分) + progressInfo(刻度信息) + waiting(等待部分) 的宽度和], 然后通过 paint.getTextWidths() 和 calcTotal() 方法得到 progressInfo所占有宽度。 那么剩下的宽度除以一百就相当于每个百分点的宽度(cellWidth)。
之后再根据传入的progress给两端的线分配宽度便大功告成了,内容并不复杂,主要是需要对canvas和paint有所了解。
如何去使用:
在布局代码中添加:
<com.example.view.ProgressLineView
android:id="@+id/plv"
android:layout_width="match_parent"
android:layout_height="20dip" />
修改之后的使用方式一样为调用 updateProgress() 方法,只不过实现方式变为了重写onDraw() 。
对比两种方式我个人推荐第二种,原因主要是对应第一种少了一个组合控件布局文件,同时占用的内存比第一个少。(第一种会产生4个View 对象,而第二种只产生1个View对象。)
本人的疑问:当使用第二种方式继承于View的时候。虽然在布局文件中设置layout_height="wrap_content" ,但是加载出来的效果是占满全屏,这是为什么呢?
希望有知道的朋友留个言,谢谢大家。