Android Custom View Imitation QQ Health Interface _android

Source: Internet
Author: User
Tags drawtext getcolor

Recently has been learning custom view-related knowledge, today brings you the realization of the QQ health interface. First look at the effect of the picture:

You can set the number color, font color, motion number, motion rank, average movement, and the length of the blue indicator bar below the dotted line to change as the average step changes. The overall effect or the QQ sports health interface is very similar.

Customize view four to see how it is implemented.

1. Customize View Properties:

<?xml version= "1.0" encoding= "Utf-8"?>
<resources>
 //custom property name, defining public properties
 <attr name= " Titlesize "format=" Dimension ></attr>
 <attr name= "TitleText" format= "string" ></attr>
 <attr name= "titlecolor" format= "color" ></attr> <attr "name=" Outcirclecolor "
 Color" ></attr>
 <attr name= "incirclecolor" format= "color" ></attr>
 <attr name= "LineColor" format= "COLOR" ></attr>
 //Custom View Properties
 <declare-styleable name= "Myqqhealthview" >
 <attr name= "Titlecolor" ></attr>
 <attr name= "LineColor" ></attr>
 </ Declare-styleable>
</resources>

The font color is defined in turn, the color of the line is 2 properties, and format is the value type of the property.
Then we'll declare our custom view in the layout file:

 <com.example.tangyangkai.myview.myqqhealthview
  android:id= "@+id/myqqview"
  android:layout_width= " Match_parent "
  android:layout_height=" 530DP "
  android:layout_margin=" 15DP "
  myqq:linecolor=" @color Font_tips "
  myqq:titlecolor=" @color/textcolor "
  myqq:titlesize=" 50DP "/>

Custom View Properties We can set it ourselves, remember to finally introduce our namespaces,
xmlns:app= "Http://schemas.Android.com/apk/res-auto"

2. Get the properties of the custom view:

 Public Myqqhealthview {This
 (context, NULL);
 }

 Public Myqqhealthview (context, AttributeSet attrs) {This
 (context, attrs, 0);
 }

 Public Myqqhealthview (context, AttributeSet attrs, int defstyleattr) {
 Super (context, attrs, defstyleattr);

 //Get our custom style properties
 TypedArray array = Context.gettheme (). Obtainstyledattributes (Attrs, R.styleable.myqqhealthview, defstyleattr, 0);
 int n = array.getindexcount ();
 for (int i = 0; i < n; i++) {
  int attr = Array.getindex (i);
  Switch (attr) {
  case R.styleable.myqqhealthview_titlecolor:
   //default color set to black
   TextColor = Array.getcolor ( attr, color.black);
   break;
  Case R.styleable.myqqhealthview_linecolor:
   linecolor = Array.getcolor (attr, color.black);
   break;
  }

 }
 Array.recycle ();
 Init ();
 }

Custom view generally requires the implementation of three construction methods, the three constructs are one layer of the call layer, is a progressive relationship. So, we just need to get the view property in the last constructor.

The first step is to get the theme style array of the custom control through the Theme.obtainstyledattributes () method;
The second step is to traverse each attribute to obtain the value of the corresponding attribute, which is the attribute value we write in the XML layout file;
The third step is to remember to call Array.recycle () to reclaim resources after the end of the loop;
The fourth step is to do the necessary initialization, not recommended in the process of OnDraw to instantiate the object, because this is a frequent repeated execution of the process, new is the need to allocate memory space, if in a frequent repetition of the process of a large number of new objects will cause memory waste.

3. Rewrite the Onmesure method to determine the view size:
when you do not rewrite the Onmeasure method, the system calls the default Onmeasure method:

