Android 6.0 Launcher2源码分析之布局简介

转载 极客导航  2018-06-14 20:24  阅读 31 次 评论 0 条
摘要:


由于博客资源有些,放的图有点小,要看大图,请移到百度云

链接:https://pan.baidu.com/s/1XqfIOhwDVSJ0H4FmY6FE8w 密码:niue

本此分析的Android6.0的Launcher2源码,我是直接运行在Eclipse中的直接运行调试的。

至于如何正确导入把Launcher2直接导入Eclipse中,并且还可以直接运行,有兴趣的可以看看《正确把Android 6.0的Launcher2导入Eclipse

好了,现在我们直接进入正题。

在Android项目中,拿到代码后最好先直接看AndroidManifest.xml中的配置。

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.launcher" >

      ......

     <!- Launcher 需要的权限 -->
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.SET_WALLPAPER" />
    <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.BIND_APPWIDGET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
    <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />

     <!- application是一个应用启动最早的一个部,这个启动后,Activity,BroadcastReceiver等才会执行-->
    <application
        android:name="com.android.launcher2.LauncherApplication"
        android:hardwareAccelerated="true"
        android:icon="@mipmap/ic_launcher_home"
        android:label="@string/application_name"
        android:largeHeap="@bool/config_largeHeap"
        android:supportsRtl="true" >
        <activity
            android:name="com.android.launcher2.Launcher"
            android:clearTaskOnLaunch="true"
            android:launchMode="singleTask"
            android:screenOrientation="nosensor"
            android:stateNotNeeded="true"
            android:theme="@style/Theme"
            android:windowSoftInputMode="adjustPan" >
            <intent-filter>
                <!- 指定了此Activity是Android系统入口和默认启动Activity界面-->
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.DEFAULT" />
                <!-用于mokey 测试-->
                <category android:name="android.intent.category.MONKEY" />
            </intent-filter>
        </activity>

       ......
      
    </application>

</manifest>

看了上面AndroidMainfest.xml文件,我们知道Launcher启动时是先进入LauncherApplication.java,再进入Launcher.java

1、LauncherApplication.java

public class LauncherApplication extends Application {
    ......
    @Override
    public void onCreate() {
        super.onCreate();
        ......

        //LauncherModel 是个广播
        mModel = new LauncherModel(this, mIconCache); 
        LauncherApps launcherApps = (LauncherApps)
                getSystemService(Context.LAUNCHER_APPS_SERVICE);
        //注册Package的update,remove,add的回调
        launcherApps.registerCallback(mModel.getLauncherAppsCallback());

        // Register intent receivers  注册广播
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
        registerReceiver(mModel, filter);
        filter = new IntentFilter();
        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
        registerReceiver(mModel, filter);
        filter = new IntentFilter();
        filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
        registerReceiver(mModel, filter);

        // Register for changes to the favorites 
        ContentResolver resolver = getContentResolver();
        resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
                mFavoritesObserver);
    }
    ......
    /**
     * There's no guarantee that this function is ever called.
     */
    @Override
    public void onTerminate() {
        super.onTerminate();
         //这里注销了
        unregisterReceiver(mModel);
        ContentResolver resolver = getContentResolver();
        resolver.unregisterContentObserver(mFavoritesObserver);
    }
    ......
}

在LauncherApplication中注册了广播和ContentProvider的监听,同时在onTerminate()中注销了

接着启动Launcher.java

2、Launcher.java

    protected void onCreate(Bundle savedInstanceState) {
        ......
        checkForLocaleChange(); //监听mnc mcc locale的变化,
        //加载布局
        setContentView(R.layout.launcher); 
        
        //初始化控件,并绑定
        setupViews();
        //显示第一次时的导航界面,,只是第一次才显示
        showFirstRunWorkspaceCling();

        registerContentObservers(); //注册ContentProvider监听

        ......
         //mRestoring默认false
        if (!mRestoring) {
            if (sPausedFromUserAction) { //默认false,在onUserLeaveHint()中会更改为true
                // If the user leaves launcher, then we should just load items asynchronously when
                // they return.
                mModel.startLoader(true, -1);
            } else {
                // We only load the page synchronously if the user rotates (or triggers a
                // configuration change) while launcher is in the foreground
                mModel.startLoader(true, mWorkspace.getCurrentPage());
            }
        }

      ......
    }

本章我们先关注launcher.xml布局中的自定义控件,以及简单的了解个布局的作用,其它的我们后续在讲解。

