GitHub
Shield是一个模块化UI界面解决方案,它不仅仅是一个Native(Android&iOS)的UI开发框架,而是美团点评到店综合团队基于自身复杂的业务场景沉淀出来的UI开发最佳实践,它不但具备高可复用性,协同开发等特性,还包括后端动态配置,动态模块等一系列解决方案
一个典型的模块化页面主要由页面和多个模块构成,页面是由一个个模块(Agent)组成的。:
- 页面通过模块配置(Config)确定加载哪些模块构成页面。
- 模块又分为业务逻辑(Agent)和视图逻辑部分(SectionCellInterface, 包括 Section 和 Row)。
- 页面包含两个管理器,模块管理器(AgentManager)和视图管理器(CellManager)。
- 其中模块管理器决定了如何创建,更新,恢复及销毁模块以及如何将模块添加到页面中;
- 视图管理器则决定了页面使用何种视图容器管理视图以及模块中的视图组件(SectionCellInterface)如何添加到视图容器中。
- 除此之外,页面还持有一个支持数据订阅和通知的白板(WhiteBoard),用于模块与页面,模块与模块之间的数据交互
简介
官方DEMO大量依赖了recyclerView,内部封装了较多附加逻辑,个人认为可能不太适用于理解整体Shield的的设计实现,因此我们还是使用最基础的ViewGroup和ListAdapter讲解
- 页面容器:具有生命周期的元素,譬如Activity、Fragment等;
- 布局容器:页面容器的布局中为盛放模块的父布局;
- 模块组 包含多个模块,在实际上就是一个List<模块>
- 模块组构成配置类AgentListConfig :描述该模块组是否应当被展示,和内部包含各个模块的Class信息及排序
- 模块配置信息AgentInfo:描述一个模块 对应的Class和其在组内的排序位置;
- 模块:具有和页面容器相同生命周期的单元,包含部分页面UI展示和与之相关的业务逻辑
- 片段视图
- 行视图
一、模块
【图1】
在Shield框架里,页面是由一个个模块(Agent)拼接而成的(我们说的页面容器往往指的是Activity、Fragment、Dialog等具有Windows的页面)。
模块是根据视图和业务逻辑对页面进行的横向切分,注意它和MVP或MVVM的设计模式并不冲突,开发者完全可以在Shield的某个模块里运用MVP或MVVM的架构方式,来对页面的逻辑进行进一步的拆分以提升代码复用性,使模块逻辑变得更加清晰.
模块遵循与页面容器相匹配的生命周期,从而保证自己拥有独立的业务逻辑处理能力;同时也包含诸如Create View和 Update View等UI操作能力。
这些模块按线性的方式排布在页面中,可以很灵活地调换位置且互不影响,每个模块都有自己独立的生命周期,可以单独通过网络获取数据、渲染视图等等
【图2】
如果将Fragment理解为是针对Actvity的一种更小粒度层面的复用,那么模块是较于Fragment更小力度的一种复用:
- 每一个模块都有自己独立的逻辑和UI,模块之间完全解耦,这样就可以很方便地通过排列模块来完成不同的页面定制化需求,使一个页面可以展示不同的内容。
- 同时,由于模块并不依赖某一具体页面,模块也可以在不同的页面之间进行复用
1.1 模块接口AgentInterface
为了更好地抽象UI界面开发的各种场景,Shield框架赋予了模块完整的页面能力,包括完整的页面生命周期和上下文环境(Context)等。这样模块的开发方式与原有的页面开发方式完全一致,页面不再关心具体的UI展现,而是把这些都交给模块。
同时模块具备自己的UI视图操作能力,可以单独开发维护,运行在任意接入了Shield框架的页面中
因此,我们抽象一个模块的时候,它应该具备以下3样功能:
- 遵循页面容器的生命周期回调;
- 提供一个SectionCellInterface片段视图的操作手柄;
- 一些Utils方法
public interface AgentInterface {
/*****************************1. 遵循页面容器的生命周期回调;******************************************/
void onCreate(Bundle savedInstanceState);
void onStart();
void onResume();
void onPause();
void onStop();
void onDestroy();
Bundle saveInstanceState();
void onActivityResult(int requestCode, int resultCode, Intent data);
/****************************2. 提供SectionCellInterface 视图片段实体,提供视图View创建和更新功能;*******************************/
SectionCellInterface getSectionCellInterface();
/****************************3. Utils**********************************************************************/
/**
* 当前模块在模块组中的排序
* 譬如:1.0, 2.0
*/
String getIndex();
void setIndex(String index);
/**
* 当前模块在模块组中的命名Key
*/
String getHostName();
void setHostName(String hostName);
@Deprecated
void onAgentChanged(Bundle data);
String getAgentCellName();
}
1.2 抽象实现LightAgent
LightAgent之于接口其实并未封装其他逻辑,只是扩展了以下功能:
- 模块嵌套:使得模块可以再次承载 包含模块 的能力,也就是说使得模块可以再次充当模块容器的作用;
- 实现基本的Utils方法;
- 持有Fragment类型的页面容器本身,从而进一步持有页面容器的相关能力;
- 持有页面容器的生命周期回调PageContainerInterface;
- 持有模块驱动器,使得模块具有(访问白板 的能力) 和 (通知模块管理器以更新模块 的能力)
1.2.1 模块容器的能力抽象
我们往往是将一个页面容器,作为模块的承载容器,然而某些需求下,我们可能期望模块内部嵌套模块,因此我们需要将页面容器中作为模块的容器的功能进行抽象:
- 是对页面容器针对Shield管理能力的一种抽象;
- 1.持有 模块组构成配置类列表;
- 2.持有模块管理器和视图管理器;
- 3.具备重置模块的能力;
public interface ShieldContainerInterface {
/**
* 1. 持有 模块组构成配置类列表;
*/
ArrayList<AgentListConfig> generaterConfigs();
/**
* 2. 持有模块管理器和视图管理器;
*/
CellManagerInterface getHostCellManager();
AgentManagerInterface getHostAgentManager();
/**
* 3. 具备重置模块的能力;
* 在运行过程中,agentlist有变化的时候调用,更新agentlist列表本身的值,区别于dispatchCellChanged,
* 单纯只更新现有的agentlist中的agent内容
*/
void resetAgents(Bundle savedInstanceState);
}
1.2.2 实现
public abstract class LightAgent implements AgentInterface, ShieldContainerInterface {
public String index = "";
public String hostName = "";
/**
* 3. 持有Fragment类型的页面容器本身,从而进一步持有页面容器的相关能力;
*/
protected Fragment fragment;
/**
* 4. 持有页面容器的生命周期回调PageContainerInterface;
*/
protected PageContainerInterface pageContainer;
/**
* 5. 持有模块驱动器,使得模块具有(访问白板 的能力) 和 (通知模块管理器以更新模块 的能力)
*/
protected DriverInterface bridge;
public LightAgent(Fragment fragment, DriverInterface bridge, PageContainerInterface pageContainer) {
this.fragment = fragment;
this.bridge = bridge;
this.pageContainer = pageContainer;
}
/*****************************1. 遵循页面容器的生命周期回调;******************************************/
@Override
public void onCreate(Bundle savedInstanceState) {
}
@Override
public void onStart() {
}
@Override
public void onResume() {
}
@Override
public void onPause() {
}
@Override
public void onStop() {
}
@Override
public void onDestroy() {
}
@Override
public Bundle saveInstanceState() {
return new Bundle();
}
@Deprecated
@Override
public void onAgentChanged(Bundle data) {
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
/****************2. 提供SectionCellInterface 视图片段实体,提供视图View创建和更新功能;****************/
@Override
public SectionCellInterface getSectionCellInterface() {
return null;
}
/****************************3. Utils**********************************************************************/
/**
* 当前模块在模块组中的排序
* 譬如:1.0, 2.0
*/
@Override
public String getIndex() {
return index;
}
@Override
public void setIndex(String index) {
this.index = index;
}
/**
* 当前模块在模块组中的命名Key
*/
@Override
public String getHostName() {
return hostName;
}
@Override
public void setHostName(String hostName) {
this.hostName = hostName;
}
@Override
public String getAgentCellName() {
return hashCode() + "-" + this.getClass().getCanonicalName();
}
/***********************************模块容器的能力抽象******************************************/
//模块嵌套开始
@Override
public ArrayList<AgentListConfig> generaterConfigs() {
return null;
}
@Override
public void resetAgents(Bundle savedInstanceState) {
if (fragment instanceof ShieldContainerInterface) {
((ShieldContainerInterface) fragment).resetAgents(savedInstanceState);
}
}
@Override
public CellManagerInterface getHostCellManager() {
if (fragment instanceof ShieldContainerInterface) {
return ((ShieldContainerInterface) fragment).getHostCellManager();
}
return null;
}
@Override
public AgentManagerInterface getHostAgentManager() {
if (fragment instanceof ShieldContainerInterface) {
return ((ShieldContainerInterface) fragment).getHostAgentManager();
}
return null;
}
/************************************************************************************/
/**
* 5. 持有模块驱动器,使得模块具有(访问白板 的能力) 和 (通知模块管理器以更新模块 的能力)
*/
public void updateAgentCell() {
bridge.updateAgentCell(this);
}
public WhiteBoard getWhiteBoard() {
return bridge.getWhiteBoard();
}
/**
* 3. 持有Fragment类型的页面容器本身,从而进一步持有页面容器的相关能力;
*/
public Fragment getHostFragment() {
return fragment;
}
public void startActivity(Intent intent) {
fragment.startActivity(intent);
}
public void startActivityForResult(Intent intent, int requestCode) {
fragment.startActivityForResult(intent, requestCode);
}
public Context getContext() {
return fragment.getContext();
}
}
1.3 ControlButtonAgent 示例
只是简单的在 OnCreate()的时候创建了一个[视图片段接口,提供视图View创建和更新功能] 实例,并实现了访问接口
public class ControlButtonAgent extends LightAgent {
private ControlButtonViewCell mControlButtonViewCell;
public ControlButtonAgent(Fragment fragment, DriverInterface bridge, PageContainerInterface pageContainer) {
super(fragment, bridge, pageContainer);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mControlButtonViewCell = new ControlButtonViewCell(getContext());
getWhiteBoard().putString("status", "DONE");
}
@Override
public SectionCellInterface getSectionCellInterface() {
return mControlButtonViewCell;
}
}
public class ClickSectionThreeAgent extends LightAgent {
private ClickViewCell mClickViewCell;
public ClickSectionThreeAgent(Fragment fragment, DriverInterface bridge, PageContainerInterface pageContainer) {
super(fragment, bridge, pageContainer);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mClickViewCell = new ClickViewCell(getContext());
mClickViewCell.setOnItemClickListener(
new ItemClickInterface.OnItemClickListener() {
@Override
public void onItemClick(View view, int section, int position) {
Toast.makeText(getHostFragment().getActivity(),
"Module0, Body, Section" + section + ", Row" + position + " clicked", Toast.LENGTH_SHORT).show();
}
}
);
}
@Override
public void onResume() {
super.onResume();
}
@Override
public SectionCellInterface getSectionCellInterface() {
return mClickViewCell;
}
}
在示例Demo中,共还实现了以下18个Agent(逻辑上并不具备分组性,但是demo的Group配置是按以下方式):
- ClickFragment : ClickAgentConfig
- ClickSectionThreeAgent 点击示例Agent:行视图具有点击事件
- ClickSectionTwoAgent
- HeaderFooterCellFragment :HeaderFooterCellAgentConfig
- HeaderFooterCellFirstAgent 头尾示例Agent:指定片段具有头部或者尾部
- HeaderFooterCellSecondAgent
- StatusFragment : StatusAgentConfig
- ControlButtonAgent
- LoadingStatusAgent
- LoadingStatusMoreAgent
- LoadingStatusMoreAgent
DividerFragment:DividerAgentConfig
- DefaultDividerAgent
- SectionDividerAgent
- RowDividerAgent
LinkTypeFragment : LinkTypeAgentConfig
- LinkTypeCustomAgent
- LinkTypeFirstAgent
- LinkTypeSecondAgent
- LinkTypeThirdAgent
- LinkTypeFourthAgent
- MixFragment : MixAgentConfig
- MixLoadingAgent
- MixCellAgent
二、模块组
【图3】
模块可以理解为Shield中最小的具备业务能力的封装体,模块组则是几个模块的组合,具备一定的排序规则和命名。
模块组提供了一种更粗力度的复用,在代码上的体现其实就是一个 List<模块>,在模块管理器中构建和持有;
为了将模块构建为一个有序的List,定义了【模块组构成配置类】
2.1 模块组构成配置类AgentListConfig
模块组构成配置类AgentListConfig用于表达 由模块组成模块组的规则,具体的组成过程是由AgentManagerInterface控制的
2.1.1 定义
模块组构成配置类
描述该模块组是否应当被展示,和内部包含各个模块的Class信息及排序:
- 该模块组 是否应该被展示;
- 该模块组 中的各个模块的ClassList;
- 该模块组 中的各个模块的模块配置信息List;
其中2 和 3 只需要选择其一配置即可,一般使用3模块配置信息List,它包含自定义的排序,而前者则是自动生成
配置的主要内容就是:
- 当前模块在模块组中的命名Key;
- 当前模块 Class Name(以便以反射方式自动生成);
- 当前模块的排序;
public interface AgentListConfig {
/**
* 1. 此函数返回此模块组是否该被展示
* 需非常谨慎,因为不管是否展示,此方法都会被调用,绝对不许在此方法中crash
*
* @return true 会被展示,false 不被展示
*/
boolean shouldShow();
/**
* 3. 得到该组所包含的 待排序的 各个模块配置信息
* 当前模块在模块组中的命名Key : 模块配置信息
* @return
*/
Map<String, AgentInfo> getAgentInfoList();
/**
* 2. 该模块组 中的各个模块的ClassList
* 当前模块在模块组中的命名Key : 模块类信息
*/
Map<String, Class<? extends AgentInterface>> getAgentList();
}
/**
* 模块配置信息
* 描述一个模块 对应的Class和其在组内的排序位置;
* 1. 该模块对应的 Class
* 2. 该模块在 模块组内的排序
*/
public class AgentInfo implements Serializable {
/**
* 自定义的模块类
*/
public Class<? extends AgentInterface> agentClass;
/**
* 该模块在序列中排序
*/
public String index;
/**
* 带排序的Agent
*
* @param agentClass
* @param index 可以写类似"0200Basic.10Address" 不允许传null
*/
public AgentInfo(Class<? extends AgentInterface> agentClass, String index) {
if (index == null) {
throw new RuntimeException("index 不许为null 可以传空字符串");
}
this.agentClass = agentClass;
this.index = index;
}
@Override
public String toString() {
return agentClass.getSimpleName() + " " + index;
}
}
2.1.2 StatusAgentConfig示例
StatusAgentConfig对应的StatusAgent模块组,包含四个模块
public class StatusAgentConfig implements AgentListConfig {
private static final String AGENT_PKG_NAME = "com.example.shield.status.agent.";
@Override
public boolean shouldShow() {
return true;
}
//1. 当前模块在模块组中的命名Key
//2. 当前模块 Class Name(以便以反射方式自动生成)
@Override
public Map<String, AgentInfo> getAgentInfoList() {
String[][][] agentArray = {
{
{"loading_button", AGENT_PKG_NAME + "ControlButtonAgent"},
{"loading", AGENT_PKG_NAME + "LoadingStatusAgent"}
},
{
{"loading_more", AGENT_PKG_NAME + "LoadingStatusMoreAgent"}
},
{
{"loading_more_again", AGENT_PKG_NAME + "LoadingStatusMoreAgent"}
}
};
return AgentInfoHelper.getAgents(agentArray);
}
@Override
public Map<String, Class<? extends AgentInterface>> getAgentList() {
return null;
}
}
结果:
"loading_button" -> "ControlButtonAgent 0.0"
"loading" -> "LoadingStatusAgent 0.1"
"loading_more" -> "LoadingStatusMoreAgent 1.0"
"loading_more_again" -> "LoadingStatusMoreAgent 2.0"
在这里我们把Demo中其他5个的配置项也罗列出来:
- ClickFragment ClickAgentConfig 共有2个模块
- ”module_a” -> “ClickSectionThreeAgent 0.0”
- “module_b” -> “ClickSectionTwoAgent 1.0”
- DividerFragment:DividerAgentConfig 共有3个模块
- ”default_divider” -> “DefaultDividerAgent 0.0”
- “section_divider” -> “SectionDividerAgent 1.0”
- “row_divider” -> “RowDividerAgent 2.0”
- HeaderFooterCellFragment :HeaderFooterCellAgentConfig 共有2个模块
- ”headerfootercellagent1” -> “HeaderFooterCellFirstAgent 0.0”
- “headerfootercellagent2” -> “HeaderFooterCellSecondAgent 1.0”
- LinkTypeFragment : LinkTypeAgentConfig 共有5个模块
- ”linktype_custom” -> “LinkTypeCustomAgent 0.0”
- “linktype0” -> “LinkTypeFirstAgent 1.0”
- “linktype1” -> “LinkTypeSecondAgent 2.0”
- “linktype2” -> “LinkTypeThirdAgent 3.0”
- “linktype3” -> “LinkTypeFourthAgent 4.0”
- MixFragment : MixAgentConfig 共有2个模块
- ”mix0” -> “MixLoadingAgent 0.0”
- “mix1” -> “MixCellAgent 0.1”
2.1.3 AgentInfoHelper索引辅助类
public class AgentInfoHelper {
/*******************将字符数组转化为具有顺序索引的模块配置信息Map*************************/
/**
* 从agent多维数组中生成 该模块组中的各个模块的模块配置信息Map
*
* @return config map.
*/
public static Map<String, AgentInfo> getAgents(String[][][] configArray) {
if (configArray == null) return null;
HashMap<String, AgentInfo> agents = new LinkedHashMap<String, AgentInfo>();
for (int i = 0; i < configArray.length; i++) {
if (configArray[i] == null) continue;
for (int j = 0; j < configArray[i].length; j++) {
try {
agents.put(configArray[i][j][0],
new AgentInfo((Class<? extends AgentInterface>) Class.forName(configArray[i][j][1]),
addZeroPrefix(i, getIntStrLength(configArray.length)) //指定层级的排序Index
+ "." + addZeroPrefix(j, getIntStrLength(configArray[i].length))));//指定层级的排序Index
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
return agents;
}
/**
* 获取指定Num用String表示时的位数
* 譬如: 10 需要 Length的2的字符串
* 譬如: 3 需要Length为1的字符串
* @param num
* @return
*/
public static int getIntStrLength(int num) {
return ((int) Math.log10(num) + 1);
}
/**
* 将目标Num转换为指定长度的Str,不够则前方补0
* 譬如:(0,1)--->0
* 譬如:(1,2)--->01
* @param num 待转化为String的Num
* @param zerolength 应该满足的长度
* @return
*/
public static String addZeroPrefix(int num, int zerolength) {
int j = getIntStrLength(num);
String zeroStr = "";
for (int k = 0; k < zerolength - j; k++) {
zeroStr = zeroStr + "0";
}
return zeroStr + num;
}
}
三、视图
一般而言,模块组内的模块都会持有自己的视图,一个模块视图可以从显示上再次切分为片段视图,每一块片段视图可以是视觉上的单行(Row)视图,也可以是多行视图
- 模块视图
- 片段视图
- 行视图
3.1 模块视图
3.1.1 模块视图接口SectionCellInterface
我们对模块视图所承载的功能进行抽象:
- 视图片段接口,提供视图View创建和更新功能;
-
- 提供 片段和行 的个数;
-
- 提供 片段&行 : View类型 的映射关系;
-
- 根据View类型创建对应的View视图;
-
- 更新指定片段、指定行、指定View的显示;
-
public interface SectionCellInterface {
/***************************1. 提供 片段和行 的个数;**************************************/
int getSectionCount();
int getRowCount(int sectionPosition);
/***************************2. 提供 片段&行 : View类型 的映射关系;**************************************/
int getViewType(int sectionPosition, int rowPosition);
int getViewTypeCount();
/***************************3. 根据View类型创建对应的View视图;**************************************/
View onCreateView(ViewGroup parent, int viewType);
/***************************4. 更新指定片段、指定行、指定View的显示;**************************************/
void updateView(View view, int sectionPosition, int rowPosition, ViewGroup parent);
}
3.1.2 支持片段头脚的模块视图SectionExtraCellInterface
- 在SectionCellInterface模块视图的基础上加入 头脚视图的控制能力
public interface SectionExtraCellInterface extends SectionCellInterface {
/**
* headercell
*/
//该section是否有headercell
boolean hasHeaderForSection(int sectionPostion);
//是否显示该section的headercell的上分割线
boolean hasTopDividerForHeader(int sectionPosition);
//是否显示该section的headercell的下分割线
boolean hasBottomDividerForHeader(int sectionPosition);
//headercell的分割线左边距
float getHeaderDividerOffset(int sectionPosition);
//headercell的类型总数
int getHeaderViewTypeCount();
//headercell的类型
int getHeaderViewType(int sectionPosition);
//当headercell被创建时调用
View onCreateHeaderView(ViewGroup parent, int viewType);
//当headercell被更新时调用
void updateHeaderView(View view, int sectionPostion, ViewGroup parent);
/**
* footercell
*/
//该section是否有footercell
boolean hasFooterForSection(int sectionPostion);
//是否显示该section的footercell的下分割线
boolean hasBottomDividerForFooter(int sectionPosition);
//footercell的分割线左边距
float getFooterDividerOffset(int sectionPosition);
//footercell的类型总数
int getFooterViewTypeCount();
//footercell的类型
int getFooterViewType(int sectionPosition);
//当footercell被创建时调用
View onCreateFooterView(ViewGroup parent, int viewType);
//当footercell被更新时调用
void updateFooterView(View view, int sectionPostion, ViewGroup parent);
}
3.1.3 抽象视图 BaseViewCell
一个视图除了基础的 当前|头|尾布局的操作外,还应该支持一些其他的操作,由于Shield内部将控件布局进行了封装且并未对外开放,因此需要一系列的接口来对这些附属的能力进行抽象
代码就不贴了,基本就是下边的空实现
3.1.3.1 视图点击监听ItemClickInterface
当前|头|尾布局需要各自监听自己的点击事件
public interface ItemClickInterface {
public void setOnItemClickListener(OnItemClickListener listener);
public OnItemClickListener getOnItemClickListener();
public interface OnItemClickListener {
void onItemClick(View view, int section, int row);
}
}
3.1.3.2 视图ID器ItemIdInterface
在某些场景下,需要获得指定item的资源ID,定义这个回调以开放给用户调用
public interface ItemIdInterface {
long getItemId(int section, int position);
}
3.1.3.3 片段间隔关联器SectionLinkCellInterface
片段时间默认是有间隔的,有些场景下,我们需要知道,不同片段之间的链接关系。以确定是否需要去除间隔:
- 这种间隔是由前后共同决定的,都允许相连才相连
- 一个DEFAULT的话表示,由另一个节点决定;
- 都DEFAULT的话,不连接
public class LinkType {
public enum Previous {
//section之间不连接
DEFAULT,
//与前一个section连接
LINK_TO_PREVIOUS,
//禁止前一个section与我连接
DISABLE_LINK_TO_PREVIOUS
}
public enum Next {
//section之间不连接
DEFAULT,
//与后一个section连接
LINK_TO_NEXT,
//禁止后一个section与我连接
DISABLE_LINK_TO_NEXT
}
}
因此定义了如下接口:
public interface SectionLinkCellInterface {
LinkType.Previous linkPrevious(int sectionPosition);
LinkType.Next linkNext(int sectionPosition);
float getSectionHeaderHeight(int sectionPoisition);
float getSectionFooterHeight(int sectionPoisition);
}
3.1.3.4 刷新状态控制器CellStatusInterface
有些视图需要表达刷新状态:
- 刷新中需要显示 loadingView
- 刷新失败需要显示 failView,并具有重试监听事件
- 刷新成功则显示 正常的 布局View
- 刷新成功但是无数据 需要显示 emptyView
我们对该逻辑进行抽象,可以得到以下:
public class CellStatus {
public enum LoadingStatus {
EMPTY,
LOADING,
FAILED,
DONE,
UNKNOWN
}
}
public interface CellStatusInterface {
CellStatus.LoadingStatus loadingStatus();
View loadingView();
View loadingFailedView();
View emptyView();
View.OnClickListener loadingRetryListener();
}
3.1.3.5 加载更多状态控制器CellStatusMoreInterface
有些视图需要表达加载更多状态:
- 加载更多状态中需要显示 loadingMoreView
- 加载更多失败需要显示 loadingMoreFailedView,并具有监听事件
- 加载更多则显示 正常的 布局View
我们对该逻辑进行抽象,可以得到以下:
public class CellStatus {
public enum LoadingMoreStatus {
LOADING,
FAILED,
DONE,
UNKNOWN
}
}
public interface CellStatusMoreInterface {
CellStatus.LoadingMoreStatus loadingMoreStatus();
void onBindView(CellStatus.LoadingMoreStatus status);
View loadingMoreView();
View loadingMoreFailedView();
View.OnClickListener loadingMoreRetryListener();
}
3.1.3.6 分割线管理器DividerInterface
某些情况下,期望对片段或者行视图进行分割线的控制,注意不是分割:
- 根据片段的行,定义分割线是否显示
- 根据片段的行,自定义分割线的Drawable;
- 分割线的左侧缩进;
- 定义片段分割线的显示类型;
- 只展示Section顶部和底部分割线
- 展示所有分割线
- 隐藏所有分割线
- 隐藏Section顶部和底部分割线,只展示Row之间分割线
- 隐藏Section顶部分割线
public interface DividerInterface {
//分割线ShowType
enum ShowType {
//只展示Section顶部和底部分割线
TOP_END,
//展示所有分割线
ALL,
//隐藏所有分割线
NONE,
//隐藏Section顶部和底部分割线,只展示Row之间分割线
MIDDLE,
//隐藏Section顶部分割线
NO_TOP,
DEFAULT
}
//自定义某个Row下方的分割线View,以Drawable形式提供
Drawable getDivider(int sectionPosition, int rowPosition);
//自定义某个Row下方的分割线距Section边缘的左边距
int dividerOffset(int sectionPosition, int rowPosition);
//自定义某个Row下方的分割线是否展示
boolean showDivider(int sectionPosition, int rowPosition);
//自定义某个Section的分割线ShowType
ShowType dividerShowType(int sectionPosition);
}
3.2 示例
我们在 【模块示例】章节,给出了Demo中6个Fragment对应的的18个Agaent,我们来分别看下他们的视图。
- ClickFragment : ClickAgentConfig
- ClickSectionThreeAgent 点击示例Agent:行视图具有点击事件
- ClickSectionTwoAgent
- HeaderFooterCellFragment :HeaderFooterCellAgentConfig
- HeaderFooterCellFirstAgent 片段头尾示例Agent:指定片段具有头部或者尾部
- HeaderFooterCellSecondAgent
- DividerFragment:DividerAgentConfig
- DefaultDividerAgent 片段头尾示例Agent:指定片段具有头部或者尾部
- SectionDividerAgent 片段分割线示例Agent
- RowDividerAgent 行分割线示例Agent
- LinkTypeFragment : LinkTypeAgentConfig
- LinkTypeCustomAgent
- LinkTypeFirstAgent 片段之间间隔控制示例Agent
- LinkTypeSecondAgent
- LinkTypeThirdAgent
- LinkTypeFourthAgent
- StatusFragment : StatusAgentConfig
- ControlButtonAgent
- LoadingStatusAgent 模块联动和刷新状态更新 示例Agent
- LoadingStatusMoreAgent 加载更多状态更新 示例Agent
- LoadingStatusMoreAgent
- MixFragment : MixAgentConfig
- MixLoadingAgent
- MixCellAgent 混合使用各种视图控制 示例Agent
3.2.1 basicfeatureandclick 简单示例视图
【图5】
先看一个简单的ClickSectionThreeAgent#ClickViewCell, 它虽然继承自BaseViewCell,但是其实并未实现所附加的视图功能,而是仅仅实现了SectionCellInterface:
- 3个片段:第一个片段是2行,其他为3行
- 只有一个行视图类型,每行都使用该类型
- 指定的行视图类型,会被onCreateView创建为一个 包含 TextView的LinearLayout,该linearLayout的Tag持有内部ViewHolder
- updateView 实现不同片段的不同行,具有不同的文字和颜色(第一个片段第一行为红色,其他片段第一行为蓝色,其他为灰色)
private class ClickViewCell extends BaseViewCell {
protected ItemViewHolder itemViewHolder;
public ClickViewCell(Context context) {
super(context);
}
/***************************1. 提供 片段和行 的个数;**************************************/
@Override
public int getSectionCount() {
return 3;
}
@Override
public int getRowCount(int sectionPosition) {
if (sectionPosition == 0)
return 2;
else
return 3;
}
/***************************2. 提供 片段&行 : View类型 的映射关系;**************************************/
@Override
public int getViewType(int sectionPosition, int rowPosition) {
return 0;
}
@Override
public int getViewTypeCount() {
return 1;
}
/***************************3. 根据View类型创建对应的View视图;**************************************/
@Override
public View onCreateView(ViewGroup parent, int viewType) {
LinearLayout rootView = new LinearLayout(mContext);
rootView.setOrientation(LinearLayout.VERTICAL);
rootView.setBackgroundColor(getContext().getResources().getColor(android.R.color.white));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.leftMargin = ViewUtils.dip2px(getContext(), 30);
itemViewHolder = new ItemViewHolder();
TextView textView = new TextView(mContext);
textView.setHeight(ViewUtils.dip2px(getContext(), 50));
textView.setGravity(Gravity.CENTER_VERTICAL);
itemViewHolder.textView = textView;
rootView.addView(textView, params);
rootView.setTag(itemViewHolder);
return rootView;
}
/***************************4. 更新指定片段、指定行、指定View的显示;**************************************/
@Override
public void updateView(View view, int sectionPosition, int rowPosition, ViewGroup parent) {
if (view != null && (view.getTag() instanceof ItemViewHolder)) {
TextView v = ((ItemViewHolder) view.getTag()).textView;
v.setText("Module0,Body,Section" + sectionPosition + ",Row" + rowPosition);
SectionPositionColorUtil.setSectionPositionColor(v, getContext(), sectionPosition, rowPosition);
}
}
}
3.2.2 headerfootercell 片段头角的示例视图
- 首先定义了一个基类,实现了 头 | 片段行 | 尾 的布局生成
/**
* 它虽然继承自BaseViewCell,但是其实并未实现所附加的视图功能,而是仅仅实现了SectionExtraCellInterface
* 1. x个片段:第一个片段是x行,其他为x行
* 2. 只有x个行视图类型,每行都使用x类型
* 3. 指定的行视图类型,会被onCreateView创建为一个 指定视图的View
* 4. updateView 实现不同片段的不同行,具有不同的文字和颜色(第一个片段第一行为红色,其他片段第一行为蓝色,其他为灰色)
* Created by nihao on 2017/7/14.
*/
public abstract class HeaderFooterViewBaseCell extends BaseViewCell {
public HeaderFooterViewBaseCell(Context context) {
super(context);
}
/***************************3. 根据View类型创建对应的View视图;**************************************/
@Override
public View onCreateView(ViewGroup parent, int viewType) {
return LayoutInflater.from(getContext()).inflate(R.layout.module_cell_item, parent, false);
}
/***************************4. 更新指定片段、指定行、指定View的显示;**************************************/
@Override
public void updateView(View view, final int sectionPosition, final int rowPosition, ViewGroup parent) {
TextView textView = (TextView) view.findViewById(R.id.header_footer_item_tx);
textView.setText("module : " + getModuleIndex() + " section : " + sectionPosition + " row : " + rowPosition);
textView.setTextColor(getTextColor());
}
/**
* headercell
*/
@Override
public View onCreateHeaderView(ViewGroup parent, int headerViewType) {
return LayoutInflater.from(getContext()).inflate(R.layout.module_cell_item, parent, false);
}
@Override
public void updateHeaderView(View view, int sectionPostion, ViewGroup parent) {
TextView textView = (TextView) view.findViewById(R.id.header_footer_item_tx);
textView.setBackgroundColor(Color.parseColor("#DAF1B8"));
textView.setText("header for section: " + sectionPostion);
}
/**
* footercell
*/
@Override
public View onCreateFooterView(ViewGroup parent, int footerViewType) {
return LayoutInflater.from(getContext()).inflate(R.layout.module_cell_item, parent, false);
}
@Override
public void updateFooterView(View view, int sectionPostion, ViewGroup parent) {
TextView textView = (TextView) view.findViewById(R.id.header_footer_item_tx);
textView.setBackgroundColor(Color.parseColor("#98D839"));
textView.setText("footer for section : " + sectionPostion);
}
/*****************************其他***************************/
protected abstract int getModuleIndex();
protected abstract int getTextColor();
}
然后分别定义了两个片段视图:
- HeaderFooterViewFirstCell
- 3个片段:0:2 1:3 2:3 other:0
- 只有1个行视图类型,每行都使用1类型
- 指定的行视图类型,会被onCreateView创建为一个 指定视图的View
- updateView 实现不同片段的不同行,具有不同的文字,和相同指定色
- 片段0具有头部(一个text),片段2具有尾部(一个text)
- HeaderFooterViewSecondCell
- 2个片段:0:2 1:1 other:0
- 只有1个行视图类型,每行都使用1类型
- 指定的行视图类型,会被onCreateView创建为一个 指定视图的View
- updateView 实现不同片段的不同行,具有不同的文字,和相同指定色
- 片段0具有头部(一个text),片段1具有尾部(一个text)
public class HeaderFooterViewFirstCell extends HeaderFooterViewBaseCell {
public HeaderFooterViewFirstCell(Context context) {
super(context);
}
/***************************1. 提供 片段和行 的个数;**************************************/
@Override
public int getSectionCount() {
return 3;
}
@Override
public int getRowCount(int sectionPosition) {
switch (sectionPosition) {
case 0: // section0;
return 2;
case 1: // section1;
return 3;
case 2: // section2;
return 3;
default: // default;
return 0;
}
}
/***************************2. 提供 片段&行 : View类型 的映射关系;**************************************/
@Override
public int getViewType(int sectionPosition, int rowPosition) {
return 0;
}
@Override
public int getViewTypeCount() {
return 1;
}
/**
* headercell
*/
@Override
public boolean hasHeaderForSection(int sectionPostion) {
return sectionPostion == 0;
}
/**
* footercell
*/
@Override
public boolean hasFooterForSection(int sectionPostion) {
return sectionPostion == 2;
}
/*****************************其他***************************/
@Override
protected int getModuleIndex() {
return 0;
}
@Override
protected int getTextColor() {
return Color.parseColor("#00CC66");
}
}
3.2.3 divider 分割线控制
DefaultDividerViewCell :一个简单实现头尾部的片段视图
-
- 3个片段:0:2 other:3
-
- 只有1个行视图类型,每行都使用x类型
-
- 指定的行视图类型,会被onCreateView创建为一个 包含 TextView的LinearLayout,该linearLayout的Tag持有内部ViewHolder
-
- updateView 实现不同片段的不同行,具有不同的文字,和不同指定色
-
- 片段0具有头部(一个Lin包text),最后一个片段具有尾部(一个Lin包一个text)
-
SectionDividerViewCell 实现行片段视图的分割线控制
-
- 5个片段:0:2 other:3
-
- 只有1个行视图类型,每行都使用x类型
-
- 指定的行视图类型,会被onCreateView创建为一个 包含 TextView的LinearLayout,该linearLayout的Tag持有内部ViewHolder
-
- updateView 实现不同片段的不同行,具有不同的文字,和不同指定色
-
- 片段0具有头部(一个Lin包text),最后一个片段具有尾部(一个Lin包一个text)
- * 6. 5个片段展示5种分割线显示控制; *
-
@Override
public ShowType dividerShowType(int sectionPosition) {
switch (sectionPosition) {
case 0:
return ShowType.DEFAULT;
case 1:
return ShowType.NONE;
case 2:
return ShowType.TOP_END;
case 3:
return ShowType.MIDDLE;
case 4:
return ShowType.ALL;
}
return ShowType.DEFAULT;
}
- RowDividerViewCell 实现行级视图的分割线控制
-
- 5个片段:0:2 other:3
-
- 只有1个行视图类型,每行都使用x类型
-
- 指定的行视图类型,会被onCreateView创建为一个 包含 TextView的LinearLayout,该linearLayout的Tag持有内部ViewHolder
-
- updateView 实现不同片段的不同行,具有不同的文字,和不同指定色
-
- 片段0具有头部(一个Lin包text),最后一个片段具有尾部(一个Lin包一个text)
- * 6. 片段1的1行分割线样式控制, 片段2的左缩进控制,片段3的行显示控制*
-
@Override
public Drawable getDivider(int sectionPosition, int rowPosition) {
if (rowPosition == 0 && sectionPosition == 1)
return getContext().getResources().getDrawable(R.drawable.shield_demo_setdivider_color);
else
return getContext().getResources().getDrawable(R.drawable.section_recycler_view_divider);
}
@Override
public int dividerOffset(int sectionPosition, int rowPosition) {
if (sectionPosition == 2) {
if (rowPosition == 0)
return ViewUtils.dip2px(mContext, 30);
else
return ViewUtils.dip2px(mContext, 50);
} else
return super.dividerOffset(sectionPosition, rowPosition);
}
@Override
public boolean showDivider(int sectionPosition, int rowPosition) {
if (sectionPosition == 3 && (rowPosition == 0 || rowPosition == 2))
return false;
else
return true;
}
3.2.4 linktype 片段间隔关联
LinkTypeFirstCell 3片1行的简单片段视图
LinkTypeSecondCell 前两片链接
- 0:” link_to_next”
- 1 :” link_to_next link_to_previous”
- 2 :” disable_link_to_previous”
@Override
public LinkType.Next linkNext(int sectionPosition) {
if (sectionPosition == 0) {
return LinkType.Next.LINK_TO_NEXT;
}
if (sectionPosition == 1) {
return LinkType.Next.LINK_TO_NEXT;
}
return super.linkNext(sectionPosition);
}
@Override
public LinkType.Previous linkPrevious(int sectionPosition) {
if (sectionPosition == 1) {
return LinkType.Previous.LINK_TO_PREVIOUS;
}
if (sectionPosition == 2) {
return LinkType.Previous.DISABLE_LINK_TO_PREVIOUS;
}
return super.linkPrevious(sectionPosition);
}
- LinkTypeThirdCell 全连接
@Override
public LinkType.Next linkNext(int sectionPosition) {
if (sectionPosition == 0){
return LinkType.Next.LINK_TO_NEXT;
}
return super.linkNext(sectionPosition);
}
@Override
public LinkType.Previous linkPrevious(int sectionPosition) {
if (sectionPosition == 2){
return LinkType.Previous.LINK_TO_PREVIOUS;
}
return super.linkPrevious(sectionPosition);
}
- LinkTypeFourthCell 指定间隔高度
@Override
public float getSectionHeaderHeight(int sectionPoisition) {
if (sectionPoisition == 0) {
return ViewUtils.dip2px(getContext(), 20);
}
if (sectionPoisition == 1) {
return ViewUtils.dip2px(getContext(), 30);
}
return super.getSectionHeaderHeight(sectionPoisition);
}
@Override
public float getSectionFooterHeight(int sectionPoisition) {
if (sectionPoisition == 1) {
return ViewUtils.dip2px(getContext(), 40);
}
return super.getSectionFooterHeight(sectionPoisition);
}
3.2.5 status 刷新加载状态
3.2.5.1 ControlButtonAgent & LoadingStatusAgent 模块联动和刷新状态更新
- ControlButtonViewCell
- 一个具有5个按钮的1片段1行视图
-
- 不同的片段被点击后,会向白板中设置指定key的对应value,并触发其观察者进行更新;
public class ControlButtonAgent extends LightAgent {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mControlButtonViewCell = new ControlButtonViewCell(getContext());
//默认是 Done模式
getWhiteBoard().putString("status", "DONE");
}
public class ControlButtonViewCell extends BaseViewCell {
@Override
public View onCreateView(ViewGroup parent, int viewType) {
LinearLayout rootView = new LinearLayout(mContext);
rootView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
rootView.setOrientation(LinearLayout.VERTICAL);
rootView.setBackgroundColor(getContext().getResources().getColor(android.R.color.white));
// View containView = LinearLayout.inflate(getContext(), R.layout.agent_status_loading, null);
View containView = LayoutInflater.from(getContext()).inflate(R.layout.agent_status_loading,rootView,false);
setLoadingStatus(containView);
rootView.addView(containView);
return rootView;
}
private void setLoadingStatus(View containView) {
Button btLoading = (Button) containView.findViewById(R.id.bt_loading);
btLoading.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getWhiteBoard().putString(ControlButtonConstant.STATUS, "LOADING");
}
});
Button btFailed = (Button) containView.findViewById(R.id.bt_failed);
btFailed.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getWhiteBoard().putString(ControlButtonConstant.STATUS, "FAILED");
}
});
Button btEmpty = (Button) containView.findViewById(R.id.bt_empty);
btEmpty.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getWhiteBoard().putString(ControlButtonConstant.STATUS, "EMPTY");
}
});
Button btDone = (Button) containView.findViewById(R.id.bt_done);
btDone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getWhiteBoard().putString(ControlButtonConstant.STATUS, "DONE");
}
});
Button btIncreaseSection = (Button) containView.findViewById(R.id.bt_increase_section);
btIncreaseSection.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getWhiteBoard().putInt(ControlButtonConstant.SECTIONCOUNT, sectionCount++);
}
});
}
}
}
- LoadingStatusAgent
- 响应 白板中 状态key对应Value的改变
updateAgentCell():更新当前模块的视图,其中会调用【刷新状态控制器CellStatusInterface】.loadingStatus,并据此触发相关函数,显示对应状态View
- 响应 白板中 状态key对应Value的改变
- LoadingStatusCell
- 1片段4行的视图
- 其父类实现 【刷新状态控制器CellStatusInterface】相关接口
public class LoadingStatusAgent extends LightAgent {
private LoadingStatusCell mLoadingStatusCell;
private Subscription statusSubscription;
private Subscription sectionCountSubscription;
private String mStatusStr;
private int mSectionCount;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLoadingStatusCell = new LoadingStatusCell(getContext());
mLoadingStatusCell.setLoadingRetryListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getHostFragment().getActivity(), "loading retry: 重新加载", Toast.LENGTH_SHORT).show();
}
});
mLoadingStatusCell.setResetOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mLoadingStatusCell.setStatus(CellStatus.LoadingStatus.DONE);
updateAgentCell();
}
});
//响应 白板中 状态key对应Value的改变,
statusSubscription = getWhiteBoard().getObservable(ControlButtonConstant.STATUS).subscribe(new Action1() {
@Override
public void call(Object o) {
if (o instanceof String) {
mStatusStr = (String) o;
if (mStatusStr.equals("LOADING")) {
mLoadingStatusCell.setStatus(CellStatus.LoadingStatus.LOADING);
} else if (mStatusStr.equals("FAILED")) {
mLoadingStatusCell.setStatus(CellStatus.LoadingStatus.FAILED);
} else if (mStatusStr.equals("EMPTY")) {
mLoadingStatusCell.setStatus(CellStatus.LoadingStatus.EMPTY);
} else {
mLoadingStatusCell.setStatus(CellStatus.LoadingStatus.DONE);
}
//更新当前模块的视图,其中会调用【刷新状态控制器CellStatusInterface】.loadingStatus,并据此触发相关函数,显示对应状态View
updateAgentCell();
}
}
});
sectionCountSubscription = getWhiteBoard().getObservable(ControlButtonConstant.SECTIONCOUNT).subscribe(new Action1() {
@Override
public void call(Object o) {
if (o instanceof Integer) {
mSectionCount = (Integer) o;
if (mSectionCount != 1) {
//增加一个片段
mLoadingStatusCell.setSectionCount(mSectionCount);
}
//更新当前模块的视图
updateAgentCell();
}
}
});
}
@Override
public void onDestroy() {
if (statusSubscription != null) {
statusSubscription.unsubscribe();
statusSubscription = null;
}
if (sectionCountSubscription != null) {
sectionCountSubscription.unsubscribe();
sectionCountSubscription = null;
}
super.onDestroy();
}
@Override
public SectionCellInterface getSectionCellInterface() {
return mLoadingStatusCell;
}
public class LoadingStatusCell extends LoadingBaseCell {
private CellStatus.LoadingStatus status = CellStatus.LoadingStatus.DONE;
public void setSectionCount(int sectionCount) {
this.mSectionCount = sectionCount;
}
private int mSectionCount = 1;
public void setStatus(CellStatus.LoadingStatus status) {
this.status = status;
}
@Override
public CellStatus.LoadingStatus loadingStatus() {
return status;
}
...
}
}
- LoadingBaseCell
- 实现 【刷新状态控制器CellStatusInterface】相关接口
public abstract class LoadingBaseCell extends BaseViewCell {
private View.OnClickListener mLoadingRetryListener;
private View.OnClickListener mLoadingMoreRetryListener;
private View.OnClickListener mResetOnClickListener;
@Override
public View loadingView() {
LinearLayout rootView = (LinearLayout) getRootView();
TextView textView = new TextView(mContext);
textView.setHeight(ViewUtils.dip2px(getContext(), 50));
textView.setText("loading: 加载中");
textView.setGravity(Gravity.CENTER);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mResetOnClickListener != null) {
mResetOnClickListener.onClick(v);
}
}
});
rootView.addView(textView);
return rootView;
}
@Override
public View loadingFailedView() {
LinearLayout rootView = (LinearLayout) getRootView();
TextView textView = new TextView(mContext);
textView.setHeight(ViewUtils.dip2px(getContext(), 50));
textView.setText("loading fail: 点击重新加载");
textView.setGravity(Gravity.CENTER);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mLoadingRetryListener != null) {
mLoadingRetryListener.onClick(v);
}
}
});
rootView.addView(textView);
return rootView;
}
@Override
public View emptyView() {
LinearLayout rootView = (LinearLayout) getRootView();
TextView textView = new TextView(mContext);
textView.setHeight(ViewUtils.dip2px(getContext(), 50));
textView.setText("loading empty: 您查询的内容为空");
textView.setGravity(Gravity.CENTER);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mResetOnClickListener != null) {
mResetOnClickListener.onClick(v);
}
}
});
rootView.addView(textView);
return rootView;
}
private ViewGroup getRootView() {
LinearLayout rootView = new LinearLayout(mContext);
rootView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
rootView.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(ViewUtils.dip2px(getContext(), 30), 0, 0, 0);
rootView.setBackgroundColor(getContext().getResources().getColor(android.R.color.white));
return rootView;
}
public void setResetOnClickListener(View.OnClickListener resetOnClickListener) {
this.mResetOnClickListener = resetOnClickListener;
}
public void setLoadingRetryListener(View.OnClickListener loadingRetryListener) {
this.mLoadingRetryListener = loadingRetryListener;
}
public void setLoadingMoreRetryListener(View.OnClickListener loadingMoreRetryListener) {
this.mLoadingMoreRetryListener = loadingMoreRetryListener;
}
}
3.2.5.2 LoadingStatusMoreCell 加载更多状态更新
- 1片段 4行 2类型: 片段1是顶部button,下边是三行文本
public class LoadingStatusMoreCell extends LoadingBaseCell {
protected ItemViewHolder itemViewHolder;
protected int titleType = 0;
protected int cellType = 1;
protected CellStatus.LoadingMoreStatus statusMore = CellStatus.LoadingMoreStatus.DONE;
private int sectionCount = 1;
public LoadingStatusMoreCell(Context context) {
super(context);
}
public void setStatusMore(CellStatus.LoadingMoreStatus statusMore) {
this.statusMore = statusMore;
}
@Override
public int getSectionCount() {
return sectionCount;
}
@Override
public int getRowCount(int sectionPosition) {
return 4;
}
@Override
public int getViewType(int sectionPosition, int rowPosition) {
if (sectionPosition == 0 && rowPosition == 0)
return titleType;
else
return cellType;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public View onCreateView(ViewGroup parent, int viewType) {
LinearLayout rootView = new LinearLayout(mContext);
rootView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
rootView.setOrientation(LinearLayout.VERTICAL);
rootView.setBackgroundColor(getContext().getResources().getColor(android.R.color.white));
if (viewType == 0) {
View containView = LayoutInflater.from(getContext()).inflate(R.layout.agent_status_loading_more, rootView, false);
setLoadingStatus(containView);
rootView.addView(containView);
} else {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(ViewUtils.dip2px(getContext(), 30), 0, 0, 0);
itemViewHolder = new ItemViewHolder();
TextView textView = new TextView(mContext);
textView.setHeight(ViewUtils.dip2px(getContext(), 50));
textView.setGravity(Gravity.CENTER_VERTICAL);
itemViewHolder.textView = textView;
rootView.addView(textView, params);
rootView.setTag(itemViewHolder);
}
return rootView;
}
@Override
public void updateView(View view, int sectionPosition, int rowPosition, ViewGroup parent) {
if (view != null && (view.getTag() instanceof ItemViewHolder)) {
TextView v = ((ItemViewHolder) view.getTag()).textView;
v.setText("Section" + sectionPosition + ",Row" + rowPosition);
SectionPositionColorUtil.setSectionPositionColor(v, getContext(), sectionPosition, rowPosition);
}
}
@Override
public CellStatus.LoadingMoreStatus loadingMoreStatus() {
return statusMore;
}
private void setLoadingStatus(View containView) {
Button btMoreLoading = (Button) containView.findViewById(R.id.bt_more_loading);
btMoreLoading.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
statusMore = CellStatus.LoadingMoreStatus.LOADING;
updateAgentCell();
}
});
Button btMoreFailed = (Button) containView.findViewById(R.id.bt_more_failed);
btMoreFailed.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
statusMore = CellStatus.LoadingMoreStatus.FAILED;
updateAgentCell();
}
});
Button btMoreDone = (Button) containView.findViewById(R.id.bt_more_done);
btMoreDone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
statusMore = CellStatus.LoadingMoreStatus.DONE;
updateAgentCell();
}
});
Button btIncreaseSection = (Button) containView.findViewById(R.id.bt_increase_section);
btIncreaseSection.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sectionCount++;
updateAgentCell();
}
});
}
}
public abstract class LoadingBaseCell extends BaseViewCell {
@Override
public View loadingMoreView() {
LinearLayout rootView = (LinearLayout) getRootView();
TextView textView = new TextView(mContext);
textView.setHeight(ViewUtils.dip2px(getContext(), 50));
textView.setText("loadingmore: 正在载入");
textView.setGravity(Gravity.CENTER);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mResetOnClickListener != null) {
mResetOnClickListener.onClick(v);
}
}
});
rootView.addView(textView);
return rootView;
}
@Override
public View loadingMoreFailedView() {
LinearLayout rootView = (LinearLayout) getRootView();
TextView textView = new TextView(mContext);
textView.setHeight(ViewUtils.dip2px(getContext(), 50));
textView.setText("loadingmore fail: 点击重新加载");
textView.setGravity(Gravity.CENTER);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mLoadingMoreRetryListener != null) {
mLoadingMoreRetryListener.onClick(v);
}
}
});
rootView.addView(textView);
return rootView;
}
}
3.2.6 mix
MixLoadingCell
- 单片段单行
- 4个button触发 对应白板事件
MixCell 混合使用各种视图控制
读到这里,你应该对demo中 各个模块的配置和显示有了一个直观的认识,那么我们就接下来继续看,如何实现这些模块的管理
四、模块管理器
模块管理器是针对模块组的相关管理操作的封装
4.1 接口AgentManagerInterface
模块管理器AgentManagerInterface,需要在页面容器的相关时机中显示调用触发
- 负责模块的创建、销毁等工作;
- 根据页面容器的生命周期,进行模块生命周期分发;
public interface AgentManagerInterface {
/******************** 模块管理能力*************************/
/**
* 反射创建模块组,触发其onCreate生命周期,并通知视图管理器updateCells更新视图
*/
void setupAgents(Bundle savedInstanceState, ArrayList<AgentListConfig> defaultConfig);
/**
* 重置模块组,根据当前已有的模块组状态触发其生命周期,并同时视图管理器更新视图
*/
void resetAgents(Bundle savedInstanceState, ArrayList<AgentListConfig> defaultConfig);
/**
* 根据模块Key 返回其对应 模块
*/
AgentInterface findAgent(String name);
/************************生命周期分发***********************/
void startAgents();
void resumeAgents();
void pauseAgents();
void stopAgents();
void destroyAgents();
void onSaveInstanceState(Bundle outState);
void onActivityResult(int requestCode, int resultCode, Intent data);
}
4.2 基类模块管理器LightAgentManager
针对 LightAgent的模块管理器,所管理的Agent必须继承自LightAgent,* 内部针对LightAgent实现了反射实例化,必须传入对应的构建参数*
其实可以看出 模块管理能力中,只是对模块的处理,视图的处理全部交给了driverInterface
4.2.1 构造函数
public final static String AGENT_SEPARATE = "/";
// 已排序的模块key列表
protected final ArrayList<String> agentKeyList = new ArrayList<String>();
// 模块列表,方便快速查找模块
protected final HashMap<String, AgentInterface> agents = new HashMap<String, AgentInterface>();
/**
* 页面容器
* 构建LigthAgent使用
*/
protected Fragment fragment;
/**
* 【模块管理器与视图管理器的沟通桥梁】
* 1. 提供给模块管理器中,触发视图管理器更新视图的能力
*/
protected AgentCellBridgeInterface agentCellBridgeInterface;
/**
* 【模块驱动器】
* 构建LigthAgent使用
* 1. 提供给模块 访问白板 的能力
* 2. 提供给模块 通知管理器以更新模块 的能力
*/
protected DriverInterface driverInterface;
/**
* 【页面容器接口,提供定制化的容器能力(生命周期回调)】
* 构建LigthAgent使用
* 需要在页面容器中对应生命周期回调中显式调用;
* ps: 譬如,如果页面容器是Fragment,则提供Fragment的生命周期
*/
protected PageContainerInterface pageContainer;
public LightAgentManager(Fragment fragment
, AgentCellBridgeInterface agentCellBridgeInterface
, DriverInterface driverInterface
, PageContainerInterface pageContainerInterface) {
this.fragment = fragment;
this.agentCellBridgeInterface = agentCellBridgeInterface;
this.driverInterface = driverInterface;
this.pageContainer = pageContainerInterface;
}
4.2.2 生命周期分发
遍历持有的各个模块,并触发其对应的生命周期监听
/****************************生命周期分发************************************/
@Override
public void startAgents() {
for (String name : agentKeyList) {
AgentInterface agent = agents.get(name);
if (agent != null) {
agent.onStart();
}
}
}
@Override
public void resumeAgents() {
for (String name : agentKeyList) {
AgentInterface agent = agents.get(name);
if (agent != null) {
agent.onResume();
}
}
}
@Override
public void pauseAgents() {
for (String name : agentKeyList) {
AgentInterface agent = agents.get(name);
if (agent != null) {
agent.onPause();
}
}
}
@Override
public void stopAgents() {
for (String name : agentKeyList) {
AgentInterface agent = agents.get(name);
if (agent != null) {
agent.onStop();
}
}
}
@Override
public void destroyAgents() {
for (String name : agentKeyList) {
AgentInterface agent = agents.get(name);
if (agent != null) {
agent.onDestroy();
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
for (String name : agentKeyList) {
AgentInterface agent = agents.get(name);
if (agent != null) {
Bundle b = agent.saveInstanceState();
if (b != null) {
outState.putBundle("agent/" + name, b);
}
}
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
for (String name : agentKeyList) {
AgentInterface agent = agents.get(name);
if (agent != null) {
agent.onActivityResult(requestCode, resultCode, data);
}
}
}
4.2.3 模块管理能力
4.2.3.1 创建setupAgents
反射创建模块组,触发其onCreate生命周期,并通知视图管理器updateCells更新视图
/**
* 反射创建模块组,触发其onCreate生命周期,并通知视图管理器updateCells更新视图
* @param savedInstanceState
* @param defaultConfig 模块组构成配置类的自定义列表
*/
@Override
public void setupAgents(Bundle savedInstanceState, ArrayList<AgentListConfig> defaultConfig) {
// A 重置 及 构建模块组的 模块列表
setupAgents(defaultConfig);
// B 调用 各个模块的 onCreate生命周期
ArrayList<AgentInterface> addList = new ArrayList<AgentInterface>();
for (String name : agentKeyList) {
AgentInterface agent = agents.get(name);
if (agent != null) {
Bundle b = savedInstanceState == null ? null : savedInstanceState.getBundle("agent/"
+ name);
agent.onCreate(b);
addList.add(agent);
}
}
// C 通知视图管理器 更新当前创建模块的视图
agentCellBridgeInterface.updateCells(addList, null, null);
// D 已废弃,通知模块列表视图更新
dispatchCellChanged(fragment.getActivity(), null, null);
}
/**
* 【A】初始化
* 1. 清空 已排序的模块key列表 和 模块列表
* 2. 根据 模块组构成配置类的自定义列表 构建 模块列表
* @param defaultConfig 模块组构成配置类的自定义列表:意味着可以有多个模块组,但只有一个显示
*/
private void setupAgents(ArrayList<AgentListConfig> defaultConfig) {
//【A.1】清空 已排序的模块key列表
agentKeyList.clear();
//清空 模块列表
agents.clear();
//【A.2】根据 模块组构成配置类的自定义列表 构建 模块列表
setDefaultAgent(defaultConfig);
}
/**
* 【A.2】根据 模块组构成配置类的自定义列表 构建 模块列表
* @param defaultConfig 模块组配置类的缓存列表
*/
protected void setDefaultAgent(ArrayList<AgentListConfig> defaultConfig) {
setDefaultAgent(defaultConfig, "", "");
}
//【A.2】根据 模块组构成配置类的自定义列表 构建 模块列表
protected void setDefaultAgent(ArrayList<AgentListConfig> defaultConfig, String keyPath, String index) {
//【A.2.1】 获取 【需要显示的模块组】的 【组内所包含的 待排序的 模块配置信息列表】
Map<String, AgentInfo> defaultAgent = getDefaultAgentList(defaultConfig);
//【A.2.2】根据 【模块配置信息列表】 创建并构建 模块列表
if (defaultAgent == null) {
//throw new RuntimeException("generaterDefaultAgent() can not return null");
Log.e("Shield", "generaterDefaultAgent() can not return null");
return;
}
try {
//遍历 【模块配置信息列表】
for (Map.Entry<String, AgentInfo> entry : defaultAgent.entrySet()) {
//【A.2.2.1】 生成 模块Key
String key;
if (!"".equals(keyPath)) {
//如果 传入了自定义的路径命名,则模块Key加入该路径
key = keyPath + AGENT_SEPARATE + entry.getKey();
} else {
key = entry.getKey();
}
//【A.2.2.2】 构建模块
if (!agents.containsKey(key)) {
//1. 加入该模块Key
agentKeyList.add(key);
//
AgentInterface cellAgent = constructAgents(entry.getValue().agentClass);
if (cellAgent != null) {
String childIndex;
if (!"".equals(keyPath)) {
childIndex = index + "." + entry.getValue().index;
} else {
childIndex = entry.getValue().index;
}
cellAgent.setIndex(childIndex);
cellAgent.setHostName(key);
agents.put(key, cellAgent);
if (cellAgent instanceof ShieldContainerInterface) {
setDefaultAgent(((ShieldContainerInterface) cellAgent).generaterConfigs(), key, childIndex);
}
}
}
}
} catch (Exception e) {
// Log.e(TAG, e.toString());
}
}
/**
* 【A.2.1】 获取 【需要显示的模块组】的 【组内所包含的 待排序的 模块配置信息列表】
* 1. 从 模块组构成配置类的自定义列表 中 找出需要显示的组,并返回该组的 【模块配置信息列表】
* 2. 如果需要显示模块组的 【模块配置类列表】为null, 则根据 item的 【模块类列表】自行构建一个 【模块配置类列表】
* @param defaultConfig 模块组配置类的缓存列表
* @return
*/
protected Map<String, AgentInfo> getDefaultAgentList(ArrayList<AgentListConfig> defaultConfig) {
if (defaultConfig == null) {
Log.e("Shield", "generaterDefaultConfigAgentList return null");
// throw new RuntimeException("generaterDefaultConfigAgentList return null");
return null;
}
//遍历 模块组构成配置类的自定义列表
for (AgentListConfig agentListConfig : defaultConfig) {
try {
//判断 该模块组需要显示
if (agentListConfig.shouldShow()) {
//1. 从 模块组构成配置类的自定义列表 中 找出需要显示的组,并返回该组的 【组内所包含的 待排序的 各个模块配置信息】
Map<String, AgentInfo> result = agentListConfig.getAgentInfoList();
//2. 如果需要显示模块组的 【模块配置类列表】为null, 则根据 item的 【模块类列表】自行构建一个 【模块配置类列表】
if (result == null) {
result = new LinkedHashMap<String, AgentInfo>();
Map<String, Class<? extends AgentInterface>> tmp = agentListConfig.getAgentList();
for (Map.Entry<String, Class<? extends AgentInterface>> entry : tmp.entrySet()) {
result.put(entry.getKey(), new AgentInfo(entry.getValue(), ""));
}
}
return result;
}
} catch (Exception e) {
//if (Environment.isDebug()) {
// throw new RuntimeException("there has a exception " + e);
// }
e.printStackTrace();
return null;
}
}
//throw new RuntimeException("getDefaultAgentList() agentListConfig no one should be shown?");
Log.e("Shield", "getDefaultAgentList() agentListConfig no one should be shown?");
return null;
}
/**
* 【A.2.2.2】 根据反射构造 模块
*/
protected AgentInterface constructAgents(Class<? extends AgentInterface> agentClass) {
AgentInterface cellAgent = null;
try {
cellAgent = agentClass.getConstructor(Object.class).newInstance(
fragment);
} catch (Exception e) {
// e.printStackTrace();
}
if (cellAgent == null) {
//对应 LightAgent(Fragment fragment, DriverInterface bridge, PageContainerInterface pageContainer)
try {
Class<?>[] params = {Fragment.class,
DriverInterface.class, PageContainerInterface.class};
Object[] values = {fragment, driverInterface, pageContainer};
cellAgent = agentClass.getConstructor(params).newInstance(
values);
} catch (Exception e) {
//e.printStackTrace();
}
}
if (cellAgent == null) {
try {
cellAgent = agentClass.newInstance();
} catch (Exception e) {
// e.printStackTrace();
}
}
if (cellAgent == null) {
Log.e("Shield", agentClass.getCanonicalName() + ":Failed to construct Agent");
}
return cellAgent;
}
4.2.3.2 重置 resetAgents
/**
* 重置模块组,触发其生命周期,并通知视图管理器更新视图
* 三种可能
* 1.以前没有的agent,新增的,要重新onCreat,onResume
* 2.以前有的agent,现在还有,那什么都不做,还原这个agent,等待之后统一dispatchCellChanged
* 3.以前有的agent,后来没有了,那要调用stop,destory
* resetAgent的时候,对agent列表做一个整理
* @param savedInstanceState
* @param defaultConfig
*/
@Override
public void resetAgents(Bundle savedInstanceState, ArrayList<AgentListConfig> defaultConfig) {
//A 克隆当前的 模块组
ArrayList<String> copyOfAgentKeyList = (ArrayList<String>) agentKeyList.clone();
HashMap<String, AgentInterface> copyOfAgents = (HashMap<String, AgentInterface>) agents.clone();
// 因为现在需求同一个Cell根据AgentList的不同,放在不同的位置,所以也要更新Cell的index
ArrayList<AgentInterface> addList = new ArrayList<AgentInterface>();
ArrayList<AgentInterface> updateList = new ArrayList<AgentInterface>();
ArrayList<AgentInterface> deleteList = new ArrayList<AgentInterface>();
//B 重置 及 构建模块组的 模块列表
setupAgents(defaultConfig);
Bundle b;
//C 根据状态 触发生命周期
for (String name : agentKeyList) {
// 情况1,后续新增的
if (!copyOfAgentKeyList.contains(name)) {
AgentInterface agent = agents.get(name);
b = savedInstanceState == null ? null : savedInstanceState.getBundle("agent/"
+ name);
if (agent != null) {
agent.onCreate(b);
agent.onStart();
agent.onResume();
addList.add(agent);
}
} else {
// 情况2,为了筛选出情况3
copyOfAgentKeyList.remove(name);
// 得到复用的agent
AgentInterface cellAgent = copyOfAgents.get(name);
if (cellAgent != null && agents.get(name) != null) {
// 更新复用agent的index
cellAgent.setIndex(agents.get(name).getIndex());
// 更新对应的cell
updateList.add(cellAgent);
// 把原来已经生成好的agent替换空的agent,避免两次onCreate
agents.put(name, cellAgent);
cellAgent.setHostName(name);
}
}
}
// 情况3,之前有的,现在又没有了
for (String name : copyOfAgentKeyList) {
AgentInterface agent = copyOfAgents.get(name);
if (agent != null) {
if (fragment.isResumed()) {
agent.onPause();
}
agent.onStop();
agent.onDestroy();
//删除不再存在的Agent对应的Cell
deleteList.add(agent);
}
}
copyOfAgentKeyList.clear();
copyOfAgents.clear();
// D 通知视图管理器更新视图
agentCellBridgeInterface.updateCells(addList, updateList, deleteList);
dispatchCellChanged(fragment.getActivity(), null, null);
}
4.2.3.3 查找模块
/**
* 根据模块Key 返回其对应 模块
* @param name
* @return
*/
@Override
public AgentInterface findAgent(String name) {
return agents.get(name);
}
4.3 基类模块管理器CommonAgentManager
CommonAgentManager对 LightAgentManager(针对 LightAgent的模块管理器,所管理的Agent必须继承自LightAgent)的扩展
LightAgentManager所涉及的LightAgent的构造参数必须是有参的,这里提供一种无参的反射实例化方式
public class CommonAgentManager extends LightAgentManager {
public CommonAgentManager(Fragment fragment, AgentCellBridgeInterface agentCellBridgeInterface,
DriverInterface featureBridgeInterface, PageContainerInterface pageContainerInterface) {
super(fragment, agentCellBridgeInterface, featureBridgeInterface, pageContainerInterface);
}
/**
* 【A.2.2.2】 根据反射构造 模块
*/
@Override
protected AgentInterface constructAgents(Class<? extends AgentInterface> agentClass) {
AgentInterface cellAgent = null;
try {
cellAgent = agentClass.getConstructor(Object.class).newInstance(
fragment);
} catch (Exception e) {
// e.printStackTrace();
}
if (cellAgent == null) {
try {
Class<?>[] params = {Fragment.class,
DriverInterface.class, PageContainerInterface.class};
Object[] values = {fragment, driverInterface, pageContainer};
cellAgent = agentClass.getConstructor(params).newInstance(
values);
} catch (Exception e) {
// e.printStackTrace();
}
}
if (cellAgent == null) {
try {
cellAgent = agentClass.newInstance();
} catch (Exception e) {
// e.printStackTrace();
}
}
return cellAgent;
}
}
五、视图管理器
5.1 接口 CellManagerInterface
视图管理器
- 设置Shield模块的承载布局容器
- 负责将模块所提供的视图片段(SectionCellInterface)有序地添加到页面中
- 在适当的时候对这些视图进行更新
public interface CellManagerInterface<T extends ViewGroup> {
/**
* 1. 设置Shield模块的承载布局容器
*/
void setAgentContainerView(T containerView);
/**
* 2.负责将模块所提供的视图片段(SectionCellInterface)有序地添加到页面中
* 3.在适当的时候对这些视图进行更新
* 调用:
* AgentManager的ResetAgents 和 SetupAgents,会调用AgentCellBridgeInterface.updateCells
* ----> AgentCellBridgeInterface会被页面容器实现---->实现函数中调用此处的 CellManagerInterface.updateCells
*
* 函数的实现:移除当前视图Views,并根据参数 添加、修改、删除列表重新构建
*/
void updateCells(ArrayList<AgentInterface> addList, ArrayList<AgentInterface> updateList, ArrayList<AgentInterface> deleteList);
/**
* 更新当前模块的视图
* 调用:
* 1. 譬如,刷新示例Agent中,响应白板中状态变换后,触发该函数已重新生成指定模块的视图;
* 2. 譬如,LoadingStatusCell中 点击“加入一个新片段”按钮,触发视图重新绘制
* 期间 Agent会调用DriverInterface.updateAgentCell---->DriverInterface接口被页面容器实现---->实现函数中,调用此处CellManagerInterface.updateAgentCell
*
* 该函数的实现:无非是移除已创建的视图,并根据AgentInterface重新生成
*/
void updateAgentCell(AgentInterface agent);
//手动刷新接口
void notifyCellChanged();
}
5.2 ViewGroupCellManager
public class ViewGroupCellManager implements CellManagerInterface<ViewGroup> {
protected final HashMap<String, Cell> cells = new HashMap<String, Cell>();
private Context mContext;
/**
* Shield模块的承载布局容器
*/
private ViewGroup agentContainerView;
/******************************构造函数*******************************/
public ViewGroupCellManager(Context mContext) {
this.mContext = mContext;
}
/**************** 1. 设置Shield模块的承载布局容器***************************/
@Override
public void setAgentContainerView(ViewGroup containerView) {
agentContainerView = containerView;
}
/******************2. 更新视图控制器*************************************/
/**
* 在agentlist发生变化时或resetagents时会被调用,更新完后会notifycellchange
* @param addList
* @param updateList
* @param deleteList
*/
@Override
public void updateCells(ArrayList<AgentInterface> addList
, ArrayList<AgentInterface> updateList
, ArrayList<AgentInterface> deleteList) {
//添加新的模块
if (addList != null && !addList.isEmpty()) {
for (AgentInterface addCell : addList) {
//SectionCellInterface 视图片段实体,提供视图View创建和更新功能
if (addCell.getSectionCellInterface() != null) {
//遍历片段
for (int sectionPosition = 0; sectionPosition < addCell.getSectionCellInterface().getSectionCount(); sectionPosition++) {
//遍历行
for (int rowPosition = 0; rowPosition < addCell.getSectionCellInterface().getRowCount(sectionPosition); rowPosition++) {
Cell c = new Cell();
c.owner = addCell;
String sectionName = addZeroPrefix(sectionPosition);
String rowName = addZeroPrefix(rowPosition);
//内部name 排序先比较外部index再比较内部name
c.name = addCell.getAgentCellName() + sectionName + "." + rowName;
c.view = addCell.getSectionCellInterface().onCreateView(null, addCell.getSectionCellInterface().getViewType(sectionPosition, rowPosition));
addCell.getSectionCellInterface().updateView(c.view, sectionPosition, rowPosition, null);
//Map key 模块在模块序列中的排序String + 外部index加上内部name
String cellNameSwp = getCellName(addCell, addCell.getAgentCellName() + sectionName + "." + rowName);
cells.put(cellNameSwp, c);
}
}
}
}
}
//更新原来有的位置
//只更新之前存在的Agent的Cell的index
//因为是viewgroup会有多个cell,需要把agent对应的多个cell的index都更新,之后统一notify
if (updateList != null && !updateList.isEmpty()) {
HashMap<String, Cell> copyOfCells = (HashMap<String, Cell>) cells.clone();
for (AgentInterface updateCell : updateList) {
if (updateCell.getSectionCellInterface() != null) {
//现在是三重循环了,
for (int sectionPosition = 0; sectionPosition < updateCell.getSectionCellInterface().getSectionCount(); sectionPosition++) {
for (int rowPosition = 0; rowPosition < updateCell.getSectionCellInterface().getRowCount(sectionPosition); rowPosition++) {
// for (int i = 0; i < updateCell.getSectionCellInterface().getViewCount(); i++) {
//因为每个cell都要找一遍,所以是双重循环
for (Map.Entry<String, Cell> entry : copyOfCells.entrySet()) {
//获取当前Cell的加零Name
String sectionName = addZeroPrefix(sectionPosition);
String rowName = addZeroPrefix(rowPosition);
//判断owner属于该agent,并且之前的name是和目前cellNam+内部顺序一致(找到对应的Cell)
if (entry.getValue().owner == updateCell && entry.getValue().name == updateCell.getAgentCellName() + sectionName + "." + rowName) {
//替换cell的index
Cell temp = entry.getValue();
cells.remove(entry.getKey());
cells.put(getCellName(updateCell, temp.name), temp);
}
}
}
}
}
}
}
//删除需要删除的
if (deleteList != null && !deleteList.isEmpty()) {
for (AgentInterface deleteCell : deleteList) {
Iterator<Map.Entry<String, Cell>> itr = cells.entrySet().iterator();
while (itr.hasNext()) {
Cell c = itr.next().getValue();
if (c.owner == deleteCell) {
itr.remove();
}
}
}
}
notifyCellChanged();
}
/******************2. 根据cells,进行实际的视图更新*************************************/
@Override
public void notifyCellChanged() {
handler.removeCallbacks(notifyCellChanged);
handler.post(notifyCellChanged);
}
public final static Handler handler = new Handler(Looper.getMainLooper());
private final Runnable notifyCellChanged = new Runnable() {
@Override
public void run() {
handler.removeCallbacks(this);
updateAgentContainer();
}
};
public void updateAgentContainer() {
ArrayList<Cell> sort = new ArrayList<Cell>(cells.values());
Collections.sort(sort, cellComparator);
resetAgentContainerView();
for (Cell c : sort) {
// String host = agentHolderInterface.findHostForAgent(c.owner);
// if (TextUtils.isEmpty(host)) {
// return;
// }
addCellToContainerView(c);
}
}
protected static final Comparator<Cell> cellComparator = new Comparator<Cell>() {
@Override
public int compare(Cell lhs, Cell rhs) {
return lhs.owner.getIndex().equals(rhs.owner.getIndex()) ? lhs.name.compareTo(rhs.name)
: lhs.owner.getIndex().compareTo(rhs.owner.getIndex());
}
};
/******************3. 更新指定 模块的视图*************************************/
@Override
public void updateAgentCell(AgentInterface agent) {
Iterator<Map.Entry<String, Cell>> itr = cells.entrySet().iterator();
while (itr.hasNext()) {
Cell c = itr.next().getValue();
if (c.owner == agent) {
itr.remove();
}
}
if (agent.getSectionCellInterface() != null) {
for (int sectionPosition = 0; sectionPosition < agent.getSectionCellInterface().getSectionCount(); sectionPosition++) {
for (int rowPosition = 0; rowPosition < agent.getSectionCellInterface().getRowCount(sectionPosition); rowPosition++) {
Cell c = new Cell();
c.owner = agent;
String sectionName = addZeroPrefix(sectionPosition);
String rowName = addZeroPrefix(rowPosition);
//Map key 外部index加上内部name
String cellNameSwp = getCellName(agent, agent.getAgentCellName() + sectionName + "." + rowName);
//内部name 排序先比较外部index再比较内部name
c.name = agent.getAgentCellName() + sectionName + "." + rowName;
c.view = agent.getSectionCellInterface().onCreateView(null, agent.getSectionCellInterface().getViewType(sectionPosition, rowPosition));
agent.getSectionCellInterface().updateView(c.view, sectionPosition, rowPosition, null);
cells.put(cellNameSwp, c);
}
}
}
notifyCellChanged();
}
/*********************get | Set*****************************/
public ViewGroup getAgentContainerView() {
return agentContainerView;
}
/*********************utils*****************************/
/**
* 布局操作
*/
public void resetAgentContainerView() {
agentContainerView.removeAllViews();
}
public void addCellToContainerView(Cell cell) {
agentContainerView.addView(cell.view);
}
/**
* key生成
*/
// 根据num,返回对应规则的0字符串前缀
private String addZeroPrefix(int num) {
//返回以10为底的double的值 + 1
int j = ((int) Math.log10(num) + 1);
//加上几个0
String zeroStr = "";
for (int k = 0; k < 6 - j; k++) {
zeroStr = zeroStr + "0";
}
return zeroStr + num;
}
protected String getCellName(AgentInterface agent, String name) {
return TextUtils.isEmpty(agent.getIndex()) ? name : agent.getIndex() + name;
}
/**
* cell操作
*/
public void addAgentCell(AgentInterface agent) {
if (agent.getSectionCellInterface() != null) {
for (int sectionPosition = 0; sectionPosition < agent.getSectionCellInterface().getSectionCount(); sectionPosition++) {
for (int rowPosition = 0; rowPosition < agent.getSectionCellInterface().getRowCount(sectionPosition); rowPosition++) {
Cell c = new Cell();
c.owner = agent;
//Map key 外部index加上内部name
//根据 片段位置和行位置,生成带0字符前缀的
String sectionName = addZeroPrefix(sectionPosition);
String rowName = addZeroPrefix(rowPosition);
String cellNameSwp = getCellName(agent, agent.getAgentCellName() + sectionName + "." + rowName);
//内部name 排序先比较外部index再比较内部name
c.name = agent.getAgentCellName() + sectionName + "." + rowName;
c.view = agent.getSectionCellInterface().onCreateView(null, agent.getSectionCellInterface().getViewType(sectionPosition, rowPosition));
agent.getSectionCellInterface().updateView(c.view, sectionPosition, rowPosition, null);
cells.put(cellNameSwp, c);
addCellToContainerView(c);
}
}
}
}
public void addCell(AgentInterface agentInterface, String name, View view) {
String cellName = getCellName(agentInterface, name);
Cell c = new Cell();
c.owner = agentInterface;
c.name = name;
c.view = view;
cells.put(cellName, c);
notifyCellChanged();
}
public void removeAllCells(AgentInterface agent) {
Iterator<Map.Entry<String, Cell>> itr = cells.entrySet().iterator();
while (itr.hasNext()) {
Cell c = itr.next().getValue();
if (c.owner == agent) {
itr.remove();
}
}
notifyCellChanged();
}
public boolean hasCell(AgentInterface cellAgent, String name) {
Cell c = cells.get(getCellName(cellAgent, name));
if (c == null)
return false;
return cellAgent == null || c.owner == cellAgent;
}
public void removeCell(AgentInterface cellAgent, String name) {
if (hasCell(cellAgent, name)) {
cells.remove(getCellName(cellAgent, name));
notifyCellChanged();
}
}
public Cell getFirstCellForAgent(AgentInterface agent) {
ArrayList<Cell> sort = new ArrayList<Cell>(cells.values());
Collections.sort(sort, cellComparator);
for (Cell c : sort) {
if (c.owner == agent) {
return c;
}
}
return null;
}
}
5.3 AdapterCellManager
public class AdapterCellManager implements CellManagerInterface<ListView> {
protected Context mContext;
protected ListView listView;
private boolean isSetList;
protected MergeAdapter mergeAdapter;
protected final HashMap<String, Cell> cells = new HashMap<String, Cell>();
/******************************构造函数*******************************/
public AdapterCellManager(Context mContext) {
this.mContext = mContext;
mergeAdapter = new MergeAdapter();
}
/**************** 1. 设置Shield模块的承载布局容器***************************/
@Override
public void setAgentContainerView(ListView containerView) {
isSetList = true;
this.listView = containerView;
}
/******************2. 更新视图控制器*************************************/
/**
* 在agentlist发生变化时或resetagents时会被调用,更新完后会notifycellchange
* @param addList
* @param updateList
* @param deleteList
*/
@Override
public void updateCells(ArrayList<AgentInterface> addList, ArrayList<AgentInterface> updateList, ArrayList<AgentInterface> deleteList) {
if (addList != null && !addList.isEmpty()) {
//添加新的
for (AgentInterface addCell : addList) {
if (addCell.getSectionCellInterface() != null) {
ListAdapter adapter;
SectionCellInterface cellInterface = addCell.getSectionCellInterface();
adapter = createListAdapter(cellInterface);
Cell c = new Cell();
c.owner = addCell;
c.name = addCell.getAgentCellName();
c.adpater = adapter;
cells.put(getCellName(addCell), c);
}
}
}
//更新原来有的位置
//只更新之前存在的Agent的Cell的index
//因为是viewgroup会有多个cell,需要把agent对应的多个cell的index都更新,之后统一notify
if (updateList != null && !updateList.isEmpty()) {
HashMap<String, Cell> copyOfCells = (HashMap<String, Cell>) cells.clone();
for (AgentInterface updateCell : updateList) {
if (updateCell.getSectionCellInterface() != null) {
for (Map.Entry<String, Cell> entry : copyOfCells.entrySet()) {
//判断owner属于该agent,并且之前的name是和目前cellNam+内部顺序一致(找到对应的Cell)
if (entry.getValue().owner == updateCell) {
//替换cell的index
Cell temp = entry.getValue();
cells.remove(entry.getKey());
cells.put(getCellName(updateCell), temp);
}
}
}
}
}
//删除需要删除的
if (deleteList != null && !deleteList.isEmpty()) {
for (AgentInterface deleteCell : deleteList) {
Iterator<Map.Entry<String, Cell>> itr = cells.entrySet().iterator();
while (itr.hasNext()) {
Cell c = itr.next().getValue();
if (c.owner == deleteCell) {
itr.remove();
}
}
}
}
notifyCellChanged();
}
protected ListAdapter createListAdapter(final SectionCellInterface sectionCellInterface) {
ListAdapter adapter = new BaseAdapter() {
@Override
public int getCount() {
int count = 0;
for (int i = 0; i < sectionCellInterface.getSectionCount(); i++) {
count = count + sectionCellInterface.getRowCount(i);
}
return count;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public int getViewTypeCount() {
return sectionCellInterface.getViewTypeCount();
}
@Override
public int getItemViewType(int position) {
int rowPosition = position;
int sectionPosition = 0;
for (int i = 0; i < sectionCellInterface.getSectionCount(); i++) {
if (rowPosition < sectionCellInterface.getRowCount(i)) {
sectionPosition = i;
break;
} else {
rowPosition = rowPosition - sectionCellInterface.getRowCount(i);
}
}
return sectionCellInterface.getViewType(sectionPosition, rowPosition);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null || convertView.getTag(R.id.adapter_cell_tag_id) == null ||
getItemViewType(position) != (int) convertView.getTag(R.id.adapter_cell_tag_id)) {
view = sectionCellInterface.onCreateView(parent, getItemViewType(position));
view.setTag(R.id.adapter_cell_tag_id, getItemViewType(position));
} else {
view = convertView;
}
int rowPositon = position;
for (int i = 0; i < sectionCellInterface.getSectionCount(); i++) {
if (rowPositon < sectionCellInterface.getRowCount(i)) {
sectionCellInterface.updateView(view, i, rowPositon, parent);
break;
} else {
rowPositon = rowPositon - sectionCellInterface.getRowCount(i);
}
}
return view;
}
};
return adapter;
}
/******************2. 根据cells,进行实际的视图更新*************************************/
@Override
public void notifyCellChanged() {
handler.removeCallbacks(notifyCellChanged);
handler.post(notifyCellChanged);
}
public final static Handler handler = new Handler(Looper.getMainLooper());
private final Runnable notifyCellChanged = new Runnable() {
@Override
public void run() {
handler.removeCallbacks(this);
updateAgentContainer();
}
};
public void updateAgentContainer() {
ArrayList<Cell> sort = new ArrayList<Cell>(cells.values());
Collections.sort(sort, cellComparator);
resetAgentContainerView();
for (Cell c : sort) {
if (c.adpater == null)
continue;
// String host = agentHolderInterface.findHostForAgent(c.owner);
// if (TextUtils.isEmpty(host)) {
// return;
// }
addCellToContainerView(c);
}
mergeAdapter.notifyDataSetChanged();
if (isSetList) {
listView.setAdapter(mergeAdapter);
isSetList = false;
}
}
protected static final Comparator<Cell> cellComparator = new Comparator<Cell>() {
@Override
public int compare(Cell lhs, Cell rhs) {
return lhs.owner.getIndex().equals(rhs.owner.getIndex()) ? lhs.name.compareTo(rhs.name)
: lhs.owner.getIndex().compareTo(rhs.owner.getIndex());
}
};
/******************3. 更新指定 模块的视图*************************************/
@Override
public void updateAgentCell(AgentInterface agent) {
Cell targetCell = findCellForAgent(agent);
if (targetCell != null && targetCell.adpater != null && targetCell.adpater instanceof BaseAdapter) {
((BaseAdapter) targetCell.adpater).notifyDataSetChanged();
}
}
/*********************get | Set*****************************/
public ViewGroup getAgentContainerView() {
return this.listView;
}
/*********************utils*****************************/
/**
* 布局操作
*/
public void resetAgentContainerView() {
mergeAdapter.clear();
}
public void addCellToContainerView(Cell cell) {
mergeAdapter.addAdapter(cell.adpater);
}
/**
* key生成
*/
protected String getCellName(AgentInterface agent) {
return TextUtils.isEmpty(agent.getIndex()) ? agent.getAgentCellName() : agent.getIndex() + agent.getAgentCellName();
}
/**
* cell操作
*/
public void addAgentCell(AgentInterface agent) {
if (agent.getSectionCellInterface() != null) {
ListAdapter adapter;
SectionCellInterface cellInterface = agent.getSectionCellInterface();
adapter = createListAdapter(cellInterface);
Cell c = new Cell();
c.owner = agent;
c.name = agent.getAgentCellName();
c.adpater = adapter;
cells.put(getCellName(agent), c);
}
notifyCellChanged();
}
public void removeAllCells(AgentInterface agent) {
Iterator<Map.Entry<String, Cell>> itr = cells.entrySet().iterator();
while (itr.hasNext()) {
Cell c = itr.next().getValue();
if (c.owner == agent) {
itr.remove();
}
}
notifyCellChanged();
}
public Cell findCellForAgent(AgentInterface c) {
for (Map.Entry<String, Cell> entry : cells.entrySet()) {
if (c == entry.getValue().owner) {
return entry.getValue();
}
}
return null;
}
}
5.4 SectionRecyclerCellManager
- 支持 ItemIdInterface
- 支持 DividerInterface
- 支持 SectionExtraCellInterface
- 支持 CellStatusMoreInterface
- 支持 CellStatusInterface
- 支持 SectionLinkCellInterface
- 支持 ItemClickInterface
- 支持 SetTopInterface
- 支持 SetBottomInterface
- 支持 SetZoomInterface
其中使用了支持多种功能recyclerView,不太懂的可能不利于了解整体的设计思路,这里我们就不讲解此处的代码,有兴趣的可以自行了解下。
六、白板
在某些场景下,页面中的一些视图片段会根据用户操作发生一些联动。而当这些视图片段处于不同的模块中时,这些模块就需要进行通信。
在这种情况下,如果让模块与模块直接进行交互,就无法避免模块之间的耦合,这样既无法保证模块的独立性,也影响可复用性。于是我们基于RxJava设计实现了观察者模式的白板组件,在Shield框架中称之为WhiteBoard。WhiteBoard在一个页面中唯一,所有模块共享,模块之间或是模块与页面的通信都通过WhiteBoard来进行。
【图4】
6.1 实现WhiteBoard
- 提供模块交互能力的共享内存区域;
-
- 数据的存取功能;
-
- 观察者模式实现的通知方式;
官方使用RxJava的实现方式,如果产品不期望引入的话,可以使用java原生方式进行改造
- 观察者模式实现的通知方式;
-
- 依赖页面容器的生命周期回调;
-
public class WhiteBoard {
/**
* 存放数据的Bundle
* 会以WHITE_BOARD_DATA_KEY名称,存放在页面容器的bundle中
*/
protected Bundle mData;
// The key WhiteBoard used to save the data set in savedInstanceState.
public static final String WHITE_BOARD_DATA_KEY = "White_Board_Data";
/**
* 观察者列表
* An Observable Map.
* (Here we actually save Subjects, which give us the ability to emit new changes to the Subscribers. )
*/
protected HashMap<String, Subject> subjectMap;
/********************构造函数******************************/
public WhiteBoard() {
this(null);
}
public WhiteBoard(Bundle data) {
mData = data;
if (mData == null) {
mData = new Bundle();
}
subjectMap = new HashMap<>();
}
/**********************生命周期********************************/
/**
* Called to initial the WhiteBoard from the savedInstanceState.
* 获取或创建 共享内存载体Bundle
* @param savedInstanceState
*/
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mData = savedInstanceState.getBundle(WHITE_BOARD_DATA_KEY);
}
if (mData == null) {
mData = new Bundle();
}
}
/**
* Called to save the data set to the input parameter.
* 将共享内存载体Bundle存放入页面容器的Bundle中
* @param outState
*/
public void onSaveInstanceState(Bundle outState) {
if (outState != null) {
// here we must save a new copy of the mData into the outState
outState.putBundle(WHITE_BOARD_DATA_KEY, new Bundle(mData));
}
}
/**
* Called to destory the WhiteBoard.
* 清除Bundle
*/
public void onDestory() {
subjectMap.clear();
mData.clear();
}
/***********************************2. 观察者模式实现的通知方式;*******************************************************/
/**
* Find the value of the key in the data set, and
* returns an Observable with the value as its initial state.
* If the key is not contained in the data set,
* returns an Observable with null as its initial state.
* 返回key对应的 被观察者对象
* @param key
* @return
*/
public Observable getObservable(final String key) {
Subject res = null;
if (subjectMap.containsKey(key)) {
res = subjectMap.get(key);
} else {
res = PublishSubject.create();
subjectMap.put(key, res);
}
if (getData(key) != null) {
return res.startWith(getData(key));
} else {
return res;
}
}
/**
* Update the Observable of a data of a given key.
* 通知key对应的 被观察者对象的所有观察者事件变化
*/
protected void notifyDataChanged(String key) {
if (subjectMap.containsKey(key)) {
subjectMap.get(key).onNext(mData.get(key));
}
}
/****************************************1. 数据的存取功能;****************************************************************/
/**
* Remove the value of a given key.
*/
public void removeData(String key) {
mData.remove(key);
notifyDataChanged(key);
}
/**
* Get the value of a given key.
*
*/
public Object getData(String key) {
return mData.get(key);
}
/**
* Check if data set contains a certain key.
*/
public boolean containsKey(String key) {
return mData.containsKey(key);
}
/**
* Inserts all mappings from the given Bundle into this Bundle.
*/
public void putAll(Object caller, Bundle bundle) {
mData.putAll(bundle);
for (String key : bundle.keySet()) {
notifyDataChanged(key);
}
}
/**
* Returns a Set containing the Strings used as keys in this Bundle.
*
* @return a Set of String keys
*/
public Set<String> keySet() {
return mData.keySet();
}
/**
* Inserts a Boolean value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a boolean
*/
public void putBoolean(@Nullable String key, boolean value) {
mData.putBoolean(key, value);
notifyDataChanged(key);
}
}
建议
- 参考设计思想,反射方式实现构建的确实现了远程动态配置,但是使用时更需要谨慎对待;
- 不建议直接使用,因为其实内部嵌套了很多其他的实现,而这块可能各自的项目中已有了,还是遵照思路,改成适合自己的。