"Android" Custom FlowLayout, supports multiple layout optimizations--android-flowlayout

Source: Internet
Author: User

Objective

Flow layout, streaming layouts, is common in mobile or front-end development, especially in multi-label displays, and often plays a key role. Android, however, does not provide such a layout for developers, so many developers have done it themselves, and many custom FlowLayout have appeared on GitHub. Recently, I also realized such a flowlayout, I feel may be the best use of the current FlowLayout (cover face), here to do share.
Project Address: Https://github.com/lankton/android-flowlayout

Show


The first diagram shows the constant addition of sub-view to the FlowLayout
The second picture shows the compressed sub-view so that they can make the best use of space
The third picture shows the spacing between the sub-view to align the rows to the left and right

Use the # Basic streaming layout feature

Use FlowLayout in the layout file:

<cn.lankton.flowlayout.FlowLayout        android:id="@+id/flowlayout"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:padding="10dp"        app:lineSpacing="10dp"        android:background="#F0F0F0"></cn.lankton.flowlayout.FlowLayout>

As you can see, a custom parameter, linespacing, is provided to control the spacing between rows and rows.

Compression
flowLayout.relayoutToCompress();

Compression is done by re-ordering the pair view, which makes it more reasonable to squeeze space, which is explained in detail later.

Snap To
flowLayout.relayoutToAlign();

Alignment does not change the layout of the child view, nor does it achieve compression.

Implementing the implementation of streaming layout overriding the Generatelayoutparams method
@Override   protectedgenerateLayoutParams(LayoutParams p) {       returnnew MarginLayoutParams(p);   }   @Override   publicgenerateLayoutParams(AttributeSet attrs)   {       returnnew MarginLayoutParams(getContext(), attrs);   }

It is necessary to override the 2 overloads of this method. The layoutparams of this element is Marginlayoutparam, which contains the margin attribute, which is exactly what we need.

Rewrite onmeasure

There are 2 main purposes, the first is to measure the width and height of each child element, and the second is to set the measured value of the flowlayout according to the measured value of the child element.

