Android 中动态的向布局中添加控件





先看一下效果图:




注: 这里使用的是一个自定义的布局文件,你可以向这个布局文件中添加任何控件,它也会动态的依据控件 的大小,动态的排列控件的分布

注:这里使用到的更新界面的方法:http://blog.csdn.net/zl18603543572/article/details/49915701


java代码中的实现过程:


import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TextView;

public class MainActivity extends Activity {

    private ScrollView mScroller;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);
        mScroller = new ScrollView(this);
        /**
         * 这里使用的是自定义的一个布局文件
         */

        final FlowLayout flowLayout = new FlowLayout(this);

        /**
         * 这里设置的值是为方便而这样写出的,这里只演示方法
         */
        //设置四周的padding值
        flowLayout.setPadding(10, 10, 10, 10);
        //设置水平间距
        flowLayout.setHorizontalSpacing(20);
        //设置垂直间距
        flowLayout.setVerticalSpacing(20);


        Button button = new Button(this);

        button.setWidth(getWindowManager().getDefaultDisplay().getWidth());
        button.setText("addView");

        flowLayout.addView(button);
        addChildView(flowLayout);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /**
                 * 点击按钮 动态的向布局文件中添加一个textview
                 */
                TextView textView = new TextView(MainActivity.this);
                textView.setText("addviewones");
                textView.setTextColor(Color.RED);
                textView.setTextSize(20);
                /**
                 * 添加到布局文件中去
                 */
                flowLayout.addView(textView);
                /**
                 * 更新界面
                 */
                flowLayout.invalidate();

            }
        });
    }

    /**
     * 设置界面的方法
     * @param flowLayout
     */
    public void addChildView(View flowLayout){
        mScroller.addView(flowLayout);
        setContentView(mScroller);


    }

}



自定义的布局文件


import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;

public class FlowLayout extends ViewGroup {
    private final int DEFAULT_SPACING = 10;
    private int horizontalSpacing = DEFAULT_SPACING;//水平间距
    private int verticalSpacing = DEFAULT_SPACING;//行与行之间的垂直间距

    //用来保存所有的行对象
    private ArrayList<Line> lineList = new ArrayList<Line>();
    public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FlowLayout(Context context) {
        super(context);
    }





    /**
     * 在onMeasure中完成分行的操作,就是排座位表;
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        lineList.clear();
        //1.获取FlowLayout的宽度
        int width = MeasureSpec.getSize(widthMeasureSpec);
        //2.计算用于实际比较的宽度,就是width减去左右的padding值
        int noPaddingWidth = width - getPaddingLeft()- getPaddingRight();

        //3.遍历所有的子TextView,进行分行操作
        Line line = new Line();//只要不换行,始终都是同一个Line对象
        for (int i = 0; i<getChildCount(); i++) {
            View childView = getChildAt(i);//获取子TextView
            childView.measure(0,0);//引起view的onMeasure方法回调,从而保证后面的方法能够有值

            //4.如果当前line中 没有TextView,则直接放入当前Line中
            if(line.getViewList().size()==0){
                line.addLineView(childView);
            }else if(line.getWidth()+horizontalSpacing+childView.getMeasuredWidth()>noPaddingWidth) {
                //5.如果当前line的宽+水平间距+childView的宽大于noPaddingWidth,则换行
                lineList.add(line);//先保存之前的line对象

                line = new Line();//重新创建Line
                line.addLineView(childView);//将chidlView放入新的Line
            }else {
                //6.如果小于noPaddingWidth,则将childView放入当前Line中
                line.addLineView(childView);
            }

            //7.如果当前childView是最后一个,那么就会造成最后的一个Line对象丢失,
            if(i==(getChildCount()-1)){
                lineList.add(line);//保存最后的line对象
            }
        }

        //for循环结束后,lineList就存放了所有的Line对象,而每个line中有记录自己的所有TextView
        //为了能够垂直的摆放所有的Line的TextView,所以要给当前FlowLayout设置对应的宽高,
        //计算所需要的高度:上下的padding + 所有line的高度   + 所有line之间的垂直间距
        int height = getPaddingTop()+getPaddingBottom();
        for (int i = 0; i < lineList.size(); i++) {
            height += lineList.get(i).getHeight();
        }
        height += (lineList.size()-1)*verticalSpacing;

        setMeasuredDimension(width,height);//向父View申请对应的宽高
    }

    /**
     * 摆放操作,让所有的子TextView摆放到指定的位置上面
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        for (int i = 0; i < lineList.size(); i++) {
            Line line = lineList.get(i);//获取line对象

            //从第二行开始,他们的top总是比上一行多一个行高+垂直间距
            if(i>0){
                paddingTop += lineList.get(i-1).getHeight()+verticalSpacing;
            }
            ArrayList<View> viewList = line.getViewList();//获取line所有的TextView
            //1.计算出当前line的留白区域的值
            int remainSpacing = getLineRemainSpacing(line);
            //2.计算每个TextView分到多少留白
            float perSpacing = remainSpacing/viewList.size();

            for (int j = 0; j < viewList.size(); j++) {
                View childView = viewList.get(j);//获取每个TextView
                //3.将perSpacing增加到每个TextView的宽度上
                int widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (childView.getMeasuredWidth()+perSpacing),MeasureSpec.EXACTLY);
                childView.measure(widthMeasureSpec,0);

                if(j==0){
                    //摆放每行的第一个TextView
                    childView.layout(paddingLeft,paddingTop,paddingLeft+childView.getMeasuredWidth()
                            ,paddingTop+childView.getMeasuredHeight());
                }else {
                    //摆放后面的TextView,需要参照前一个View
                    View preView = viewList.get(j-1);
                    int left = preView.getRight()+horizontalSpacing;
                    childView.layout(left,preView.getTop(),left+childView.getMeasuredWidth(),
                            preView.getBottom());
                }
            }
        }
    }

    /**
     * 获取line的留白区域
     * @param line
     * @return
     */
    private int getLineRemainSpacing(Line line){
        return getMeasuredWidth()-getPaddingLeft()-getPaddingRight()-line.getWidth();
    }

