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作为监听器。这样当他们被点击时,会拥有自己的行为