@Override
 protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {
 super.onmeasure ( Widthmeasurespec, Heightmeasurespec);
 

The purpose of this method is to measure the size of the control. In fact, the Android system in the load layout is the system to measure the size of each child view to tell the parent view I need to occupy a lot of space, and then the parent view will be based on their size to decide how much space allocated to the child view. There are three kinds of specmode models in Measurespec:

measurespec.exactly: The parent view expects the size of the child view to be the size specified in the specsize, usually by setting a definite value or match_parent
measurespec.at_most: The size of the child view is the maximum size in the specsize, which means that the child layout is restricted to a maximum value, typically warp_content
measurespec.unspecified: The parent view does not impose any restrictions on the child view, it can get any size it wants, and it shows how big the child layout wants, and rarely uses it.

To set warp_content, simply rewrite the Onmeasure method:

 @Override
 protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {
 int widthmode = Measurespec.getmode (WIDTHMEASURESPEC);
 int widthsize = measurespec.getsize (widthmeasurespec);
 int heightmode = Measurespec.getmode (heightmeasurespec);
 int heightsize = measurespec.getsize (heightmeasurespec);
 int width;
 int height;
 If the layout inside the set is fixed value, here take the fixed value of the layout, if set is Match_parent, then take the parent layout size if
 (Widthmode = = measurespec.exactly) {
  width = widthsize;
 } else {

  //If a fixed value is not set inside the layout, here take the width of the layout of the 1/2 width
  = widthsize * 1/2;
 }

 if (Heightmode = = measurespec.exactly) {
  height = heightsize;
 } else {
  //If a fixed value is not set inside the layout, this takes 3/4 of the height of the layout
  height = heightsize * 3/4;
 }
 WIDTHBG = width;
 HEIGHTBG = height;
 Setmeasureddimension (width, height);
 Startanim ();
 }

I am here in order not to let the layout appear too small, so warp_content respectively take a width of 1/2, high 3/4, the specific situation varies from person to person.

4. Rewrite the OnDraw method for painting:
Interface is more complex, we from top to bottom, step-by-step:

 Draw the lowest background
 RADIUSBG = widthbg/20;
 Pathbg.moveto (0, HEIGHTBG);
 Pathbg.lineto (0, RADIUSBG);
 Pathbg.quadto (0, 0, RADIUSBG, 0);
 Pathbg.lineto (WIDTHBG-RADIUSBG, 0);
 Pathbg.quadto (WIDTHBG, 0, WIDTHBG, RADIUSBG);
 Pathbg.lineto (WIDTHBG, HEIGHTBG);
 Pathbg.lineto (0, HEIGHTBG);
 Backgroundpaint.setcolor (color.white);
 Canvas.drawpath (PATHBG, backgroundpaint);

The bottom of the entire custom view is a white background with an upper left, a radian on the right, a lower left, and a right angle to the bottom. The rectangle that is implemented using Canvas.drawroundrect is Four corners with radians that do not meet expectations. But the first-order Bezier curve plus the Hill Bessel curve can achieve this special situation well.
MoveTo (x,y): will not be drawn, only used to move the brush, to determine the starting point coordinates, and other methods in line with the use;
LineTo (x,y): First Order Bezier curve, coordinate is the endpoint coordinate, and the MoveTo method is used for line drawing;
quadto (x1,y1,x2,y2): Hill Bessel Curve, (x1,y1) for the control Point, (X2,y2) as the end point, used to draw a smooth curve;
The last Call to Canvas.drawpath (Path,paint) achieves this effect

 Draw Arc
 Arcpaint.setstrokewidth (WIDTHBG/20);
 Setting Hollow
 Arcpaint.setstyle (Paint.Style.STROKE);
 Anti
 -Jitter Arcpaint.setdither (true);
 The connecting place is the circular arc
 arcpaint.setstrokejoin (Paint.Join.ROUND);
 The brush strokes are rounded corners
 arcpaint.setstrokecap (Paint.Cap.ROUND);
 Arcpaint.setcolor (LineColor);
 Arc range
 Arcrect = new RECTF (WIDTHBG * 1/4, WIDTHBG * 1/4, WIDTHBG * 3/4, WIDTHBG * 3/4);
 Draw the background large arc Canvas.drawarc (Arcrect,,,,
 false, arcpaint);
 Arcpaint.setcolor (textcolor);
 Draw Fractional Arc
 Canvas.drawarc (Arcrect, Arcnum, False, Arcpaint);

Drawing an arc first determines the range of the arc, and the four parameters passed are the coordinates of the outer rectangle of the circle where the arc is located. The five parameters of the Canvas.drawarc are arc range; the starting angle; the angle of the arc; the fourth is true, the center of the circle is included when the arc is drawn, and is usually used to draw the pie, we choose false here; The brush of the arc

 Draw the digital Textpaint.setcolor (TextColor) within the circle;
 Textpaint.settextsize (WIDTHBG/10);
 Canvas.drawtext (String.valueof (walknum), WIDTHBG * 3/8, WIDTHBG * 1/2 +, textpaint);
 Draw Rank textpaint.settextsize (WIDTHBG/15);

 Canvas.drawtext (ranknum), WIDTHBG * 1/2-string.valueof, WIDTHBG * 3/4 +, textpaint);
 Draw other text textpaint.setcolor (LineColor);
 Textpaint.settextsize (WIDTHBG/25);
 Canvas.drawtext ("13:45 is Gone", WIDTHBG * 3/8-WIDTHBG * 5/12-10, Textpaint);
 Canvas.drawtext ("Friend average 2781 steps", WIDTHBG * 3/8-Ten, WIDTHBG * 2/3-textpaint);
 Canvas.drawtext ("A", WIDTHBG * 1/2-II, WIDTHBG * 3/4 +, textpaint);

 Canvas.drawtext ("name", WIDTHBG * 1/2 +, WIDTHBG * 3/4 + textpaint);
 Draw the text outside the Circle Canvas.drawtext ("Last 7 Days", WIDTHBG * 1/15, WIDTHBG, Textpaint);
 Myaveragetxt = string.valueof (averagesize);
 Canvas.drawtext ("average", WIDTHBG * 10/15-15, WIDTHBG, Textpaint);
 Canvas.drawtext (Myaveragetxt, WIDTHBG * 11/15, WIDTHBG, Textpaint); Canvas.dRawtext ("Step/day", WIDTHBG * 12/15 +, WIDTHBG, textpaint);

 

