Android动态添加View方法总结

本文总结了在Android开发中动态添加View的方法,包括addView的不同重载方法,参数解读,以及如何删除已添加的View。还探讨了setContentView()与addContentView()的区别,并介绍了如何向当前界面动态添加View,涉及DecorView、ContentView的获取。

一.问题

        在写学生签到页面时需要根据签到的状态和方式不同展示不同的页面,如果把他们都写在一个页面那么页面的布局就会很臃肿,这时想到了可不可以进行动态添加View,在查阅了一些博客之后学习了一些添加方法,在这里做个总结。
        

二.addView的初步了解

addView是ViewGroup中的方法,官方文档,它有如下五个重载方法

方法解释
addView (View child)添加子视图。如果尚未在子级上设置任何布局参数,则在子级上为此ViewGroup设置默认参数。
addView (View child, int width, int height)使用指定的宽度和高度添加子视图。
addView (View child, ViewGroup.LayoutParams params)添加具有指定布局参数的子视图。
addView (View child, int index)指定添加子项的位置添加子视图
addView (View child, int index,ViewGroup.LayoutParams params)设置的布局参数和子项的位置添加子视图
addView (View child) 和 addView (View child, int index)学习
  • xml布局

在布局中嵌套一个LinearLayout准备在这个LinearLayout中添加布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <!--
    添加view的容器
    -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/linearLayout"
        app:layout_constraintTop_toTopOf="parent">
        <!--第一个子布局-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
  • 通过addView(View child)添加布局
		//第一步,通过id找到要添加控件或布局的view
		LinearLayout linearLayout = findViewById(R.id.linearLayout);
        //第二步,得到要添加的子view(这里以Button为例)
        AppCompatButton button = new AppCompatButton(this);
        button.setText("这是子View");
        //第三步:添加
        linearLayout.addView(button);

前后布局对比

addView之前
addView之后
        可以发现我们添加的view放在布局文件中第一个子布局之后,由此我们可以得出调用addView(View child)方法时,会在指定的方向的最后一个View的下面添加上child这个View
  • 通过addView(View child,int index)添加布局
		//第一步,通过id找到要添加控件或布局的view
        LinearLayout linearLayout = findViewById(R.id.linearLayout);
        //第二步,得到要添加的子view(这里以Button为例)
        AppCompatButton button = new AppCompatButton(this);
        button.setText("这是子View");
        //第三步:添加
        linearLayout.addView(button,0);

前后布局对比

addView之前
addView之后

        可以发现我们添加的view放在布局文件中第一个子布局的位置,而第一个子布局向后移动,由此我们可以得出调用addView(View child,int index)时,如果index合理,布局会放在我们指定的index位置,其他布局随之改动
注意:index要合理,且从0开始

  • 部分源码解读
//如果调用addView(View child)相当于调用addView(child, -1);
public void addView(View child) {
        addView(child, -1);
    }
//继续查看发现当index为-1时,会默认让index为子布局的个数
if (index < 0) {
            index = mChildrenCount;
        }
        addInArray(child, index);
private void addInArray(View child, int index) {
		//这个children数组里维护的就是我们加入这个ViewGroup中的布局
        View[] children = mChildren;
        final int count = mChildrenCount;
        final int size = children.length;
        //如果是index等于mChildrenCount(即在最后插入)
        if (index == count) {
            if (size == count) {
            //扩展View[]数组的长度
                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
                System.arraycopy(children, 0, mChildren, 0, size);
                children = mChildren;
            }
            //把我们新加入的view添加进children数组中,并让mChildrenCount计数器加一,即子布局的个数加1
            children[mChildrenCount++] = child;
        } else if (index < count) {		 //这种情况就是指定了合理的index,把布局插入到指定位置
            if (size == count) {
                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
                System.arraycopy(children, 0, mChildren, 0, index);
                System.arraycopy(children, index, mChildren, index + 1, count - index);
                children = mChildren;
            } else {
            //把children数组中从index下标开始的统一后移一位
                System.arraycopy(children, index, children, index + 1, count - index);
            }
            //把要添加的view添加到index对应的位置
            children[index] = child;
            mChildrenCount++;
            if (mLastTouchDownIndex >= index) {
                mLastTouchDownIndex++;
            }
        } else {
       	//如果传入的index大于子布局的个数,抛出异常	
            throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
        }
    }

基于上面源码的分析,可以发现

  1. 调用addView(View child)就是调用addView(child, -1);
  2. 如果index小于0,(-1,-2……),都是默认在最后添加(在LinearLayout中)
  3. 如果index的值大于当前子布局的个数,会抛出异常
