It can be so simple to create progress bars for Android, and so easy to implement for android

Source: Internet
Author: User
Tags drawtext getcolor

It can be so simple to create progress bars for Android, and so easy to implement for android
Reprinted please indicate the source: http://blog.csdn.net/lmj623565791/article/details/43371299, this article from: [Zhang Hongyang blog] 1, Overview

Recently, we need to use a progress bar and stick to the principle of not repeating the wheel. I searched on github and looked at some nice ProgressBar, such as daimajia. After a simple look at the code, it is basically inherited from the View, completely customized a progress bar. Staring at the colorful scroll bar, I suddenly felt that the system has provided the ProgressBar and its features to write a scroll bar through the View. We do not need to build a new one, however, the system is ugly, and the realization of different versions is not necessarily the same. The goal is to change the system ProgressBar.

Zookeeper is right. We don't have to go from 0 to create a ProgressBar. Although it is not easy to see, the features and stability are just new, all we need to do is set it down.

Speaking of plastic surgery, we all know that our controls are drawn through onDraw (), so we only need to overwrite the onDraw () method and write it down.

By the way, I have created a public account. Please scan the column on the left.

Next, we will post:

2. 1. Horizontal progress bar


2. Circular progress bar


That's right. This is our progress bar, which imitates the daimajia progress bar horizontally. However, we inherit the child ProgressBar, which is simple for its entire content, and the code is clear and easy to understand. Why?

For the horizontal progress bar, you will get drawLine () and drawText (). Then, you can get the width of the control through getWidth () and then get the progress through getProgress, proportional Control of the length of the draw line, the word location is not a matter of minutes.

Github Source Code address: Android-ProgressBarWidthNumber welcome to star or fork.


3. Implementation

Some attributes are required for horizontal scroll bar painting, such as the color, width, text color, and size of the progress that has not been reached.
Originally, I want to extract some attributes from the progressDrawable OF THE SYSTEM ProgressBar to complete the parameters required for drawing. However, in the end, the Code becomes complicated. Therefore, the custom attributes are used in the end. We should be familiar with custom attributes.

1. HorizontalProgressBarWithNumber 1. Custom Attributes

Values/attr_progress_bar.xml:

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="HorizontalProgressBarWithNumber">        <attr name="progress_unreached_color" format="color" />        <attr name="progress_reached_color" format="color" />        <attr name="progress_reached_bar_height" format="dimension" />        <attr name="progress_unreached_bar_height" format="dimension" />        <attr name="progress_text_size" format="dimension" />        <attr name="progress_text_color" format="color" />        <attr name="progress_text_offset" format="dimension" />        <attr name="progress_text_visibility" format="enum">            <enum name="visible" value="0" />            <enum name="invisible" value="1" />        </attr>    </declare-styleable>        <declare-styleable name="RoundProgressBarWidthNumber">        <attr name="radius" format="dimension" />    </declare-styleable></resources>

2. Get in Construction