Drawing text is a little simpler, where you can compute your own location

 Draw dashed Linepaint.setstyle (Paint.Style.STROKE);
 Linepaint.setstrokewidth (2);
 Linepaint.setcolor (LineColor);
 Linepath.moveto (WIDTHBG * 1/15, WIDTHBG + 80);
 Linepath.lineto (WIDTHBG * 14/15, WIDTHBG + 80);
 Linepaint.setpatheffect (effects);

 Canvas.drawpath (Linepath, linepaint);
 Rectsize = WIDTHBG/12;
 Rectagheight = WIDTHBG/10;
  Draws the rounded bar on the dotted line for (int i = 0; i < FourActivity.sizes.size (); i++) {rectpaint.setstrokewidth (WIDTHBG/25);
  Rectpaint.setstyle (Paint.Style.STROKE);
  Rectpaint.setstrokejoin (Paint.Join.ROUND);
  Rectpaint.setstrokecap (Paint.Cap.ROUND);
  Float Startheight = WIDTHBG + + rectagheight;
  Rectpath.moveto (Rectsize, startheight);
  Double percentage = double.valueof (FourActivity.sizes.get (i))/double.valueof (averagesize);
  Double height = percentage * rectagheight;
  Rectpath.lineto (Rectsize, (float) (startheight-height));
  Rectpaint.setcolor (TextColor);
  Canvas.drawpath (Rectpath, rectpaint);
Draw the text below Textpaint.setcolor (LineColor);  Canvas.drawtext ("0" + (i + 1) + "Day", rectSize-25, Startheight +, textpaint);
 Rectsize + = WIDTHBG/7;

 }

The role of Dashpatheffect is to dash line segments of path. The constructor is Dashpatheffect (float[] intervals, float offset), where intervals is an on and off array of dashed lines, the length of which must be greater than or equal to 2,float[0], float[1] Represents the length of the first solid line and the first dash, in turn, and repeats the first number if no more data is available after the array. Offset is the number of offsets when drawing.

Dashpatheffect effects = new Dashpatheffect (New float[]{5,5}, 1);

This instance is then used as a parameter to Linepaint.setpatheffect (). Drawing the text below is a simple loop, and the length of the vertical bar is computed based on the size of the step array. After changing the average number of steps in the sample diagram, the length of the vertical bar has also changed.

 Draw the bottom ripple
 weavpaint.setcolor (textcolor);
 Weavpath.moveto (0, HEIGHTBG);
 Weavpath.lineto (0, HEIGHTBG * 10/12);
 Weavpath.cubicto (WIDTHBG * 1/10, HEIGHTBG * 10/12, WIDTHBG * 3/10, HEIGHTBG * 11/12, WIDTHBG, HEIGHTBG * 10/12);
 Weavpath.lineto (WIDTHBG, HEIGHTBG);
 Weavpath.lineto (0, HEIGHTBG);
 Canvas.drawpath (Weavpath, weavpaint);

 Draw the bottom text
 weavpaint.setcolor (color.white);
 Weavpaint.settextsize (WIDTHBG/20);
 Canvas.drawtext ("Good grades, keep trying!", WIDTHBG * 1/10-20, HEIGHTBG * 11/12 +, weavpaint);

