相信從我們寫下"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
