嗨 大家好,我是不服不行 。
今天为大家带来一个手机软件中所使用到的这个,这个一个常客。不管是下载什么,还是加载什么。都可以用它。
实现这个效果可以使用组合控件的方式
可以将进度条看为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>
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; } }
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 的测量机制