Android custom ViewGroup to implement rounded rectangular menus with arrows _android

Source: Internet
Author: User
Tags getcolor

This article and you make a rounded rectangular menu with arrows, probably long below this appearance:


The top arrow is required to align the menu anchor, the menu item is pressed against the color, and the background color and press color are configurable.
The easiest thing to do is to get UX to put a picture of a triangle up, but then think that this is not too low, and the different resolution is not very good fit, simply customize a viewgroup bar!
Custom ViewGroup is actually very simple, basically are according to a certain routine.

One, define a attrs.xml
is to declare that you have this custom view has configurable properties that can be configured in the future when used. This declares 7 properties, namely: arrow width, arrowhead height, arrow horizontal offset, fillet radius, menu background color, shadow color, shadow thickness.

 <resources>
  <declare-styleable name= "Arrowrectangleview" >
    <attr name= "Arrow_width" format= "Dimension"/>
    <attr name= "Arrow_height" format= "Dimension"/> <attr "name=" Arrow_offset
    "Dimension"/>
    <attr name= "radius" format= "Dimension"/> <attr "name=" Background_color "format="
    Color "/>
    <attr name=" shadow_color "format=" color "/> <attr" name= "shadow_thickness"
    Dimension "/>
  </declare-styleable>
</resources> 

