Android 動(dòng)態(tài)布局開車

相信從我們寫下"hello world"的小demo開始,我們就有了一個(gè)觀念,Android UI布局是通過layout目錄下的XML文件定義的。當(dāng)然,xml文件的優(yōu)勢(shì)很多,可預(yù)覽,結(jié)構(gòu)清晰等,但是,總有那么些時(shí)候,在實(shí)現(xiàn)需求的時(shí)候用xml的方式會(huì)不太靈活,比如布局要求動(dòng)態(tài)變化,這個(gè)時(shí)候,我們通過使用java代碼來控制,在程序運(yùn)行時(shí),動(dòng)態(tài)的實(shí)現(xiàn)對(duì)應(yīng)布局。
我們可以從兩個(gè)方面,簡(jiǎn)單的了解一下:

  • 動(dòng)態(tài)添加view,擺脫layout下的xml;
  • 熟悉Drawable子類,擺脫drawable下的xml;

動(dòng)態(tài)添加View

我們?nèi)粘J褂玫腢I組件大致可以分為兩類:

  • 控件(View),如Button、ImageView,一般用于呈現(xiàn)內(nèi)容和交互;
  • 容器(ViewGroup),如RelativeLayout、LinearLayout,一般用來裝控件和容器;

所以,我們?cè)谟么a動(dòng)態(tài)添加布局的時(shí)候,也需要遵循在xml中編寫代碼的規(guī)則,來段代碼直觀了解。

1.首先,我們因?yàn)椴皇褂脁ml文件來setContentView了,所以,先new一個(gè)父布局:

        //添加父布局
        RelativeLayout root = new RelativeLayout(this);
        root.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
                                                        ViewGroup.LayoutParams.MATCH_PARENT));
        root.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
        setContentView(root);

2.然后,我們?cè)陧?yè)面中間添加一個(gè)button

        //添加一個(gè)在頁(yè)面中間的btn
        Button button = new Button(this);
        button.setId(View.generateViewId());
        button.setText("Hello");
        RelativeLayout.LayoutParams btnParams = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        btnParams.addRule(RelativeLayout.CENTER_IN_PARENT);
        button.setLayoutParams(btnParams);
        root.addView(button);

到這里,我們可以看見,只需要3步,一個(gè)控件(button)就已經(jīng)添加到容器(root)中了:

  • new Button(),并初始化控件相關(guān)屬性;
  • 根據(jù)root的類型,new button的 LayoutParams;
  • 最后把button添加到容器中, root.addView(button);

3.嗯,我們?cè)偻卣挂幌拢赽utton的右下方添加一個(gè)listview,

        //添加一個(gè)list 在button右下方
        ListView listView = new ListView(this);
        listView.setId(View.generateViewId());
        listView.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
        listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, 
                new String[]{"one", "two", "three"}));
        RelativeLayout.LayoutParams listParams = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        listParams.addRule(RelativeLayout.BELOW, button.getId());
        listParams.addRule(RelativeLayout.RIGHT_OF, button.getId());
        listView.setLayoutParams(listParams);
        root.addView(listView);

需要注意的是,上面代碼中的addRule()方法,該方法有兩種重載方法:

  • addRule(int verb)
    該方法表示所設(shè)置節(jié)點(diǎn)的屬性不能與其他兄弟節(jié)點(diǎn)相關(guān)或者屬性值為布爾值。比如btnParams.addRule(RelativeLayout.CENTER_IN_PARENT)就表示在RelativeLayout中的相應(yīng)節(jié)點(diǎn)是基于父布局居中。
  • addRule(int verb,int anchor)
    該方法表示所設(shè)置節(jié)點(diǎn)的屬性必須關(guān)聯(lián)其他兄弟節(jié)點(diǎn)或者屬性值為布爾值。比如listParams.addRule(RelativeLayout.BELOW, button.getId())就表示RelativeLayout中的相應(yīng)節(jié)點(diǎn)放置在一個(gè)
    button這個(gè)兄弟節(jié)點(diǎn)的下面。

