阿里Android開發規範:UI 與佈局

leeyh發表於2018-03-06

以下內容摘自 阿里巴巴Android開發手冊

我們的目標是:

  • 防患未然,提升質量意識,降低故障率和維護成本;
  • 標準統一,提升協作效率;
  • 追求卓越的工匠精神,打磨精品程式碼。
  • 【強制】必須遵守,違反本約定或將會引起嚴重的後果;
  • 【推薦】儘量遵守,長期遵守有助於系統穩定性和合作效率的提升;
  • 【參考】充分理解,技術意識的引導,是個人學習、團隊溝通、專案合作的方向。

阿里Android開發規範:資原始檔命名與使用規範
阿里Android開發規範:四大基本元件
阿里Android開發規範:UI 與佈局
阿里Android開發規範:程式、執行緒與訊息通訊
阿里Android開發規範:檔案與資料庫
阿里Android開發規範:Bitmap、Drawable 與動畫
阿里Android開發規範:安全與其他

1、【強制】佈局中不得不使用 ViewGroup 多重巢狀時,不要使用 LinearLayout 巢狀,改用 RelativeLayout,可以有效降低巢狀數。 說明: Android 應用頁面上任何一個 View 都需要經過 measure、layout、draw 三個步驟才能被正確的渲染。從 xml layout 的頂部節點開始進行 measure,每個子節點都需要向自己的父節點提供自己的尺寸來決定展示的位置,在此過程中可能還會重新measure(由此可能導致 measure 的時間消耗為原來的 2-3 倍)。節點所處位置越深,套嵌帶來的 measure 越多,計算就會越費時。這就是為什麼扁平的 View 結構會效能更好。同時,頁面擁上的 View 越多,measure、layout、draw 所花費的時間就越久。要縮短這個時間,關鍵是保持 View 的樹形結構儘量扁平,而且要移除所有不需要渲染的View。理想情況下,總共的 measure,layout,draw 時間應該被很好的控制在 16ms以內,以保證滑動螢幕時 UI 的流暢。要找到那些多餘的 View(增加渲染延遲的 view),可以用 Android Studio Monitor裡的 Hierarachy Viewer 工具,視覺化的檢視所有的 view。
正例:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout>
	<RelativeLayout>
		<TextView/>
		...
		<ImageView/>
	</RelativeLayout>
</android.support.constraint.ConstraintLayout>
複製程式碼

反例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout>
	<LinearLayout>
		<RelativeLayout>
			<TextView/>
			...
			<ImageView/>
		</RelativeLayout>
	</LinearLayout>
</LinearLayout>
複製程式碼

多重巢狀導致 measure 以及 layout 等步驟耗時過多。 擴充套件參考:

  1. developer.android.com/studio/prof…
  2. mrpeak.cn/android/201…

2、【推薦】在 Activity 中顯示對話方塊或彈出浮層時,儘量使用 DialogFragment,而非Dialog/AlertDialog,這樣便於隨Activity生命週期管理對話方塊/彈出浮層的生命週期。
正例:

public void showPromptDialog(String text){
	DialogFragment promptDialog = new DialogFragment() {
		@Override
		public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
			getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
			View view = inflater.inflate(R.layout.fragment_prompt, container);
			return view;
		}
	};
	promptDialog.show(getFragmentManager(), text);
}
複製程式碼

3、【推薦】原始檔統一採用 UTF-8 的形式進行編碼。
4、【強制】禁止在非 ui 執行緒進行 view 相關操作。
5、【推薦】文字大小使用單位 dp,view 大小使用單位 dp。對於 Textview,如果在文字大小確定的情況下推薦使用 wrap_content 佈局避免出現文字顯示不全的適配問題。
6 、【強制】禁止在設計佈局時多次設定子 view 和父 view 中為同樣的背景造成頁面過度繪製,推薦將不需要顯示的佈局進行及時隱藏。
正例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:orientation="vertical" >
	<TextView
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:text="@string/hello" />
	<Button
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:text="click it !"
		android:id="@+id/btn_mybuttom" />
	<ImageView
		android:id="@+id/img"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:visibility="gone"
		android:src="@drawable/youtube" />
	<TextView
		android:text="it is an example!"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content" />
</LinearLayout>
複製程式碼

反例:

@Override
protected void onDraw(Canvas canvas) {
	super.onDraw(canvas);
	int width = getWidth();
	int height = getHeight();
	mPaint.setColor(Color.GRAY);
	canvas.drawRect(0, 0, width, height, mPaint);
	mPaint.setColor(Color.CYAN);
	canvas.drawRect(0, height/4, width, height, mPaint);
	mPaint.setColor(Color.DKGRAY);
	canvas.drawRect(0, height/3, width, height, mPaint);
	mPaint.setColor(Color.LTGRAY);
	canvas.drawRect(0, height/2, width, height, mPaint);
}
複製程式碼

