Launcher3 壁紙分析
1. WallpaperPickerActivity結構
1.1 父類WallpaperCropActivity
WallpaperPickerActivity是WallpaperCropActivity的派生類。當前者執行onCreate()時,它會調用父類的onCreate(),在父類執行onCreate()時又會調用init(),而WallpaperPickerActivity複寫了init()方法,故又回到自己的init()中。
1.2 佈局文件wallpaper_picker.xml
在init()方法中,對佈局文件wallpaper_picker.xml中的許多控件進行了初始化。以下是wallpaper_picker.xml的部分代碼。
<com.android.launcher3.WallpaperRootView
android:id="@+id/wallpaper_root">
<com.android.launcher3.CropView
android:id="@+id/cropView"/>
<ProgressBar
android:id="@+id/loading"/>
<LinearLayout
android:id="@+id/wallpaper_strip">
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@drawable/tile_shadow_top" />
<HorizontalScrollView
android:id="@+id/wallpaper_scroll_container">
<LinearLayout android:id="@+id/master_wallpaper_list">
<LinearLayout android:id="@+id/wallpaper_list"/>
<LinearLayout android:id="@+id/live_wallpaper_list"/>
<LinearLayout android:id="@+id/third_party_wallpaper_list"/>
</LinearLayout>
</HorizontalScrollView>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@drawable/tile_shadow_bottom" />
</LinearLayout>
</com.android.launcher3.WallpaperRootView>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
com.Android.launcher3.CropView是一個自定義View,用於顯示壁紙大圖。
HorizontalScrollView下面有一個LinearLayout,master_wallpaper_list,它又有兩個子Layout,wallpaper_list和live_wallpaper_list,third_party_wallpaper_list節點已經被移除。
佈局中各節點的位置如下圖所示:
在圖 2中,壁紙大圖對應的是CropView,常規壁紙對應wallpaper_list的LinearLayout,而動態壁紙則是live_wallpaper_list。
2. 初始化流程
2.1 加載壁紙列表
在init()方法中,系統壁紙和用戶從圖庫中選擇過的壁紙會被加載到視圖中,下列三小節會描述這三種壁紙的加載過程。
2.1.1 系統壁紙
在init()中,先從findBundledWallpapers()中獲取一個WallpaperTileInfo類型的ArrayList。在findBundledWallpapers()中,需要從資源中獲取壁紙。根據/WallpaperPicker/res/values-nodpi/wallpapers.xml中的wallpapers數組,把壁紙資源生成WallpaperTileInfo對象加入到ArrayList中。
2.1.2 圖庫壁紙
用戶如果在圖庫中選擇過壁紙,那麼在init過程中,我們還要讀取那些用戶選擇的壁紙,並把它們加入到壁紙圖塊列表中。
mSavedImages是一個SavedWallpaperImages對象,該類時BaseAdapter的派生類,在loadThumbnailsAndImageIdList()方法中,把壁紙從數據庫中加載到Adapter中,然後再通過。populateWallpapersFromAdapter()方法把圖片列表加入到父佈局中。
mSavedImages = new SavedWallpaperImages(this);
mSavedImages.loadThumbnailsAndImageIdList();
populateWallpapersFromAdapter(mWallpapersView, mSavedImages, true);
- 1
- 2
- 3
- 1
- 2
- 3
2.1.3 動態壁紙
動態壁紙的加載方式與前兩種壁紙稍有區別。因爲加載動態壁紙所需要的時間更長,所以對LiveWallpaperListAdapter對象註冊了DataSetObserver。在動態壁紙列表的數據加載完成時會調用DataSetObserver的onChanged()回調方法,把壁紙圖塊加入LinearLayout中。
2.2 加載圖庫壁紙按鍵
在上述幾種類型的壁紙加載完成後,列表中還要加載一個從圖庫中選擇壁紙的按鍵,這個按鍵是動態加入到LinearLayout中的,所以在wallpaper_picker不會出現。
該按鍵的初始化代碼如下:
LinearLayout masterWallpaperList = (LinearLayout)findViewById(R.id.master_wallpaper_list);
FrameLayout pickImageTile = (FrameLayout) getLayoutInflater().
inflate(R.layout.wallpaper_picker_image_picker_item, masterWallpaperList, false);
mPickImageTile = pickImageTile;
setWallpaperItemPaddingToZero(pickImageTile);
masterWallpaperList.addView(pickImageTile, 0);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
可以看到,該按鍵是以一個FrameLayout的形式加入到壁紙列表中的,與populateWallpapersFromAdapter()方法中的形式相同。
該按鍵是一個CheckableFrameLayout,顧名思義,這種Layout是可以被點擊。它包含了一張圖片和一個TextView,具體的佈局文件是WallpaperPicker/res/layout/wallpaper_picker_image_picker_item.xml。上面的代碼是把該佈局inflate進來作爲一個按鍵並加入到壁紙列表中。
2.3 Actionbar的初始化和響應
在壁紙的初始化過程中,ActionBar的樣式被改變了。通過調用actionBar.setCustomView(R.layout.actionbar_set_wallpaper),可以把自定義的佈局設置爲ActionBar的視圖。該佈局中是一個AlphaDisableableButton,它重寫了View.setEnabled()方法。
ActionBar的點擊事件是用於確認當前選擇壁紙的。在點擊ActionBar後,獲取當前選定的圖塊Info,嘗試將其設爲壁紙,並finish當前Activity,把結果傳回上一個Activity。
2.4 點擊事件
在描述點擊事件之前,首先看看一個抽象類,WallpaperTileInfo。這個類中有一個View,一個Drawable以及onClick(),onSave()等方法。WallpaperTileInfo有幾個派生類,如下圖所示:
這些派生類在加載不同類別的壁紙時會有不同的實現。比如PickImageInfo,它代表上文中說到的加載圖庫壁紙按鍵的類型。它的onClick()方法中的實現是發一個Intent去打開DocumentsUI,挑選一張壁紙。其他派生類,如果是壁紙的話,onClick()方法中則會根據自己的壁紙信息,計算寬高和座標,把縮略圖對應的大圖設爲背景,這樣用戶就可以預覽壁紙效果。
在初始化過程中,有一個專門用於壁紙列表的監聽器,mThumbnailOnClickListener。它的onClick()方法實現中有如下語句:
WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
if (info == null) {
return;
}
if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
selectTile(v);
}
info.onClick(WallpaperPickerActivity.this);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
在前文中提到的populateWallpapersFromAdapter()方法中,所有壁紙圖塊,包括mPickImageTile,都將此listener作爲監聽器。這樣當他們被點擊時,會擁有自己的行爲