Home>

Foreword:

The previous articles covered the principle of drawing custom controls.Detailed explanation of the basic principles of android custom control (1),Custom properties of android custom controls (b),Android custom control custom combination control (three), As the saying goes:"It ’s better to remember things than to write bad things,Just say you don't practice fake styles! !! !! "As a scumbag, I fell here because I didn't follow this famous saying,So follow the instructions carefully,Pay attention to the combination of theory and practice,Today, I used a custom viewgroup to implement the tag cloud used in the project.

Demand background:

Companies need to implement a label display of knowledge points,The length of each label is unknown.As shown below

Basic drawing process:

The drawing principle is no longer introduced here.

• Constructor gets custom attributes

•onmeasure () method, measure the size of the child control

•onlayout () method, layout child controls

1.) Custom attributes

<declare-styleable name="tagslayout">
  <attr name="tagverticalspace" />
  <attr name="taghorizontalspace" />
</declare-styleable>

2.) Get custom attribute values ​​in the constructor

private int childhorizontalspace;
 private int childverticalspace;
 public tagslayout (context context, attributeset attrs) {
  super (context, attrs);
  typedarray attrarray=context.obtainstyledattributes (attrs, r.styleable.tagslayout);
  if (attrarray!=null) {
   childhorizontalspace=attrarray.getdimensionpixelsize (r.styleable.tagslayout_taghorizontalspace, 0);
   childverticalspace=attrarray.getdimensionpixelsize (r.styleable.tagslayout_tagverticalspace, 0);
   attrarray.recycle ();
  }
 }

3.) the onmeasure function measures the size of the child controls,Then set the current control size