    /**
     * 定义行对象,用来封装每行的所有TextView,以及宽和高
     * @author Administrator
     *
     */
    class Line{
    //用来记录当前行的所有TextView
        private ArrayList<View> viewList = new ArrayList<View>();
        //表示当前行所有TextView的宽,还有他们之间的水平间距
        private int width;
        //当前行的高度
        private int height;

        /**
         * 添加一个TextView到viewList中
         * @param lineView
         */
        public void addLineView(View lineView){
            if(!viewList.contains(lineView)){
                viewList.add(lineView);

                //更新width
                if(viewList.size()==1){
                    //如果是第一个TextView,那么width就是lineView的宽度
                    width = lineView.getMeasuredWidth();
                }else {
                    //如果不是第一个,则要在当前width的基础上+水平间距+lineView的宽度
                    width += horizontalSpacing + lineView.getMeasuredWidth();
                }
                //更新height,在此所有的TextView的高度都是一样的
                height = Math.max(height,lineView.getMeasuredHeight());
            }
        }

        /**
         * 获取当前Line中的所有TextView
         * @return
         */
        public ArrayList<View> getViewList() {
            return viewList;
        }
        /**
         * 获取当前Line的宽度
         * @return
         */
        public int getWidth() {
            return width;
        }
        /**
         * 获取当前Line的高度
         * @return
         */
        public int getHeight() {
            return height;
        }

    }






    /**
     * 设置行与行之间的垂直间距
     * @param
     */
    public void setVerticalSpacing(int verticalSpacing){
        if(verticalSpacing>0){
            this.verticalSpacing = verticalSpacing;
        }
    }
    /**
     * 设置子View直接的水平间距
     * @param horizontalSpacing
     */
    public void setHorizontalSpacing(int horizontalSpacing){
        if(horizontalSpacing>0){
            this.horizontalSpacing = horizontalSpacing;
        }
    }




}



注:在使用的时候 ,可以直接复制本代码到项目中即可



FlowLayoutPanel 一些应用程序需要一个布局可随窗体大小的调整或其内容大小的改变而自动进行适当排列的窗体。在需要动态布局并且不希望在代码显式处理 Layout 事件时,可考虑使用布局面板。 FlowLayoutPanel是.NET Framework的新增控件。顾名思义,面板可以采用Web窗体的方式给Windows窗体布局。FlowLayoutPanel是一个容器,允许以垂直或水平的方式放置包含的控件。除了放置控件之外,还可以剪辑控件。放置的方向使用FlowDirection属性和FlowDirection枚举来设置。WrapContents属性确定在重新设置窗体的大小时,控件是放在下一行、下一列,还是剪辑控件。 FlowLayoutPanel 按特定的流方向排列其内容:水平或垂直。其内容可从一行换到下一行,或者从一列换到下一列。另一种情况是不换行,而是将其内容截掉。 相信大家在做WinForm项目的时候,要对大量的控件进行排序(位置摆放),这个容器肯定最受欢迎,但很遗憾的是,此容器本身虽支持Dock和Anchor属性,但不支持放入此容器内的控件的Dock和Anchor属性(自动调整宽度),也就说,但窗体伸缩,FlowLayoutPanel容器自身可以缩放,但是里面的控件就没那么幸运了,不支持自动缩放,这样就必须写方法来触发新的事件来调整控件的大小,这样就会导致窗体的闪烁(重绘)。 借助ManagedSpy工具,我们可以看到此容器里面的器件的结构,我们可以在Form1里面添加一个事件SizeChanged 对容器里面每个器件重新给它大小 就行了。 附件:FlowLayoutPanel的Demo
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

早起的年轻人

创作源于分享

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值