Android custom control: four implementation methods of progress bar

Source: Internet
Author: User

Android custom control: four implementation methods of progress bar

I have been learning custom controls recently. I have searched many small tutorials shared by my Blog and found some similar widgets on GitHub. I don't know much about reading it. I just want to write this article as a study note.

I. Introduction to controls:

Progress bars are very common in apps, such as download progress, loading pictures, opening articles, opening webpages, etc ...... We need such an effect to let users know that our App is reading to build a good interaction. If there is no such effect, the user cannot know whether the object has been downloaded, the image has been loaded, and the article has been opened ...... Will make the user very uncomfortable. Based on this scenario, our UI designers have created such a control.

Ii. knowledge points involved in this article:

Just like me, Android cainiao, I recommend that you first learn about these knowledge points before reading them. I will also recommend some blogs for you to look at these knowledge points. I suggest you read the explanations in this document. Of course, you can ignore them directly ......

1. ClipDrawable class: This class can be used to cut a drawable class (that is, only show the area of a certain part and hide the other part), and the area to be displayed is controlled by level (the level value is 0 ~ 10000)

[Blog: Workshop]

2. Custom View: Deep Learning of guolin

[Android LayoutInflater principle analysis, which gives you a step-by-step insight into the View]

[The Android View rendering process is completely parsed, so you can learn more about the View step by step]

[Analysis of the status and redrawing process of the Android View, giving you a step-by-step insight into the View]

[Android custom View implementation method, which gives you a step-by-step insight into the View]

3. I have not read the Android custom control-the third-level menu of the old version of Youku. You may need to take a look at this:

[RotateAnimation details -- http://blog.csdn.net/u012403246/article/details/41415799]

Iii. Implementation Methods on Android:

(The first three methods are relatively simple. The fourth method is the analysis of the GitHub project. If you are not interested in the first three methods, you can directly jump to the back ......)

 

1 ,:

 

The progress bar transformation process is divided into a frame-by-frame image, which is connected to a frame-by-frame image to form an animation. It is often used in scenarios where you do not need to know the loading progress when loading images or articles when reading webpages or visiting communities on your mobile phone, but you need to know whether to load images or articles.

 

In this way, you can create an XML file of the animation-list and then provide the indeterminateDrawable attribute of the ProgressBar provided by the system API. (This property should be similar to setting an animation ......)

2 ,:

 

In the previous blog about custom controls we used a RotateAnimation class to achieve the rotation effect (http://blog.csdn.net/u012403246/article/details/41309161), in fact, we can also put a picture here, by rotating, achieve what we want. Essentially, it is no different from the previous method.

We only need to create a rotate XML file, perform some simple settings on its attributes, and then add the image we want to use.

XML:

Android: Required ty = "50%" android: fromDegrees = "0" android: toDegrees = "360" android: interpolator = "@ android: anim/accelerate_decelerate_interpolator"> [Html] 3 ,:

We can get two pictures. The first one is pure black. Then we can dig a circle from the center of the picture to make the area white and the circle to the second. We may as well overlay the two pictures to show them. At first, we completely "covered" the second picture. As the loading progress increases, we reduced the area to cover and slowly showed the second picture.

On Android, there is such a ClipDrawable class, which can achieve the tailoring process. Let's see how to customize a progress bar control in this way.

Code:


  1. Detail {privatebooleanrunning; privateintprogress = 0; Progress = 10000; privateClipDrawableclip; privateHandlerhandler = newHandler () {@ Override publicvoidhandleMessage (android. OS. messagemsg) {if (msg. what = 0x123) clip. setLevel (progress) ;}}; publicMyProgressBar (Contextcontext) {this (context, null, 0);} publicMyProgressBar (Contextcontext, AttributeSetattrs) {this (context, null, 0 );} publicMyProgressBar (Contextcontext, AttributeSetattrs, intdefStyle) {super (context, attrs, defStyle); Init (context);} publicvoidInit (Contextcontext) {Viewview = LayoutInflater. from (context ). inflate (R. layout. view, null); ImageViewiv = (ImageView) view. findViewById (R. id. progress_img); addView (view); clip = (ClipDrawable) iv. getDrawable (); Threadthread = newThread (newRunnable () {@ Override publicvoidrun () {running = true; while (running) {handler. sendEmptyMessage (0x123); if (progress = MAX_PROGRESS) progress = 0; progress + = 100; try {Thread. sleep (18);} catch (InterruptedExceptione) {e. printStackTrace () ;}}}); thread. start () ;}publicvoidstop () {progress = 0; running = false ;}}
    The Code shows that the logic is very simple. The key lies in the setLevel () method of ClipDrawable, which sets the cropping effect.

     

    4 ,:

    Implement a Child class of View-Progress Wheel class to implement the Progress bar effect. I have written all the specific content in comments. If you do not know about custom controls, you can go to the guolin blog to explain the four-part custom views.

    Code:


    1. Optional {// View length and bandwidth used for drawing privateintlayout_height = 0; privateintlayout_width = 0; privateintfullRadius = 100; bandwidth = 80; privateintbarLength = 60; privateintbarWidth = 20; privateintrimWidth = 20; privateinttextSize = 20; privatefloatcontourSize = 0; // distance between the page and the page edge = 5; distance = 5; privateintpaddingLeft = 5; privateintpaddingRight = 5; // View Color: privateintbarColor = 0xAA000000; privateint0000color = 0xAA000000; bandwidth = 0x00000000; bandwidth = 0 xAADDDDDD; privateinttextColor = 0xFF000000; // paint brush used = newPaint (); watermark = newPaint (); watermark = newPaint (); privatePainttextPaint = newPaint (); privatepaint1_paint = newPaint (); // draw the rectangle to use @ SuppressWarnings ("unused") privateRec TFrectBounds = newRectF (); then = newRectF (); // animation // number of pixels to be moved each time. privateintspinSpeed = 2; // time interval of the painting process privateintdelayMillis = 0; intprogress = 0; booleanisSpinning = false; // other privateStringtext = ""; privateString [] splitText = {}; /*** ProgressWheel construction method ** @ paramcontext * @ paramattrs */publicProgressWheel (Contextcontext, AttributeSetattrs) {super (context, attrs); parseAttributes (context. obtainStyledAttributes (attrs, R. styleable. progressWheel);} // initialize some elements // ------------------------------------------/** when calling this method, draw the View as a square * From: http://www.jayway.com/2012/12/12/creating-custom-android-views-part-4-measuring-and-how-to-force-a-view-to-be-square/ **/@ Override protectedvo IdonMeasure (intwidthMeasureSpec, intheightMeasureSpec) {// first, we need to call the superclass onMeasure excuse // The reason is that we implement a method to get the length and width, which is too troublesome // It is very convenient and complicated to use the superclass Method controllable details super. onMeasure (widthMeasureSpec, heightMeasureSpec); // here we cannot use getWidth () and getHeight (). // Because the two methods can only be used after the View Layout is complete, and the painting process of a View is to draw elements first, and then draw Layout // so we must use getMeasuredWidth () and getMeasuredHeight () intsize = 0; intwidth = getMeasuredWidth (); intheight = getMeasuredHeight (); distance = width-getPaddingLeft ()-getPaddingRight (); distance = height-getPaddingTop () -getPaddingBottom (); // finally, we use some simple logic to calculate the View Size and call setMeasuredDimension () set the size of the View // we do not consider the Spacing Before comparing the length and width of the View, but when we set the size of the View When we need to plot the area, we need to consider it // The View without considering the spacing (the actual picture in the View) should be square at this time, but due to the existence of the spacing, the final View area may not be square if (widthWithoutPadding> heigthWithoutPadding) {size = heigthWithoutPadding;} else {size = widthWithoutPadding;} // if you overwrite the onMeasure () method, you must call the setMeasuredDimension () method // This is the only way to set the View Size. // if you do not call the setMeasuredDimension () method, the parent control throws an exception, and the program will crash // if we use the onMeasure () method of the superclass, we do not need the setMeasuredDimension () method. // However, rewrite onMeasure () the method is to change the existing drawing process. So we must call the setMeasuredDimension () method to achieve our goal. setMeasuredDimension (size + getPaddingLeft () + getPaddingRight (), size + getPaddingTop () + getPaddingBottom ());} /*** use the onSizeChanged method instead of onAttachedToWindow to obtain the View area * because this method will be called immediately after MATCH_PARENT and WRAP_CONTENT are measured * use the obtained area to set View */@ Override protectedvoidonSizeChanged (intw, inth, intoldw, intoldh) {super. onSizeChanged (w, h, oldw, oldh); // Sharethedimensions layout_width = w; l Ayout_height = h; setupBounds (); setupPaints (); invalidate () ;}/ *** set the progresswheel color we want to draw */privatevoidsetupPaints () {barPaint. setColor (barColor); barPaint. setAntiAlias (true); barPaint. setStyle (Style. STROKE); barPaint. setStrokeWidth (barWidth); rimPaint. setColor (rimColor); rimPaint. setAntiAlias (true); rimPaint. setStyle (Style. STROKE); rimPaint. setStrokeWidth (rimWidth); circlePaint. setColor (circl EColor); circlePaint. setAntiAlias (true); circlePaint. setStyle (Style. FILL); textPaint. setColor (textColor); textPaint. setStyle (Style. FILL); textPaint. setAntiAlias (true); textPaint. setTextSize (textSize); repeated paint. setColor (gradient color); gradient Paint. setAntiAlias (true); repeated paint. setStyle (Style. STROKE); volume paint. setStrokeWidth (Margin Size);}/*** set the Element Boundary */privatevoidsetupBounds () {// to maintain The width is the same as the length. We need to obtain a smaller value in layout_width and layout_height to draw a circle intminValue = Math. min (layout_width, layout_height); // calculate the offset intxOffset = layout_width-minValue in the x and y directions during painting; intyOffset = layout_height-minValue; // the padding plus the offset paddingTop = this. getPaddingTop () + (yOffset/2); paddingBottom = this. getPaddingBottom () + (yOffset/2); paddingLeft = this. getPaddingLeft () + (xOffset/2); paddingRight = this. getPaddingRight () + (xOffset/2); int Width = getWidth (); // this. getLayoutParams (). width; intheight = getHeight (); // this. getLayoutParams (). height; rows = newRectF (paddingLeft, paddingTop, width-paddingRight, height-paddingBottom); rows = newRectF (paddingLeft + barWidth, paddingTop + barWidth, width-paddingRight-barWidth, height-paddingBottom-barWidth); circleInnerContour = newRectF (circleBounds. left + (rimWidth/2.0f) + (hour size/2.0f), c IrcleBounds. top + (rimWidth/2.0f) + (small size/2.0f), circleBounds. right-(rimWidth/2.0f)-(hour size/2.0f), circleBounds. bottom-(rimWidth/2.0f)-(Bytes size/2.0f); circleOuterContour = newRectF (circleBounds. left-(rimWidth/2.0f)-(partition size/2.0f), circleBounds. top-(rimWidth/2.0f)-(large size/2.0f), circleBounds. right + (rimWidth/2.0f) + (small size/2.0f), circleBounds. bottom + (rimWidth/2.0f) + (Bytes size/2. 0f); fullRadius = (width-paddingRight-barWidth)/2; circleRadius = (fullRadius-barWidth) + 1 ;} /*** parse control attributes from XML ** @ paramatheattributestoparse */privatevoidparseAttributes (TypedArraya) {barWidth = (int). getDimension (R. styleable. progressWheel_barWidth, barWidth); rimWidth = (int). getDimension (R. styleable. progressWheel_rimWidth, rimWidth); spinSpeed = (int). getDimension (R. styleable. progressWheel_spinSpeed, SpinSpeed); delayMillis =. getInteger (R. styleable. progressWheel_delayMillis, delayMillis); if (delayMillis <0) {delayMillis = 0;} barColor =. getColor (R. styleable. progressWheel_barColor, barColor); barLength = (int). getDimension (R. styleable. progressWheel_barLength, barLength); textSize = (int). getDimension (R. styleable. progressWheel_textSize, textSize); textColor = (int). getColor (R. styleable. progressWheel _ TextColor, textColor); // if the text is empty, ignore it if (. hasValue (R. styleable. progressWheel_text) {setText (. getString (R. styleable. progressWheel_text);} rimColor = (int). getColor (R. styleable. progressWheel_rimColor, rimColor); circleColor = (int). getColor (R. styleable. progressWheel_circleColor, circleColor); gradient color =. getColor (R. styleable. progressWheel_contourColor, gradient color); gradient size =. getDimensi On (R. styleable. progressWheel_contourSize, contourSize); // when using TypedArray to obtain control attributes, you must note that the object a of TypedArray must be recycled after use. recycle ();} // ---------------------------------- // animation // ---------------------------------------- protectedvoidonDraw (Canvascanvas) {super. onDraw (canvas); // draw the incircle canvas. drawArc (circleBounds, 360,360, false, circlePaint); // draw the border canvas. drawArc (circleBounds, 360,360, false, rimPaint); canvas. drawArc (circle OuterContour, 360,360, false, background paint); canvas. drawArc (circleInnerContour, 360,360, false, Volume paint); // draw a stripe if (isSpinning) {canvas. drawArc (circleBounds, progress-90, barLength, false, barPaint);} else {canvas. drawArc (circleBounds,-90, progress, false, barPaint);} // draw the text we want to set (and display it at the center of the horizontal and vertical circles) floattextHeight = textPaint. descent ()-textPaint. ascent (); floatverticalTextOffset = (textHeight/2)-textPain T. descent (); for (Strings: splitText) {floathorizontalTextOffset = textPaint. measureText (s)/2; canvas. drawText (s, this. getWidth ()/2-horizontalTextOffset, this. getHeight ()/2 + verticalTextOffset, textPaint);} if (isSpinning) {scheduleRedraw () ;}} privatevoidscheduleRedraw () {progress ++ = spinSpeed; if (progress> 360) {progress = 0;} postInvalidateDelayed (delayMillis);}/*** determines whether the wheel is being rotated */publicbooleanisSpinn Ing () {if (isSpinning) {returntrue;} else {returnfalse;}/*** reset the progress bar value */publicvoidresetCount () {progress = 0; setText ("0%"); invalidate ();}/*** stop progress bar rotation */publicvoidstopSpinning () {isSpinning = false; progress = 0; postInvalidate ();} /*** enable the rotation mode of the progress bar */publicvoidspin () {isSpinning = true; postInvalidate () ;}/ *** increase the progress bar by 1 (the maximum value is 360) each time) */publicvoidincrementProgress () {isSpinning = false; progress ++; if (Progress> 360) progress = 0; setText (Math. round (float) progress/360) * 100) + "%"); postInvalidate ();} /*** set the progress bar to an exact value */publicvoidsetProgress (inti) {isSpinning = false; progress = I; postInvalidate ();} // progress // get and set Methods // progress/*** set the progressbar text and do not need to refresh the View ** @ paramtextthetexttoshow ('\ n' constitutesanewline) */publicvoidsetText (Stringtext) {This. text = text; splitText = this. text. split ("\ n");} publicintgetCircleRadius () {returncircleRadius;} publicvoidsetCircleRadius (intcircleRadius) {this. circleRadius = circleRadius;} publicintgetBarLength () {returnbarLength;} publicvoidsetBarLength (intbarLength) {this. barLength = barLength;} publicintgetBarWidth () {returnbarWidth;} publicvoidsetBarWidth (intbarWidth) {this. barWidth = barWidth; if (thi S. barPaint! = Null) {this. barPaint. setStrokeWidth (this. barWidth) ;}} publicintgetTextSize () {returntextSize;} publicvoidsetTextSize (inttextSize) {this. textSize = textSize; if (this. textPaint! = Null) {this. textPaint. setTextSize (this. textSize) ;}} publicintgetPaddingTop () {returnpaddingTop;} publicvoidsetPaddingTop (intpaddingTop) {this. paddingTop = paddingTop;} publicintgetPaddingBottom () {returnpaddingBottom;} publicvoidsetPaddingBottom (intpaddingBottom) {this. paddingBottom = paddingBottom;} publicintgetPaddingLeft () {returnpaddingLeft;} publicvoidsetPaddingLeft (intpaddingLeft) {this. p AddingLeft = paddingLeft;} publicintgetPaddingRight () {returnpaddingRight;} publicvoidsetPaddingRight (intpaddingRight) {this. paddingRight = paddingRight;} publicintgetBarColor () {returnbarColor;} publicvoidsetBarColor (intbarColor) {this. barColor = barColor; if (this. barPaint! = Null) {this. barPaint. setColor (this. barColor) ;}} publicintgetCircleColor () {returncircleColor;} publicvoidsetCircleColor (intcircleColor) {this. circleColor = circleColor; if (this. circlePaint! = Null) {this. circlePaint. setColor (this. circleColor) ;}} publicintgetRimColor () {returnrimColor;} publicvoidsetRimColor (intrimColor) {this. rimColor = rimColor; if (this. rimPaint! = Null) {this. rimPaint. setColor (this. rimColor) ;}} publicShadergetRimShader () {returnrimPaint. getShader ();} publicvoidsetRimShader (Shadershader) {this. rimPaint. setShader (shader);} publicintgetTextColor () {returntextColor;} publicvoidsetTextColor (inttextColor) {this. textColor = textColor; if (this. textPaint! = Null) {this. textPaint. setColor (this. textColor) ;}} publicintgetSpinSpeed () {returnspinSpeed;} publicvoidsetSpinSpeed (intspinSpeed) {this. spinSpeed = spinSpeed;} publicintgetRimWidth () {returnrimWidth;} publicvoidsetRimWidth (intrimWidth) {this. rimWidth = rimWidth; if (this. rimPaint! = Null) {this. rimPaint. setStrokeWidth (this. rimWidth) ;}} publicintgetDelayMillis () {returndelayMillis;} publicvoidsetDelayMillis (intdelayMillis) {this. delayMillis = delayMillis;} publicintgetContourColor () {returncontourColor;} publicvoidsetContourColor (intcontourColor) {this. coloring color = coloring color; if (coloring paint! = Null) {this. repeated paint. setColor (this. required color) ;}} publicfloatgetContourSize () {returnthis. required size;} publicvoidset1_size (float1_size) {this. required size = required size; if (required paint! = Null) {this. Repeated paint. setStrokeWidth (this. Distinct size );}}}

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.