Launcher3模块化-应用列表

223 阅读3分钟

vx公众号:Android部落格

工程代码在:

github.com/chuck9091/P…

1.背景

Launcher3工程实践过程中,列表定制需求太多,但是该功能耦合代码过多,耽误程序员太多时间去理解梳理功能逻辑。

2.思维导图

icon列表思维导图.png

按照思路,只需要将这七个类独立剥离出来,并将相关耦合的类删除即可。其中LauncherRootView作为ViewGroup的最顶层,可以定制为自己的类,作为对外的接口。

其中还有持久化相关的ContentProvider数据库操作逻辑无处不在,这里也先剥离掉,需要更新数据库的地方,通过interface回调对外暴露接口。

3.各个击破

3.1 配置

将DeviceProfile相关的逻辑删除。这里面定义了icon大小,排列row和column,间距等。在主Activity启动的时候会读取xml配置并填充,这里面的逻辑比较复杂,总是需要去找配置里面的数值,然后做定制,比较费时间,这里相关的都去掉。

改为通过一个以构造者模式为主的类,所有配置参数在这里面定义,灵活且方便查看。

public final class PaginationProfile {
	private int params;
	public int get(){
		return params;
	}
	public PaginationProfile(Builder builder) {
		this.params = builder.params;
	}
	public static final class Builder {
		public int params;
		public void set(){}
		
		public PaginationProfile build() {}
	}
}

实现在:java/com/chuck/paginationscrollview/builder/PaginationProfile.java

可在Application中做View参数的初始化。

3.2 顶级父类

LauncherRootView改为PaginationScrollView,前者核心功能是Insets,关注边界消费;后者改为关注自己的子孙,及其状态接收和外发,将Insets去掉。

public class PaginationScrollView extends FrameLayout {
	public void initChildViews(Workspace workspace, DragLayer dragLayer) {}
	public DragLayer getDragLayer(){}
	public Workspace getWorkspace(){}
	public CellLayout getCellLayout(){}
	public void bindItems(final List<ItemInfo> items){}
	public void setItemInfoChangedCallBack(ItemInfoChangedCallBack itemInfoChangedCallBack)
	 {}
}

实现在:java/com/chuck/paginationscrollview/view/PaginationScrollView.java

这里集中关键的实现类,同时配置相关的类一次初始化,全局可以调用,与业务类解耦。

3.3 子类实现

DragLayer,Workspace,CellLayout,ShortAndWidgetContainer,BubbleTextView,这几个关键的类里面,将Widget,Folder,FolderIcon相关的逻辑去掉,仅保留*ITEM_TYPE_APPLICATION*相关的实现。

实现在:com/chuck/paginationscrollview/view/,com/chuck/paginationscrollview/dragndrop

这一块改动量最大,如果光贴代码没什么意思;如果贴代码又一行一行去讲解,也没什么意思,过于枯燥。其实吃鸡蛋的时候,咱们很少去考虑是哪只鸡生的,只需要在定制某一块功能的时候,再去深究就可以了。

4.上层调用

4.1 xml定义

比如主module的页面是activity_main.xml,可以定义如下:

<?xml version="1.0" encoding="utf-8"?>
<com.chuck.paginationscrollview.view.PaginationScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:launcher="http://schemas.android.com/apk/res-auto"
    android:id="@+id/pagination_scroll_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    launcher:dragLayer="@+id/drag_layer"
    launcher:workspace="@+id/workspace">

    <com.chuck.paginationscrollview.dragndrop.DragLayer
        android:id="@+id/drag_layer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:importantForAccessibility="no">

        <!-- The workspace contains 5 screens of cells -->
        <!-- DO NOT CHANGE THE ID -->
        <com.chuck.paginationscrollview.view.Workspace
            android:id="@+id/workspace"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:theme="@style/HomeScreenElementTheme"
            launcher:pageIndicator="@+id/page_indicator" />

        <!-- Keep these behind the workspace so that they are not visible when
         we go into AllApps -->
        <com.chuck.paginationscrollview.pageindicators.WorkspacePageIndicator
            android:id="@+id/page_indicator"
            android:layout_width="match_parent"
            android:layout_height="@dimen/vertical_drag_handle_size"
            android:layout_gravity="bottom|center_horizontal"
            android:theme="@style/HomeScreenElementTheme" />
    </com.chuck.paginationscrollview.dragndrop.DragLayer>

</com.chuck.paginationscrollview.view.PaginationScrollView>

从视图层级看,这里与原生的保持一致,基本不需要做额外的修改。

4.2 配置定义

PaginationProfile paginationProfile = (PaginationProfile) new PaginationProfile.Builder()
                .setHeightPx(750)
                .setWidthPx(360)
                .setCellHeightPx(48)
                .setCellWidthPx(48)
                .setNumColumns(column)
                .setNumRows(row)
                .setIconDrawablePaddingPx(2)
                .setDefaultPageSpacingPx(2)
                .setEdgeMarginPx(2)
                .setIconSizePx(36)
                .setIconTextSizePx(16)
                .setCellTextColor(getColor(R.color.black))
                .setWorkspacePadding(new Rect(0, 0, 0, 0))
                .build();

定义行列数,格子尺寸,icon大小,padding,文字大小等。

PaginationScrollView paginationScrollView = findViewById(R.id.pagination_scroll_view);
paginationScrollView.setPaginationProfile(paginationProfile);
paginationScrollView.initChildViews(findViewById(R.id.workspace), findViewById(R.id.drag_layer));

配置初始化以及视图初始化。

4.3 数据绑定

初始化数据:

int sizePerPage = row * column;

List<ItemInfo> itemInfos = new ArrayList<>();
int currentRow = -1;
for (int i = 0; i < fruits.length; i++) {
    ItemInfo itemInfo = new ItemInfo();
    itemInfo.iconDrawable = getDrawable(fruits[i]);
    itemInfo.title = "水果" + i;
    itemInfo.spanX = 1;
    itemInfo.spanY = 1;

    int currentItemIndexInPage = i % sizePerPage;
    if (currentItemIndexInPage == 0) {
        currentRow = 0;
    }

    //column
    itemInfo.cellX = currentItemIndexInPage % column;
    LogUtils.d(TAG, "cellX: " + itemInfo.cellX);
    if (currentItemIndexInPage != 0 && currentItemIndexInPage % column == 0) {
        currentRow++;
    }
    //row
    itemInfo.cellY = currentRow;
    itemInfo.screenId = i / sizePerPage;
    itemInfos.add(itemInfo);
    LogUtils.d(TAG, "itemInfo: " + itemInfo);
}
paginationScrollView.bindItems(itemInfos, false);

调用bindItems方法之后,所有的item就被添加到视图里面去了,不局限于应用icon,其他任何业务的icon和text都可以展示,并享受Launcher3的排挤,拖动换页的逻辑。