前兩天分析了菜鳥裹裹的具體業務,菜鳥裹裹能夠成功快遞行業的王牌產品,業務當然是非常重要的一環,不過App的操作體驗,以及能夠讓使用者使用上更加方便快捷,也是成功的重要因素,所以這次就來分析菜鳥裹裹的UI框架設計。
這次對主要功能頁面進行分析,分析工具:
- UIAutomatorViewer-用來掃描和分析Android應用程式的UI元件的GUI工具
- jadx-反編譯工具
首頁
圖中看出UI設計主要分為了:底部的menu_and_navigation_bar_container
和內容區域navigation_bar_content
佈局檔案
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout android:id="@id/navigation_bar_root" android:clipChildren="false" android:layout_width="fill_parent" android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<FrameLayout android:layout_gravity="top" android:id="@id/navigation_bar_content"
android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginBottom="@dimen/navigation_bar_height" />
<ViewStub android:id="@id/navigation_bar_loading_view"
android:layout="@layout/cainiao_progress_dialog" android:inflatedId="@id/rn_loading_view" android:layout_width="fill_parent" android:layout_height="fill_parent" />
<FrameLayout android:layout_gravity="bottom" android:id="@id/menu_and_navigation_bar_container"
android:clipChildren="false" android:layout_width="fill_parent" android:layout_height="@dimen/navigation_bar_height">
<com.cainiao.commonlibrary.navigation.NavigationBarView android:gravity="bottom"
android:layout_gravity="center_vertical" android:id="@id/navigation_bar_view"
android:clipChildren="false" android:layout_width="fill_parent" android:layout_height="fill_parent" />
</FrameLayout>
<ViewStub android:id="@id/full_screen_splash_view" android:layout="@layout/libs_full_screen_splash_view"
android:inflatedId="@id/splash_layout" android:layout_width="fill_parent" android:layout_height="fill_parent" />
</FrameLayout>
複製程式碼
內容區域佈局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<com.cainiao.wireless.uikit.view.feature.PtrBirdFrameLayout android:id="@id/store_house_ptr_frame"
android:background="@color/full_transparent" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_below="@id/header_title_view">
<ListView android:id="@id/package_listview" android:background="@color/homepage_fragment_listview_background"
android:scrollbars="none" android:layout_width="fill_parent" android:layout_height="fill_parent"
android:layout_marginBottom="10.0dip" android:listSelector="#00000000" android:divider="@null"
android:choiceMode="singleChoice" android:overScrollMode="never" />
</com.cainiao.wireless.uikit.view.feature.PtrBirdFrameLayout>
<LinearLayout android:orientation="vertical" android:id="@id/homepage_fragment_scrollable_layout"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_below="@id/header_title_view">
<com.cainiao.wireless.homepage.presentation.view.widget.newfeatureview.HomepageNewFeatureLayout android:id="@id/home_page_fragment_new_grid_feature_enter_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<com.cainiao.wireless.homepage.presentation.view.widget.HomepageTitleView android:id="@id/header_title_view"
android:layout_width="fill_parent" android:layout_height="45.0dip" />
</RelativeLayout>
複製程式碼
佈局檔案的具體結構圖:
menu_and_navigation_bar_container
主要包含了NavigationBarView
,底部導航內容:首頁,取件,寄件,驛站,我。navigation_bar_content
:內容區域。通過底部導航的切換,內容顯示不同的頁面。main_activity_content
:首頁的內容區域顯示。分為三部分:header_title_view
:通過自定義ViewHomepageTitleView
實現homepage_fragment_scrollable_layout
:通過HomepageNewFeatureLayout
實現store_house_ptr_frame
:展現列表和下拉重新整理,分別通過ListView
和PtrBirdFrameLayout
實現
物流詳情
物流詳情的頁面比較有特色,地圖展示訂單的軌跡,列表展示從發貨到收貨各個節點資訊。
UI結構圖:
- 最底層是全屏mapview
- 選單操作層覆蓋在mapview上
- 最上面一層是物流從發貨到收貨的節點列表
不過從反編譯中無法找到對應的佈局xml,通過關鍵字logistic
在AndroidManifest.xml檔案搜尋到相關的Activity。發現com.cainiao.wireless.logisticsdetail.presentation.view.activity.ShowGoodInfoActivity
有相關性,ShowGoodInfoActivity
中使用ShowGoodInfoFragment
渲染,
ShowGoodInfoFragment
中有個方法會真正進入到詳情的邏輯。
public void showGoodInfo(List<LogisticsDetailGoodsDO> packageItems) {
if (packageItems != null) {
if (packageItems.size() == 0) {
Bundle bundle = new Bundle();
bundle.putString("orderCode", this.mOrderCode);
bundle.putString("mailNo", this.mMailNo);
bundle.putString("cpCode", this.mCpCode);
//進入詳情的Router宣告
Router.from(getActivity()).withExtras(bundle)
.toUri("guoguo://go/logistic");
getActivity().overridePendingTransition(0, 0);
finish();
}
if (packageItems.size() == 1) {
Router.from(getActivity()).toUri(((LogisticsDetailGoodsDO) packageItems.get(0)).taobaoGoodUrl);
getActivity().overridePendingTransition(0, 0);
finish();
return;
}
this.mSlideShowView.setDatas(packageItems);
}
}
複製程式碼
該方法通過Router進行跳轉到guoguo://go/logistic
,在AndroidManifest.xml有對應的宣告:
<activity android:configChanges="keyboardHidden|orientation" android:exported="false"
android:name="com.taobao.cainiao.newlogistic.LogisticDetailActivity"
android:screenOrientation="portrait" android:theme="@style/Theme.NoBackgroundAndTitle.TabPage"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="go"
android:path="/logistic"
android:scheme="guoguo"/>
</intent-filter>
<meta-data android:name="bundleLocation" android:value="com.taobao.cainiao"/>
</activity>
複製程式碼
沒錯就是它, LogisticDetailActivity
就是我們要找的顯示物流詳情的頁面。
反編譯的程式碼中沒有對應的LogisticDetailActivity
的原始碼,這些部分應該是通過Atlas動態部署的。
LogisticDetailActivity
在Atlas框架中的宣告
package android.taobao.atlas.framework;
public class FrameworkProperties {
public static String autoStartBundles = "com.android.update,com.cainiao.wireless.pr";
public static String bundleInfo = "
...
{"activities":["com.taobao.cainiao.newlogistic.LogisticDetailActivity"],"contentProviders":[],"dependency":[],"isInternal":true,"pkgName":"com.taobao.cainiao","receivers":[],"services":[],"unique_tag":"67520454e3fdb8fd2307b1c08c602abf","version":"4.7.1@1.1.1.12"}
....";
public static String group = "cainiao4android";
public static String outApp = "false";
private String version = "4.7.1";
public String getVersion() {
return this.version;
}
}
複製程式碼
寄件記錄
為什麼分析這個頁面,因為這個cn_wx_page_container
,看id的名字判斷出這是weex頁面。
UI佈局:
<LinearLayout android:orientation="vertical" android:background="#fff2f2f2" android:layout_width="fill_parent" android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<com.cainiao.android.cnweexsdk.weex.view.CNWXTopBar android:id="@id/cn_wx_page_topbar" android:layout_width="fill_parent" android:layout_height="?cnWXTopBarHeightStyle" />
<FrameLayout android:layout_width="fill_parent" android:layout_height="fill_parent">
<FrameLayout android:id="@id/cn_wx_page_container" android:background="#ffffffff" android:layout_width="fill_parent" android:layout_height="fill_parent" />
<FrameLayout android:layout_gravity="bottom" android:id="@id/cn_wx_page_cover" android:background="@color/cn_wx_transparent" android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:textSize="16.0sp" android:textColor="@color/cn_wx_exception_msg_color" android:layout_gravity="center" android:id="@id/cn_wx_container_page_exception" android:visibility="gone" android:layout_width="140.0dip"
android:layout_height="80.0dip" android:text="@string/cn_wx_reload_weex_txt" />
</FrameLayout>
</LinearLayout>
複製程式碼
這個頁面,通過抓包分析,該頁面的請求url:
https://cn.alicdn.com/cainiao-weex/order_center/0.3.0/main/order-center-homepage.js?navtype=weex&fc=true&bs=black&orderType=send&referrer=guoguo%3A%2F%2Fgo%2Fsendpackage
裡面主要是Vue編寫的頁面程式碼和邏輯。
總結
分析了App三個主要的功能,大致對菜鳥裹裹的UI框架有所瞭解。
在Android工程師角度分析App使用的開源框架-3.菜鳥裹裹一文中,羅列了App使用的一些框架。
通過分析,物流詳情中使用了Atlas,寄件記錄使用了Weex,其他的頁面,有興趣的同學可以自行分析。
雖然物流App,不像電商App(淘寶,天貓,京東等)那樣要求很高的動態化,不過菜鳥裹裹App使用了很多動態化框架,在不影響操作體驗的前提下,又增加了運營可以操作性,還是非常值得學習的。
以後有關菜鳥裹裹的分享,會集中在從零開始高仿菜鳥裹裹App(計劃中),希望同學們多多支援。