一般產生 Layout 的作法大多數是使用 xml 來達成,但在一些特殊情況下必須在 java code 建立 Layout。
本篇紀錄如何在 java code 中產生 Layout。
首先貼上示意圖
可以看到示意圖中主要有4個區塊,黑,藍,紅,綠,其中黑跟綠的width都佔滿屏,藍和紅的width佔滿屏的0.5。
而Height部份,黑佔0.2,其他區塊佔0.4。
以下為source code,
第18行的藉由 Display 來計算螢幕的 width 和 height 。
第38行為了在各元件中手動設定id,提供了generateID()方法,內容參考View.generateID()。
第52行createLayout()方法提供給Activity設定Layout。
第67,84,103,121行的initxxx方法,則是各個區塊的初始化內容。
import java.util.concurrent.atomic.AtomicInteger; import android.content.Context; import android.graphics.Color; import android.os.Build; import android.view.Display; import android.view.View; import android.view.WindowManager; import android.widget.RelativeLayout; import android.widget.TextView; public class ViewCreater { private static final String TAG = "ViewCreater"; private static int mId = 0; private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1); private Context mContext; private Display mDisplay; private RelativeLayout upLayout; private RelativeLayout mMiddleLeftLayout; private RelativeLayout mMiddleRightLayout; private RelativeLayout mDownLayout; public ViewCreater(Context context) { mContext = context; initDisplay(); } private void initDisplay() { WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mDisplay = manager.getDefaultDisplay(); } public static int generateId() { if (Build.VERSION.SDK_INT < 17) { for (;;) { final int result = sNextGeneratedId.get(); int newValue = result + 1; if (newValue > 0x00FFFFFF) newValue = 1; if (sNextGeneratedId.compareAndSet(result, newValue)) { return result; } } } else { return View.generateViewId(); } } public RelativeLayout createLayout() { RelativeLayout rootlayout = new RelativeLayout(mContext); rootlayout.setId(generateId()); initUpLayout(); initMiddleLeftLayout(); initMiddleRightLayout(); initDownLayout(); rootlayout.addView(mDownLayout); rootlayout.addView(mMiddleLeftLayout); rootlayout.addView(upLayout); rootlayout.addView(mMiddleRightLayout); return rootlayout; } private void initUpLayout() { upLayout = new RelativeLayout(mContext); upLayout.setId(generateId()); upLayout.setTag("upLayout"); RelativeLayout.LayoutParams layoutParamsOfUp = new RelativeLayout.LayoutParams( mDisplay.getWidth() / 1, mDisplay.getHeight() / 5); layoutParamsOfUp.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE); upLayout.setLayoutParams(layoutParamsOfUp); TextView textViewOfUplayout = new TextView(mContext); textViewOfUplayout.setId(generateId()); textViewOfUplayout.setText("upLayout"); textViewOfUplayout.setTextSize(20); upLayout.addView(textViewOfUplayout); upLayout.setBackgroundColor(Color.BLACK); } private void initMiddleRightLayout() { mMiddleRightLayout = new RelativeLayout(mContext); mMiddleRightLayout.setId(generateId()); RelativeLayout.LayoutParams layoutParamsOfMiddleRightLayout = new RelativeLayout.LayoutParams( mDisplay.getWidth() / 2, mDisplay.getHeight() * 2 / 5); TextView textViewOfMiddleRightLayout = new TextView(mContext); textViewOfMiddleRightLayout.setId(generateId()); textViewOfMiddleRightLayout.setText("middleRightLayout"); textViewOfMiddleRightLayout.setTextSize(20); textViewOfMiddleRightLayout.setTextColor(Color.parseColor("#ffff00")); mMiddleRightLayout.addView(textViewOfMiddleRightLayout); layoutParamsOfMiddleRightLayout.addRule(RelativeLayout.BELOW, upLayout.getId()); layoutParamsOfMiddleRightLayout .addRule(RelativeLayout.RIGHT_OF, mMiddleLeftLayout.getId()); mMiddleRightLayout.setLayoutParams(layoutParamsOfMiddleRightLayout); mMiddleRightLayout.setBackgroundColor(Color.RED); } private void initMiddleLeftLayout() { mMiddleLeftLayout = new RelativeLayout(mContext); mMiddleLeftLayout.setId(generateId()); RelativeLayout.LayoutParams layoutParamsOfMiddleLeftLayout = new RelativeLayout.LayoutParams( mDisplay.getWidth() / 2, mDisplay.getHeight() * 2 / 5); TextView textViewOfMiddleLeftLayout = new TextView(mContext); textViewOfMiddleLeftLayout.setId(generateId()); textViewOfMiddleLeftLayout.setText("middleLeftLayout"); textViewOfMiddleLeftLayout.setTextSize(20); textViewOfMiddleLeftLayout.setTextColor(Color.parseColor("#ff0000")); mMiddleLeftLayout.addView(textViewOfMiddleLeftLayout); layoutParamsOfMiddleLeftLayout.addRule(RelativeLayout.BELOW, upLayout.getId()); layoutParamsOfMiddleLeftLayout.addRule(RelativeLayout.ALIGN_LEFT, upLayout.getId()); mMiddleLeftLayout.setLayoutParams(layoutParamsOfMiddleLeftLayout); mMiddleLeftLayout.setBackgroundColor(Color.BLUE); } private void initDownLayout() { mDownLayout = new RelativeLayout(mContext); mDownLayout.setId(generateId()); RelativeLayout.LayoutParams layoutParamsOfDownLayout = new RelativeLayout.LayoutParams( mDisplay.getWidth() / 1, mDisplay.getHeight() * 2 / 5); layoutParamsOfDownLayout.addRule(RelativeLayout.BELOW, mMiddleLeftLayout.getId()); TextView textViewOfDownLayout = new TextView(mContext); textViewOfDownLayout.setId(generateId()); textViewOfDownLayout.setText("DownLayout"); textViewOfDownLayout.setTextSize(20); textViewOfDownLayout.setTextColor(Color.parseColor("#ffffff")); mDownLayout.addView(textViewOfDownLayout); mDownLayout.setLayoutParams(layoutParamsOfDownLayout); mDownLayout.setBackgroundColor(Color.GREEN); } }
有幾個必須注意的要點。
1.即使加入所有UI widget之後,還是可以調整各個UI widget的屬性。
比如說若修改createLayout方法, 其中第14,15,16行重新修改了綠色區塊的width。
public RelativeLayout createLayout() { RelativeLayout rootlayout = new RelativeLayout(mContext); rootlayout.setId(generateId()); initUpLayout(); initMiddleLeftLayout(); initMiddleRightLayout(); initDownLayout(); rootlayout.addView(mDownLayout); rootlayout.addView(mMiddleLeftLayout); rootlayout.addView(upLayout); rootlayout.addView(mMiddleRightLayout); RelativeLayout.LayoutParams scaleLayoutParams = new RelativeLayout.LayoutParams(mDisplay.getWidth()/2, mDisplay.getHeight()*2/5); scaleLayoutParams.addRule(RelativeLayout.BELOW, mMiddleLeftLayout.getId()); mDownLayout.setLayoutParams(scaleLayoutParams); return rootlayout; }
2.對Parent View來說,addView的順序和各child View的相依性並沒有一定的關係。
以本範例來說,Parent View就是rootView, 而child view就是 upLayout, mMiddleLeftLayout, mMiddleRightLayout, mDownLayout。
各元件的相依性指的是以藍色區塊來說,它必須是在黑色區塊下方,且貼齊左邊,code如下
private void initMiddleLeftLayout() { ... layoutParamsOfMiddleLeftLayout.addRule(RelativeLayout.BELOW, upLayout.getId()); layoutParamsOfMiddleLeftLayout.addRule(RelativeLayout.ALIGN_LEFT, upLayout.getId()); ... }
也就是說藍色區塊在位置的相依性上必須依賴黑色區塊,沒有黑色區塊,藍色區塊就不知道自己的位置在哪。
然而位置的相依性與被加入到rootView中的順序沒有關係。對rootView而言可以先加入藍色區塊再加入黑色區塊,如下
public RelativeLayout createLayout() { ... rootlayout.addView(mMiddleLeftLayout); rootlayout.addView(upLayout); ... }
3.調整UI Widget的重點在於LayoutParams。
以initMiddleLeftLayout方法來說,
第4~5行初始化layoutParamsofMiddleLeftLayout可設定其元件所佔的width, height。
第6~7行則是指定該元件的位置屬性。
private void initMiddleLeftLayout() { ... RelativeLayout.LayoutParams layoutParamsOfMiddleLeftLayout = new RelativeLayout.LayoutParams( mDisplay.getWidth() / 2, mDisplay.getHeight() * 2 / 5); layoutParamsOfMiddleLeftLayout.addRule(RelativeLayout.BELOW, upLayout.getId()); layoutParamsOfMiddleLeftLayout.addRule(RelativeLayout.ALIGN_LEFT, upLayout.getId()); mMiddleLeftLayout.setLayoutParams(layoutParamsOfMiddleLeftLayout); ... }