代码之家  ›  专栏  ›  技术社区  ›  nikib3ro

Android-带包装子级的LinearLayout水平

  •  79
  • nikib3ro  · 技术社区  · 14 年前

    Android的linearlayout是否需要设置一个属性,使其能够正确包装子控件?

    意思是-我的孩子数量是可变的,我想把他们横向排列如下:

    Example: Control1, Control2, Control3, ...
    

    我通过设置:

    ll.setOrientation(LinearLayout.HORIZONTAL);
    foreach (Child c in children)
      ll.addView(c);
    

    然而,如果我有很多孩子,最后一个孩子会被切断,而不是去下一行。

    你知道怎么解决这个问题吗?

    8 回复  |  直到 6 年前
        1
  •  42
  •   mysteryhobo    6 年前

    截至2016年5月,谷歌已经创建了自己的 FlexBoxLayout 这可以解决你的问题。

    您可以在这里找到Github回购: https://github.com/google/flexbox-layout

        2
  •  54
  •   Tunaki Vishal Singh    8 年前

    这应该是您想要的:

    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.View;
    import android.view.ViewGroup;
    
    /**
     *
     * @author RAW
     */
    public class FlowLayout extends ViewGroup {
    
        private int line_height;
    
        public static class LayoutParams extends ViewGroup.LayoutParams {
    
            public final int horizontal_spacing;
            public final int vertical_spacing;
    
            /**
             * @param horizontal_spacing Pixels between items, horizontally
             * @param vertical_spacing Pixels between items, vertically
             */
            public LayoutParams(int horizontal_spacing, int vertical_spacing) {
                super(0, 0);
                this.horizontal_spacing = horizontal_spacing;
                this.vertical_spacing = vertical_spacing;
            }
        }
    
        public FlowLayout(Context context) {
            super(context);
        }
    
        public FlowLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            assert (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED);
    
            final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
            int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
            final int count = getChildCount();
            int line_height = 0;
    
            int xpos = getPaddingLeft();
            int ypos = getPaddingTop();
    
            int childHeightMeasureSpec;
            if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
            } else {
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            }
    
    
            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec);
                    final int childw = child.getMeasuredWidth();
                    line_height = Math.max(line_height, child.getMeasuredHeight() + lp.vertical_spacing);
    
                    if (xpos + childw > width) {
                        xpos = getPaddingLeft();
                        ypos += line_height;
                    }
    
                    xpos += childw + lp.horizontal_spacing;
                }
            }
            this.line_height = line_height;
    
            if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
                height = ypos + line_height;
    
            } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
                if (ypos + line_height < height) {
                    height = ypos + line_height;
                }
            }
            setMeasuredDimension(width, height);
        }
    
        @Override
        protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
            return new LayoutParams(1, 1); // default of 1px spacing
        }
    
        @Override
        protected android.view.ViewGroup.LayoutParams generateLayoutParams(
            android.view.ViewGroup.LayoutParams p) {
            return new LayoutParams(1, 1, p);
        }
    
        @Override
        protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
            if (p instanceof LayoutParams) {
                return true;
            }
            return false;
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            final int count = getChildCount();
            final int width = r - l;
            int xpos = getPaddingLeft();
            int ypos = getPaddingTop();
    
            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    final int childw = child.getMeasuredWidth();
                    final int childh = child.getMeasuredHeight();
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    if (xpos + childw > width) {
                        xpos = getPaddingLeft();
                        ypos += line_height;
                    }
                    child.layout(xpos, ypos, xpos + childw, ypos + childh);
                    xpos += childw + lp.horizontal_spacing;
                }
            }
        }
    }
    

    和XML文件

    /* you must write your package name and class name */
    <org.android.FlowLayout
                    android:id="@+id/flow_layout"
                    android:layout_marginLeft="5dip"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"/>
    
        3
  •  42
  •   Gopal Singh Sirvi    9 年前

    对于任何需要这种行为的人:

    private void populateLinks(LinearLayout ll, ArrayList<Sample> collection, String header) {
    
        Display display = getWindowManager().getDefaultDisplay();
        int maxWidth = display.getWidth() - 10;
    
        if (collection.size() > 0) {
            LinearLayout llAlso = new LinearLayout(this);
            llAlso.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
                    LayoutParams.WRAP_CONTENT));
            llAlso.setOrientation(LinearLayout.HORIZONTAL);
    
            TextView txtSample = new TextView(this);
            txtSample.setText(header);
    
            llAlso.addView(txtSample);
            txtSample.measure(0, 0);
    
            int widthSoFar = txtSample.getMeasuredWidth();
            for (Sample samItem : collection) {
                TextView txtSamItem = new TextView(this, null,
                        android.R.attr.textColorLink);
                txtSamItem.setText(samItem.Sample);
                txtSamItem.setPadding(10, 0, 0, 0);
                txtSamItem.setTag(samItem);
                txtSamItem.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        TextView self = (TextView) v;
                        Sample ds = (Sample) self.getTag();
    
                        Intent myIntent = new Intent();
                        myIntent.putExtra("link_info", ds.Sample);
                        setResult("link_clicked", myIntent);
                        finish();
                    }
                });
    
                txtSamItem.measure(0, 0);
                widthSoFar += txtSamItem.getMeasuredWidth();
    
                if (widthSoFar >= maxWidth) {
                    ll.addView(llAlso);
    
                    llAlso = new LinearLayout(this);
                    llAlso.setLayoutParams(new LayoutParams(
                            LayoutParams.FILL_PARENT,
                            LayoutParams.WRAP_CONTENT));
                    llAlso.setOrientation(LinearLayout.HORIZONTAL);
    
                    llAlso.addView(txtSamItem);
                    widthSoFar = txtSamItem.getMeasuredWidth();
                } else {
                    llAlso.addView(txtSamItem);
                }
            }
    
            ll.addView(llAlso);
        }
    }
    
        4
  •  10
  •   kito    9 年前

    旧问题,但如果有人最终来到这里,两个库会做到这一点:

    https://github.com/blazsolar/FlowLayout

    https://github.com/ApmeM/android-flowlayout

        5
  •  1
  •   lannyf    10 年前

    寻找类似但更简单的问题的解决方案,即在水平布局中包装子文本内容。 Kape123的解决方案工作正常。但是使用ClickableSpan为这个问题找到一个更简单的方法。 也许它对一些简单的案例有用。 片段:

            String[] stringSource = new String[sourceList.size()];
            for (int i = 0; c < sourceList.size(); i++) {
                String text = sourceList.get(i);
                stringSource[i] = text;
            }
    
            SpannableString totalContent = new SpannableString(TextUtils.join(",", stringSource));
            int start = 0;
            for (int j = 0; j < stringSource.length(); j++) {
                final String text = stringSource[j];
                ClickableSpan span = new ClickableSpan() {
    
            @Override
                    public void updateDrawState(TextPaint ds) {
                        ds.setUnderlineText(true);
                        ds.setColor(getResources().getColor(R.color.green));
                    }
                    @Override
                    public void onClick(View widget) {
                        // the text clicked
                    }
                };
        int end = (start += text.length());
                totalContent.setSpan(span, start, end, 0);
                star = end + 1;
            }
    
            TextView wrapperView = (TextView) findViewById(horizontal_container_id);
            wrapperView.setMovementMethod(LinkMovementMethod.getInstance());
    
            wrapperView.setText(totalContent, BufferType.SPANNABLE);
        }
    
        6
  •  0
  •   M.Ali El-Sayed    12 年前
    //this method will add image view to liner grid and warp it if no space in new child LinearLayout grid 
    private void addImageToLinyerLayout(LinearLayout ll , ImageView v)
    {
        //set the padding and margin and weight 
        v.setPadding(5, 5, 5, 5);
    
        Display display = getWindowManager().getDefaultDisplay();
        int maxWidth = display.getWidth() - 10;
        int maxChildeNum = (int) ( maxWidth / (110)) ; 
        Toast.makeText(getBaseContext(), "c" + v.getWidth() ,
                Toast.LENGTH_LONG).show();
        //loop through all child of the LinearLayout
        for (int i = 0; i < ll.getChildCount(); i++) {
            View chidv = ll.getChildAt(i);
            Class c = chidv.getClass();
            if (c == LinearLayout.class) {
                //here we are in the child lay out check to add the imageView if there is space 
                //Available else we will add it to new linear layout 
                LinearLayout chidvL = (LinearLayout)chidv; 
                if(chidvL.getChildCount() < maxChildeNum)
                {
                    chidvL.addView(v);
                    return;
                }
            } else{
               continue;
            } 
        }
    
        //if you reached here this means there was no roam for adding view so we will 
        //add new linear layout 
        LinearLayout childLinyer = new LinearLayout(this);
        childLinyer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
                LayoutParams.WRAP_CONTENT));
    
        childLinyer.setOrientation(LinearLayout.HORIZONTAL);
        ll.addView(childLinyer);
        childLinyer.addView(v);
    
    }
    

    上面的方法将在布局中并排添加imgeview。

      <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" 
        android:id="@+id/imageslayout"
        ></LinearLayout>
    

    我发布了这个解决方案,希望它能帮助一些人,节省一些时间,并在我的应用程序中使用它

        8
  •  0
  •   janosch    6 年前

    代码的修改版本 Randy Sugianto 'Yuku's answer 我最终选择了:

    import android.content.Context
    import android.util.AttributeSet
    import android.view.View
    import android.view.View.MeasureSpec.*
    import android.view.ViewGroup
    import androidx.core.content.withStyledAttributes
    import androidx.core.view.children
    import *.*.*.R
    
    
    class FlowLayout(context: Context, attributeSet: AttributeSet) : ViewGroup(context, attributeSet) {
    
        private var lineHeight: Int = 0
    
        private var horizontalSpacing = 0F
        private var verticalSpacing = 0F
    
        init {
            context.withStyledAttributes(attributeSet, R.styleable.FlowLayout) {
                horizontalSpacing = getDimension(R.styleable.FlowLayout_horizontalSpacing, 0F)
                verticalSpacing = getDimension(R.styleable.FlowLayout_verticalSpacing, 0F)
            }
        }
    
        override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    
            val width = getSize(widthMeasureSpec) - paddingLeft - paddingRight
            var height = getSize(heightMeasureSpec) - paddingTop - paddingBottom
    
            var xPosition = paddingLeft
            var yPosition = paddingTop
    
            val childHeightMeasureSpec = makeMeasureSpec(
                height, if (getMode(heightMeasureSpec) == AT_MOST) AT_MOST else UNSPECIFIED
            )
    
            children.forEach { child ->
                if (child.visibility != GONE) {
                    val layoutParams = child.layoutParams as LayoutParamsWithSpacing
                    child.measure(makeMeasureSpec(width, AT_MOST), childHeightMeasureSpec)
                    val childWidth = child.measuredWidth
                    lineHeight =
                            Math.max(lineHeight, child.measuredHeight + layoutParams.verticalSpacing)
    
                    if (xPosition + childWidth > width) {
                        xPosition = paddingLeft
                        yPosition += lineHeight
                    }
    
                    xPosition += childWidth + layoutParams.horizontalSpacing
                }
            }
    
            if (getMode(heightMeasureSpec) == UNSPECIFIED ||
                getMode(heightMeasureSpec) == AT_MOST && yPosition + lineHeight < height
            ) {
                height = yPosition + lineHeight
            }
    
            setMeasuredDimension(width, height)
        }
    
        override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
            val width = right - left
            var xPosition = paddingLeft
            var yPosition = paddingTop
    
            children.forEach { child ->
                if (child.visibility != View.GONE) {
                    val layoutParams = child.layoutParams as LayoutParamsWithSpacing
                    val childWidth = child.measuredWidth
                    if (xPosition + childWidth > width) {
                        xPosition = paddingLeft
                        yPosition += lineHeight
                    }
                    child.layout(
                        xPosition, yPosition, xPosition + childWidth,
                        yPosition + child.measuredHeight
                    )
                    xPosition += layoutParams.horizontalSpacing
                    xPosition += childWidth
                }
            }
        }
    
        override fun generateDefaultLayoutParams(): ViewGroup.LayoutParams =
            LayoutParamsWithSpacing(1, 1)
    
        override fun generateLayoutParams(layoutParams: LayoutParams) =
            LayoutParamsWithSpacing(horizontalSpacing.toInt(), verticalSpacing.toInt())
    
        override fun checkLayoutParams(layoutParams: LayoutParams) =
            layoutParams is LayoutParamsWithSpacing
    
        class LayoutParamsWithSpacing(val horizontalSpacing: Int, val verticalSpacing: Int) :
            ViewGroup.LayoutParams(0, 0)
    }
    

    在style/attrs.xml文件中:

    <resources>
        <declare-styleable name="FlowLayout">
            <attr name="horizontalSpacing" format="dimension" />
            <attr name="verticalSpacing" format="dimension" />
        </declare-styleable>
    </resources>
    

    用途:

    <*.*.*.*.FlowLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:horizontalSpacing="8dp"
        app:verticalSpacing="8dp">
    
        <!-- ... -->
    
    </*.*.*.*.FlowLayout>