Why do you want to customize a control
Sometimes, native controls do not meet our needs for appearance and functionality, so custom controls can be customized to customize the appearance or functionality, and, at times, native controls can implement the desired functionality through complex encodings, at which point the control can be customized to improve the reusability of the code.
How to customize a control
Let me take a look at the method of customizing the control through my open source Android-calendarview project in GitHub. The custom control class name in the project is CalendarView. This custom control overrides some of the methods that custom controls often need to rewrite.
Constructors
To support this control, you can use both XML layout file declarations and dynamic creation in Java files to implement three constructors.
Public CalendarView (context context, AttributeSet attrs, int defstyle);
Public CalendarView (context context, AttributeSet attrs);
Public CalendarView (context context);
You can write your initialization code in the first method with the longest argument list, and the following two constructors call the first one.
Public CalendarView (context, AttributeSet attrs) {This
(context, attrs, 0);
}
Public CalendarView {This
(context, NULL);
}
So what do you do in a constructor?
1 read from definition parameters
Read the custom properties that may be set in the layout file (the Calendar control only customizes one mode parameter to represent the calendar's pattern). The code is as follows. As long as you customize the properties in Attrs.xml, some r.styleable variables are created automatically.
Copy Code code as follows:
TypedArray TypedArray = context.obtainstyledattributes (Attrs, R.styleable.calendarview);
mode = Typedarray.getint (R.styleable.calendarview_mode, constant.mode_show_data_of_this_month);
Then attach the Attrs.xml file under the values directory in the Res directory, where you need to declare custom parameters for your custom control.
<?xml version= "1.0" encoding= "Utf-8"?>
<resources>
<declare-styleable name= "CalendarView" >
<attr name= "mode" format= "integer"/>
</declare-styleable>
</resources>
2 initializing related parameters for drawing controls
such as the font color, size, control the size of each part.
3 initializing parameters related to logic
For a calendar, you need to be able to determine whether each cell in the calendar is valid for the current year, and, if so, how much of the day the value represents. Initialized with the current time before the date is set. Implemented as follows.
/**
* Calculate the values of date[] and legal range of index of date[]/
private void Initial () {in
T DayOfWeek = Calendar.get (Calendar.day_of_week);
int monthstart =-1;
if (DayOfWeek >= 2 && dayofweek <= 7) {
Monthstart = dayOfWeek-2;
} else if (DayOfWeek = 1) {
Monthstart = 6;
}
Curstartindex = Monthstart;
Date[monthstart] = 1;
int daysofmonth = Daysofcurrentmonth ();
for (int i = 1; i < Daysofmonth i++) {
Date[monthstart + i] = i + 1;
}
Curendindex = Monthstart + daysofmonth;
if (mode = = Constant.mode_show_data_of_this_month) {
Calendar tmp = Calendar.getinstance ();
Todayindex = Tmp.get (calendar.day_of_month) + monthStart-1;
}
}
Where date[] is an integer array with a length of 42, because a calendar requires up to 6 rows to display (6*7=42), Curstartindex and Curendindex determine the legitimate subscript range of the date[] array, which means the first day of the month in date[] The subscript for the array, which represents the subscript for the last day of the month in the date[] array.
4 Binds a Ontouchlistener listener
A touch event that listens on the control.
Onmeasure method
This method measures the width and height of the control. CalendarView overrides the Onmeasure () method of the view class, because the first day of the month may be any of Monday through Sunday, and the number of days per month varies, so the number of rows in the calendar control varies, and the height of the control changes. Therefore, the height (width to screen width) of the current calculated control will need to be displayed. Implemented as follows.
@Override
protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {
Widthmeasurespec = View.MeasureSpec.makeMeasureSpec (ScreenWidth, View.MeasureSpec.EXACTLY);
Heightmeasurespec = View.MeasureSpec.makeMeasureSpec (Measureheight (), View.MeasureSpec.EXACTLY);
Setmeasureddimension (Widthmeasurespec, heightmeasurespec);
Super.onmeasure (Widthmeasurespec, Heightmeasurespec);
}
Where screenwidth is the screen width that has been acquired in the constructor, Measureheight () is the height required to calculate the control based on the month. The implementation is as follows and has been written in very detailed comments.
/** * Calculate the total height of the widget */private int measureheight () {/**
* The weekday of the the the month, Sunday ' s is 1 and Monday 2 and Saturday 7, etc.
* * int dayofweek = Calendar.get (Calendar.day_of_week);
/** * The number of days of current month/int daysofmonth = Daysofcurrentmonth ();
/** * Calculate the total lines, which equals to 1 (head of the Calendar) + 1 (the "the") + N/7 + (n%7==0?0:1)
* and n means numberofdaysexceptfirstline */int numberofdaysexceptfirstline =-1;
if (DayOfWeek >= 2 && dayofweek <= 7) {numberofdaysexceptfirstline = Daysofmonth-(8-dayofweek + 1);
}else if (DayOfWeek = = 1) {numberofdaysexceptfirstline = daysOfMonth-1;
int lines = 2 + NUMBEROFDAYSEXCEPTFIRSTLINE/7 + (numberofdaysexceptfirstline% 7 = 0? 0:1);
return (int) (cellheight * lines); }
OnDraw method
This method implements the drawing of the control. Where Drawcircle draws a circle with the given center and radius, DrawText is given a coordinate x,y draw text.
/** * Render/@Override protected void OnDraw (Canvas Canvas) {Super.ondraw (Canvas);
/** * Render the head */float baseline = renderutil.getbaseline (0, Cellheight, weektextpaint); for (int i = 0; i < 7; i++) {Float weektextx = renderutil.getstartx (cellwidth * i + cellwidth * 0.5f, Weektextpaint,
Weektext[i]);
Canvas.drawtext (Weektext[i], WEEKTEXTX, Baseline, weektextpaint); } if (mode = = Constant.mode_calendar) {for (int i = Curstartindex i < Curendindex; i++) {DrawText (canvas, I, TEXTP
Aint, "" + date[i]); }}else if (mode = = Constant.mode_show_data_of_this_month) {for (int i = Curstartindex; i < Curendindex; i++) {if (i
< Todayindex) {if (Data[date[i]]) {drawcircle (canvas, I, Bluepaint, cellheight * 0.37f);
Drawcircle (canvas, I, Whitepaint, cellheight * 0.31f);
Drawcircle (canvas, I, Blackpaint, cellheight * 0.1f);
}else{drawcircle (Canvas, I, Graypaint, cellheight * 0.1f); }}else if (i = = Todayindex) {if (Data[date[i]]) {DrawCircle (canvas, I, Bluepaint, cellheight * 0.37f);
Drawcircle (canvas, I, Whitepaint, cellheight * 0.31f);
Drawcircle (canvas, I, Blackpaint, cellheight * 0.1f);
}else{drawcircle (Canvas, I, Graypaint, cellheight * 0.37f);
Drawcircle (canvas, I, Whitepaint, cellheight * 0.31f);
Drawcircle (canvas, I, Blackpaint, cellheight * 0.1f);
}}else{drawText (canvas, I, Textpaint, "" + date[i]);
}
}
}
}
What needs to be explained is that when the text is drawn, this x represents the x-coordinate of the starting position (the text is the most left), and this y is not the topmost y-coordinate of the text, but should be passed into the baseline of the text. Therefore, if you want to draw the text in a region centered section, you need to go through a calculation. This project encapsulates it in the Renderutil class. Implemented as follows.
/** * Get the baseline to draw between top and bottom in the
Middle
*
/public static float Getbaseline (float t OP, float bottom, Paint Paint) {
Paint.fontmetrics fontmetrics = Paint.getfontmetrics ();
Return (top + bottom-fontmetrics.bottom-fontmetrics.top)/2;
}
/**
* Get the X position to draw around the middle
/public static float Getstartx (float middle, Paint Paint , String text) {return
middle-paint.measuretext (text) * 0.5f;
}
Custom Listener
Control needs to customize some listeners to provide an external interface to handle something when a control has some behavior or interaction. The CalendarView of this project provides two interfaces, Onrefreshlistener and Onitemclicklistener, all of which are custom interfaces. Onitemclick only a single argument, the year and month can be obtained by the getyear and GetMonth methods of the CalendarView object.
Interface onitemclicklistener{
void Onitemclick (int day);
}
Interface onrefreshlistener{
void Onrefresh ();
}
First of all, the two mode,calendarview provide two modes, the first ordinary calendar mode, the calendar each location to show the number of day, the second plan completed this month pattern, drawing a number of graphs to indicate whether or not this month to complete the plan (imitation from the Yue Run circle, A circle indicates that the day has run.
Onrefreshlistener is used to refresh the calendar data and then callback. The two modes define different refresh methods, and the Onrefreshlistener are recalled. Refresh0 is used for the first pattern, and REFRESH1 for the second mode.
/** * Used for Mode_calendar * Legal values of month:1-12/@Override public void Refresh0 (int year, int month) {i
F (mode = = Constant.mode_calendar) {selectedyear = year;
Selectedmonth = month;
Calendar.set (Calendar.year, selectedyear);
Calendar.set (Calendar.month, selectedMonth-1);
Calendar.set (Calendar.day_of_month, 1);
Initial ();
Invalidate ();
if (Onrefreshlistener!= null) {Onrefreshlistener.onrefresh (); }}/** * used for Mode_show_data_of_this_month * "The index 1 to" (Big MONTH), 1 to (small MONTH), 1-28 (Feb of Normal year), 1-29 (Feb of Leap year) * are better to being accessible in the parameter data, illegal indexes'll be ignore D with default False value */@Override public void Refresh1 (boolean[] data) {/** * The month and year, eg. 31st becomes Feb 1st after refreshing) */if (mode = = constant.mode_show_data_of_this_month) {calendar = Calendar.g
Etinstance ();
Selectedyear = Calendar.get (calendar.year); Selectedmonth = CalenDar.get (Calendar.month) + 1;
Calendar.set (Calendar.day_of_month, 1);
for (int i = 1; I <= daysofcurrentmonth (); i++) {if (I < data.length) {This.data[i] = Data[i];
}else{This.data[i] = false;
} initial ();
Invalidate ();
if (Onrefreshlistener!= null) {Onrefreshlistener.onrefresh ();
}
}
}
Onitemclicklistener is used to respond to the event clicked on the calendar one day. The Click Judgment is implemented in the Ontouch method. Implemented as follows. In the same position received Action_down and action_up two events before the completion of the click.
@Override Public
Boolean Ontouch (View V, motionevent event) {
float x = Event.getx ();
Float y = event.gety ();
Switch (event.getaction ()) {case
Motionevent.action_down:
if (Coordiscalendarcell (y)) {
int index = Getindexbycoordinate (x, y);
if (Islegalindex (index)) {
actiondownindex = index;
}}
break;
Case MOTIONEVENT.ACTION_UP:
if (Coordiscalendarcell (y)) {
int actionupindex = getindexbycoordinate (x, y);
if (Islegalindex (Actionupindex)) {
if (Actiondownindex = = Actionupindex) {
actiondownindex =-1;
int day = Date[actionupindex];
if (Onitemclicklistener!= null) {
Onitemclicklistener.onitemclick (day);
}}} break;
return true;
}
About the Calendar control
The Calendar Control Demo effect chart is as follows: the normal calendar mode and the plan completion mode for this month respectively.
What you need to note is that the CalendarView control section consists of only the calendar header and the following calendar, which is the other control, which is used only as a way to show how you can customize this part of the style.
In addition, the text of the calendar header supports a variety of options, such as the Monday four kinds of representations: first, Monday, Monday, Mon. In addition, there are some other control styles of the interface, see Source code: Android-calendarview.
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.