vx公众号:Android部落格
工程代码在:
1.背景
Launcher3工程实践过程中,列表定制需求太多,但是该功能耦合代码过多,耽误程序员太多时间去理解梳理功能逻辑。
2.思维导图
按照思路,只需要将这七个类独立剥离出来,并将相关耦合的类删除即可。其中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的排挤,拖动换页的逻辑。