The bottom water ripple is implemented using the Sanche Besel curve:
Cubicto (x1, y1, x2, y2, X3, y3): Sanche Besel Curve, (x1,y1) for control points, (X2,y2) for control points, (X3,y3) for end points, for drawing complex curves.

About rewriting the OnDraw method, personal advice is best in the book will need to complete the custom view outline of the picture down, mark the coordinates and position, calculate the high width. Then draw the custom view, from top to bottom, from outside to inside, step by step. It's really practical, so the logic is clear and layered, and it's very helpful for you to write code.

5. The realization of the animation:

 private void Startanim () {//Step animation implementation Valueanimator Walkanimator = Valueanimator.ofint (0, mysize); Walkanimator.addupdatelistener (New Valueanimator.animatorupdatelistener () {@Override public void onanimationupdate (
  Valueanimator animation) {walknum = (int) animation.getanimatedvalue ();
  Postinvalidate ();
 }
 });
 The realization of the ranking animation valueanimator rankanimator = valueanimator.ofint (0, rank); Rankanimator.addupdatelistener (New Valueanimator.animatorupdatelistener () {@Override public void onanimationupdate (
  Valueanimator animation) {ranknum = (int) animation.getanimatedvalue ();
  Postinvalidate ();

 }
 });
 Double size = mysize;
 Double avgsize = averagesize;
 if (Size > avgsize) {size = Avgsize;
 }//Arc animation implementation Valueanimator Arcanimator = valueanimator.offloat (0, (float) (size/avgsize * 300)); Arcanimator.addupdatelistener (New Valueanimator.animatorupdatelistener () {@Override public void onanimationupdate ( Valueanimator animation) {arcnum = (float) animation.gEtanimatedvalue ();
  Postinvalidate ();
 }
 });
 Animset.setduration (3000);
 Animset.playtogether (Walkanimator, Rankanimator, arcanimator);
 Animset.start ();

 }

Here we have to mention the superiority of attribute animation, not only can be used in view, but also can be applied to an object. Only need to set a good start and end value, add an animation listener, you can get the value of change, and then use the Postinvalidate () method, so call the OnDraw method to change the value. Finally, set up a combination of animation--animatorset, so that three animation to achieve synchronization consistent effect.
Then I used the animation, and in the beginning I wrote this method in init () initialization function, and it never reached the desired effect. Later learned that the custom view initialization, the combination of some of the animation needs: The number of steps, ranking, the average number of steps have not passed over, so the animation can not be completed. Finally, I write this method behind the Onmeasure () method to achieve the effect, here I am grateful to colleagues to remind.

6. Set the size of the steps and use in the activity:

 public void ReSet (int mysize, int myrank, int myaveragesize) {
 walknum = 0;
 Arcnum = 0;
 Ranknum = 0;
 Mysize = mysize;
 rank = Myrank;
 Averagesize = myaveragesize;
 Startanim ();
 }

The value of the setting is passed through the construction method, and finally the method to open the animation is invoked. The code for the corresponding activity:

public class Fouractivity extends Appcompatactivity {

 private myqqhealthview view;
 public static list<integer> sizes = new arraylist<> ();
 Private Button btn;
 @Override
 protected void onCreate (Bundle savedinstancestate) {
 super.oncreate (savedinstancestate);
 Setcontentview (r.layout.activity_four);
 Initview ();
 }

 private void Initview () {
 view = (Myqqhealthview) Findviewbyid (R.id.myqqview);
 View.setmysize (2345);
 View.setrank (one);
 View.setaveragesize (5436);
 Sizes.add (1234);
 Sizes.add (2234);
 Sizes.add (4234);
 Sizes.add (6234);
 Sizes.add (3834);
 Sizes.add (7234);
 Sizes.add (5436);
 BTN = (Button) Findviewbyid (R.ID.SET_BTN);
 Btn.setonclicklistener (New View.onclicklistener () {
  @Override public
  void OnClick (View v) {
  View.reset (6534, 8, 4567);
  }
 );}


You can set the value you want according to the situation.

The whole process goes down and you find that customizing view is not as difficult as it might seem. More practice, perfect, I believe that the complex interface can not live our.

OK, Next Custom View goodbye.

The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.

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.