@Override    protected void onmeasure(intWidthmeasurespec,intHEIGHTMEASURESPEC) {intMpaddingleft = Getpaddingleft ();intMpaddingright = Getpaddingright ();intMpaddingtop = Getpaddingtop ();intMpaddingbottom = Getpaddingbottom ();intWidthsize = Measurespec.getsize (Widthmeasurespec);intHeightmode = Measurespec.getmode (Heightmeasurespec);intHeightsize = Measurespec.getsize (Heightmeasurespec);intlineused = Mpaddingleft + mpaddingright;intLiney = Mpaddingtop;intLineheight =0; for(inti =0; I < This. Getchildcount (); i++) {View child = This. Getchildat (i);if(child.getvisibility () = = GONE) {Continue; } measurechildwithmargins (Child, Widthmeasurespec,0, Heightmeasurespec, Liney); Marginlayoutparams MLP = (marginlayoutparams) child.getlayoutparams ();intChildwidth = Child.getmeasuredwidth ();intChildheight = Child.getmeasuredheight ();intSpacewidth = mlp.leftmargin + childwidth + mlp.rightmargin;intSpaceheight = mlp.topmargin + childheight + mlp.bottommargin;if(lineused + spacewidth > Widthsize) {//approach the limit of width and move to next lineLiney + = lineheight + linespacing;                lineused = Mpaddingleft + mpaddingright; Lineheight =0; }if(Spaceheight > Lineheight)            {lineheight = Spaceheight;        } lineused + = Spacewidth; } setmeasureddimension (widthsize, Heightmode = = measurespec.exactly? heightsize:l    Iney + lineheight + mpaddingbottom); }

The code logic is simple, that is, traversing the child elements, calculating the cumulative length, more than one row can hold the width, will accumulate the length of 0, and assume that continue to place a row of child elements. Why is it hypothetical, because the process of actually placing child elements in FlowLayout is in the OnLayout method.
Focus on the last Setmeasureddimension method. In the daily use of FlowLayout, our width is often fixed value, or match_parent, do not need to change according to the content, so the width of the value directly with the widthsize, that is, from the measured values passed in the width obtained.
Height according to Measurespec mode to judge, exactly means and width, directly with the width of the measured value can be, otherwise, it is wrap_content, need to use the height of the sub-element arrangement to judge.

Rewrite onlayout
@Override protected void OnLayout(BooleanChangedintLintTintRintb) {intMpaddingleft = Getpaddingleft ();intMpaddingright = Getpaddingright ();intMpaddingtop = Getpaddingtop ();intLineX = Mpaddingleft;intLiney = Mpaddingtop;intLineWidth = r-l; Usefulwidth = Linewidth-mpaddingleft-mpaddingright;intlineused = Mpaddingleft + mpaddingright;intLineheight =0; for(inti =0; I < This. Getchildcount (); i++) {View child = This. Getchildat (i);if(child.getvisibility () = = GONE) {Continue; } marginlayoutparams MLP = (marginlayoutparams) child.getlayoutparams ();intChildwidth = Child.getmeasuredwidth ();intChildheight = Child.getmeasuredheight ();intSpacewidth = mlp.leftmargin + childwidth + mlp.rightmargin;intSpaceheight = mlp.topmargin + childheight + mlp.bottommargin;if(lineused + spacewidth > LineWidth) {//approach the limit of width and move to next lineLiney + = lineheight + linespacing;             lineused = Mpaddingleft + mpaddingright;             LineX = Mpaddingleft; Lineheight =0; } child.layout (LineX + mlp.leftmargin, Liney + mlp.topmargin, LineX + mlp.leftmargin + childwidth, Liney + mlp.top Margin + childheight);if(Spaceheight > Lineheight)         {lineheight = Spaceheight;         } lineused + = Spacewidth;     LineX + = Spacewidth; } }

This code is also very well understood, to determine the child element, whether to continue to place in the bank, or the need for a newline placement. This step and onmeasure, basically all the flowlayout will be rewritten, my nature is nothing special, these two pieces are not highlighted. The following highlights the implementation of 2 layout optimizations.

Implementation of compression

On how to achieve compression, this problem really started to make me a headache. Because there's only a general idea in my mind, which is what kind of effect compression should be, and this vague concept is hard to translate into concrete mathematical models. Without a mathematical model, it is impossible to solve this problem with code, and I would like to return to the university's re-learning algorithm. But one idea is clear, and that is to solve the problem, which is actually the reordering of the child elements.
Later decided to simplify the idea, with a similar greedy algorithm thinking to solve the problem, that is: line-by-row solution, each line to maximize the full.
1. Starting from the first line, from the child element collection, select a part, so that this part of the child elements can occupy the maximum extent of this line;
2. Take this part of the selected from the collection and continue to the first step on the next line.
This idea has been established, how do we choose a subset from the set to occupy the largest part of a row?
The conditions we know:
1. Collection of child elements
2. Each row can hold the width
3. Width of each child element
This time, the mind is thinking of 01 knapsack problem:
Known
1. Collection of items
2. Total backpack capacity
3. Value of each item
4. The volume of each item
Find the maximum value of a backpack containing items (and its solution
Some friends may have doubts, they do look alike, but aren't there a condition? Well, yes. But in the current situation, because we want to fill a line as much as possible, then the width of each child element is not only limited, but also the value.
In this way, the situation is completely consistent with the 01 knapsack problem. After that, you can solve the problem with dynamic planning . about how to use dynamic planning to solve the 01 knapsack problem, in fact, I also forget the almost, but also on the internet to look up information, while reviewing, one side to achieve. So I'm not going to introduce myself here, nor do I post my own code (interested in GitHub to see it) and put a link. I feel this link in the explanation of my review of relevant knowledge points to help a lot, interested can also see ~
Knapsack problem--"01 backpack" explanation and implementation (including the solution of the specific items in the backpack)

Snap To

This feature, I first saw on Bilibili's ipad client, is as follows.

I thought it was pretty, and I thought about it, but I didn't think about it. This implementation of the FlowLayout, it is handy to use this alignment style with their own ideas to achieve a bit.

 Public void relayouttoalign() {intChildCount = This. Getchildcount ();if(0= = ChildCount) {//no need to sort if flowlayout have no child view        return; }intCount =0; for(inti =0; i < ChildCount; i++) {View v = getchildat (i);if(VinstanceofBlankview) {//blankview is just to make childs look in alignment, we should ignore them when we relayout            Continue;    } count++; } view[] Childs =NewView[count];int[] spaces =New int[Count];intn =0; for(inti =0; i < ChildCount; i++) {View v = getchildat (i);if(VinstanceofBlankview) {//blankview is just to make childs look in alignment, we should ignore them when we relayout            Continue;        } Childs[n] = V; Marginlayoutparams MLP = (marginlayoutparams) v.getlayoutparams ();intChildwidth = V.getmeasuredwidth ();        Spaces[n] = mlp.leftmargin + childwidth + mlp.rightmargin;    n++; }intLineTotal =0;intStart =0; This. Removeallviews (); for(inti =0; I < count; i++) {if(LineTotal + spaces[i] > Usefulwidth) {intBlankwidth = Usefulwidth-linetotal;intend = i-1;intBlankcount = End-start;if(Blankcount >0) {intEachblankwidth = Blankwidth/blankcount; Marginlayoutparams LP =NewMarginlayoutparams (Eachblankwidth,0); for(intj = start; J < End; J + +) { This. AddView (Childs[j]); Blankview blank =NewBlankview (Mcontext); This. AddView (blank, LP); } This. AddView (Childs[end]);                start = i;                I--; LineTotal =0; }        }Else{lineTotal + = Spaces[i]; }    } for(inti = start; I < count; i++) { This. AddView (Childs[i]); }}

The code is long, but it's easy to say. Get a list of child elements, starting from scratch, to determine which child elements are on the same line. That is, start to end every time. Then calculate how many of these sub-elements fill a line, set to D. The spacing required between each of the two child elements is D/(End-start). If we set the spacing, we must first of all be unable to change the nature of the child elements themselves. Then, you can only fill in the middle of two sub-elements with a width of D/(End-start) Blankview.
As to what this blankview is, it is defined as follows:

class BlankView extends View {    publicBlankView(Context context) {        super(context);    }}

You see, you didn't do anything at all. So what's the point of writing a new class to inherit view? In fact, from the upper alignment of the code can also be seen, so that when we traverse flowlayout sub-elements, we can be instance of the Blankview to determine whether the real need to process, calculate the sub-elements, or we later add the complement view .

Summarize

The code is not all posted, because all the code is on GitHub ~ Here's The project address:
Https://github.com/lankton/android-flowlayout

This project, there must be a lot of places to optimize, you are welcome to make a variety of comments or suggestions, but also look forward to being used by everyone.
Thank you.

"Android" Custom FlowLayout, supports multiple layout optimizations--android-flowlayout

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.