public class HorizontalProgressBarWithNumber extends ProgressBar{private static final int DEFAULT_TEXT_SIZE = 10;private static final int DEFAULT_TEXT_COLOR = 0XFFFC00D1;private static final int DEFAULT_COLOR_UNREACHED_COLOR = 0xFFd3d6da;private static final int DEFAULT_HEIGHT_REACHED_PROGRESS_BAR = 2;private static final int DEFAULT_HEIGHT_UNREACHED_PROGRESS_BAR = 2;private static final int DEFAULT_SIZE_TEXT_OFFSET = 10;/** * painter of all drawing things */protected Paint mPaint = new Paint();/** * color of progress number */protected int mTextColor = DEFAULT_TEXT_COLOR;/** * size of text (sp) */protected int mTextSize = sp2px(DEFAULT_TEXT_SIZE);/** * offset of draw progress */protected int mTextOffset = dp2px(DEFAULT_SIZE_TEXT_OFFSET);/** * height of reached progress bar */protected int mReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_REACHED_PROGRESS_BAR);/** * color of reached bar */protected int mReachedBarColor = DEFAULT_TEXT_COLOR;/** * color of unreached bar */protected int mUnReachedBarColor = DEFAULT_COLOR_UNREACHED_COLOR;/** * height of unreached progress bar */protected int mUnReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_UNREACHED_PROGRESS_BAR);/** * view width except padding */protected int mRealWidth;protected boolean mIfDrawText = true;protected static final int VISIBLE = 0;public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs){this(context, attrs, 0);}public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs,int defStyle){super(context, attrs, defStyle);setHorizontalScrollBarEnabled(true);obtainStyledAttributes(attrs);mPaint.setTextSize(mTextSize);mPaint.setColor(mTextColor);}/** * get the styled attributes *  * @param attrs */private void obtainStyledAttributes(AttributeSet attrs){// init values from custom attributesfinal TypedArray attributes = getContext().obtainStyledAttributes(attrs, R.styleable.HorizontalProgressBarWithNumber);mTextColor = attributes.getColor(R.styleable.HorizontalProgressBarWithNumber_progress_text_color,DEFAULT_TEXT_COLOR);mTextSize = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_text_size,mTextSize);mReachedBarColor = attributes.getColor(R.styleable.HorizontalProgressBarWithNumber_progress_reached_color,mTextColor);mUnReachedBarColor = attributes.getColor(R.styleable.HorizontalProgressBarWithNumber_progress_unreached_color,DEFAULT_COLOR_UNREACHED_COLOR);mReachedProgressBarHeight = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_reached_bar_height,mReachedProgressBarHeight);mUnReachedProgressBarHeight = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_unreached_bar_height,mUnReachedProgressBarHeight);mTextOffset = (int) attributes.getDimension(R.styleable.HorizontalProgressBarWithNumber_progress_text_offset,mTextOffset);int textVisible = attributes.getInt(R.styleable.HorizontalProgressBarWithNumber_progress_text_visibility,VISIBLE);if (textVisible != VISIBLE){mIfDrawText = false;}attributes.recycle();}

Well, it seems that the code is quite long. In fact, they are all getting custom attributes and there is no technical content.


3. onMeasure

Just now I can't write in onDraw. Why should I change onMeasure? It's mainly because all our attributes, such as the width of the progress bar, have been customized, so our measurement has to be slightly changed.

@Overrideprotected synchronized void onMeasure(int widthMeasureSpec,int heightMeasureSpec){int heightMode = MeasureSpec.getMode(heightMeasureSpec);if (heightMode != MeasureSpec.EXACTLY){float textHeight = (mPaint.descent() + mPaint.ascent());int exceptHeight = (int) (getPaddingTop() + getPaddingBottom() + Math.max(Math.max(mReachedProgressBarHeight,mUnReachedProgressBarHeight), Math.abs(textHeight)));heightMeasureSpec = MeasureSpec.makeMeasureSpec(exceptHeight,MeasureSpec.EXACTLY);}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}

The width remains unchanged. Therefore, the custom attribute does not involve width or height. We only consider the case that it is not EXACTLY (if you specify it explicitly, We will ignore it ), calculate what you want based on the padding and progress bar width. If it is not EXACTLY, we encapsulate the padding theight and pass it to the control to measure the height.

After the measurement is complete, it's our onDraw ~~~

4. onDraw

@ Overrideprotected synchronized void onDraw (Canvas canvas) {canvas. save (); // the paint brush is moved to the specified position of paddingLeft and getHeight ()/2. Note that the coordinates are 0 and 0canvas in the future. translate (getPaddingLeft (), getHeight ()/2); boolean noNeedBg = false; // ratio of current progress to total value float radio = getProgress () * 1.0f/getMax (); // float progressPosX = (int) (mRealWidth * radio); // The drawn text String text = getProgress () + "% "; // get the font width and height float textWidth = mPaint. measu ReText (text); float textHeight = (mPaint. descent () + mPaint. ascent ()/2; // if the progress bar is reached, you do not need to draw if (progressPosX + textWidth> mRealWidth) {progressPosX = mRealWidth-textWidth; noNeedBg = true;} // draw the reached progress float endX = progressPosX-mTextOffset/2; if (endX> 0) {mPaint. setColor (mReachedBarColor); mPaint. setStrokeWidth (mReachedProgressBarHeight); canvas. drawLine (0, 0, endX, 0, mPaint);} // draw text if (mIfD RawText) {mPaint. setColor (mTextColor); canvas. drawText (text, progressPosX,-textHeight, mPaint);} // draw an unarrived progress bar if (! NoNeedBg) {float start = progressPosX + mTextOffset/2 + textWidth; mPaint. setColor (mUnReachedBarColor); mPaint. setStrokeWidth (mUnReachedProgressBarHeight); canvas. drawLine (start, 0, mRealWidth, 0, mPaint);} canvas. restore () ;}@ Overrideprotected void onSizeChanged (int w, int h, int oldw, int oldh) {super. onSizeChanged (w, h, oldw, oldh); mRealWidth = w-getPaddingRight ()-getPaddingLeft ();}

In fact, the core method is onDraw, but onDraw is also very simple, draw lines, draw text, draw lines, and end.


There are two simple auxiliary methods:

/** * dp 2 px *  * @param dpVal */protected int dp2px(int dpVal){return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpVal, getResources().getDisplayMetrics());}/** * sp 2 px *  * @param spVal * @return */protected int sp2px(int spVal){return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spVal, getResources().getDisplayMetrics());}