/**
  * Responsible for setting the measurement mode and size of the child controls
  * /
 @override
 protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {
  super.onmeasure (widthmeasurespec, heightmeasurespec);
  //get the measurement mode and size set by its parent container
  int sizewidth=measurespec.getsize (widthmeasurespec);
  int sizeheight=measurespec.getsize (heightmeasurespec);
  int modewidth=measurespec.getmode (widthmeasurespec);
  int modeheight=measurespec.getmode (heightmeasurespec);
  //If it is warp_content, record the width and height
  int width=0;
  int height=0;
  /**
   * Record the width of each line,continuously take the maximum width
   * /
  int linewidth=0;
  /**
   * The height of each line,Accumulate to height
   * /
  int lineheight=0;
  int count=getchildcount ();
  int left=getpaddingleft ();
  int top=getpaddingtop ();
  //traverse each child element
  for (int i=0;i<count;i ++) {
   view child=getchildat (i);
   if (child.getvisibility () == gone)
    continue;
   //measure the width and height of each child
   measurechild (child, widthmeasurespec, heightmeasurespec);
   //get child's lp
   marginlayoutparams lp=(marginlayoutparams) child.getlayoutparams ();
   //the width actually occupied by the current subspace
   int childwidth=child.getmeasuredwidth () + lp.leftmargin + lp.rightmargin + childhorizontalspace;
   //the height actually occupied by the current subspace
   int childheight=child.getmeasuredheight () + lp.topmargin + lp.bottommargin + childverticalspace;
   /**
    * If the current child is added, the maximum width is exceeded,Then up to the current maximum width to width, add height to the class and open a new line
    * /
   if (linewidth + childwidth>sizewidth-getpaddingleft ()-getpaddingright ()) {
    width=math.max (linewidth, childwidth);//take the largest
    linewidth=childwidth;//reopen the new line,Start recording
    //overlay the current height,    height +=lineheight;
    //Turn on recording the height of the next line
    lineheight=childheight;
    child.settag (new location (left, top + height, childwidth + left-childhorizontalspace, height + child.getmeasuredheight () + top));
   } else {//Otherwise the accumulated value linewidth, lineheight take the maximum height
    child.settag (new location (linewidth + left, top + height, linewidth + childwidth-childhorizontalspace + left, height + child.getmeasuredheight () + top));
    linewidth +=childwidth;
    lineheight=math.max (lineheight, childheight);
   }
  }
  width=math.max (width, linewidth) + getpaddingleft () + getpaddingright ();
  height +=lineheight;
  sizeheight +=getpaddingtop () + getpaddingbottom ();
  height +=getpaddingtop () + getpaddingbottom ();
  setmeasureddimension ((modewidth == measurespec.exactly)?sizewidth:width, (modeheight == measurespec.exactly)?sizeheight:height);
 }

Get the size of each child control by calling the measurechild function through all the child controls,Then use the width superposition to determine whether to wrap.The height of the overlay control,At the same time record the coordinates of the current child control,Here the record coordinates refer to an internal class location.java written by myself

/**
  * Record the coordinates of child controls
  * /
 public class location {
  public location (int left, int top, int right, int bottom) {
   this.left=left;
   this.top=top;
   this.right=right;
   this.bottom=bottom;
  }
  public int left;
  public int top;
  public int right;
  public int bottom;
 }

4.) onlayout function to re-layout all child controls

@override
 protected void onlayout (boolean changed, int l, int t, int r, int b) {
  int count=getchildcount ();
  for (int i=0;i<count;i ++) {
   view child=getchildat (i);
   if (child.getvisibility () == gone)
    continue;
   location location=(location) child.gettag ();
   child.layout (location.left, location.top, location.right, location.bottom);
  }
 }

Here iterate directly through all the child controls to call the layout function of the child controls for layout.

how to use:1). Directly quote yourself in the layout

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:lee="http://schemas.android.com/apk/res-auto"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">
 <com.whoislcj.views.tagslayout
  android:id="@ + id/image_layout"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_margin="10dp"
  lee:taghorizontalspace="10dp"
  lee:tagverticalspace="10dp" />
</linearlayout>

2). Add tags to the code

tagslayout imageviewgroup=(tagslayout) findviewbyid (r.id.image_layout);
 viewgroup.marginlayoutparams lp=new viewgroup.marginlayoutparams (viewgroup.layoutparams.wrap_content, viewgroup.layoutparams.wrap_content);
  string [] string={"Since the day I wrote the code,I have no intention to write code "," Since the day I wrote the code "," I have no intention to write code "," No intention "," Write code "};
  for (int i=0;i<5;i ++) {
   textview textview=new textview (this);
   textview.settext (string [i]);
   textview.settextcolor (color.white);
   textview.setbackgroundresource (r.drawable.round_square_blue);
   imageviewgroup.addview (textview, lp);
  }

Specific effect

3.) Attach all codes of tagslayout at the end

public class tagslayout extends viewgroup {
 private int childhorizontalspace;
 private int childverticalspace;
 public tagslayout (context context, attributeset attrs) {
  super (context, attrs);
  typedarray attrarray=context.obtainstyledattributes (attrs, r.styleable.tagslayout);
  if (attrarray!=null) {
   childhorizontalspace=attrarray.getdimensionpixelsize (r.styleable.tagslayout_taghorizontalspace, 0);
   childverticalspace=attrarray.getdimensionpixelsize (r.styleable.tagslayout_tagverticalspace, 0);
   attrarray.recycle ();
  }
 }
 /**
  * Responsible for setting the measurement mode and size of the child controls
  * /
 @override
 protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {
  super.onmeasure (widthmeasurespec, heightmeasurespec);
  //get the measurement mode and size set by its parent container
  int sizewidth=measurespec.getsize (widthmeasurespec);
  int sizeheight=measurespec.getsize (heightmeasurespec);
  int modewidth=measurespec.getmode (widthmeasurespec);
  int modeheight=measurespec.getmode (heightmeasurespec);
  //If it is warp_content, record the width and height
  int width=0;
  int height=0;
  /**
   * Record the width of each line,continuously take the maximum width
   * /
  int linewidth=0;
  /**
   * The height of each line,Accumulate to height
   * /
  int lineheight=0;
  int count=getchildcount ();
  int left=getpaddingleft ();
  int top=getpaddingtop ();
  //traverse each child element
  for (int i=0;i<count;i ++) {
   view child=getchildat (i);
   if (child.getvisibility () == gone)
    continue;
   //measure the width and height of each child
   measurechild (child, widthmeasurespec, heightmeasurespec);
   //get child's lp
   marginlayoutparams lp=(marginlayoutparams) child.getlayoutparams ();
   //the width actually occupied by the current subspace
   int childwidth=child.getmeasuredwidth () + lp.leftmargin + lp.rightmargin + childhorizontalspace;
   //the height actually occupied by the current subspace
   int childheight=child.getmeasuredheight () + lp.topmargin + lp.bottommargin + childverticalspace;
   /**
    * If the current child is added, the maximum width is exceeded,Then up to the current maximum width to width, add height to the class and open a new line
    * /
   if (linewidth + childwidth>sizewidth-getpaddingleft ()-getpaddingright ()) {
    width=math.max (linewidth, childwidth);//take the largest
    linewidth=childwidth;//reopen the new line,Start recording
    //overlay the current height,    height +=lineheight;
    //Turn on recording the height of the next line
    lineheight=childheight;
    child.settag (new location (left, top + height, childwidth + left-childhorizontalspace, height + child.getmeasuredheight () + top));
   } else {//Otherwise the accumulated value linewidth, lineheight take the maximum height
    child.settag (new location (linewidth + left, top + height, linewidth + childwidth-childhorizontalspace + left, height + child.getmeasuredheight () + top));
    linewidth +=childwidth;
    lineheight=math.max (lineheight, childheight);
   }
  }
  width=math.max (width, linewidth) + getpaddingleft () + getpaddingright ();
  height +=lineheight;
  sizeheight +=getpaddingtop () + getpaddingbottom ();
  height +=getpaddingtop () + getpaddingbottom ();
  setmeasureddimension ((modewidth == measurespec.exactly)?sizewidth:width, (modeheight == measurespec.exactly)?sizeheight:height);
 }
 @override
 protected void onlayout (boolean changed, int l, int t, int r, int b) {
  int count=getchildcount ();
  for (int i=0;i<count;i ++) {
   view child=getchildat (i);
   if (child.getvisibility () == gone)
    continue;
   location location=(location) child.gettag ();
   child.layout (location.left, location.top, location.right, location.bottom);
  }
 }
 /**
  * Record the coordinates of child controls
  * /
 public class location {
  public location (int left, int top, int right, int bottom) {
   this.left=left;
   this.top=top;
   this.right=right;
   this.bottom=bottom;
  }
  public int left;
  public int top;
  public int right;
  public int bottom;
 }
}

to sum up:So far, we have introduced the simple custom control.Very complex controls in the project now involve relatively few,Make notes after using them later.

  • Previous AngularJs page filter tag small function
  • Next Automatically generate business cards with user information using PHP