reprint Please indicate source:http://blog.csdn.net/lmj623565791/article/details/42407923 , this article from:"Zhang Hongyang's Blog"
1. Overview
In other words, with the upgrading of the Android SDK version, many of the controls added new properties to facilitate our use, such as LinearLayout: Divider, showdividers, etc., used to add separation for their internal elements; Such a property cannot be supported in the lower version of the SDK, so we may have this requirement in the development process: try to make this new feature as backward-compatible as possible. Some people say, you can write a new control to achieve, it is true, but will not be too domineering point. Is there no way of grounding gas? Well, this is one of the purposes of this article, in a more grounded gas way, to achieve new properties of backward compatibility.
This kind of situation in Android certainly will be many, hoped can take this to initiate, everybody encounters the similar situation, provides certain mentality. This is the real purpose of this blog!
2. Divider related usage
In order to guarantee the profile, there is no discussion about how divider is good for God horse, because it is not our focus. Of course, here is a divider reference: Grid-spacing-on-android (Basic is to elicit the usefulness of divider, interested in the look, this article's demo look will also refer to this link).
Let's look at one first:
If you want to implement this, what you will do with these 3 buttons (mostly button):
Simple: A horizontal linear layout, the internal three button weight are 1, and then the second button to set leftmargin,rightmargin on it.
Well, no problem, suppose I have a need now: after an operation Button3 hidden, then let Button1 and Button2 layout as follows:
This feeling is not good, though one less, does not affect the aesthetics at all; but, if you follow the above answer
"A horizontal linear layout, the internal three button weight are 1, and then the second button set Leftmargin,rightmargin on the right side of the Button2 will be a rightmargin more."
So, this way of making is obviously not the best, the best solution is to use LinearLayout's divider, Showdividers properties:
The layout code is as follows:
<linearlayout xmlns:android= "http://schemas.android.com/apk/res/android" android:layout_width= "Match_parent" android:layout_height= "Wrap_content" android:padding= "20DP" android:layout_margin= "10DP" android:background= "#224 44444 "android:orientation=" vertical "> <textview android:layout_width=" match_parent "Android:la yout_height= "128DP" android:background= "@android: Color/darker_gray" android:gravity= "Center" android: text= "Application_logo"/> <linearlayout android:id= "@+id/buttons_container" android:layout_width= " Match_parent "android:layout_height=" wrap_content "android:layout_margintop=" 10DP "android:divider=" @ Drawable/divider "android:orientation=" horizontal "android:showdividers=" middle "> <button Android:id= "@+id/btn_first" android:layout_width= "0DP" android:layout_height= "Wrap_content" Android:layout_weight= "1" android:background= "#ff0000" android:text= "button_1"/> <button Android Oid:id= "@+id/btn_second" android:layout_width= "0DP" android:layout_height= "Wrap_content" android:layout_weight= "1" android:background= "#00ff00" android:text= "button_2"/> <butt On android:id= "@+id/btn_third" android:layout_width= "0DP" android:layout_height= "Wrap_con Tent "android:layout_weight=" 1 "android:background=" #0000ff "android:text=" Button_3 "/> ; </LinearLayout></LinearLayout>
In fact, the core is placed button LinearLayout set android:divider= "@drawable/divider" and android:showdividers= "middle";
Of course, some people will say, I am wayward, I use margin to achieve, disappear, I show to control button of the RightMargin is 0 can also. Well, yes, you're not too troublesome. So now the problem is, I'm asking for the interval between each button is blue, what do you do? Note: The value of our divider here is set to a drawable Oh ~ ~.
Drawable of this example (Divider.xml):
<?xml version= "1.0" encoding= "Utf-8"? ><shape xmlns:android= "Http://schemas.android.com/apk/res/android" android:shape= "Rectangle" > <size android:width= "15DP"/> <solid android:color= "@android: Color/transparent "/></shape>
Below is a brief introduction to the following divider, Showdividers, dividerpadding:
Divider can set a drawable as the interval between elements;
Showdividers: The desired value is: middle (between child elements), beginning (left of first element), end (right side of the last element), none; "Similar to the vertical direction"
Dividerpadding: Sets the upper and lower padding of the drawing interval element.
Very simple, we do the experiment on their own to know.
Well, here we have a brief description of the benefits of divider and how to use them. But it's so graceful to realize that the interval between elements is only supported at 3.0 or less, so what happens under 3.0?
Don't be afraid, the following start the focus of this article, let divider compatible to 3.0.
3. Custom LinearLayout
Read the title, we think it is a custom LinearLayout ~ ~
Well, inheriting linearlayout is certain, we have no way to change its source code, but can be inherited to change some features.
Note: The aim now is to be compatible to 3.0 or less:
First look at a 3.0 or less, or you say I lied to you:
The above layout file is displayed below 3.0, completely ignoring the interval.
First of all consider a question, for divider, showdividers 3.0 below the LinearLayout certainly ignore it, how to do?
We implement a linearlayout subclass, let it know divider and showdividers~~~ pay attention to here, here is a big step forward for us, after encountering similar problems, are doing so.
1. Identify high-version properties
public class Icslinearlayout extends linearlayout{private static final int[] LL = new int[]{//android. R.attr.divider,//android. R.attr.showdividers,//android. r.attr.dividerpadding//};p rivate static final int ll_divider = 0;private static final int ll_show_divider = 1;private STA Tic Final int ll_divider_padding = 2;/** * android:dividers */private drawable mdivider;/** * corresponds to: android:showdividers */PR ivate int mshowdividers;/** * corresponds to: android:dividerpadding */private int mdividerpadding;private int mDividerWidth;private int Mdividerheight;public Icslinearlayout (context context, AttributeSet Attrs) {Super (context, attrs); TypedArray a = Context.obtainstyledattributes (Attrs, LL); Setdividerdrawable (A.getdrawable (Icslinearlayout.ll_ DIVIDER)); mdividerpadding = a.getdimensionpixelsize (ll_divider_padding, 0); mshowdividers = A.getinteger (LL_SHOW_ DIVIDER, Show_divider_none); A.recycle ();} /** * Set delimited element, initialize wide high */public void setdividerdrawable (drawable divider) {if (divider = = Mdivider) {return;} Mdivider =DIVIDER;IF (divider! = null) {mdividerwidth = Divider.getintrinsicwidth (); mdividerheight = Divider.getintrinsicheight ( );} Else{mdividerwidth = 0;mdividerheight = 0;} Setwillnotdraw (divider = = null); Requestlayout ();}
The member variables and our constructor are posted here, and the member variables contain the receive variables corresponding to the 3 properties, and then we obtain and assign values to the corresponding properties in the construction of the three attributes.
It's pretty confusing here, I've defined an integer array, and then several variables are subscript, and finally the array and subscript are used to get the value in the construct. Do you want to ask, why do you write so, how do you know?
Well, let's just download the article I previously included with custom attributes, or you wrote it yourself:
Here I took the Android Bitmapshader real-round, rounded image of the source code in this example, we do not have to download, look at me below to understand, I have in this example to customize the two properties: Type and Border_radius, See what kind of code is generated in our R.java:
public static final int border_radius=0x7f010001; public static final int type=0x7f010000; public static final int[] Roundimageviewbyshader = { 0x7f010000, 0x7f010001 }; public static final int roundimageviewbyshader_type = 0; public static final int roundimageviewbyshader_border_radius = 1;
See wood there, integer array, subscript; our Android. The r.attr.xxx corresponds to the above constants. is not the same as our definition of the previous example ~ ~
Yes, the custom attribute how to get, you follow The imitation is, nothing but the current property is Android. R.attr.xxx, not your custom, is no different.
Well, now everyone should know how to get a higher version of the properties ~ ~
2, Onmeasure
After getting to the delimited element, the separating element must be wide and high, and we'll turn the width and height of the separating element into the appropriate margin
@Overrideprotected void onmeasure (int widthmeasurespec, int heightmeasurespec) {// Converts the width of the separated element to the corresponding marginsetchildrendivider (); Super.onmeasure (Widthmeasurespec, heightmeasurespec);} /** * Converts the width of the delimited element to the corresponding margin */protected void Setchildrendivider () {final int count = Getchildcount (); for (int i = 0; i < Count i++) {//traverse each sub Viewview child = Getchildat (i);//Get index final int index = indexofchild (child);//direction final int orientation = Getori Entation (); final layoutparams params = (layoutparams) child.getlayoutparams ();//Determine if you need to draw a delimited if on the left side of the child view ( Hasdividerbeforechildat (Index)) {if (orientation = = VERTICAL) {//if desired, set TopMargin to the height of the delimited element (when vertical) Params.topmargin = Mdividerheight;} else{//if desired, set LeftMargin to the width of the delimited element (horizontal) Params.leftmargin = Mdividerwidth;}}} /** * Determine if you need to draw a delimited */public boolean hasdividerbeforechildat (int childindex) {if (Childindex = = 0 | | childindex = = GET) on the left side of the child view ChildCount ()) {return false;} if ((Mshowdividers & show_divider_middle)! = 0) {Boolean hasvisibleviewbefore = false;for (int i = childIndex-1; I >= 0; i--) {//The previous element of the current index is not GONE it is considered necessary if (Getchildat (i). getvisibility ()! = GONE) {hasvisibleviewbefore = True;break;}} return hasvisibleviewbefore;} return false;}
Onmeasure, the width and height of the divider, according to the situation of mshowdividers, set to the appropriate view margin;
In fact, will divider need to occupy the place, using margin empty out, we will finally in this empty area to draw divider, do not forget, our divider is a drawable.
3, OnDraw
Well, now that we've emptied the place that needs to be drawn through margin, here's the drawing ~ ~ ~
@Overrideprotected void OnDraw (canvas canvas) {if (Mdivider! = null) {if (getorientation () = = VERTICAL) {// Draw a vertical dividerdrawdividersvertical (canvas); else{//Draw Horizontal dividerdrawdividershorizontal (canvas);}} Super.ondraw (canvas);} /** * Draw Horizontal divider * @param canvas */private void drawdividershorizontal (canvas canvas) {Final int count = Getchildcount ( );//Traverse All sub-viewfor (int i = 0; i < count; i++) {final View child = Getchildat (i); if (child! = NULL && CHILD.GETVI Sibility ()! = GONE) {//If you need to draw Dividerif (Hasdividerbeforechildat (i)) {final Android.widget.LinearLayout.LayoutParams LP = (Android.widget.LinearLayout.LayoutParams) child.getlayoutparams ();//Get start position, GetLeft to the left of the current view, and margin on the left, So the difference is that the start area of the divider draw final int left = Child.getleft ()-lp.leftmargin/* *-* mdividerwidth */;//Draw Dividerdrawverticaldivid ER (canvas, left);}}} /** * Draw divider, according to left, draw horizontally * @param canvas * @param left */public void Drawverticaldivider (canvas canvas, int left) {//Set Divider Range Mdivider.setbounds (left, GetpadDingtop () + mdividerpadding, left+ mdividerwidth, GetHeight ()-Getpaddingbottom ()-mdividerpadding);//Draw Mdivider.draw (canvas);}
For the sake of brevity and understanding of the code, there is no vertical direction, and the entire flow of the horizontal direction is complete. The drawing code will be posted in the vertical direction later.
In fact, it is relatively simple, in the OnDraw inside the direction of judgment, here to the level for example: Traverse all sub-view, if you find the need to draw divider before it, then calculate the beginning of the divider position (Child.getleft ()- Lp.leftmargin), then call Drawverticaldivider (), set the divider range, and draw it immediately.
Perpendicular to the same direction, do not repeat, paste the code:
private void drawdividersvertical (canvas canvas) {Final int count = Getchildcount (); for (int i = 0; i < count; i++) {FINA L View child = Getchildat (i), if (child! = null && child.getvisibility ()! = GONE) {if (Hasdividerbeforechildat (i)) {F Inal android.widget.LinearLayout.LayoutParams LP = (android.widget.LinearLayout.LayoutParams) child.getlayoutparams (); final int top = Child.gettop ()-lp.topmargin/* *-* mdividerheight */;d Rawhorizontaldivider (canvas, top);}}} private void Drawhorizontaldivider (canvas canvas, int top) {mdivider.setbounds (Getpaddingleft () + mdividerpadding, top, GetWidth ()-Getpaddingright ()-mdividerpadding, top + mdividerheight); Mdivider.draw (canvas);}
The code is done, what's next? Of course it is test ~ ~ Do not test how to know the results ~ ~
4. Testing
First we replace the linelayout with the button in the layout file with our com.zhy.view.IcsLinearLayout.
Run on a machine below 3.0:
<linearlayout xmlns:android= "http://schemas.android.com/apk/res/android" android:layout_width= "Match_parent" android:layout_height= "Wrap_content" android:padding= "20DP" android:layout_margin= "10DP" android:background= "#224 44444 "android:orientation=" vertical "> <textview android:layout_width=" match_parent "Android:la yout_height= "128DP" android:background= "@android: Color/darker_gray" android:gravity= "Center" android: text= "Application_logo"/> <com.zhy.view.icslinearlayout android:id= "@+id/buttons_container" Androi D:layout_width= "Match_parent" android:layout_height= "wrap_content" android:layout_margintop= "10DP" an droid:divider= "@drawable/divider" android:orientation= "horizontal" android:showdividers= "middle" > <button android:id= "@+id/btn_first" android:layout_width= "0DP" android:layout_height= "Wrap_content" android:layout_weight= "1" android:background= "#ff0000" android:text= "button_1"/> <button Android:id= "@+id/btn_second" android:layout_width= "0DP" android:layout_height= "Wrap_cont Ent "android:layout_weight=" 1 "android:background=" #00ff00 "android:text=" button_2 "/> <button android:id= "@+id/btn_third" android:layout_width= "0DP" Android:layout_ height= "Wrap_content" android:layout_weight= "1" android:background= "#0000ff" Android:text = "Button_3"/> </com.zhy.view.IcsLinearLayout></LinearLayout>
:
A long absence ~ ~ Our separation ~ ~ can be seen in the 3.0 following machine perfect realization ~ ~ ~
But, don't be happy too early, we so change, 3.0 above the machine is what look like?
Haha, is not the perfect realization of the interval ~ ~ ~
Now I can be happy.
Everyone now must be confused, I rub, you in the structure to get divider, and then in the OnDraw inside their own draw divider, we all know 3.0 is supported Ah, certainly will be drawn, you say no conflict who believe it ~~~!!!
5. Questions and Answers
1, why and more than 3.0 did not happen some of the conflict?
Well, yes, more than 3.0 is supported, why do we draw ourselves in OnDraw, and then call Super.ondraw there is no conflict?
The reason is simple: we look at 4.4LinearLayout Source:
@Override protected void OnDraw (canvas canvas) { if (Mdivider = = null) { return; }
In fact, the source code is also inside the OnDraw to draw divider, but if mdivider is null, it will return. There is no conflict because one of our previous operations let its mdivider member variable be NULL ~ ~
Now go to LinearLayout's construction method:
Public LinearLayout (context context, AttributeSet attrs, int defstyle) { Super (context, attrs, defstyle); ... Setdividerdrawable (A.getdrawable (R.styleable.linearlayout_divider)); ... } public void setdividerdrawable (drawable divider) { if (divider = = Mdivider) { return; } Mdivider = divider; if (divider! = null) { mdividerwidth = Divider.getintrinsicwidth (); Mdividerheight = Divider.getintrinsicheight (); } else { mdividerwidth = 0; mdividerheight = 0; } Setwillnotdraw (divider = = null); Requestlayout (); }
You can see that it calls setdividerdrawable in its construction for its mdivider assignment, the key comes ~ ~ ~ ~ ~ ~ Our custom LinearLayout This method, that is to say, Setdividerdrawable will call the subclass method, The setdividerdrawable of this parent class is not called at all, causing Mdivider to be null ~ ~ ~
Null corresponds to the OnDraw inside of the drawing ~~ok~ solution is complete.
2. How did this blog come to mind? How did you know the code was written?
I believe that such a problem, many people interested in fact, is also a coincidence, before the divider this attribute, and then the previous time to write Android teach you to create cool viewpagerindicator not only high imitation miui this blog, Deliberately to see viewpagerindicator that open source project source code, found a icslinearlayout such a class, similar to what we achieved above, of course, I made a certain change; So, carefully studied the class, I think it is necessary to write a blog, To reach the end of the article at the beginning of the purpose ~ In fact, we have a heart, according to our code above, to see how linearlayout Source to draw divider, you will find the code is basically the same (PS: You did not find the problem 1 of the linearlayout source of the setdividerdrawable and we wrote the same ~);
Well, to this whole article on the end, or the sentence: "Such a situation in Android will certainly be a lot, I hope we can make a offer, we have a similar situation, to provide certain ideas." This is the real purpose of this blog! "Don't be lazy and take the time to knock, look, think, you will find that there are many things hidden inside, do not be afraid of wasting time, I study and write this blog time is definitely more than you study this blog time ~ ~
Finally, Olay come ~ ~
SOURCE Click to download
I built a QQ group, convenient for everyone to communicate. Group number:423372824
----------------------------------------------------------------------------------------------------------
Bo Master part of the video has been online, if you do not like boring text, please poke (first record, look forward to your support):
1, the use of Baidu map in Android
2. Android custom control Scratch card in real-action e-commerce activities
3. Android custom controls build Android streaming layouts and popular tags
4. android Robot "Little Mu" implementation
5, High imitation QQ5.0 slide
6, High imitation 5.2.1 main interface and message alert
Android custom controls elegantly implement split lines between elements (support 3.0 or less)