addView (View child, ViewGroup.LayoutParams params学习

        
        ViewGroup.LayoutParams用于告诉告诉父容器的一些放入规则和方式,这时候该view的LayoutParams要与父容器的LayoutParam相互对应,比如该view的父容器使用的LinearLayout.LayoutParam,该view的布局类型也要对应着LinearLayout.LayoutParam,不然的话虽然不会报错,但是指定的位置将不起作用。
        

  • 布局还是上面的布局,修改代码如下:
//第一步,通过id找到要添加控件或布局的view
 LinearLayout linearLayout = findViewById(R.id.linearLayout);
 linearLayout.setOrientation(LinearLayout.VERTICAL);     //为了效果更明显,设置布局为纵向
 //第二步,得到要添加的子view(这里以Button为例)
 AppCompatButton button = new AppCompatButton(this);
 button.setText("这是子View");
//第三步:设置LayoutParams
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.
                WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
                
layoutParams.gravity = Gravity.CENTER_HORIZONTAL;		//水平居中
layoutParams.topMargin = 500;							//距离上边500px
//第四步,添加布局
linearLayout.addView(button,layoutParams);        

前后布局对比

addView之前
addView之后
可以发现新添加的子布局达到了我们需要的效果,这里主要用到的就是LinearLayout.LayoutParam

        

  • 一个注意点

        当我把LinearLayout 对应的布局方式改为VERTICAL时,发现addView(View child)方法添加的布局宽都为MATCH_PARENT

这里的原因是LinearLayout重写了ViewGroup中的generateDefaultLayoutParams()方法

 @Override
    protected LayoutParams generateDefaultLayoutParams() {
        if (mOrientation == HORIZONTAL) {
            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        } else if (mOrientation == VERTICAL) {
            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        }
        return null;
    }            

        
其他的两个方法与这些方法类型,没啥好说的。
        

三.删除addView加入的view

一个View 只能有一个父类的ViewGroup,可以得到这个ViewGroup来删除view
代码只有一句:

((ViewGroup) View.getParent()).removeView(View);

四.setContentView()和addContentView的学习

问题:如何向当前界面动态添加view

基于上面addView的学习,我们很容易想到拿到当前界面的实例,调用addView方法,如何拿到当前界面的实例呢?

一些知识点

        DecorView 是android 界面的顶级View ,当前界面的整个即为DecorView。DecorView为FrameLayout,而DecorView 一般会包含一个竖直方向的LinearLayout。这个竖直方向的LinearLayout 一般分为两个部分(具体Android版本和主题有所不同),上部分为标题栏,下部分为内容栏,而内容栏的id 为 android.R.id.content, 内容栏也是FrameLayout,我们使用setContentView(),的布局加入的就是内容栏。

如何获取DecorView?

在Activity 中直接调用 getWindow().getDecorView()

如何获取ContentView?

在Activity中调用(两种皆可)

  1. FrameLayout contentView = (FrameLayout)getWindow().getDecorView().findViewById(android.R.id.content);
    2.FrameLayout contentView = (FrameLayout)activity.findViewById(android.R.id.content);
注意:这里的容器 是内容栏的FrameLayout,而setContentView()后R.layout.activity_main这个布局是它的第一个子布局
  • 在原来代码的基础上加上如下代码
AppCompatTextView textView = new AppCompatTextView(this);
textView.setText("这是另一个子View");

FrameLayout.LayoutParams layoutParams =  new FrameLayout.LayoutParams
                (ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER_HORIZONTAL;
layoutParams.topMargin = 550;

 FrameLayout contentView =(FrameLayout) getWindow().getDecorView().findViewById(android.R.id.content);
        contentView.addView(textView,layoutParams);

在当前界面中新加入一个view,view里面是一个textView
前后布局对比

之前
之后
可以发现已经加上去了,因为容器为FramaLayout,所以新加入的布局会覆盖在原布局之上
addContentView的使用

向当前界面添加view还有一个更简单的方法,activity 提供了一个函数,
activity.addContentView(view, lp); 可以一步到位的添加view并指定位置。

  • 在原来代码的基础上修改
AppCompatTextView textView = new AppCompatTextView(this);
textView.setText("这是另一个子View");

FrameLayout.LayoutParams layoutParams =  new FrameLayout.LayoutParams
                (ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER_HORIZONTAL;
layoutParams.topMargin = 550;

 addContentView(textView,layoutParams);

效果和上面的相同

参考博客:
Android动态添加View之addView的用法

Android 如何在代码中动态的添加View 及 指定位置

Android LayoutParams详解

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值