7、【推薦】靈活使用佈局,推薦 Merge、ViewStub 來優化佈局,儘可能多的減少 UI佈局層級,推薦使用 FrameLayout,LinearLayout、RelativeLayout 次之。
8、 【推薦】在需要時刻重新整理某一區域的元件時,建議通過以下方式避免引發全域性 layout重新整理:

  1. 設定固定的 view 大小的高寬,如倒數計時元件等;
  2. 呼叫 view 的 layout 方式修改位置,如彈幕元件等;
  3. 通過修改 canvas 位置並且呼叫 invalidate(int l, int t, int r, int b)等方式限定重新整理區域;
  4. 通過設定一個是否允許 requestLayout 的變數,然後重寫控制元件的 requestlayout、onSizeChanged 方法 , 判 斷 控 件 的大小 沒 有 改 變 的 情況下 , 當 進 入requestLayout 的時候,直接返回而不呼叫 super 的 requestLayout 方法。

9、【推薦】不能在 Activity 沒有完全顯示時顯示 PopupWindow 和 Dialog。
10、【推薦】儘量不要使用 AnimationDrawable,它在初始化的時候就將所有圖片載入到記憶體中,特別佔記憶體,並且還不能釋放,釋放之後下次進入再次載入時會報錯。 說明: Android 的幀動畫可以使用 AnimationDrawable 實現,但是如果你的幀動畫中如果包含過多幀圖片,一次性載入所有幀圖片所導致的記憶體消耗會使低端機發生 OOM異常。幀動畫所使用的圖片要注意降低記憶體消耗,當圖片比較大時,容易出現 OOM。
正例: 圖片數量較少的 AnimationDrawable 還是可以接受的。

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true">
	<item android:duration="500" android:drawable="@drawable/ic_heart_100"/>
	<item android:duration="500" android:drawable="@drawable/ic_heart_75"/>
	<item android:duration="500" android:drawable="@drawable/ic_heart_50"/>
	<item android:duration="500" android:drawable="@drawable/ic_heart_25"/>
	<item android:duration="500" android:drawable="@drawable/ic_heart_0"/>
</animation-list>
複製程式碼

反例:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
	<item android:drawable="@drawable/soundwave_new_1_40" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_41" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_42" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_43" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_44" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_45" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_46" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_47" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_48" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_49" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_50" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_51" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_52" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_53" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_54" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_55" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_56" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_57" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_58" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_59" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_60" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_61" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_62" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_63" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_64" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_65" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_66" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_67" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_68" android:duration="100" />
	<item android:drawable="@drawable/soundwave_new_1_69" android:duration="100" />
</animation-list>
複製程式碼

上述如此多圖片的動畫就不建議使用 AnimationDrawable 了。 擴充套件參考:

  1. stackoverflow.com/questions/8…
  2. blog.csdn.net/wanmeilang1…
  3. segmentfault.com/a/119000000…
  4. developer.android.com/reference/a…

11、【強制】不能使用 ScrollView 包裹 ListView/GridView/ExpandableListVIew;因為這樣會把 ListView 的所有 Item 都載入到記憶體中,要消耗巨大的記憶體和 cpu 去繪製圖面。
說明: ScrollView 中巢狀 List 或 RecyclerView 的做法官方明確禁止。除了開發過程中遇到的各種視覺和互動問題,這種做法對效能也有較大損耗。ListView 等 UI 元件自身有垂直滾動功能,也沒有必要在巢狀一層 ScrollView。目前為了較好的 UI 體驗,更貼近 Material Design 的設計,推薦使用 NestedScrollView。
正例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout>
	<android.support.v4.widget.NestedScrollView>
		<LinearLayout>
			<ImageView/>
			...
			<android.support.v7.widget.RecyclerView/>
		</LinearLayout>
	</android.support.v4.widget.NestedScrollView>
</LinearLayout>
複製程式碼

反例:

<ScrollView>
	<LinearLayout>
		<TextView/>
		...
		<ListView/>
		<TextView />
	</LinearLayout>
</ScrollView>
複製程式碼

擴充套件參考:

  1. developer.android.com/reference/a…
  2. developer.android.com/reference/a…

阿里Android開發規範:資原始檔命名與使用規範
阿里Android開發規範:四大基本元件
阿里Android開發規範:UI 與佈局
阿里Android開發規範:程式、執行緒與訊息通訊
阿里Android開發規範:檔案與資料庫
阿里Android開發規範:Bitmap、Drawable 與動畫
阿里Android開發規範:安全與其他

相關文章