• Android动态添加View之addView的用法


     

     

    对于日常开发来说,一般我们都是在XML中创建想要的View,然后在代码中通过id来找到对应的View,对其进行相应的操作。但是,这样做有一个前提是,你需要事先知道View的确切位置,无论其是显示状态还是隐藏状态。那么问题来了,当我们有这样一个需求,我们在启动一个界面以后,在某一条件下需要再向Activity中添加一个View,而这个View的位置我们也是事先未知的,其坐标是某一随机值或者是相对于某一View而进行设置的,这个时候我们就要通过addView的方式动态向布局中添加View了。(ps:addView是ViewGroup中特有的方法,而单一的View是不存在该方法的)

    二、addView的使用

    1.方法的几种形式:

    addView(View child)   // child-被添加的View
    addView(View child, int index)   // index-被添加的View的索引
    addView(View child, int width, int height)   // width,height被添加的View指定的宽高
    addView(View view, ViewGroup.LayoutParams params)   // params被添加的View指定的布局参数
    addView(View child, int index, LayoutParams params) 
    

    2.在LinearLayout中的使用:

    这里我选择使用LinearLayout来举例是因为在线性布局中能更好的理解index这个参数的含义。大家都知道,LinearLayout中View的排列是按照指定的方向上线性排列的,子View的索引也是从零开始按照排列的顺序依次递增的。

    1.首先新建一个Activity并在布局中指定一个LinearLayout作为容器。布局文件如下:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.example.android.testapp.MainActivity">
    
        //添加View的容器
        <LinearLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="400dp"
            android:background="#ffa200"
            android:orientation="vertical">
    
            //事先存在的View
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:text="最初index为0"
                android:textColor="#ffffff"
                android:textSize="25sp" />
    
            //事先存在的View
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:text="最初index为1"
                android:textColor="#ffffff"
                android:textSize="25sp" />
    
        </LinearLayout>
    
        //点击按钮实现添加View
        <Button
            android:id="@+id/btn_add"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#ffff00"
            android:textAllCaps="false"
            android:text="Add View"/>
    
    </LinearLayout >
    

    界面的原始布局如图所示:

     
    primary.png
    2.现在我们编写Activity的代码,对控件进行初始化以及点击事件的设置,如下所示:
    public class MainActivity extends AppCompatActivity {
    
        private LinearLayout mContainer;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mContainer = findViewById(R.id.container);
        }
    
        /**
         * 按钮点击事件,向容器中添加TextView
         * @param view
         */
        public void addView(View view) {
            TextView child = new TextView(this);
            child.setTextSize(20);
            child.setTextColor(getResources().getColor(R.color.colorAccent));
            // 获取当前的时间并转换为时间戳格式, 并设置给TextView
            String currentTime = dateToStamp(System.currentTimeMillis());
            child.setText(currentTime);
            // 调用一个参数的addView方法
            mContainer.addView(child);
        }
    
        /**
         * 将时间戳转换为时间
         */
        public String dateToStamp(long s) {
            String res;
            try {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date date = new Date(s);
                res = simpleDateFormat.format(date);
            } catch (Exception e) {
                return "";
            }
            return res;
        }
    }
    

    现在,我们分别看一下点击一次按钮和点击三次按钮的效果图,如下所示:

     
    click.jpg
    3.addView(View child)方法的分析:

    由上述效果图我们可以初步分析得出结论,在线性布局中,我们调用addView(View child)方法时,会在指定的方向的最后一个View的下面添加上child这个View,也就是说被添加的View的索引总是等于容器中当前子View的个数。为了证实这一结论,我们只好看一下源码了,我们顺着方法的调用一路找到了addViewInner方法(下面只是复制了关键性的代码,可以自己去源码查看哈)。

    private void addViewInner(View child, int index, LayoutParams params,
                boolean preventRequestLayout) {
            // 这里当我们未传入index时,默认值为-1,因此在此index = mChildrenCount
            if (index < 0) {
                index = mChildrenCount;
            }
    
            addInArray(child, index);
        }
    

    现在我们可以肯定的说,此方法每次添加的View最终index(索引)都为未添加之前父布局中子view的总数,因此每次都是在最后一个View的后面添加child。

    4.addView(View child, int index)方法的分析:

    此方法相对于上面的方法多了一个index参数,也就是调用此方法时我们会给被添加的View指定一个索引。下面,我们来修改一下上面的代码:

    public void addView(View view) {
            TextView child = new TextView(this);
            child.setTextSize(20);
            child.setTextColor(getResources().getColor(R.color.colorAccent));
            // 获取当前的时间并转换为时间戳格式, 并设置给TextView
            String currentTime = dateToStamp(System.currentTimeMillis());
            child.setText(currentTime);
            // 调用两个参数的addView方法,指定索引为1
            mContainer.addView(child, 1);
        }
    

    我们运行程序,并同样看一下点击以此按钮和三次按钮的效果图:

     
    click.png

    效果一目了然吧,当我们为添加的View指定了index后,我们被添加的View就会被添加到容器中指定的索引位置处,并把之前的View(包括此View后面的View)全部向后“挤”了一位,没错,就是这么强势!
    那么细心人的人都会有一个疑问吧!这个index我们可不可以随意定义呢?答案当然是不可以了。凡事都要讲究一个顺序嘛,总不能原来容器中只有2个子View,最大的索引才是1,你就一下子想把添加的View指定到索引10吧。因此,在我们向指定索引的时候,我们应当先做一个判断,确保我们指定的index不能大于当前容器内View的总数量。代码可以如下:

    int index = new Random().nextInt();
    if (index > mContainer.getChildCount()) {  // 当index大于当前容器子View数量时,让他等于容器内子View的数量。
         index = mContainer.getChildCount();
    }
    mContainer.addView(child, index);
    

    可能还有人问了,怎么就不行了呢?难道会崩溃吗!那我也只能很负责任的告诉你,会的!迎接你的就是著名的数字越界异常!这里其实我个人觉着Google完全可以将这里设置一个容错处理,不需要开发者自行判断,以免有些时候真的马虎大意了造成一些不必要的损失。

    4.小结:

    LinearLayout中addView的使用就只介绍这两种方法,这里我指定线性布局的排列方向为垂直方向,当然指定为水平方向也是一样的效果,只是在添加View的方向上变为了水平方向的改变。在这里讲解调用这两个参数的方法主要是因为在LinearLayout中能更好的理解一些。下面看一下addView方法在RelativeLayout中的使用。

    3.在RelativeLayout中的使用:

    1.布局文件:

    首先修改布局文件,这里我将最初顶部的容器改为一个空的RelativeLayout。底下的按钮变成了两个,分别用于添加颜色不同的View。布局代码就不粘贴了,看一下改完的初始效果图:

     
    primary.jpg
    2.定义两个按钮的点击事件,代码如下:
       // 左边按钮点击事件
       public void addWhite(View view) {
            TextView child = new TextView(this);
            child.setTextSize(25);
            child.setTextColor(getResources().getColor(R.color.white));
            child.setText("LayoutParams");
            mContainer.addView(child);
        }
        //右边按钮点击事件
        public void addBlack(View view) {
            TextView child = new TextView(this);
            child.setTextSize(25);
            child.setTextColor(getResources().getColor(R.color.black));
            child.setText("LayoutParams");
            // 定义LayoutParam
            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            params.leftMargin = 100;
            // 调用带有LayoutParams参数的addView方法
            mContainer.addView(child, params);
        }
    

    运行程序,先点击一次左侧按钮,再点击一次右侧按钮,效果图如下(只截取了内如区域):

     
    leftright.png

    看到图中的黑色的TextView相对于父容器的左边产生了100像素的间距,说明指定的LayoutParams确实生效了。而此处我也只是运用了LayoutParams相对简单的使用方式,更多的关于LayoutParams的使用方式还请自行学习哈,不是本章的重点。这里只是为了说明,addView方法,可以为添加的View指定LayoutParams。
    而在这里,我选择设置LayoutParams的leftMargin属性,其实是想指出一个起初我纠结的问题。其实这是RelativeLayout和LinearLayout布局对子View排列逻辑的不同。当我们在RelayoutLayout中设置类似于Margin这样的属性时,它是相对于父容器而产生的作用。而当我们在LinearLayout中指定时,它则是相对于它上一个View产生的作用(可以自行验证一下,这里就不做证明了)。
    因此,其实更多时候我们想动态添加View的时候都是事先不知道它的具体位置,一般都是相对于外围容器指定位置的,而如果事先知道它与其他子View的关系时,也大可不必使用addView,直接在XML中定义好,想要用的时候置为可见就好了。

    3.index在RelativeLayout中有用吗?

    上面运行结果是我先点击左侧的按钮,后点击的后侧按钮。现在我们反过来,先点击右侧的按钮,再点击左侧的按钮,效果如下:

     
    rightleft.jpg

    不知道细心的同学们有没有看出两次效果的不同。第一张是黑色的字体在上面,而第二张是白色的字体在上面。那么根据此结果,我们其实可以理解在RelativeLayout中index的含义了,可以认为它指定了View在里面的层级。一个View的index越大,说明它越在上面。这一点在FrameLayout中是一样的!(注意,如果在使用addView时候想设置index,也要遵循上面说到的规则)

    4.小结:

    在RelativeLayout中使用addView方法就介绍这么多。现在,addView中不同的参数就已经都知道什么意义了,那么即使有的方法是混合使用它们的也应该会使用了。剩下一个是指定宽高的方法我就不介绍了,这个有点太通俗易懂了。
    另外,FrameLayout中的使用我就不再描述了,有了上面两种类型中的使用案例,相信大家能够自己知道如何在FrameLayout中使用它。

    三、总结:

    动态添加View在日常开发中其实也很常见,因此有必要学习了解一下。以上,是本人在开发中总结下来的内容,希望可以帮助到大家。如果觉着不错还希望点个赞哈!
  • 相关阅读:
    导弹拦截
    背包求方案的字典序
    分组背包
    关于字符串的简单dp
    dp进阶——饥饿的奶牛
    压缩维度oj P1173+P1174+P1164
    搜索——迭代加深
    委外倒冲领料
    QLIKVIEW-SALESORDERDELIVERYNOTICEOUTSTOCKINVOICE
    设置采购订单供应商权限设置
  • 原文地址:https://www.cnblogs.com/xgjblog/p/14048402.html
Copyright © 2020-2023  润新知