Android View 加载进度条

嗨 大家好,我是不服不行 。

今天为大家带来一个手机软件中所使用到的这个,这个一个常客。不管是下载什么,还是加载什么。都可以用它。




实现这个效果可以使用组合控件的方式

可以将进度条看为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" ,但是加载出来的效果是占满全屏,这是为什么呢?

希望有知道的朋友留个言,谢谢大家。


                             -- view 的测量机制






你可能感兴趣的:(android,UI,view)