上一章节介绍到Launcher的四大组件组成、Launcher layout xml的组成及使用到的数据库表 favorites | screens
本章简介:
-
DragLayer组成分析
-
Workspace及ScreenView组成分析
-
CellLayout及CellScreen组成分析
-
Shortcut组成分析
-
Hotseats组成分析
-
DeleteZone组成分析
第一部分:Launcher APP组成分析(二)
从Launcher.xml 中看到Launcher的布局由最外层的DragLayer包裹着,中间由Workspace、Hotseats及DeleteZone几个主要组成部件构成。先抛开桌面上的数据如何读取,如果读者希望在自己写代码的过程中有一个直观的显示感受,可以不妨放写图片或者文字来临时填放用于调试运行。
图(1)
1.DragLayer 组成分析:
从组成来讲,DragLayer最重要的部分是用于显示壁纸背景,关于拖动的部分,等到分析到触摸再拿来分析。
壁纸,MIUI系统中将壁纸显示的方式使用SharedPreferences的方式存储。
/**
* 功能: 桌面背景显示, 从配置表中读取壁纸的显示方式
* 调用: 使用于Launcher.java
*/
public void updateWallpaper() {
String wallpaperScrollType = PreferenceManager.getDefaultSharedPreferences(mContext).
getString("pref_key_wallpaper_scroll_type", "byTheme");
if (wallpaperScrollType.equals("byTheme")){
wallpaperScrollType = getResources().getString(R.string.wallpaper_scrolling);
}
mWpScrolling = false;
if (wallpaperScrollType.equals("left")) {
mWpOffsetX = 0F;
} else if (wallpaperScrollType.equals("center")) {
mWpOffsetX = 0.5F;
} else if (wallpaperScrollType.equals("right")) {
mWpOffsetX = 1F;
} else {
mWpScrolling = true;
}
mLauncher.getWindow().setFormat(PixelFormat.TRANSPARENT);
mWallpaper = null;
updateWallpaperOffset();
}
/**
* 功能: 壁纸显示起点
*/
public void updateWallpaperOffset() {
if (mWallpaper == null) {
mWallpaperManager.setWallpaperOffsetSteps(mWpStepX, mWpStepY);
if (getWindowToken() == null) {
removeCallbacks(OffsetUpdater);
postDelayed(OffsetUpdater, 50L);
}else {
try {
WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).
setWallpaperPosition(getWindowToken(), mWpOffsetX, mWpOffsetY, mWpStepX, mWpStepY);
} catch (RemoteException e) { }
}
}else {
int offsetX = (int)((float)(mWpWidth - mScreenSize.x) * mWpOffsetX);
if (mOldOffsetX != offsetX) {
mOffsetChanged = true;
}
mOldOffsetX = offsetX;
}
}
public void updateWallpaperOffset(float xStep, float yStep, float xOffset, float yOffset) {
if (mWpScrolling && mWpOffsetX != xOffset) {
mWpStepX = xStep;
mWpStepY = yStep;
mWpOffsetX = xOffset;
mWpOffsetY = yOffset;
updateWallpaperOffset();
}
}
public void updateWallpaperOffsetAnimate(final float xStep, final float yStep,
final float xOffset, final float yOffset) {
final float xStepDelta = xStep - mWpStepX;
final float yStepDelta = yStep - mWpStepY;
final float mWpOffsetXDelta = xOffset - mWpOffsetX;
final float mWpOffsetYDelta = yOffset - mWpOffsetY;
ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float f = ((Float)animation.getAnimatedValue()).floatValue();
updateWallpaperOffset(xStep - f * xStepDelta, yStep - f * yStepDelta,
xOffset - f * mWpOffsetXDelta, yOffset - f * mWpOffsetYDelta);
}
});
valueAnimator.start();
}
可以看到,Wallpaper在DragLayer中分三种类型“left”, "right", "center" 来显示,根据壁纸的大小来计算X,Y的偏移量,updateWallpaperOffsetAnimate主要是用于拖动时动画显示滑动到指定位置。
2.Workspace及ScreenView组成分析
从代码中可以看到Workspace继承与DragableScreenView,DragableScreenView再继承与ScreenView,由于DragableScreenView主要是用于处理触摸动作,这里暂时不做分析,直接看ScreenView。ScreenView是一个ViewGroup,Workspace如果构成的ViewGroup呢?这里必须要涉及到CellLayout及CellScreen,因为这两个组件才是组成Workspace的Viewgroup的元素。实例图如下:
图(二)
CellScreen.xml的layout:
<cn.minking.launcher.CellScreen android:animationCache="false" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android"> <FrameLayout android:id="@id/background_container" android:visibility="invisible" android:animationCache="false" android:layout_width="fill_parent" android:layout_height="fill_parent"> ...... ...... </FrameLayout> <cn.minking.launcher.CellLayout android:id="@id/cell_layout" android:paddingBottom="@dimen/workspace_cell_padding_bottom" android:animationCache="false" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </cn.minking.launcher.CellScreen>
在Launcher的Model将数据读取给CellScreen,然后将每个CellScreen使用addView的方式添加至ViewGroup, 使用 onLayout显示出来,至于详细的步骤会在后续详细的分析。
3. CellScreen | CellLayout组成分析
CellLayout被分割成Horizontal*Vetical个单元格,下图中示为4x4的CellLayout,此处可以根据屏幕的分辨率不同设置不同的大小,比如MIUI现在使用的则是4x5的布局;
图(三)
// 横向及纵向的单元格个数
mHCells = ResConfig.getCellCountX();
mVCells = ResConfig.getCellCountY();
每个单元格的大小及边界距离:
Resources resources = context.getResources();
mCellWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
mCellHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
mWidgetCellPaddingTop = resources.getDimensionPixelSize(R.dimen.workspace_widget_padding_top);
mWidgetCellPaddingBottom = resources.getDimensionPixelSize(R.dimen.workspace_widget_padding_bottom);
mPaddingTop = resources.getDimensionPixelSize(R.dimen.workspace_padding_top);
mPaddingLeft = resources.getDimensionPixelSize(R.dimen.workspace_padding_side);
mPaddingRight = mPaddingLeft;
一个View控件可以占有单个单元格,比如APP图标或者Shortcut快捷方式,也可以一个View控件占有多个单元格,比如2x2的时钟,或者4x4的时钟。
// 标识单元格视图的二维数组
int ai[] = new int[]{mHCells, mVCells};
int aiBak[] = new int[]{mHCells, mVCells};
mOccupiedCell = (View[][])Array.newInstance(View.class, ai);
mOccupiedCellBak = (View[][])Array.newInstance(View.class, aiBak);
此部分和原生的Android的launcher是类似的,原生的launcher使用的pagedview,同样是继承与ViewGroup,同样也使用了CellLayout,区别是MIUI多了CellScreen,为何这么做?由于MIUI是单界面操作,所以在桌面需要更多的操作,比如进入Widget编辑模式。
4. ShortCut组成分析:
这部分本应该是需要和Widget一起分析,但是MIUI中增加了Gadget,涉及到主题资源的引入,所以在此我们先不分析Widget。
Launcher.java中创建Shortcut布局的接口,看到调用的是application layout
Launcher.java
/**
* 功能: 以Layout application为样式创建快捷方式的布局
* @param viewgroup
* @param shortcutinfo
* @return
*/
private ShortcutIcon createShortcutIcon(ViewGroup viewgroup, ShortcutInfo shortcutinfo){
return ShortcutIcon.fromXml(R.layout.application, this, viewgroup, shortcutinfo);
}
ShortcutIcon.java
/**
* 功能: 从XML中的得到快捷图标的布局
* @param layout
* @param launcher
* @param viewgroup
* @param shortcutinfo
* @return
*/
static ShortcutIcon fromXml(int layout, Launcher launcher,
ViewGroup viewgroup, ShortcutInfo shortcutinfo){
ShortcutIcon shortcutIcon = (ShortcutIcon)LayoutInflater.from(launcher).inflate(layout, viewgroup, false);
shortcutIcon.updateInfo(launcher, shortcutinfo);
return shortcutIcon;
}
application.xml
<cn.minking.launcher.ShortcutIcon android:background="@drawable/shortcut_selector" android:focusable="true" android:animationCache="false" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 图标阴影 --> <include layout="@layout/icon_shadow" /> <!-- 创建文件夹时显示的背景 --> <ImageView android:layout_gravity="top|center" android:id="@id/icon_folder_creation_bg" android:visibility="invisible" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/icon_side_margin" android:layout_marginRight="@dimen/icon_side_margin" android:src="@drawable/icon_folder" android:scaleType="centerInside" /> <!-- 图标 --> <include layout="@layout/icon_icon" /> <!-- 标题 --> <include layout="@layout/icon_title" /> <!-- 消息个数 --> <include layout="@layout/icon_message" /> </cn.minking.launcher.ShortcutIcon>
5. Hotseats组成分析
Hotseats采用的是线性布局LinearLayout, 根据MAX SEAT的个数,依次布局
图(四)
Hotseats.java
@Override
protected void onFinishInflate() {
super.onFinishInflate();
for (int i = 0; i < MAX_SEATS; i++) {
LayoutInflater.from(mContext).inflate(R.layout.hotseat_button, this, true);
}
}
HotseatButton.java
public void bind(ItemIcon itemicon, DragController dragcontroller){
mIcon = itemicon;
// 构建一个FrameLayout, 将HOT Seat中的各个ItemIcon加人到Layout中
addView(itemicon);
if (itemicon instanceof DropTarget) {
dragcontroller.addDropTarget((DropTarget)itemicon);
}
}
6. DeleteZone组成分析
DeleteZone默认是隐藏的,等有Item被拖动时才会出现在屏幕的顶部。布局很简单,就以launcher.xml中布局显示。此布局绑定了几个动画及拖动的操作,等后续分析拖动时再详细讲解。
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 垃圾箱图标
mTrashIcon = (ImageView)findViewById(R.id.trash);
// 提示信息
mEditingTips = (TextView)findViewById(R.id.editing_tips);
mEditingTips.setDrawingCacheEnabled(true);
// 显示及消失的动画, 淡入淡出及伸缩的效果
mFadeIn = AnimationUtils.loadAnimation(getContext(), R.anim.fade_in);
mFadeOut = AnimationUtils.loadAnimation(getContext(), R.anim.fade_out);
mShrinkToTop = AnimationUtils.loadAnimation(getContext(), R.anim.shrink_to_top);
mShrinkToTop.setAnimationListener(this);
mStretchFromTop = AnimationUtils.loadAnimation(getContext(), R.anim.stretch_from_top);
mTransition = (TransitionDrawable)mTrashIcon.getBackground();
}