BTW,規(guī)則如果定義的是一個(gè)view相對(duì)于另一個(gè)view的,一定要初始化另一個(gè)view(button)的id不為0,否則規(guī)則會(huì)失效。通常,為了防止id重復(fù),建議使用系統(tǒng)方法來生成id,也就是第二段代碼中的button.setId(View.generateViewId())。

最終,我們出來的效果是這個(gè)樣子的:

Drawable子類

OK,第二節(jié),我們來看看drawable下的xml文件。drawable目錄下的文件,通常是定義了一些selector、shape等,那么,考慮到一個(gè)場(chǎng)景,如果我們需要使用后臺(tái)下發(fā)的圖片,而不是使用res下面現(xiàn)有的資源呢?根據(jù)上一節(jié)的經(jīng)驗(yàn),xml定義能實(shí)現(xiàn)的,java代碼中肯定也能實(shí)現(xiàn)。下面,就介紹幾個(gè)常用的Drawable子類:
1. StateListDrawable:對(duì)應(yīng)selector,主要用來描述按鈕等的點(diǎn)擊態(tài)。
下面這段代碼大家都熟悉:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/numpad_button_bg_selected" android:state_selected="true"></item>
    <item android:drawable="@drawable/numpad_button_bg_pressed" android:state_pressed="true"></item>
    <item android:drawable="@drawable/numpad_button_bg_normal"></item>
</selector>

這是一個(gè)給button使用的背景選擇,這種不同狀態(tài)顯示不同背景的xml文件我們稱為selector。其實(shí)selector的本質(zhì)是一個(gè)drawable對(duì)象。如果要用java代碼實(shí)現(xiàn)上述的selector該如何實(shí)現(xiàn)呢?直接上代碼:

button.setBackground(DrawableUtil.addStateListBgDrawable(this, R.mipmap.ic_background_normal,
                R.mipmap.ic_background_selected));

    /**
     * selector
     *
     * @param context
     * @param idNormal
     * @param idPressed
     * @return
     */
    public static StateListDrawable addStateListBgDrawable(Context context, int idNormal, int idPressed) {
        StateListDrawable drawable = new StateListDrawable();
        drawable.addState(new int[]{android.R.attr.state_selected}, context.getResources().getDrawable(idPressed));
        drawable.addState(new int[]{android.R.attr.state_pressed}, context.getResources().getDrawable(idPressed));
        drawable.addState(new int[]{android.R.attr.state_enabled}, context.getResources().getDrawable(idNormal));
        drawable.addState(new int[]{}, context.getResources().getDrawable(idNormal));

        return drawable;
    }

簡(jiǎn)單來說,2步:

  • new 一個(gè)StateListDrawable對(duì)象;
  • 給drawable對(duì)象挨個(gè)添上狀態(tài),并添加圖片資源,和xml中邏輯一樣;

2.GradientDrawable:對(duì)應(yīng)漸變色。
直接上代碼:

button2.setBackground(DrawableUtil.addGradientBgDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
                new int[]{getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorAccent)}));

/**
     * 漸變色
     * @param orientation
     * @param colors
     * @return
     */
    public static GradientDrawable addGradientBgDrawable(GradientDrawable.Orientation orientation, int[] colors) {
        GradientDrawable drawable = new GradientDrawable();
        drawable.setOrientation(orientation); //定義漸變的方向
        drawable.setColors(colors); //colors為int[],支持2個(gè)以上的顏色

        return drawable;
    }

簡(jiǎn)單來說,也是2步:

  • new一個(gè)GradientDrawable 對(duì)象;
  • 給對(duì)象設(shè)置orientation和colors;

最終效果如下:

Paste_Image.png

demo下載地址:https://github.com/dys1715/DynamicLayoutDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容