Previously wrote a weather app with 4-day forecasts and 5-day historical information. So want to add a line chart to show the weather trend, rare to have time, wrote a bit, here to do some records, the head is not easy to forget things.
Let's put the effect first:
Control content is relatively simple, is a normal line chart, the upper and lower respectively with the number, click on the time to display the difference in the temperature of the day.
Create a class that inherits from view and adds two construction methods:
Public class extends View { public trendgraph (Context context) {//Creates a call to Super in Java code ( context); } Public Trendgraph (Context context, AttributeSet attrs) {//create in XML call Super(context, attrs); }}
Because there is no need to consider the situation of wrap_content, so the Onmeasure method does not need to rewrite, the key is OnDraw, and OnDraw method is not difficult, only need to determine the specific location of each point is good, because the line is also needed to point the coordinates, the code is more verbose, Can skip:
1 @Override2 protected voidOnDraw (canvas canvas) {3 Super. OnDraw (canvas);4 if(Melements = =NULL|| Melements.size () = = 0) {5 return;6 }7 DoubleMAX_UP =getmaxup ();8 DoubleMin_down =Getmindown ();9 Canvas.setdrawfilter (mdrawfilter);Ten mpaint.setstrokewidth (lineweith); One floatwidth =getwidth (); A floatGrap = width/melements.size (); - floatTextSize =mtextpaint.gettextsize (); - intTextmargin = Circleradius * 2; the floatMargin_top = textSize + 2 *Textmargin; -LOG.D (TAG, "OnDraw:" + margin_top + "|" +textSize); - floatHeight = getheight ()-2 *Margin_top; - + for(inti = 0; I < Melements.size ()-1; i++) { - floatStartX = i * grap + GRAP/2; + floatSTOPX = (i + 1) * Grap + GRAP/2; A floatStarty = (float) (Max_up-melements.get (i). GetUp ())/(float) (MAX_UP- atMin_down) * height +Margin_top; - floatStopy = (float) (Max_up-melements.get (i + 1). GetUp ())/(float) (MAX_UP- -Min_down) * height +Margin_top; - -Canvas.drawtext ((int) Melements.get (i). GetUp () + "℃", Startx-textsize, Starty- - Textmargin, mtextpaint); in canvas.drawcircle (StartX, Starty, Circleradius, mpaint); - canvas.drawline (StartX, Starty, Stopx, Stopy, mpaint); to if(i = = Melements.size ()-2) { +Canvas.drawtext ((int) Melements.get (i + 1). GetUp () + "℃", stopx-textSize, Stopy --Textmargin, mtextpaint); the canvas.drawcircle (stopx, Stopy, Circleradius, mpaint); * } $ Panax NotoginsengStarty = (float) (Max_up-melements.get (i). Getdown ())/(float) (Max_up-min_down) * -Height +Margin_top; theStopy = (float) (Max_up-melements.get (i + 1). Getdown ())/(float) (MAX_UP- +Min_down) * height +Margin_top; ACanvas.drawtext ((int) Melements.get (i). Getdown () + "℃", startx-textsize, Starty + theTextSize +Textmargin, mtextpaint); + canvas.drawcircle (StartX, Starty, Circleradius, mpaint); - canvas.drawline (StartX, Starty, Stopx, Stopy, mpaint); $ if(i = = Melements.size ()-2) { $Canvas.drawtext ((int) Melements.get (i + 1). Getdown () + "℃", stopx-TextSize, -Stopy + textSize +Textmargin, mtextpaint); - canvas.drawcircle (stopx, Stopy, Circleradius, mpaint); the } - }Wuyi}
Consider the need to allow users to make simple settings, such as point size, text size, and so on, so define some custom attributes (Res/values/attr.xml):
<?XML version= "1.0" encoding= "Utf-8"?><Resources> <declare-styleablename= "Trendgraph"> <attrname= "LineWidth"format= "Dimension"/> <attrname= "Circleradius"format= "Dimension" /> <attrname= "TextSize"format= "Dimension" /> <attrname= "TextColor"format= "Reference" /> </declare-styleable></Resources>
Format refers to the formatting of the property, which is specified as dimension, which is the size, the unit of value is DP, SP, or PX and so on, while reference is a reference, that is, a generic reference to other resources in XML, such as @string/app_name. There are other types of documents that you can find yourself.
To parse the custom attribute, this parsing needs to be done in the second constructed method defined above, with the following code:
Public Trendgraph (Context context, AttributeSet attrs) { Super(context, attrs); = GetContext (). Obtainstyledattributes (Attrs, r.styleable.trendgraph); = Array.getdimensionpixelsize (R.styleable.trendgraph_circleradius, 5); = Array.getdimensionpixelsize (R.styleable.trendgraph_linewidth, 3); )); Mtextpaint.setcolor (Array.getcolor (R.styleable.trendgraph_textcolor, Color.Black)); Array.recycle (); }
The Getdimensionpixelsize method is converted to a specific pixel (px) value by passing in the value, which eliminates the hassle of manual conversion. However, it is important to note that the DefaultValue is still px.
You can then specify these properties through XML, adding namespaces to the layout:
xmlns:app= "Http://schemas.android.com/apk/res-auto"
Then Android Studio will be introduced automatically, and can be complete, specific use:
< com.fndroid.byweather.views.TrendGraph Android:id = "@+id/tg" android:layout_width= "Match_parent" app:textcolor= "@color/ Coloraccent " app:textsize=" 22SP " app:circleradius=" 2DP " android:layout_height= "200DP"/>
Finally, add an event listener to make a callback when clicking on the view:
① Defining interfaces:
Public Interface onitemclicklistener{ void onitemclick (view view, element Element); }
② add an interface object to the view and set the setter method:
Public class extends View { private onitemclicklistener monitemclicklistener; // Omit code Public void Setonitemclicklistener (Onitemclicklistener onitemclicklistener) { = onitemclicklistener; }}
③ handle Ontouchevent, override the method, and the code is as follows:
@Override Public Booleanontouchevent (Motionevent event) {intViewwidth =getwidth (); intItemwidth = viewwidth/melements.size (); intViewheight =getheight (); BooleanIsmove =false;//The outermost layer in the interface is a nestedscrollview, so it is also triggered to avoid sliding, adding variable handlingSwitch(Event.getaction ()) { CaseMotionEvent.ACTION_MOVE:isMove=true; Break; Casemotionevent.action_up:if(!ismove) {//Judge callback only when clickedintPosition = (int) (Event.getx ()/itemwidth); Get the location of the click Monitemclicklistener.onitemclick ( This, Melements.get (position)); CALLBACK} Break; } return true; }
④ in activity, listen to the settings and process:
Historygraph.setonitemclicklistener (this);
@Override publicvoid onitemclick (view view, Trendgraph.element Element) { int dt = (int) (Element.getup ()- element.getdown ()); " The difference of the day is: "+ dt +" ℃ ", Snackbar.length_short). Show ();
The effect is complete! We welcome your attention to communication.
Android Development Learning Path-custom controls (Weather Trend line chart)