2015已经来了快半月了,始终还是没感受到一点新年的气氛,但是终归是还没过年这对于我们来说好像也确实正常。
目前来说自我感觉还是沉浸在年末的那种气氛中,那就暂且年末吧。年末因为部分原因再次需要接触Launcher的代码了,记得14年最开始的时候就看过一部分Launcher2的代码,现在重新回头来看却发现感触颇多,竟然不知不觉忘掉了很多,也在看了许久之后又有点重新认识的感觉。
回归正题,此次不仅仅是要修改launcher2的源码部分,还要加一点自己的东西,因为还是依托于原生的launcher2的原因,只是想在固有launcher2的UI架构上增加一点自己的东西,关于launcher2的UI架构部分,我也就啰嗦下再次重温下,毕竟之前应该是没有记录过的。
首先最直观的是UI界面部分的大概分类。简单截张图算是凑合看看
之所以我区分为三个区域也是出于这三个部分已经占据了UI布局的大部分,分别是从左到右 搜索框/删除框所占区域,应用图标/窗口小部件所占区域,快捷功能键所占区域。如果按布局文件launcher.xml区分就得更为细致了。在此可以借鉴下网友解析的代码段
<com.android.launcher2.DragLayer xmlns:android="http://schemas.android.com/apk/res/android" xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher" android:id="@+id/drag_layer" ... > <!-- Keep these behind the workspace so that they are not visible when we go into AllApps --> <include android:id="@+id/dock_divider" layout="@layout/workspace_divider" ... /> <!-- 分页指示器 --> <include android:id="@+id/paged_view_indicator" layout="@layout/scroll_indicator" ... /> <!-- The workspace contains 5 screens of cells --> <com.android.launcher2.Workspace android:id="@+id/workspace" ... > <!-- 五个分屏,默认显示cell3 --> <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> <!-- 搜索框/删除框 --> <include android:id="@+id/qsb_bar" layout="@layout/qsb_bar" /> <!-- 显示具体全部应用的界面,包括APPS、WIGHETS的tab标签,以及显示ALL APP的页面和现实APP WIGHETS的页面 --> <include layout="@layout/apps_customize_pane" android:id="@+id/apps_customize_pane" .. /> <!-- WorkSpace最下面的五个快捷位置 --> <include layout="@layout/hotseat" android:id="@+id/hotseat" .. /> <!-- 刚启动的时候显示的指导页 --> <include layout="@layout/workspace_cling" android:id="@+id/workspace_cling" ... /> <!-- 是第一次进入全部应用之后显示的指导页 --> <include layout="@layout/folder_cling" android:id="@+id/folder_cling" ... /> </com.android.launcher2.DragLayer>上面部分的解析已经非常清楚明了了,不再解释什么了。
对于launcher2的修改也就是从上面几个地方下手了。可以看出整个launcher的布局都是包含在com.android.launcher2.DragLayer中的,再详细看这部分java代码就会明白之前一直在说对于framelayout这个布局的最佳实例就是launcher这话并没错,因为launcher的源码才是将这个布局的各个特点发挥的淋漓尽致了。可想而知Framelayout做出来的launcher在安卓这个平台作为门面展示给所有人的将会有多么全面了。
之前一直想法是要在这个固有分区的基础上增加一个自己的分区来增加一点自己的功能,于是果断修改了launcher.xml布局,并将DragLayer的外层增加了一层LinearLayout同时在这个布局左边增加一个LinearLayout来显示自己的一点东西。但是这个简单的改动编译时有错误,log显示
Workspace can only be used in EXACTLY mode.
跟进到PageeView.java代码中找到这句异常提示
if (widthMode != MeasureSpec.EXACTLY) { throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); }不知道出于什么考虑要将workspace区域设置为EXACTLY模式,查了下这个模式的资料貌似必须指定width或者height的具体数据或者fill_parent,match_parent才能判定为那个模式,因为时间问题暂时就没考虑直接注释,果断编译通过,剩下的工作就是计算自己区域的宽度将剩余区域分给workspace并指定合理的图标排布数量launcher就基本正常了。
再回到自己的Framelayout,因为想在自己的区域来根据不同情况显示不同的view,因此首先想到过Fragment,但是随后考虑再三决定不采用这个方法,因为Fragment的切换需要将调用Fragment显示的主Activity继承自FragmentAcitivity,显然我感觉对于一个简单的修改去改动Launcher.java的继承关系有点过于复杂,因此想了下用FrameLayout的方式就再简单不过了。于是在自己的LinearLayout中做了简单的测试,首先是要在launcher布局中加入这样的测试布局
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/myframelayout" > <include android:id="@+id/view1" layout="@layout/mylayout1"></include> <include android:id="@+id/view2" layout="@layout/mylayout2"></include> </FrameLayout> </LinearLayout>加完这些,还需重新做另外两个简单的布局,因为仅仅是测试,所以仅需要每个layout中加一个TextView即可,能区分就够了。
然后是在Launcher中的操作
LayoutInflater minflater; View myview1; View myview2; FrameLayout myframelayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); minflater = getLayoutInflater(); myview1 = (View)minflater.inflate(R.layout.mylayout1, null); myview2 = (View)minflater.inflate(R.layout.mylayout2, null); myframelayout = (FrameLayout) findViewById(R.id.myframelayout); ........ }此处给出了部分最终的代码,我之前说的view切换也就是这中嵌套的layout 的切换,我开始的做法是直接调用myview1.setVisibility(View.GONE)这种办法使其隐藏或者显示,但是我发现始终是无效的,可能如一个网友说的framelayout的特性就是分层叠加,后面的布局会逐层叠加上去,即使View.GONE了但是依然达不到显示另一个的效果。所以最后经过尝试还是采用了上述代码在需要显示其中一个view的时候,
myframelayout.removeAllViews();
myframelayout.addView(myview2);
这种方法的原理很简答就是每次需要重新显示一个view的时候先将framelayout中的所有嵌入view都清除,然后重新将索要显示的view显示出来。
后续如果需要监听嵌入layout布局中的按键响应,可以用类似myview2.findViewById(R.id.xxx)的方法找到相应id的按键去重写监听。
到此起码我最初想的是简单实现了,具体的效率和隐患因为时间关系暂时没测试,后续会继续测试和关注。