Android動態改變布局,Android動態布局
遇到這麼個需求,先看圖:
其實是一個軟體的登入介面,初始是第一個圖的樣子,當軟鍵盤彈出後變為第二個圖的樣子,因為登入介面有使用者名稱、密碼、登入按鈕,不這樣的話軟鍵盤彈出後會遮住登入按鈕(其實之前的實現放到了ScrollView裡面,監聽軟鍵盤彈出後滾動到底部,軟鍵盤隱藏後滾動到頂部,也是可以的)。
最簡單的方法就是多加幾個冗餘的View,根據軟鍵盤的狀態隱藏不需要的View,顯示需要的View,但這樣感覺太挫了,然後就想起了前兩年研究的RelativeLayout布局,RelativeLayout中子控制項的布局都是相對位置,只需要在軟鍵盤彈出隱藏時改變應用的位置規則就行了。
先來看一下布局檔案
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/root" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="20dp" tools:context="${packageName}.${activityClass}" > <RelativeLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" > <ImageView android:id="@+id/logo" android:layout_width="150dp" android:layout_height="150dp" android:layout_centerHorizontal="true" android:scaleType="centerCrop" android:src="@drawable/ic_launcher" tools:ignore="ContentDescription" /> <TextView android:id="@+id/label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/logo" android:layout_centerHorizontal="true" android:layout_marginLeft="10dp" android:layout_marginTop="10dp" android:text="@string/hello_world" android:textSize="20sp" /> </RelativeLayout> <EditText android:id="@+id/input" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/container" android:layout_margin="16dp" android:hint="Input sth." tools:ignore="HardcodedText" /></RelativeLayout>
軟鍵盤的彈出隱藏用OnGlobalLayoutListener監聽實現,對Activity應用android:windowSoftInputMode="stateHidden|adjustResize",這樣開始時軟鍵盤不顯示,當軟鍵盤彈出時布局被Resize。
接下來是代碼,所有的代碼都在這裡了
public class MainActivity extends Activity { private View root; // 最外層布局 private View logo; // Logo表徵圖 private View label; // Logo附近的文字 private int rootBottom = Integer.MIN_VALUE; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); root = findViewById(R.id.root); logo = findViewById(R.id.logo); label = findViewById(R.id.label); root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect r = new Rect(); root.getGlobalVisibleRect(r); // 進入Activity時會布局,第一次調用onGlobalLayout,先記錄開始軟鍵盤沒有彈出時底部的位置 if (rootBottom == Integer.MIN_VALUE) { rootBottom = r.bottom; return; } // adjustResize,軟鍵盤彈出後高度會變小 if (r.bottom < rootBottom) { RelativeLayout.LayoutParams lp = (LayoutParams) logo.getLayoutParams(); // 如果Logo不是水平置中,說明是因為接下來的改變Logo大小位置導致的再次布局,忽略掉,否則無限迴圈 if (lp.getRules()[RelativeLayout.CENTER_HORIZONTAL] != 0) { // Logo顯示到左上方 lp.addRule(RelativeLayout.CENTER_HORIZONTAL, 0); // 取消水平置中 lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT); // 靠左對齊 // 縮小Logo為1/2 int height = logo.getHeight(); // getMeasuredHeight() int width = logo.getWidth(); lp.width = width / 2; lp.height = height / 2; logo.setLayoutParams(lp); // Logo下的文字 RelativeLayout.LayoutParams labelParams = (LayoutParams) label.getLayoutParams(); labelParams.addRule(RelativeLayout.CENTER_HORIZONTAL, 0); // 取消水平置中 labelParams.addRule(RelativeLayout.BELOW, 0); // 取消顯示到logo的下方 labelParams.addRule(RelativeLayout.RIGHT_OF, R.id.logo); // 顯示到Logo的右方 labelParams.addRule(RelativeLayout.CENTER_VERTICAL); // 垂直置中 label.setLayoutParams(labelParams); } } else { // 軟鍵盤收合或初始化時 RelativeLayout.LayoutParams lp = (LayoutParams) logo.getLayoutParams(); // 如果沒有水平置中,說明是軟鍵盤收合,否則是開始時的初始化或者因為此處if條件裡的語句修改控制項導致的再次布局,忽略掉,否則無限迴圈 if (lp.getRules()[RelativeLayout.CENTER_HORIZONTAL] == 0) { // 置中Logo lp.addRule(RelativeLayout.CENTER_HORIZONTAL); lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0); // 還原Logo為原來大小 int height = logo.getHeight(); int width = logo.getWidth(); lp.width = width * 2; lp.height = height * 2; logo.setLayoutParams(lp); // Logo下的文字 RelativeLayout.LayoutParams labelParams = (LayoutParams) label.getLayoutParams(); labelParams.addRule(RelativeLayout.CENTER_HORIZONTAL); // 設定水平置中 labelParams.addRule(RelativeLayout.BELOW, R.id.logo); // 設定顯示到Logo下面 labelParams.addRule(RelativeLayout.RIGHT_OF, 0); // 取消顯示到Logo右面 labelParams.addRule(RelativeLayout.CENTER_VERTICAL, 0); // 取消垂直置中 label.setLayoutParams(labelParams); } } } }); }}
當Activity啟動時也會進行Layout,此時用rootBottom記錄了初始時最外層布局底部的位置,此後當軟鍵盤彈出時,布局被壓縮,再次擷取同一個View底部的位置,如果比rootBottom小說明軟鍵盤彈出了,如果大於或等於rootBottom說明軟鍵盤隱藏了。
所有的代碼都在上面,也有詳細注釋,有兩點需要注意一下:
可以看到上面代碼中的過濾條件,以else語句中的為例,Activity啟動時會進入else,此時Logo是水平置中狀態,會跳過else裡面的if語句,這樣就處理掉了第一種情況。
當因為軟鍵盤收合進入else時,Logo已經因為if語句塊變為了顯示在左上方,所以會進入else中的if語句,重新改變Logo為水平置中,由於修改了Logo的大小和位置,會導致再次進入onGlobalLayout,仍是進入else,但此時已經設定Logo為水平置中了,不會再次進入else中的if語句,這樣通過一個條件判斷就處理了上面提到的兩點注意事項。
關於addRule
RelativeLayout中每一個子控制項所應用的規則都是通過數組儲存的,如下所示:
public static final int TRUE = -1;public void addRule(int verb) { mRules[verb] = TRUE; mInitialRules[verb] = TRUE; mRulesChanged = true;}public void addRule(int verb, int anchor) { mRules[verb] = anchor; mInitialRules[verb] = anchor; mRulesChanged = true;}
以某一規則的索引為下標,值就是規則對應的anchor,如果是相對於另一個子控制項,值就是另一個子控制項的ID,如果是相對於父控制項,值就是`TRUE`,即-1,如果沒有應用某一規則值就是0,可以看到,removeRule就是把相應位置的值改為了0:
public void removeRule(int verb) { mRules[verb] = 0; mInitialRules[verb] = 0; mRulesChanged = true; }
removeRule是API 17才加的方法,為了在API 17前也能使用,可以使用它的等價方法,像上面的例子中的一樣,使用addRule(verb, 0)。
android設定linearlayout布局的背景顏色,怎動態改變背景顏色?
建議還是用selector,方便。
selector_list_view_item.xml
<?xml version="1.0" encoding="UTF-8"?><selector xmlns:android="schemas.android.com/apk/res/android"> <!-- 預設顏色 --> <item android:state_pressed="false" android:drawable="@color/white"></item> <!-- 點擊時的顏色 --> <item android:state_pressed="true" android:drawable="@color/light_blue"></item> </selector>
然後ListView的item.xml(你的ListView子項布局檔案)檔案中的根Layout背景設為selector_list_view_item.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android=" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:background="@drawable/selector_list_view_item"> </LinearLayout>
ok,搞定!
其中selector_list_view_item.xml檔案中的@color/white和@color/light_blue是在colors.xml中定義的,你想設定什麼顏色都可以。
Android 動態布局該怎實現?
你的問題應該是要動態產生空間吧。
下面的答案可以參考下。
首先要卻這個介面的布局,是AbsoluteLayout,RelativeLayout還是其他,然後就可以再裡面添加控制項了:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//確定介面的布局
AbsoluteLayout abslayout=new AbsoluteLayout (this);
setContentView(abslayout);
//建立一個button按鈕
Button btn1 = new Button(this);
btn1.setText(”this is a button”);
btn1.setId(1);
//確定這個控制項的大小和位置
AbsoluteLayout.LayoutParams lp1 =
new AbsoluteLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
0,100);
abslayout.addView(btn1, lp1 );
}
一個介面可以布置一個布局,可以多個布局一起設計:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//設定介面的布局
RelativeLayout relativeLayout = new RelativeLayout(this);
setContentView(relativeLayout);
//添加一個AbsoluteLayout子布局,並給這個布局添加一個button
AbsoluteLayout abslayout=new AbsoluteLayout (this);
abslayout.setId(11);
Button btn1 = new Button(this);
btn1.setText(”this is a abslayout button”);
btn1.setId(1);
AbsoluteLayout.LayoutParams lp0 = new AbsoluteLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,100,0);
abslayout.addView(btn1, lp0 );
//將這個子布局添加到主布局中
RelativeLayout.LayoutParams lp1 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp1.addRule(RelativeLayout.ALIGN_PARENT_TOP);
lp1.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
relativeLayout.addView(ab......餘下全文>>