Example tutorials for customizing view and ViewGroup in Android app development _android

Source: Internet
Author: User
Tags call back int size static class

View
all of Android's controls are subclasses of view or view, which in fact represent a rectangular area on the screen, expressed as a Rect, Left,top represents the starting point of view relative to its parent view, width, The height represents the view's own height, which allows you to determine the location of the view on the screen, and then you can start drawing the view's contents after you've identified the position.

View Drawing Process
View drawing can be divided into the following three processes:

Measure
The view will do a measurement first to figure out how much area you need to occupy. The measure process of view exposes us to an interface onmeasure, the definition of the method is this,

protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {}

The view class already provides a basic onmeasure implementation,

protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {
 setmeasureddimension (getdefaultsize ( Getsuggestedminimumwidth (), Widthmeasurespec),
     getdefaultsize (Getsuggestedminimumheight (), HeightMeasureSpec ));
}
public static int getdefaultsize (int size, int measurespec) {
 int = size;
 int specmode = Measurespec.getmode (measurespec);
 int specsize = measurespec.getsize (measurespec);

 Switch (specmode) {case
 measurespec.unspecified: result
   = size;
   break;
 Case Measurespec.at_most: Case
 measurespec.exactly: result
   = specsize;
   break;
 return result;
}

Which invoke the Setmeasureddimension () method, set the measure process of view width, getsuggestedminimumwidth () return to the view of the smallest width,height also have a corresponding method. In a few words, the Measurespec class is an internal static class of the view class that defines three constants unspecified, at_most, and exactly, which we can actually understand, respectively, corresponding to Layoutparams match_parent , Wrap_content, XXXDP. We can rewrite the onmeasure to redefine the view's width and height.

Layout
The layout process is very simple for the view class, and the same view exposes us to the OnLayout method

protected void OnLayout (Boolean changed, int left, int. top, int right, int bottom) {
}

Because we're talking about view now, no child view needs to be sorted, so this step actually doesn't require extra work. To insert a sentence, for the ViewGroup class, OnLayout method, we need to set all the size of the child view is wide and high, which we will say in detail in the next article.

Draw
The draw process is to draw the view style we need on the canvas. The same view exposes us to the OnDraw method.

protected void OnDraw (Canvas Canvas) {
}

The default view class's OnDraw has no line of code, but provides us with a blank canvas, for example, just like a picture, we are the painter, we can draw what kind of effect, completely depends on us.

There are three more important methods in view
Requestlayout
view to invoke the layout procedure again.

Invalidate
View re-invoke a draw procedure

Forcelayout
Identity View the next time you redraw, you will need to recall the layout procedure.

Custom properties
the entire view of the drawing process we have introduced, there is a very important knowledge, custom control properties, we all know that view has some basic attributes, such as Layout_width,layout_height,background, We often need to define our own attributes, so we can do that.

1. In the Values folder, open the Attrs.xml, in fact, this file name can be arbitrary, written here more standardized point, said that all of the view is placed inside the property.
2. Because our example below uses 2 lengths, a property of a color value, we create 3 properties here first.

<declare-styleable name= "Rainbowbar" >
 <attr name= "Rainbowbar_hspace" format= "Dimension" ></attr >
 <attr name= "Rainbowbar_vspace" format= "Dimension" ></attr>
 <attr name= "Rainbowbar_" Color "format=" color "></attr>
</declare-styleable>

So how do we use it, we'll look at an example.

To achieve a relatively simple Google Rainbow progress bar.
For the sake of simplicity, here I only use a color, a variety of colors are left to everyone, we directly on the code.

public class Rainbowbar extends View {//progress bar color int barcolor = Color.parsecolor ("#1E88E5");
 Every bar segment width int hspace = UTILS.DPTOPX (n, Getresources ());
 Every bar segment height int vspace = utils.dptopx (4, getresources ());
 Space among bars int spaces = UTILS.DPTOPX (Getresources ());
 float startx = 0;
 float delta = 10f;

 Paint Mpaint;
 Public Rainbowbar {Super (context);
 Public Rainbowbar (context, AttributeSet attrs) {This (context, attrs, 0);
  Public Rainbowbar (context, AttributeSet attrs, int defstyleattr) {Super (context, attrs, defstyleattr);
  Read Custom attrs TypedArray t = context.obtainstyledattributes (attrs, R.styleable.rainbowbar, 0, 0);
  hspace = T.getdimensionpixelsize (R.styleable.rainbowbar_rainbowbar_hspace, hspace);
  vspace = T.getdimensionpixeloffset (R.styleable.rainbowbar_rainbowbar_vspace, vspace); Barcolor = T.getcolor (R.styleable.rainbowbar_rainbowbar_color, barColor);  T.recycle ();
  We should always recycle after used mpaint = new Paint ();
  Mpaint.setantialias (TRUE);
  Mpaint.setcolor (Barcolor);
 Mpaint.setstrokewidth (vspace);

 }

 .......
}

View has three construction methods that we need to rewrite, and here's a scenario where the next three methods will be invoked,

The first method, which is usually invoked when we do so, is the view view = new view (context);
The second method, when we use view in an XML layout file, is invoked when the layout is inflate.
<view layout_width= "match_parent" layout_height= "Match_parent"/>.
The third method, similar to the second, adds the style property setting, when the Inflater layout calls the third constructor method.
<view style= "@styles/mycustomstyle" layout_width= "match_parent" layout_height= "Match_parent"/>.
As you may be feeling a bit confused, I wrote the initialization read from the definition attribute hspace,vspace, and Barcolor code in the third constructor, but I rainbowbar in the linear layout without the style attribute (), which, according to our explanation above, Inflate layout should invoke the second method of construction, but we invoke the third constructor in the second construct, this (context, attrs, 0); So in the third construct method read from the defined attribute, no problem, this is a little detail to avoid code redundancy-,-

Draw
because we don't have to focus on the measrue and layout process, rewrite the OnDraw method directly.

Draw be invoke numbers.
int index = 0;
@Override
protected void OnDraw (Canvas Canvas) {
  super.ondraw (Canvas);
  Get screen width
  float sw = This.getmeasuredwidth ();
  if (startx >= SW + (hspace + space)-(sw% (hspace + spaces)) {
    startx = 0;
  } else {
    startx = Delta;
   }
  Float start = startx;
  Draw latter parse while
  (Start < SW) {
    canvas.drawline (start, 5, Start + hspace, 5, mpaint);
    Start = = (hspace + space);
  }

  start = Startx-space-hspace;

  Draw Front Parse while
  (start >=-hspace) {
    canvas.drawline (start, 5, Start + hspace, 5, mpaint);
    Start-= (hspace + space);
  }
  if (index >= 700000) {
    index = 0;
  }
  Invalidate ();
}

Layout file:

<?xml version= "1.0" encoding= "Utf-8"?> <linearlayout xmlns:android=   "http://schemas.android.com/" Apk/res/android "
xmlns:app=" Http://schemas.android.com/apk/res-auto "
android:layout_width=" Match_ Parent "
android:layout_height=" match_parent "
android:gravity=" center "
android:layout_margintop=" 40DP "
android:orientation=" vertical ">

<com.sw.demo.widget.rainbowbar 
    android:layout_width=" Match_parent "
  android:layout_height=" wrap_content "
  app:rainbowbar_color=" @android: Color/holo_blue_ Bright "
  app:rainbowbar_hspace=" 80DP "
  app:rainbowbar_vspace=" 10DP "
  ></ Com.sw.demo.widget.rainbowbar>

</LinearLayout>

In fact, it's called the DrawLine method of canvas, and then each time the draw's starting point is pushed forward, at the end of the method, we call the Invalidate method, which we've already explained, which lets the view call back the OnDraw method, So we've reached the effect that our progress bar has been drawing forward. The following is the final display effect, making GIF as if the color is poor, but the real effect is blue. We only write a short line of dozens of lines of code, custom view is not as difficult as we think, the next one we will continue to viewgroup the drawing process learning.

Custom ViewGroup
ViewGroup
we know that ViewGroup is the container class of view, the linearlayout,relativelayout that we often use are viewgroup subclasses, because ViewGroup have many child view, So its entire drawing process is a bit more complicated than view, but it's still three steps Measure,layout,draw, we'll explain it once.

Measure
measure process or measure the size of viewgroup, if LAYOUT_WIDHT and layout_height are match_parent or specific XXXDP, it is very simple to answer, Directly call the Setmeasureddimension () method, set the width of the viewgroup can be, if it is wrap_content, it is more trouble, we need to traverse all the child view, and then for each child view to measure, Then, according to the arrangement rules of the child view, calculate the size of the final viewgroup.

@Override
protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {
 int childcount = This.getchildcount ();
 for (int i = 0; i < ChildCount. i++) {
   View child = This.getchildat (i);
   This.measurechild (Child, Widthmeasurespec, Heightmeasurespec);
   int cw = Child.getmeasuredwidth ();
   int ch = child.getmeasuredheight ();
 }
}

You may need the code similar to the above, where the Getchildcount () method returns the number of child view, the Measurechild () method, and the measurement method of the child view.

Layout
in the customization of the view above, we mention that the layout process is actually the placement of the pairs view, and the OnLayout method gives me a chance to customize the view arrangement according to the rules we want.

@Override
protected void OnLayout (Boolean arg0, int arg1, int arg2, int arg3, int arg4) {
 int childcount = this.ge Tchildcount ();
 for (int i = 0; i < ChildCount. i++) {
   View child = This.getchildat (i);
   Layoutparams lparams = (layoutparams) child.getlayoutparams ();
   Child.layout (Lparams.left, lparams.top, Lparams.left + childwidth,
       lparams.top + childheight);
 }

You may also need code similar to the above, where the Child.layout (Left,top,right,bottom) method can be set for the location of the view, and the meaning of four parameters should be clear by variable names.
Draw
ViewGroup in the draw phase, in fact, according to the order of subclasses, call subclasses of the OnDraw method, because we are only the view of the container, itself generally do not need to draw additional modification, so often in the OnDraw method, You only need to invoke the ViewGroup OnDraw default implementation method.

Layoutparams
ViewGroup also has a very important knowledge layoutparams,layoutparams stores some parameter information when the child view is added to the ViewGroup, when inheriting the ViewGroup class, You'll also need to create a new Layoutparams class, Just like the familiar Linearlayout.layoutparams,relativelayout.layoutparams class in the SDK, you can do this, and in the ViewGroup subclass of your definition, create a new Layoutparams class inheritance and VIEWGR Oup. Layoutparams.

public static class Layoutparams extends Viewgroup.layoutparams {public

 int left = 0;
 public int top = 0;

 Public Layoutparams (Context arg0, AttributeSet arg1) {
   super (arg0, arg1);
 }

 Public layoutparams (int arg0, int arg1) {
   super (arg0, arg1);
 }

 Public Layoutparams (Android.view.ViewGroup.LayoutParams arg0) {
   super (arg0);
 }

}

So now that the new Layoutparams class is available, how do we get our custom viewgroup to add a child view using our custom Layoutparams class, ViewGroup also provides the following methods for us to rewrite, We rewrite the Layoutparams object to return to our custom.

@Override public
android.view.ViewGroup.LayoutParams generatelayoutparams (
   attributeset attrs) {
 return new Ninephotoview.layoutparams (GetContext (), attrs);
}

@Override
protected Android.view.ViewGroup.LayoutParams generatedefaultlayoutparams () {return
 new Layoutparams (layoutparams.wrap_content,
     layoutparams.wrap_content);
}

@Override
protected Android.view.ViewGroup.LayoutParams generatelayoutparams (
   Android.view.ViewGroup.LayoutParams p) {return
 new Layoutparams (p);
}

@Override
protected Boolean checklayoutparams (Android.view.ViewGroup.LayoutParams p) {return
 P instanceof Ninephotoview.layoutparams;
}

Instance
We still do an example to illustrate, we do today a similar micro-trust Circle store to send pictures of the control, click the + number picture, you can always add pictures, up to 9. So the micro-letter is 4 a row, we are here 3 rows, because the general rule is three rows, these are the details do not care (in addition secretly told you, micro-letter implementation is using Tablelayout,-.-).

public class Ninephotoview extends ViewGroup {public static final int max_photo_number = 9; Private int[] Constimageids = {r.drawable.girl_0, r.drawable.girl_1, r.drawable.girl_2, R.drawable.girl_3, R.drawable.

Girl_4, R.drawable.girl_5, R.drawable.girl_6, r.drawable.girl_7, r.drawable.girl_8};
Horizontal spaces among children views int hspace = UTILS.DPTOPX (Getresources ());

Vertical spaces among children views int vspace = UTILS.DPTOPX (Getresources ());
Every child view width and height.
int childwidth = 0;

int childheight = 0;
Store images Res ID arraylist<integer> mimageresarraylist = new arraylist<integer> (9);

Private View Addphotoview;

Public Ninephotoview {Super (context);}

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

 Public Ninephotoview (context, AttributeSet attrs, int defstyle) {Super (context, attrs, Defstyle); TypedArray T = context.obtainstyledattributes(Attrs, R.styleable.ninephotoview, 0, 0);
 hspace = T.getdimensionpixelsize (R.styleable.ninephotoview_ninephoto_hspace, hspace);
 vspace = T.getdimensionpixelsize (R.styleable.ninephotoview_ninephoto_vspace, vspace);

 T.recycle ();
 Addphotoview = new View (context);
 AddView (Addphotoview);
Mimageresarraylist.add (New Integer ());

 }

So far, it's almost the same as the previous one, and taking photos and choosing pictures from photo albums is not the focus of our article, so we hard-coded the pictures into the code (all beauty ...). ViewGroup initialization, we added a + Number button to give the user a click to add a new picture.

Measure

 @Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {int R
 W = measurespec.getsize (Widthmeasurespec);

 int RH = Measurespec.getsize (HEIGHTMEASURESPEC);
 Childwidth = (rw-2 * hspace)/3;

 Childheight = Childwidth;
 int childcount = This.getchildcount ();
   for (int i = 0; i < ChildCount. i++) {View child = This.getchildat (i);

   This.measurechild (Child, Widthmeasurespec, Heightmeasurespec);
   Layoutparams lparams = (layoutparams) child.getlayoutparams ();
   Lparams.left = (i% 3) * (Childwidth + hspace);
 Lparams.top = (I/3) * (Childwidth + vspace);
 int VW = RW;
 int VH = RH;
 if (ChildCount < 3) {VW = ChildCount * (childwidth + hspace);
 } VH = ((ChildCount + 3)/3) * (Childwidth + vspace);
Setmeasureddimension (VW, VH); }

We have three rows of child view, and all are squares, so we go through loops to get the position of all the child view, and note that the upper left corner of our top view is stored in our custom Layoutparams's left-hand and two fields. The layout phase is used, and finally we calculate the width of the entire viewgroup and invoke setmeasureddimension settings.

Layout

@Override protected void OnLayout (Boolean arg0, int arg1, int arg2, int arg3, int arg4) {int childcount = This.getchild
 Count ();
   for (int i = 0; i < ChildCount. i++) {View child = This.getchildat (i);
   Layoutparams lparams = (layoutparams) child.getlayoutparams ();

   Child.layout (Lparams.left, lparams.top, Lparams.left + childwidth, lparams.top + childheight); if (i = = Mimageresarraylist.size ()-1 && mimageresarraylist.size ()!= max_photo_number) {Child.setbackgroun
     Dresource (R.drawable.add_photo); Child.setonclicklistener (New View.onclicklistener () {@Override public void OnClick (View arg0) {A
       Ddphotobtnclick ();
   }
     });
     }else {Child.setbackgroundresource (constimageids[i]);
   Child.setonclicklistener (NULL); }} public void Addphoto () {if (Mimageresarraylist.size () < Max_photo_number) {View newchild = new View (Getco
   ntext ());
   AddView (newchild); Mimageresarraylist.add (New Integer ());
   Requestlayout ();
 Invalidate ();

 } public void Addphotobtnclick () {final charsequence[] items = {"Take Photo", "Photo from Gallery"};
 Alertdialog.builder Builder = new Alertdialog.builder (GetContext ());  Builder.setitems (items, new Dialoginterface.onclicklistener () {@Override public void OnClick (Dialoginterface arg0,
   int arg1) {Addphoto ();
 }

 });
Builder.show ();

 }

The most important thing is to call the layout method, which is very good for the left and top fields in the layoutparams we obtained in the measure phase, and also for the placement of each child view. Then judge the picture is not reached the maximum of 9, the default is the last one is the + picture, and then set the Click Event, pop-up dialog box for the user to select the action.

Draw
do not need to rewrite, use viewgroup default implementation can.
Attached layout file

<?xml version= "1.0" encoding= "Utf-8"?> <linearlayout xmlns:android=
"http://schemas.android.com/apk/" Res/android "
xmlns:app=" Http://schemas.android.com/apk/res-auto "
android:layout_width=" Match_parent "
android:layout_height= "match_parent"
android:layout_margintop= "40DP"
android:orientation= " Vertical ">

<com.sw.demo.widget.ninephotoview
  android:id=" @+id/photoview "
  android:layout_ Width= "Match_parent"
  android:layout_height= "wrap_content"
  app:ninephoto_hspace= "10DP"
  app: Ninephoto_vspace= "10DP"
  app:rainbowbar_color= "@android: Color/holo_blue_bright" >

</ Com.sw.demo.widget.ninephotoview>

</LinearLayout>

Finally, plus the program to run the effect of the map, today's custom viewgroup explanation on so much, I wish you every day a new harvest, every day have a good mood ~ ~ ~


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.