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在日常開發中其實也很常見,因此有必要學習了解一下。以上,是本人在開發中總結下來的內容,希望可以幫助到大家。如果覺着不錯還希望點個贊哈!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM