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="schemas.android/apk/res/android"
xmlns:tools="schemas.android/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="stapp.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"
index与match举例讲解android:text="Add View"/>
</LinearLayout >
界⾯的原始布局如图所⽰:
primary.png
2.现在我们编写Activity的代码,对控件进⾏初始化以及点击事件的设置,如下所⽰:
public class MainActivity extends AppCompatActivity {
private LinearLayout mContainer;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
mContainer = findViewById(ainer);
}
/**
* 按钮点击事件,向容器中添加TextView
* @param view
*/
public void addView(View view) {
TextView child = new TextView(this);
child.setTextSize(20);
child.setTextColor(getResources().lorAccent));
// 获取当前的时间并转换为时间戳格式, 并设置给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().lorAccent));
// 获取当前的时间并转换为时间戳格式, 并设置给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 > ChildCount()) { // 当index⼤于当前容器⼦View数量时,让他等于容器内⼦View的数量。
index = ChildCount();
}
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().lor.white));
child.setText("LayoutParams");
mContainer.addView(child);
}
//右边按钮点击事件
public void addBlack(View view) {
TextView child = new TextView(this);
child.setTextSize(25);
child.setTextColor(getResources().lor.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在⽇常开发中其实也很常见,因此有必要学习了解⼀下。以上,是本⼈在开发中总结下来的内容,希望可以帮助到⼤家。如果觉着不错还希望点个赞哈!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论