3、launcher.xml 布局代码

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
    android:id="@+id/launcher"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/workspace_bg" >

    <com.android.launcher2.DragLayer
        android:id="@+id/drag_layer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true" >

        <!-- The workspace contains 5 screens of cells -->

        <com.android.launcher2.Workspace
            android:id="@+id/workspace"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingBottom="@dimen/workspace_bottom_padding"
            android:paddingEnd="@dimen/workspace_right_padding"
            android:paddingStart="@dimen/workspace_left_padding"
            android:paddingTop="@dimen/workspace_top_padding"
            launcher:cellCountX="@integer/cell_count_x"
            launcher:cellCountY="@integer/cell_count_y"
            launcher:defaultScreen="2"
            launcher:pageSpacing="@dimen/workspace_page_spacing"
            launcher:scrollIndicatorPaddingLeft="@dimen/qsb_bar_height"
            launcher:scrollIndicatorPaddingRight="@dimen/button_bar_height" >

            <!-- 5页 -->

            <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>

        <!-- 左右两边的黑色线  qsb_divider & dock_divider -->

        <include
            android:id="@+id/qsb_divider"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:layout_marginStart="@dimen/qsb_bar_height"
            layout="@layout/workspace_divider" />

        <include
            android:id="@+id/dock_divider"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="end"
            android:layout_marginEnd="@dimen/button_bar_height"
            layout="@layout/workspace_divider" />

        <!-- workspace 当前界面只是 -->

        <include
            android:id="@+id/paged_view_indicator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            layout="@layout/scroll_indicator" />

        <!-- hotseat区域 -->

        <include
            android:id="@+id/hotseat"
            android:layout_width="@dimen/button_bar_height_plus_padding"
            android:layout_height="match_parent"
            android:layout_gravity="end"
            layout="@layout/hotseat" />

        <!-- 搜索框 ,快捷键提示框 等  qsb_bar -->

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

        <!--
             The Workspace cling must appear under the AppsCustomizePagedView below to ensure
             that it is still visible during the transition to AllApps and doesn't overlay on
             top of that view.
            第一次进入时的指导页面  workspace_cling&folder_cling
        -->

        <include
            android:id="@+id/workspace_cling"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            layout="@layout/workspace_cling"
            android:visibility="gone" />

        <include
            android:id="@+id/folder_cling"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            layout="@layout/folder_cling"
            android:visibility="gone" />

        <!-- 语音键  voice_button_proxy  -->

        <com.android.launcher2.DrawableStateProxyView
            android:id="@+id/voice_button_proxy"
            android:layout_width="@dimen/qsb_bar_height"
            android:layout_height="@dimen/app_icon_size"
            android:layout_gravity="top|start"
            android:layout_marginTop="64dp"
            android:clickable="true"
            android:importantForAccessibility="no"
            android:onClick="onClickVoiceButton"
            launcher:sourceViewId="@+id/voice_button" />

        <!-- 所有应用和Widget界面   apps_customize_pane  -->

        <include
            android:id="@+id/apps_customize_pane"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            layout="@layout/apps_customize_pane"
            android:visibility="invisible" />
    </com.android.launcher2.DragLayer>

</FrameLayout>

上面代码中都注释了,同时在文章开头也附上了Launcher界面的图(图中也带有部分的注释说明)。

我们细细整理一下launcher布局中使用的控件

一:FrameLayout

这是最外层的帧布局。

FrameLayout是最简单的布局了,所有放在布局中的控件都是从屏幕左上角层叠在一起的(如果不设置有些属性的话),后加入的放在先加入的上面。

二:DragLayer

DragLayer是一个自定义的布局,其继承于FrameLayout

public class DragLayer extends FrameLayout implements
		ViewGroup.OnHierarchyChangeListener {
       ......
}

Launcher界面的拖拽等触摸事件都是这里分发的

后面的控件都是包含在DragLayer中的。

三:Workspace
public class Workspace extends SmoothPagedView
        implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
        DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener {
      ......
}
public abstract class SmoothPagedView extends PagedView {
     ......
}

从上面可以知道Workspace是一个自定义的PagedView

同时,监听了很多事件,比如拖拽,触摸,滑动等

四:CellLayout
public class CellLayout extends ViewGroup {
   ......
}

CellLayout是包含在Workspace 中的子布局,每页可以存放应用快键图标,文件夹等

五:qsb_divider、dock_divider和paged_view_indicator

这三个都是ImageView,两条线,加PageView的指示页状态

六:Hotseat
public class Hotseat extends FrameLayout {
    ......
}

这个看文章开头的图片,第一张图右边存放通信录和短信的那部分。

hotseat中还包含着CellLayout

七:Cling
public class Cling extends FrameLayout {
   ......
}

第一次进入显示的引导页

八: DrawableStateProxyView
public class DrawableStateProxyView extends LinearLayout {
     ......
}

语音图标

九:AppsCustomizeTabHost
public class AppsCustomizeTabHost extends TabHost implements LauncherTransitionable,
        TabHost.OnTabChangeListener  {
     ......
}

用于显示所有应用和Widget(小部件)的界面

本文地址: https://www.125la.com/381.html
关注我们:请关注一下我们站长微信:扫描二维码125啦读书导航的微信号,微信号:watching_me
版权声明:本文为原创或转载文章,版权归原作者所有,欢迎分享本文,转载请保留出处!
第一个读书导航

发表评论


表情