Second, write a class that inherits ViewGroup, initialize these properties in the constructor
here you need to use a obtainstyledattributes () method to get a Typedarray object, You can then get the corresponding property value based on the type. Note that the object needs to be explicitly invoked after the Recycle () method is released.

 public class Arrowrectangleview extends ViewGroup {... public arrowrectangleview, AttributeSet

    attrs, int defstyleattr) {Super (context, attrs, defstyleattr);
    TypedArray a = Context.gettheme (). Obtainstyledattributes (Attrs, R.styleable.arrowrectangleview, defstyleattr, 0);
      for (int i = 0; i < A.getindexcount (); i++) {int attr = A.getindex (i); Switch (attr) {Case r.styleable.arrowrectangleview_arrow_width:marrowwidth = a.getdimensionpixelsize (A
          TTR, Marrowwidth);
        Break
          Case r.styleable.arrowrectangleview_arrow_height:marrowheight = a.getdimensionpixelsize (attr, mArrowHeight);
        Break
          Case R.styleable.arrowrectangleview_radius:mradius = a.getdimensionpixelsize (attr, Mradius);
        Break
          Case R.styleable.arrowrectangleview_background_color:mbackgroundcolor = A.getcolor (attr, Mbackgroundcolor);
        Break Case R.STyleable.
          Arrowrectangleview_arrow_offset:marrowoffset = A.getdimensionpixelsize (attr, Marrowoffset);
        Break
          Case R.styleable.arrowrectangleview_shadow_color:mshadowcolor = A.getcolor (attr, Mshadowcolor);
        Break Case r.styleable.arrowrectangleview_shadow_thickness:mshadowthickness = a.getdimensionpixelsize (attr, MShadowTh
          ickness);
      Break
  } a.recycle (); 

 }

Third, rewrite the Onmeasure () method

The Onmeasure () method, as its name suggests, is used to measure the width and height of your viewgroup.

Let's consider the height first:
• First to reserve the height for the arrows and rounded corners, maxheight to add these two
• Then the measurement of all visible child,viewgroup has provided a ready-made measurechild () method
• The next step is to add the height of the acquired child to the MaxHeight and, of course, consider the upper and lower margin configuration
Besides, we need to consider the upper and lower padding and the height of the shadow.
• Finally, the setmeasureddimension () setting takes effect

Consider the width:
• The first is to measure all visible child through the Measurechild () method
• Then compare the width of these child and the margin configuration, select the maximum value
• Next, plus the left and right padding, and the shadow width
• Finally, the setmeasureddimension () setting takes effect

 @Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {
    int count = Getchildcount ();
    int maxwidth = 0;
    Reserve spaces for the arrow and round corners int maxheight = marrowheight + Mradius;
      for (int i = 0; i < count; i++) {final View child = Getchildat (i);
      Final Marginlayoutparams LP = (marginlayoutparams) child.getlayoutparams ();
        if (child.getvisibility ()!= GONE) {measurechild (Child, Widthmeasurespec, Heightmeasurespec);
        MaxWidth = Math.max (MaxWidth, child.getmeasuredwidth () + Lp.leftmargin + lp.rightmargin);
      MaxHeight = MaxHeight + child.getmeasuredheight () + Lp.topmargin + lp.bottommargin;
    } maxwidth = MaxWidth + getpaddingleft () + getpaddingright () + mshadowthickness;

    MaxHeight = MaxHeight + getpaddingtop () + getpaddingbottom () + mshadowthickness;
  Setmeasureddimension (MaxWidth, maxheight); } 

Does it look very simple? And of course there are two small problems:
1. When the height is rounded to reserve size, why only leave a radius, not up and down two radius?
In fact, this is from the display effect to consider, if the upper and lower each left a radius, will cause the menu border is very thick not good-looking, after the implementation of OnLayout () you will find that we layout menu items will move up half the radius, so that the border looks much better.
2. Why can the layout parameters of child be turned into marginlayoutparams?
Here you actually need to rewrite another method Generatelayoutparams (), and return to the type of layout parameter you want. It is generally used marginlayoutparams, of course you can also use other types or custom types.

   @Override public
  viewgroup.layoutparams generatelayoutparams (AttributeSet attrs) {return
    new Marginlayoutparams (GetContext (), attrs);
  } 

Iv. rewrite the OnLayout () method
the OnLayout () method, as the name suggests, is used to lay out all the child view in this viewgroup.
In fact each view has a layout () method, all we need to do is to pass the appropriate left/top/right/bottom coordinates to this method.
Here we can see that when we layout the menu item, we lift a half radius, so the Topoffset only adds half a radius, and the coordinates on the right side only lose half the radius.

 @Override protected void OnLayout (Boolean changed, int l, int t, int r, int b) {
    int count = Getchildcount ();
    int topoffset = t + marrowheight + MRADIUS/2;
    int top = 0;
    int bottom = 0;
      for (int i = 0; i < count; i++) {final View child = Getchildat (i);
      top = topoffset + i * child.getmeasuredheight ();
      Bottom = top + child.getmeasuredheight ();
    Child.layout (L, Top, R-MRADIUS/2-mshadowthickness, bottom); }
  } 

V. Rewrite the Dispatchdraw () method
here because we are writing a viewgroup container, which itself is not needed to be drawn, we need to rewrite its Dispatchdraw () method. If you are rewriting a specific view, you can also rewrite its OnDraw () method.
The drawing process is divided into three steps:
1. Draw Fillet Rectangle
This step is relatively simple, direct call Canvas Drawroundrect () is complete.
2. Draw the triangle Arrow
This needs to be based on the configured properties, set a path, and then call Canvas's DrawPath () to complete the drawing.
3. Draw Menu Shadow
This plainly is to change a color and then draw a rounded rectangle, position slightly offset, of course, but also have a fuzzy effect.
To get a blur effect, you need to configure it through the paint setmaskfilter (), and you need to turn off the layer's hardware acceleration, which is clearly stated in the API.
In addition, you need to set the source image and the target image overlap pattern, the shadow obviously to stack to the back of the menu, according to the following figure, we need to select Dst_over mode.

Other details to see the code is clear:

 @Override protected void Dispatchdraw Canvas Canvas) {//Disable h/w acceleration for Blur mask Filter Setlay

    Ertype (view.layer_type_software, NULL);
    Paint Paint = new Paint ();
    Paint.setantialias (TRUE);
    Paint.setcolor (Mbackgroundcolor);

    Paint.setstyle (Paint.Style.FILL);

    Set Xfermode for source and shadow overlap Paint.setxfermode (new Porterduffxfermode (PorterDuff.Mode.DST_OVER));
    Draw Round Corner Rectangle Paint.setcolor (mbackgroundcolor); Canvas.drawroundrect (New RECTF (0, Marrowheight, Getmeasuredwidth ()-Mshadowthickness, Getmeasuredheight ()-

    mshadowthickness), Mradius, Mradius, paint);
    Draw Arrow Path = new path ();
    int startpoint = Getmeasuredwidth ()-marrowoffset;
    Path.moveto (StartPoint, marrowheight);
    Path.lineto (StartPoint + marrowwidth, marrowheight);
    Path.lineto (startpoint + MARROWWIDTH/2, 0);
    Path.close ();

    Canvas.drawpath (path, paint); Draw Shadow if (mshadowthicKness > 0) {paint.setmaskfilter (new Blurmaskfilter (mshadowthickness, BlurMaskFilter.Blur.OUTER));
      Paint.setcolor (Mshadowcolor); Canvas.drawroundrect (New RECTF (mshadowthickness, Marrowheight + mshadowthickness, getmeasuredwidth ()-
    Mshadowthickness, Getmeasuredheight ()-mshadowthickness), Mradius, Mradius, paint);
  } super.dispatchdraw (canvas); 

 }

Six, referencing the custom ViewGroup   in layout xml;
So far, the implementation of the custom ViewGroup is complete, so let's use it in the project! There are two small differences between using a custom viewgroup and using a system ViewGroup component:  
First, you want to specify the full package name, otherwise the component will not be found when it is run. &NBSP
Two, you need to specify a different namespace when configuring custom attributes to avoid confusion with the default Android namespace. For example, a new app namespace is specified here to refer to custom attributes.

 <?xml version= "1.0" encoding= "Utf-8"?> xmlns: Android= "Http://schemas.android.com/apk/res/android" xmlns:app= "Http://schemas.android.com/apk/res-auto" Android : layout_width= "wrap_content" android:layout_height= "wrap_content" android:orientation= "vertical" android:backg Round= "@android: Color/transparent" android:paddingleft= "3DP" android:paddingright= "3DP" Android:splitmotioneve Nts= "false" app:arrow_offset= "31DP" app:arrow_width= "16DP" app:arrow_height= "8DP" app:radius= "5DP" app : background_color= "#ffb1df83" app:shadow_color= "#66000000" app:shadow_thickness= "5DP" > <linearlayout A
    Ndroid:id= "@+id/cmx_toolbar_menu_turn_off" android:layout_width= "wrap_content" android:layout_height= "42DP" > <textview android:layout_width= "wrap_content" android:layout_height= "Wrap_content" Android:layout_g
      Ravity= "Center_vertical"Android:textsize= "16sp" android:textcolor= "#FF393F4A" android:paddingleft= "16DP" android:paddingright= "3
    2DP "android:clickable=" false "android:text=" menu Item #1 "/> </LinearLayout> <linearlayout 
    Android:id= "@+id/cmx_toolbar_menu_feedback" android:layout_width= "wrap_content" android:layout_height= "42DP" > <textview android:layout_width= "wrap_content" android:layout_height= "Wrap_content" android:layou T_gravity= "center_vertical" android:textsize= "16sp" android:textcolor= "#FF393F4A" android:paddingleft= "1 6DP "android:paddingright=" 32DP "android:clickable= false" android:text= "menu Item #2"/> </linea 
 Rlayout> </com.xinxin.arrowrectanglemenu.widget.ArrowRectangleView>

Vii. referencing the layout XML in code
This is not the same as referencing the normal layout XML, this is mainly in the creation of pop-up menus when you specify just the layout XML, specifically look at the sample code is clear.
At this point, a complete custom viewgroup process is gone, and later time may also write some of the more complex custom components, but same, the basic principles and steps are the same. This article is a good way to give some help to a friend who needs to customize ViewGroup.

SOURCE Download: Http://xiazai.jb51.net/201607/yuanma/ArrowRectangleMenu (jb51.net). rar

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.

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.