Android移植Launcher

Launcher概述

Android系统启动后,加载的第一个程序就是Launcher应用。

 

Launcher的构

成:HomeScreen(workspace(AppWidgetWallPaperLiveFolderShortCut))HotSeatsAllApps/AllApplist:GridView

这是launcher的主界面,里面有一些应用的图标,可以点击图标来启动该应用。默认的情况下,主界面由五个屏组成,每一个屏都可以放置多个程序的图标。可以用手指按住屏幕在五个屏之间来回拖动,也可以快速地滑动来快速切换不同的屏。主界面主要关注UI是如何生成的,五个屏来回滑动是如何实现的,小图标的拖放是如何实现的。

点击顶部中间的格子图标后进入程序管理器,如下图所示。

 

程序管理器列出了系统已经安装的所有应用和小部件,可以点击图标来启动应用。长按住小图标一会,可以拖动小图标回到主界面并放置在主界面作为快捷方式。程序管理器主要关注UI是如何生成的,小图标的拖放是如何实现的,拖动画面时的动画是如何实现的。

系统框图如下:

 

1.分辨率

Android原生为sw600dpsw720dp提供了两种不同的home界面布局。

sw600dphotseat放置在右侧,sw720dphotseat放置在底部。

Hotseat水平放置和垂直放置是可以调整的,在

packages/apps/Launcher2/res/layout-land/hotseat.xml中有如下属性:

launcher:cellCountX="1"

    launcher:cellCountY="@integer/hotseat_cell_count">

packages/apps/Launcher2/res/layout-port/hotseat.xml里是:

launcher:cellCountX="@integer/hotseat_cell_count"

     launcher:cellCountY="1">

即在横屏时hotseat是垂直放置的,竖屏时是水平放置的。 

因为两个分辨率下的布局不同,所以对应的value也有很大差别,如果想统一界面显示,目前的方法是把某一分辨率的布局文件和value文件都删除掉,用另一个的文件代替,这样修改工作会少很多。

因为Launcher不同分辨率的布局文件中的数值都是用dimen或者config设置,所以在做sw600dpsw480dp适配的时候,只需要修改values-sw480dpvalues-sw480dp-land以及values-sw480dp-port目录下的文件就可以了。

dimen.xml里面的参数不一一解释了,因为从名字上面就能够看出是什么布局的数值,例如<dimen name="hotseat_cell_width">60dp</dimen>就是hotseat中每个图标的width<dimen name="apps_customize_cell_width">76dp</dimen>应用列表界面中每个应用的width


2.workspace

Launcher应用的启动activityLauncher.java中的Launcher,其中的onCreate( )函数作为应用的起始点。根据其中的setContentView(R.layout.launcher); 可以看出应用的UI布局从launcher.xml开始。launcher.xml中包括有:

workspace ―― 主界面

apps_customize_pane ―― 程序管理器

qsb_bar ―― 上文中的图1顶部的搜索条。

workspace_cling ―― 是提示可以将应用拖动到主界面的提示画面。

folder_cling ―― 是提示可以将应用程序放到一个文件夹里面的提示画面。

WorkspaceLauncher的主界面,布局由workspace.xml设置:

<com.android.launcher2.Workspace

            android:id="@+id/workspace"

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:paddingStart="@dimen/workspace_left_padding"

            android:paddingEnd="@dimen/workspace_right_padding"

            android:paddingTop="@dimen/workspace_top_padding"

            android:paddingBottom="@dimen/workspace_bottom_padding"

            launcher:defaultScreen="2"

            launcher:cellCountX="@integer/cell_count_x"

            launcher:cellCountY="@integer/cell_count_y"

            launcher:pageSpacing="@dimen/workspace_page_spacing"

            launcher:scrollIndicatorPaddingLeft="@dimen/qsb_bar_height"

            launcher:scrollIndicatorPaddingRight="@dimen/button_bar_height">

 

            <include android:id="@+id/cell1" layout="@layout/workspace_screen" />

            <include android:id="@+id/cell2" layout="@layout/workspace_screen" />

            <include android:id="@+id/cell3" layout="@layout/workspace_screen" />

            <include android:id="@+id/cell4" layout="@layout/workspace_screen" />

            <include android:id="@+id/cell5" layout="@layout/workspace_screen" />

</com.android.launcher2.Workspace>

由上可以看出,workspace(继承PagedViewViewGroup)由五个workspace_screen(实际是CellLayout, ViewGroup)组成,实现五个可以来回拖动的页面,缺省显示的是第二页。背景图是系统的WallPaper,与launcher无关。

A20-android4.4中,home默认有5个屏,编号从左到右依次为0-4这样五个页面就按顺序在水平方向并排地放置。

 

PagedView通过重写onMeasure()方法来设置每个页面的宽高,重写onLayout( )方法来设置五个页面的坐标。

PagedView.onMeasure() >CellLayout.onMeasure():

  newWidth = mPaddingLeft + mPaddingRight + (mCountX * mCellWidth)

        + ((mCountX - 1) * mWidthGap);