Now, our horizontal progress is over. Is it very simple ~~ If you are using a custom View, you have to consider updates to the progress, State destruction and recovery, and other complex things.

Next, let's take a look at the progress bar of our RoundProgressBarWidthNumber circle.

2. RoundProgressBarWidthNumber

The progress bar of the circle is consistent with the basic variables of the horizontal progress bar, so I made RoundProgressBarWidthNumber extends HorizontalProgressBarWithNumber.

Then the measurement and onDraw need to be changed:

Complete code:

package com.zhy.view;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Paint.Cap;import android.graphics.Paint.Style;import android.graphics.RectF;import android.util.AttributeSet;import com.zhy.library.view.R;public class RoundProgressBarWidthNumber extendsHorizontalProgressBarWithNumber {/** * mRadius of view */private int mRadius = dp2px(30);public RoundProgressBarWidthNumber(Context context) {this(context, null);}public RoundProgressBarWidthNumber(Context context, AttributeSet attrs) {super(context, attrs);mReachedProgressBarHeight = (int) (mUnReachedProgressBarHeight * 2.5f);TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.RoundProgressBarWidthNumber);mRadius = (int) ta.getDimension(R.styleable.RoundProgressBarWidthNumber_radius, mRadius);ta.recycle();mTextSize = sp2px(14);mPaint.setStyle(Style.STROKE);mPaint.setAntiAlias(true);mPaint.setDither(true);mPaint.setStrokeCap(Cap.ROUND);}@Overrideprotected synchronized void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {int heightMode = MeasureSpec.getMode(heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int paintWidth = Math.max(mReachedProgressBarHeight,mUnReachedProgressBarHeight);if (heightMode != MeasureSpec.EXACTLY) {int exceptHeight = (int) (getPaddingTop() + getPaddingBottom()+ mRadius * 2 + paintWidth);heightMeasureSpec = MeasureSpec.makeMeasureSpec(exceptHeight,MeasureSpec.EXACTLY);}if (widthMode != MeasureSpec.EXACTLY) {int exceptWidth = (int) (getPaddingLeft() + getPaddingRight()+ mRadius * 2 + paintWidth);widthMeasureSpec = MeasureSpec.makeMeasureSpec(exceptWidth,MeasureSpec.EXACTLY);}super.onMeasure(heightMeasureSpec, heightMeasureSpec);}@Overrideprotected synchronized void onDraw(Canvas canvas) {String text = getProgress() + "%";// mPaint.getTextBounds(text, 0, text.length(), mTextBound);float textWidth = mPaint.measureText(text);float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;canvas.save();canvas.translate(getPaddingLeft(), getPaddingTop());mPaint.setStyle(Style.STROKE);// draw unreaded barmPaint.setColor(mUnReachedBarColor);mPaint.setStrokeWidth(mUnReachedProgressBarHeight);canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);// draw reached barmPaint.setColor(mReachedBarColor);mPaint.setStrokeWidth(mReachedProgressBarHeight);float sweepAngle = getProgress() * 1.0f / getMax() * 360;canvas.drawArc(new RectF(0, 0, mRadius * 2, mRadius * 2), 0,sweepAngle, false, mPaint);// draw textmPaint.setStyle(Style.FILL);canvas.drawText(text, mRadius - textWidth / 2, mRadius - textHeight,mPaint);canvas.restore();}}

First obtain its proprietary property mRadius, and then measure based on this property to complete the drawing;

What about the rendering process?

First draw a fine circle, and then draw a thick radian, the two stacked together on the line. The text is drawn in the middle ~~~ Overall, there is no amount of code.

Now, the two progress bars are here. Is it much simpler. There are some problems in the overall design. If a BaseProgressBar is extracted to obtain public attributes, the progress bars of different appearances are inherited to achieve their own measurements and appearances, respectively, this structure may be clearer ~~~


4. Use layout files
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:zhy="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical"        android:padding="25dp" >        <com.zhy.view.HorizontalProgressBarWithNumber            android:id="@+id/id_progressbar01"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_marginTop="50dip"            android:padding="5dp" />               <com.zhy.view.HorizontalProgressBarWithNumber            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_marginTop="50dip"            android:padding="5dp"            android:progress="50"            zhy:progress_text_color="#ffF53B03"            zhy:progress_unreached_color="#ffF7C6B7" />        <com.zhy.view.RoundProgressBarWidthNumber            android:id="@+id/id_progress02"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_marginTop="50dip"            android:padding="5dp"            android:progress="30" />        <com.zhy.view.RoundProgressBarWidthNumber            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_marginTop="50dip"            android:padding="5dp"            android:progress="50"            zhy:progress_reached_bar_height="20dp"            zhy:progress_text_color="#ffF53B03"            zhy:radius="60dp" />         </LinearLayout></ScrollView>

MainActivity
package com.zhy.sample.progressbar;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import com.zhy.annotation.Log;import com.zhy.view.HorizontalProgressBarWithNumber;public class MainActivity extends Activity {private HorizontalProgressBarWithNumber mProgressBar;private static final int MSG_PROGRESS_UPDATE = 0x110;private Handler mHandler = new Handler() {@Logpublic void handleMessage(android.os.Message msg) {int progress = mProgressBar.getProgress();mProgressBar.setProgress(++progress);if (progress >= 100) {mHandler.removeMessages(MSG_PROGRESS_UPDATE);}mHandler.sendEmptyMessageDelayed(MSG_PROGRESS_UPDATE, 100);};};@Log@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mProgressBar = (HorizontalProgressBarWithNumber) findViewById(R.id.id_progressbar01);mHandler.sendEmptyMessage(MSG_PROGRESS_UPDATE);}}

What is the purpose of this blog? It's just to say that if you just want to change the display style of a control like ProgressBar, there's no need to create it from 0 and rewrite onDraw. Of course it's my opinion, for your reference.


Download source code


Group Number:423372824


Some of the bloggers have already been online. If you do not like boring text, stamp it (for initial record, we look forward to your support ):

Video directory address: My recorded video tutorial



Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.