newHeight = mPaddingTop + mPaddingBottom + (mCountY * mCellHeight)

        + ((mCountY - 1) * mHeightGap);

  setMeasuredDimension(newWidth, newHeight);   //设置每一页的宽高

 

PagedView.onLayout():

   for (int i = 0; i < childCount; i++) //将五个页面指定到相应的坐标。

{

final View child = getPageAt(i);

if (child.getVisibility() != View.GONE)

{

final int childWidth = getScaledMeasuredWidth(child);

final int childHeight = child.getMeasuredHeight();

int childTop = mPaddingTop;

 

child.layout(childLeft, childTop,

        childLeft + child.getMeasuredWidth(), childTop

                + childHeight); //指定每一页的坐标

childLeft += childWidth + mPageSpacing;

}

}

 

home显示的默认应用在xml-swXXXdp/default_workspace.xml中设置。

<appwidget/>表示小部件,<favorite/>快捷图标,<search/>搜索栏,<folder/>表示文件夹。

1.<appwidget/>的属性如下:

<appwidget

    launcher:packageName="com.android.settings"

         launcher:className="com.android.settings.widget.SettingsAppWidgetProvider"

      launcher:screen="1"

      launcher:x="2"

       launcher:y="1"

       launcher:spanX="4"

       launcher:spanY="1" />

launcher:packageNamelauncher:className指定启动对应的包名和类名;如果不知道没有源码的第三方应用的包名和类名,可以启动应用的时候打印logcat就能看到了。

launcher:screen指定显示的第几个屏幕。

launcher:xlauncher:y显示的坐标,即在X/Y轴上的位置,values-swXXXdp目录下的config.xml定义了每个屏幕横竖显示多少个网格,参数为 <integer name="cell_count_x">6</integer><integer name="cell_count_y">6</integer>launcher:xlauncher:y指的就是在这些网格中的位置。在X轴上是向右从0开始依次增加,在Y轴上是向下从0开始依次增加。

launcher:spanXlauncher:spanY是小部件特有的属性,指定小部件在X/Y轴上的占用网格的个数。

 

2.<favorite/>

<favorite/>也有特定的属性:launcher:container="-101",如果定义了这个属性,那么该<favorite/>就会放置在hotseat中,而 launcher:screenlauncher:xlauncher:y 三个属性的意义就会改变。

首先需要知道在config.xml中定义了HotSeat的图标个数<integer name="hotseat_cell_count">X</integer>,编号从左到右或者从下到上依次为0X-1。上面所说的launcher:screenlauncher:x 需要等于相同的值x,表示按钮的位置,0表示第一个,launcher:y需要等于0,那么该<favorite/>就会放置在hotseat的第x-1的位置上。Hotseat的中间位置是放置appcutoms的按钮,<favorite/>放置在那里不会有效果。

 

3.<folder/>

<folder/>内部可以添加不同<favorite/>,这些<favorite/>不需要定义坐标等,只要定义包名和类名可以。根据GMS要求,就需要在桌面的添加这样一个<folder/>,在这个<folder/>里面有所有google<favorite/>

 

图标可放置图标的位置并不是随意的,而是在每一页中一个固定的表格之内,在拖动小图标时才会显示该表格。

workspace的构造函数中,计算该表格的行数和列数:

final float actionBarHeight = actionBarSizeTypedArray.getDimension(0, 0f);

 

Point minDims = new Point();

Point maxDims = new Point();

            mLauncher.getWindowManager().getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);

cellCountX = 1; 

//cellCountX初始化为1,然后通过一个while循环去计算XY方向各能放多少个应用

while (CellLayout.widthInPortrait(res, cellCountX + 1) <= minDims.x) {

 cellCountX++;

}

cellCountY = 1;

while (actionBarHeight + CellLayout.heightInLandscape(res, cellCountY + 1)

 <= minDims.y) {

     cellCountY++;

}

}

我们看看widthInPortrait方法:

static int widthInPortrait(Resources r, int numCells) {

//We use this method from Workspace to figure out how many rows/columns Launcher should

// have. We ignore the left/right padding on CellLayout because it turns out in our design

// the padding extends outside the visible screen size, but it looked fine anyway.

    int cellWidth = r.getDimensionPixelSize(R.dimen.workspace_cell_width);

    int minGap = Math.min(r.getDimensionPixelSize(R.dimen.workspace_width_gap),

            r.getDimensionPixelSize(R.dimen.workspace_height_gap));

 

    return  minGap * (numCells - 1) + cellWidth * numCells;

}

getDimensionPixelSize方法实际上是把dimens.xml中设置的cellWidth大小由dp转换成px

widthInPortrait计算后的返回值会与smallestScreenDim进行比较,如果比smallestScreenDim 小,那么说明能够再放一个APPcellCount进行加一。循环比较之后就会得出在该屏幕上能放多少行,多少列个应用。

大致如下图的虚线表格:


3.AllApp列表配置文件

配置AllAPP应用列表界面的配置文件是\res\Layout\apps_customize_pane.xml文件。AllAPP列表使用了一个TabHost组织了两个页面(全部应用和Widget),通过界面上面的TabHost进行切换。下面对部分属性进行分析:

android:id="@+id/tabs_container" //TabHost栏,可以配置TabHost栏的高度和宽度

android:id="@android:id/tabs" //TabHost上面widget的按钮

android:id="@+id/market_button" //TabHost右边的Android市场的图标,不需要可以去掉

<!--下面这里就是我们所有应用列表的选项和所有应用列表的显示View,需要注意的是AppsCustomizePagedView同时支持显示所有应用列表和Widget列表 -->

<FrameLayout

      android:id="@android:id/tabcontent"

      android:layout_width="match_parent"

      android:layout_height="match_parent">

<!-- 所有应用列表是通过自定义VIewAppsCustomizePagedView显示,后面会详细分析这个View下面只对部分重要属性加入注释 -->

<com.android.launcher2.AppsCustomizePagedView

        android:id="@+id/apps_customize_pane_content"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

//MaxAppCellCountX MaxAppCellCounY指的是所有App图标排列的最大行列数。

//一般设置为-1,表示无限制

  launcher:maxAppCellCountX="@integer/apps_customize_maxCellCountX"    launcher:maxAppCellCountY="@integer/apps_customize_maxCellCountY"

//pageLayoutWidthGappageLayoutHeightGap分别表示菜单界面与屏幕边缘的距离,
//一般小屏幕这里设置为-1。避免边框太窄误触屏幕才需要设置。                launcher:pageLayoutWidthGap="@dimen/apps_customize_pageLayoutWidthGap"         launcher:pageLayoutHeightGap="@dimen/apps_customize_pageLayoutHeightGap"

//pageLayoutPaddingXXX指的是内填充,这个和系统的padding一样                    launcher:pageLayoutPaddingTop="@dimen/apps_customize_pageLayoutPaddingTop"       launcher:pageLayoutPaddingBottom="@dimen/apps_customize_pageLayoutPaddingBottom" launcher:pageLayoutPaddingLeft="@dimen/apps_customize_pageLayoutPaddingLeft"      launcher:pageLayoutPaddingRight="@dimen/apps_customize_pageLayoutPaddingRight"

//widgetCellWithGapwidgetCellHeightGap指的是widget列表界面各个widget之间的间隔,
//和系统的margin属性类似  

       launcher:widgetCellWidthGap="@dimen/apps_customize_widget_cell_width_gap"

       launcher:widgetCellHeightGap="@dimen/apps_customize_widget_cell_height_gap" 

 //widgetCountXWidgetCountY都是表示Widget界面每行每列显示多少Widget     

  launcher:widgetCountX="@integer/apps_customize_widget_cell_count_x"                launcher:widgetCountY="@integer/apps_customize_widget_cell_count_y"

//提示界面的焦点

        launcher:clingFocusedX="@integer/apps_customize_cling_focused_x"

        launcher:clingFocusedY="@integer/apps_customize_cling_focused_y"

        launcher:maxGap="@dimen/workspace_max_gap" />

<!-- 加载全部应用时的旋转动画 -->

         <FrameLayout

                android:id="@+id/animation_buffer"

                android:layout_width="match_parent"

                android:layout_height="match_parent"

                android:background="#FF000000"

                android:visibility="gone" />

<!-- 分页符,代表多少页和当前页面-->

            <include

                android:id="@+id/paged_view_indicator"

                layout="@layout/scroll_indicator"

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:layout_gravity="bottom" />

        </FrameLayout>

    </LinearLayout>

<!--第一次进入所有应用列表的提示界面,和workspace提示界面一样-->

    <include layout="@layout/all_apps_cling"

        android:id="@+id/all_apps_cling"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:visibility="gone" />

</com.android.launcher2.AppsCustomizeTabHost>

图标下面的名称行数也可以通过修改res/values/styles.xml来调整:

<item name="android:singleLine">false</item>

<item name="android:maxLines">2</item>

这两行代码可以进行修改。


4.动画参数

res/values/config.xml文件定义了workspace到appsCustomize两界面切换时的动画参数,如下:

config_appsCustomizeSpringLoadedBgAlpha 进入appcustomize界面背景的透明度

config_workspaceUnshrinkTime  home键返回时,workspace的扩张动画时间

config_appsCustomizeWorkspaceShrinkTime  返回workspace的收缩动画时间

config_workspaceSpringLoadShrinkPercentage 把应用拖放到主界面的时候,显示主界面边框的大小

config_appsCustomizeZoomInTime/ config_appsCustomizeZoomOutTime 进入/退出appsCustomize的扩张收缩动画时间

config_appsCustomizeFadeInTime/ config_appsCustomizeFadeOutTime   进入/退出appsCustomize的透明度改变时间 

config_appsCustomizeWorkspaceAnimationStagger/ config_workspaceAppsCustomizeAnimationStagger 进入/退出workspace时桌面上图标的移动时间

PagedView.java里面定义翻页速度MAX_PAGE_SNAP_DURATION

 

 

你可能感兴趣的:(android,启动器,workspace,Launcher,homescreen)