(4.2.50)一种业务控件实现的方式

在常规的业务开发中,我们往往是先构建界面元素,然后在Activity中赋能界面元素相关业务能力,譬如下图所示的编辑采购订单场景:

在这里插入图片描述

  1. 【有形】我们往往是要构建整体的界面,这个界面往往由一些组合式控件或者布局的集合构成;
  2. 【有神】原始的控件是不具备业务能力的,我们往往会在Activity中通过给控件setClicklistener等方式,使得界面元素具备一定的业务能力。譬如:
    1. 点击“供应商组合控件”,会跳转到供应商选择界面
    2. 选择供应商后,“供应商组合控件”会进行界面上的刷新
    3. 同时,也要存储新选择的供应商,用于“最终提交数据”的构建
  3. 【血肉】作为编辑界面,我们往往会有一个“初始数据”,进入界面后要将“初始数据”中的各个成员属性,赋值到界面元素上
    1. 界面字符串。 成员属性往往是另一个Class的一个对象实例,譬如供应商(包含id、名称、分组等),这样的成员对象体现在界面上基本都是一个字符串,这就是界面字符串
    2. 成员实例对象。 界面字符串对应的真实数据,一方面用于“最终提交数据”的构建,一方面在进行界面业务操作(譬如跳转)时,往往需要传递到选择界面作为默认选择数据
  4. 【凝练】最终,在经过一系列的输入、选择等业务操作后,要通过界面元素最终收集到一个“最终提交数据”

由此可以看到,我们将大量的业务代码充斥在Activity中,而且仅实现了界面控件级别的复用,业务级别的代码基本都是各种复制,由此也催生出一种想法:

我们能不能做到业务控件级别的复用?

这就是本篇《业务套件》的由来,我们期望可以达成:

  1. 赋能基础的界面控件以业务能力
    1. 可以内置业务动作
    2. 可以在业务动作响应后,自动更新界面字符串
    3. 可以在更新界面元素后,将新的“成员实例对象”存储起来,用于“最终提交数据”的构建
  2. 可以通过构建界面控件列表,来构建出整体的界面(类似于List的使用,但是data内置view构建能力)
  3. 可以将“初始数据”的成员属性自动映射到对应界面控件的界面字符串上
  4. 可以从界面控件上收集数据,自动映射到“最终提交数据”的成员属性上

时序图

为了方便理解,我们在最开始先针对上述提及的4个核心过程,分别提供

界面构造过程

在这里插入图片描述
查看原图

控件构造过程如下图
在这里插入图片描述
查看原图

动作响应过程

在这里插入图片描述
查看原图

vo到界面过程

在这里插入图片描述
查看原图

界面到view过程

在这里插入图片描述
查看原图

一、UiViewPool 业务控件的视图控件池

其核心承载功能就是,根据 业务控件类型,生成其真实的视图控件

public interface UiViewPool {

    /**
     * 根据 业务控件类型, 返回对应的 真实视图控件
     * @param uiType 业务控件类型 {@link com.sangfor.pocket.uin.newway.UiItemTypes.ViewType}
     * @param parant 当前控件将被加入的父布局
     * @return 真实的视图控件
     */
    View getView(int uiType, ViewGroup parant);
	
}

1.1 业务控件UiType和视图控件ViewType的区别与转换

  • 视图控件
    • 对应 UiItemTypes.ViewType
    • 一种真实的视图控件样式,譬如TextView、LinearLayout、TextImageNormalForm等
  • 业务控件
    • 对应 UiItemTypes.UiType
    • 一种 业务控件样式,是针对ViewType的进一步与业务结合的封装

具体已经支持的业务控件类型和视图控件类型,可以查看com.sangfor.pocket.uin.newway.UiItemTypes

public class UiItemTypes {

	public static class ViewType {
        public static final int VT_UNKNOWN = 0;
		public static final int VT_LinearLayout_Vertical = 10;
		...
	}
	
	public static class UiType {
		public static final int UT_SELECT_NORMAL = 1;
        public static final int UT_SINGLE_SELECT_CUSTM = 4;
        public static final int UT_MULTIPLE_SELECT_CUSTM = 5;
		...
	}

}

由于整体框架遵循”业务控件“高于”视图控件“的一致性规则,因此,即便是一些不具备业务属性的基础视图控件,我们也不能直接使用其视图控件。而是需要定义其对应的业务控件去使用

1.1.1 业务控件UiType和视图控件ViewType转换器UiTypeMapToViewType

其实就是一个 对应的转换关系,将 UiType业务控件类型 转换为 viewType视图控件类型

public class UiTypeMapToViewType {
	
	 public int mapWithType(int uiType){
		int type;
        switch (uiType){
			case UiItemTypes.UiType.UT_SELECT_NORMAL:
            case UiItemTypes.UiType.UT_SELECT_TIME:
            case UiItemTypes.UiType.UT_SINGLE_SELECT_CUSTM:
            case UiItemTypes.UiType.UT_MULTIPLE_SELECT_CUSTM:
            case UiItemTypes.UiType.UT_SINGLE_PERSON:
            case UiItemTypes.UiType.UT_SINGLE_SELECT_CUSTM_BY_SALE_ORDER:
                type = UiItemTypes.ViewType.VT_TextImageNormalForm;
                break;
			case UiItemTypes.UiType.UT_CLASS_TITLE:
                //左边显示是表单
                type = UiItemTypes.ViewType.VT_CLASS_TITLE;
                break;
			//...
		
		}
	 }
}

1.2 BaseUiViewPool 基础控件池

上文我们已经说了,UiViewPool的主要目的就是

1.2.1 功能:生成视图控件View并返回

生成视图控件View并返回是UiViewPool的核心功能

它的核心实现就是借助 ViewMaker来实现的:

  1. 持有 Map<Integer, ViewMaker> pool = new HashMap<>();视图控件类型ViewType 与对应 ViewMaker视图控件制造器 的Map
  2. 借助UiTypeMapToViewType业务控件UiType和视图控件ViewType转换器,对UiType进行转换为viewtype
  3. 使用viewtype对应的ViewMaker,new出真正的视图控件
public abstract class BaseUiViewPool implements UiViewPool {

	protected Context context;

    /**
     * 视图控件类型ViewType 与对应 ViewMaker视图控件制造器 的Map
     */
    protected Map<Integer, ViewMaker> pool = new HashMap<>();

	public BaseUiViewPool(Context context) {
	
		//上下文对象,用于创建视图控件
		this.context = context;

		//业务控件UiType 和 视图控件ViewType 转换器
		map = new UiTypeMapToViewType();

		//构建支持的 视图制造器列表
		ViewMaker[] viewMakers = listViewMakers();
		if(viewMakers != null && viewMakers.length > 0){
			for(ViewMaker vm : viewMakers){
				ViewMaker put = pool.put(vm.getViewType(), vm);

			}
		}
	}

	@Override
    public View getView(int uiType, ViewGroup parent) {
	
        ViewMaker viewMaker;
        
        if(uiType == UiItemTypes.ViewType.VT_UNKNOWN){
            viewMaker = UNKNOWN_VIEW_MAKER;
        }else {
			//1. 借助`UiTypeMapToViewType`业务控件UiType和视图控件ViewType转换器,对UiType进行转换为viewtype
			//2. 获取viewtype对应的ViewMaker
            viewMaker = pool.get(map.mapWithType(uiType));
            if (viewMaker == null) {
                if (BuildConfig.DEBUG) {
                    throw new IllegalStateException("Unsupported uiType [" + uiType + "]");
                }
                viewMaker = UNSUPPORTED_VIEW_MAKER;
            }
        }

		//3. 使用viewtype对应的ViewMaker,new出真正的视图控件
        return viewMaker.makeView(context, parent);
    }

}

1.2.2 功能:初始化 持有视图控件ViewMakers 的 HashMap

在上个环节我们讲到了,借助Map<Integer, ViewMaker> pool = new HashMap<>();视图控件类型ViewType 与对应 ViewMaker视图控件制造器 的Map,来获取对应的 ViewMaker

那么这个 HashMap 是怎么初始化的呢?

  1. 在我们的实际开发中,有一些基础控件池是全局都会使用的,针对这些我们需要提供一个集中处理器帮我们直接生成
  2. 同时,在具体的一些业务中,又会需要一些自定义的业务控件,那么我们的集中处理器也必须要有一定的配置功能
public abstract class BaseUiViewPool implements UiViewPool {

	  protected abstract ViewMaker[] listViewMakers();

}

具体的,我们现在有三个子类,我们分别看下他们的实现:

public class StandardViewPool extends BaseUiViewPool {
    public StandardViewPool(Context context) {
        super(context);
    }

    @Override
    protected ViewMaker[] listViewMakers() {
        return new ViewMaker[]{
                new FlexiblePictureLayoutMaker(),
                new ApprovalShowViewMaker(),
                new RemovableOrangeClassifyTitleMaker(),
                new AddButtonViewMaker(),
                new InfoStatusBarViewMaker(),
                new EmptyViewMaker()
        };
    }
}

public class FormViewPool extends BaseUiViewPool {
    public FormViewPool(Context context) {
        super(context);
    }

    @Override
    protected ViewMaker[] listViewMakers() {
        return new ViewMaker[]{
                new TextEditableFormMaker(),
                new TextImageNormalFormMaker(),
                new LeftWrapContentTextImageNormalFormMaker(),
                new FormTipsMaker(),
                new IsolatedFormButtonMaker(),
                new LeftRightTitleMaker(),
                new PureEditableFormMaker()
        };
    }
}

public class JxcViewPool extends BaseUiViewPool {
    public JxcViewPool(Context context) {
        super(context);
    }

    @Override
    protected ViewMaker[] listViewMakers() {
        return new ViewMaker[]{
                new StockCheckDetailsViewMaker(),
                new StockCheckProductCreateViewMaker(),
                new StockCheckProductShowViewMaker(),
                new JxcProductItemViewMaker(),
        };
    }
}

1.2.3 功能:视图控件池的合并

需要注意的是,我们在上文列举的StandardViewPool,FormViewPool,JxcViewPool都是直接继承BaseUiViewPool

这也就意味着他们支持的功能区域是各自独立的,那么我如何同时使用不同Pool呢?

	 /**
     * 视图控件池的组合能力
     * @param other
     * @return
     */
    public BaseUiViewPool merge(BaseUiViewPool other) {
        pool.putAll(other.pool);
        return this;
    }

我们不使用继承结构,也是为了遵循开发中的”组合高于继承“的原则

1.3 ViewMaker 单个视图控件的制造器

根据当前指定的int 控件类型,生成对应的实际控件。

这是对**new 视图控件**能力的一种抽象,借助该制造器赋予框架生成对应试图控件的能力

public interface ViewMaker {

    /**
     * 当前 制造器 对应的 viewtype视图控件类型
     * 该值不能随意定义,必须集中管理为 {@link com.sangfor.pocket.uin.newway.UiItemTypes.ViewType}中自定义
     * @return
     */
    int getViewType();

    /**
     * 当前制造器 对应的 视图控件
     * @param context  new控件时需要的上下文
     * @param parent  控件将被包含的parent group
     * @return
     */
    View makeView(Context context, ViewGroup parent);
}

1.3.1 目前支持的视图控件

目前支持的视图控件,主要包含以下样式(com.sangfor.pocket.uin.newway.UiItemTypes#ViewType):

//框架自定义
public static final int VT_UNKNOWN = 0;
public static final int VT_STANDARD_GROUP = 6;

//android 原生View
public static final int VT_LinearLayout_Vertical = 10;
public static final int VT_EMPYT = 19;

//moa标准表单项
public static final int VT_TextEditableForm = 1;
public static final int VT_TextImageNormalForm = 2;
public static final int VT_FlexiblePictureLayout = 3;
public static final int VT_LEFT_WRAP_TEXT_IMAGE_NORMAL_FORM = 5;
public static final int VT_CLASS_TITLE = 7;
public static final int VT_ADD_BUTTON_VIEW = 8;//底部的 添加button
public static final int VT_PRODUCT_ITEM_VIEW = 9;//jxc的一个商品item,包含商品名称、批次号、数量、总价等
public static final int VT_FormTips = 11;
public static final int VT_IsolatedFormButton = 12;
public static final int VT_StockCheckDetailsView = 13;
public static final int VT_LeftRightTitle = 14;
public static final int VT_StockCheckProductCreateView = 15;
public static final int VT_InfoStatusBar = 16;
public static final int VT_StockCheckProductShowView = 17;
public static final int VT_PURE_EDITABLEFORM = 18;

//审批人
public static final int VT_APPROVAL_SHOW_VIEW = 4;

更直观的,我们简单的看两个实例:

public class TextImageNormalFormMaker implements ViewMaker {
    @Override
    public int getViewType() {
        return UiItemTypes.ViewType.VT_TextImageNormalForm;
    }

    @Override
    public View makeView(Context context, ViewGroup parent) {
        return new TextImageNormalForm(context);
    }
}

public class VerticalLinearLayoutMaker implements ViewMaker {
    @Override
    public int getViewType() {
        return UiItemTypes.ViewType.VT_LinearLayout_Vertical;
    }

    @Override
    public View makeView(Context context, ViewGroup parent) {
        LinearLayout ll = new LinearLayout(context);
        ll.setOrientation(LinearLayout.VERTICAL);
        return ll;
    }
}

二、UIItem业务控件

2.1 UiItem接口

UIItem是对业务控件的一种抽象

在了解 UiItem之前,我们必须清晰的意识到,业务控件其本质还是一种视图控件。因此,视图控件所具备的一些能力,业务控件也会被具有和抽象出来

我们思考一个场景:选择客户,并据此进行分析

  1. 针对这是一个视图控件

    1. 标识它对应TextImageNormalForm视图控件
    2. 当前视图中的唯一标识?
    3. 它具备 渲染到实际布局中、从实际布局中移除、更新等生命周期
    4. 它可能有一定的 Margins、Visible、Clickable要求
    5. 他可能有一定的 上下分割线Divider的显示及缩进要求
    6. 一些点击或者长按事件的监听
    7. 感知自己父视图是谁的能力?
  2. 针对它具备一定的业务能力,也就是说和 CustmerVo相关

    1. 需要了解 使用CustmerVo 如何 进行界面上相关 字符串或者颜色的如何转换?----> UIValue
    2. UiItem 如何取出来 CustmerVo实例对象的引用 作为自己的展示vo?---->Getter
    3. UiItem被用户进行重选后,要将返回的新的CustmerVo实例对象 赋值到哪个引用上?---->Setter
    4. 需要一个生命周期回调,能够在适当时机处理这种转化的UI渲染?—>renderView
    5. UiItem对应的展示vo是否发生了变化?—>backup\isChanged
    6. 本身所具备的一些对点击或者长按的处理,譬如点击跳转到选择客户?—>onClick\onLongClick
    7. 一些检测规则,用于用户调用去校验是否满足要求?----> CheckInfo
    8. 对支持的ActivityResult的int Code的过滤? ----> Tag
  3. UiItem的不保留活动的支持

    1. 本身是可以 序列化的?
    2. 本身不可以序列化,则需要一个生命周期函数,先提前自己new出来?HardSetting

2.1.1 业务能力的抽象

2.1.1.1 界面渲染值 UIValue

public interface UiValue<V> extends Parcelable, Cloneable{

    /**
     * 获取当前UiValue对应的界面渲染值
     */
    String getStringValue();

    /**
     * 获取当前UiValue对应的VO实例对象
     *   将会被{@link UiItem#extractFromGetter()}  赋值为 Getter中的引用
     *   或者被更新操作赋值为一个新的引用
     *
     *   一般来说 UiValue的实现类中会持有一个 vo对象的引用:
     *   1. **在初始化Getter过程中,该引用指向 业务代码中的原对象;**
     *   2. **在更新后(譬如选择了一个新的供应商分类),该引用会指向一个新的对象**
     */
    V getValue();

    /**
     * 判断UiValue是否差异,用于在 {@link UiItem#isChanged()}
     */
    boolean isTheSame(UiValue other);

    /**
     * 用于UiValue的备份,用于在 {@link UiItem#backup()}
     * 表示为界面初始值,将最终支持  {@link UiItem#isChanged()},以判断是否发生更新
     */
    UiValue<V> copy();
}

我们来看一个供应商分类选择的实例:

public class JxcSelectSupplierclassUiValue extends BaseUiValue<SupplierClass> implements Parcelable {

	private SupplierClass supplierClass;

	public JxcSelectSupplierclassUiValue(SupplierClass sClass) {
        this.supplierClass = sClass;
    }
	
	@Override
    public String getStringValue() {
        if (supplierClass != null && !TextUtils.isEmpty(supplierClass.classname)) {
            return supplierClass.classname;
        }
        return "";
    }

    @Override
    public SupplierClass getValue() {
        return supplierClass;
    }

    @Override
    public boolean isTheSame(UiValue other) {
        if (other instanceof  JxcSelectSupplierclassUiValue) {
            SupplierClass otherSupplierClass = ((JxcSelectSupplierclassUiValue) other).supplierClass;
            return supplierClass.classId == otherSupplierClass.classId && supplierClass.classname == otherSupplierClass.classname;
        }
        return false;
    }
	
}

尤其需要注意一点,一般来说 UiValue的实现类中会持有一个 vo对象的引用:

  1. 在初始化Getter过程中,该引用指向 业务代码中的原对象;
  2. 在更新后(譬如选择了一个新的供应商分类),该引用会指向一个新的对象

2.1.1.2 VO转接器Getter|Setter

Getter|Setter是作用在类级别的,如果想针对某个VO操作,必须是传入某个Vo对象实例的内部成员变量,从而实现get|setter的响应都会针对当前Vo对象实例

public interface Getter<T> {
    T get();
}

public interface Setter<T> {
    void set(T data);
}

我们来看个例子:


 public static class MyData {
	public CustomerLineVo custm;
 
	private Setter<CustomerLineVo> custmSetter = new Setter<CustomerLineVo>() {
            @Override
            public void set(CustomerLineVo data) {
                custm = data;
            }
    };
	
	private Getter<CustomerLineVo> custmGetter = new Getter<CustomerLineVo>() {
            @Override
            public CustomerLineVo get() {
                return custm;
            }
    };
 }
 
 
 
  MyData myData = new MyData();
  
  SingleSelectCustmUiItem selectCustmUiItem = new SingleSelectCustmUiItem(this);
  
  selectCustmUiItem.setSetter(myData.custmSetter());
  selectCustmUiItem.setGetter(myData.custmGetter);

2.1.1.3 UIValue、Getter和Setter的串联

在BaseUiItem中,我们可以观察到这种串联关系

从某种层次上,Getter、Setter的操作是高于 UIValue的。

这是由于,如果说模型”用户–>VO–>UIValue—>界面渲染“中UIValue割据了用户对View的直接操作,Getter|Setter相当于 框架和Vo打交道,而UiValue则是框架如何根据VO进行界面渲染和反向返回

  • Getter
    • void setGetter(Getter getter);
    • void setGetConvertor(Convertor convertor);
    • void extractFromGetter();
  • Setter
    • void setGetter(Getter getter);
    • void setGetConvertor(Convertor convertor);
    • void extractFromGetter();
  • UiValue
    • UiValue getValue();
    • void setValue(UiValue value);

2.1.1.4 ”VO->UIValue–>界面渲染“的过程

我们先来看 ”VO->UIValue–>界面渲染“的过程,extractFromGetter()

    @Override
    public void extractFromGetter() {
        if(getter != null) {
            Object value = getter.get();
            if (getConvertor != null) {
                value = getConvertor.convert(value);
            }
            UiValue uiValue = getUiValueGenerator().generateUiValue(value);
            setValue(uiValue);
        }

    }
	
	@Override
    public void setValue(UiValue value){
        this.uiValue = value;
        VT vt = mutexHolder.get();
        if(vt != null){
            updateValue(vt, uiValue);
        }
    }

可以看出基本遵循以下路线:

  1. extractFromGetter()作为入口
  2. 调用Getter,获取到VO对象实例
  3. 如果有转换器,则将Vo对象实例转换为指定类型的VO对象实例。此步骤,主要用于衔接Vo和UiValue的实际类型不统一
  4. 调用UIValue构造器UiValueGenerator构建UiValue
  5. 调用setValue(UiValue value)
  6. 内部可能触发更新更新界面逻辑

2.1.1.5 UiValueGenerator界面渲染值转换器

BaseUiItem基本业务控件提供了以下和UiValueGenerator相关的函数,基本可以看出就是以下默认的get|set函数

    @Override
    public void setUiValueGenerator(UiValueGenerator uiValueGenerator) {
        this.uiValueGenerator = uiValueGenerator;
    }
	
	    /**
     * 提供一个 默认的 UiValue生成器
     * @return
     */
    protected UiValueGenerator provideDefaultUiValueGenerator(){
        return null;
    }
	
	protected UiValueGenerator getUiValueGenerator(){
        if(uiValueGenerator == null){
            uiValueGenerator = provideDefaultUiValueGenerator();
        }
        return uiValueGenerator;
    }

那么 UiValueGenerator 到底是什么呢?

它实际上承载着 ”VO->UIValue–>界面渲染“的过程中,”Vo–>UiValue”的过程“

不同的自定UiItem通过 重写provideDefaultUiValueGenerator()实现 当前Item控制自己的转换

2.1.1.6 ”VO<–UIValue<–界面渲染“的过程

UIValue被更新后,getValue()所指向的引用将不再是原来Getter的对象地址,而是一个新的对象

譬如选了一个新的客户被赋值到 UIValue的一个自定义的引用里,因此需要我们调用setter去赋值

    @Override
    public UiValue getValue() {
        return uiValue;
    }
	
    @Override
    public void fillInSetter() {
        if(setter != null) {
            UiValue uiValue = getValue();
            Object value = null;
            if(uiValue != null) {
                value = uiValue.getValue();
            }
            if (setConvertor != null) {
                value = setConvertor.convert(value);
            }
            setter.set(value);
        }
    }

可以看出基本遵循以下路线:

  1. fillInSetter()作为入口
  2. 调用getValue(),获取到 界面渲染值。
  3. 通过界面渲染值拿到当前指向的的 vo实例
  4. 如果有转换器,则将Vo对象实例转换为指定类型的VO对象实例。此步骤,主要用于衔接Vo和UiValue的实际类型不统一
  5. 调用Setter的set函数给目标对象赋值

2.2 一个示例

public class JxcSelectSupplierClassUiItem extends TextImageNormalFormUiItem implements UiItemResultGetter {

    public JxcSelectSupplierclassUiValue mUiValue;

    public JxcSelectSupplierClassUiItem(Context context) {
        super(context);
    }

    @Override
    public void onClick() {
        JxcSelectSupplierclassUiValue supplierClass = null;
        if (getValue() != null && getValue() instanceof JxcSelectSupplierclassUiValue){
            supplierClass = (JxcSelectSupplierclassUiValue)getValue();
        }
        SupplierIntentManager.intentToGetSupplierClass(getActivityStarter(), getRequestCode(), getRequestTag(0), supplierClass == null ? null : supplierClass.getValue());
    }

    @Override
    public void getResult(Intent data, int operationTag) {
        SupplierClass supplierClass = data.getParcelableExtra(SupplierIntentManager.KEY_JXC_SECECT_SUPPLIER_CLASS);
        if (supplierClass != null) {
            mUiValue = new JxcSelectSupplierclassUiValue(supplierClass.classId == -1 ? null : supplierClass);
            setValue(mUiValue);
        }
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        dest.writeParcelable(this.mUiValue, flags);
    }

    protected JxcSelectSupplierClassUiItem(Parcel in) {
        super(in);
        this.mUiValue = in.readParcelable(JxcSelectSupplierclassUiValue.class.getClassLoader());
    }

    public static final Creator<JxcSelectSupplierClassUiItem> CREATOR = new Creator<JxcSelectSupplierClassUiItem>() {
        @Override
        public JxcSelectSupplierClassUiItem createFromParcel(Parcel source) {
            return new JxcSelectSupplierClassUiItem(source);
        }

        @Override
        public JxcSelectSupplierClassUiItem[] newArray(int size) {
            return new JxcSelectSupplierClassUiItem[size];
        }
    };
}

三、UiInteraction

UiInteraction是暴露给用户的操作手柄

从某种意义上,UiInteraction类似于RecyleView,回想下我们使用RecyleView时,仅需要配置datas、bindView和bindData就可以实现界面渲染

  • 针对List items的操作
  • 针对bindView的操作 也就是构建我们常规意义上的xml中各个view的过程
  • 针对bindData的基础布局的vo渲染,也就是我们常规意义上的针对xml的一些View配置成我们vo的某些界面显示
  • notifyItemSetChanged
  • 针对Result响应的
  • 针对判断界面内容是否改变
  • 针对各个uiItem的vo合法性的检测
  • 配置项和辅助utils

Item的Ui视图控件生成通过调用 UiInteraction#commit或者 UiInteraction#notifySetChanged()触发,当然内部会对视图控件进行 属性和渲染值的初始化设定
Item的Ui属性由ITem自己控制,调用其UiItem#commitUiProps()的函数触发其私有函数updateUiProps(VT view)
Item的UI渲染值通过 UiInteraction#dumpOnItem()—>item.extractFromGetter—>item.setValue—>item.updateValue

3.1 接口

public interface UiInteraction extends ActivityResultReceiver, SavableForm, ContextProvider, TimeConsumingOperationStatistic {
    int MODE_INDEX_DEPENDING_ON_SRC = ListPendingOperationLogic.MODE_INDEX_DEPENDING_ON_SRC;
    int MODE_INDEX_DYNAMIC = ListPendingOperationLogic.MODE_INDEX_DYNAMIC;






    /***********************************针对List<UiItem> items***********************************/
    /**
     * 增
     */
    //设置一个set操作, 需要调用commit来提交
    UiInteraction setItems(List<UiItem> items);
    //设置一个insert操作, 需要调用commit来提交
    UiInteraction insertItem(int index, UiItem item);
    //设置一个append操作, 需要调用commit来提交
    UiInteraction appendItems(List<UiItem> items);

    /**
     * 删
     */
    //设置一个remove操作, 需要调用commit来提交
    UiInteraction removeItem(UiItem item);
    //设置一个remove操作, 需要调用commit来提交
    UiInteraction removeItem(int index);


    /**
     * 查:单个
     */
    //通过一个用户定义的id 来搜寻 一个item,该item处在pending状态, 即commit方法未被调用,  会遍历树状结构,直到找到一个符合的为止
    UiItem findItemInPendingStateByUserId(int userId);
    //通过一个用户定义的id 来搜寻 一个item,会遍历树状结构,直到找到一个符合的为止
    UiItem findItemByUserId(int userId);
    //返回未经扩展的index位置的item
    UiItem getItem(int index);
    /**
     * 查:全部
     */

    //返回未经扩展的item 列表
    List<UiItem> getItems();
    //返回经过扩展的item列表(UiItemGroup被扩展成了子item )
    List<UiItem> getExtendedItems();

    /**
     * 由于在树形结构中查找一个Uitem是要进行递归调用的,因此
     * 1. 用户可以指定先把一些UiItem加入到缓存池中,以加快查询过程
     * 2. 框架在查找UiItem时,也会将其主动添加到缓存池中
     */
    //缓存一个item
    void cacheItem(int userId, UiItem item);
    //从缓存里获取item
    UiItem getItemFromCache(int userId);
    //先从缓存里查找,没有则从item树里找,找到更新到缓存
    UiItem getAnItem(int userId);


    /*****************************针对bindView的操作 也就是构建我们常规意义上的xml中各个view的过程************************/

    /**
     * 提交一系列操作
     */
    void commit();
    /**
     * 退出操作
     */
    void abort();

    /**
     * 全量更新UI:会导致全部初始化,类似与 setItems(newDatas).commit();
     */
    void notifyItemSetChanged();

    /***************针对bindData的基础布局的vo渲染,也就是我们常规意义上的针对xml的一些View配置成我们vo的某些界面显示*************/

    /**
     * 用item中的值填充实体, 需要先调用 item.setSetter 方法来进行绑定
     * ps: 界面值 和 界面目标对象的属性 并不是实时对应关系,调用此方法,赋值到目标对象实例中的指定属性上
     */
    void fillEntityUp();
    /**
     * 用实体中的字段值去填充界面item,  需要先调用 item.setGetter 方法来进行绑定
     */
    void dumpOnItem();
    void dumpOnItem(int userId);

    /***********************************************针对Result响应的********************************************/
    void setRequestCode(int requestCode);
    void setActivityStarter(ActivityStarter activityStarter);
    void setOnUiItemResultListener(OnUiItemResultListener listener);

    /******************************************判断界面内容是否改变******************************************/
    /**
     * 备份界面内容
     */
    void backup();

    /**
     * 备份userId对应的item
     * @param userId
     */
    void backup(int userId);

    /**
     * 判断界面内容是否改变
     * @return
     */
    boolean isChanged();

    /******************************************针对各个uiItem的vo合法性的检测******************************************/
    @Deprecated
    CheckResult check();
    CheckResult2 check2();
    List<CheckResult2> checkAll();


    /*************************************配置项和辅助utils****************************************/
    /**
     * 回填数据时,是否仅返回变动了的item
     * @param onlyGetChangedValue
     */
    void setOnlyGetChangedValue(boolean onlyGetChangedValue);

    /**
     * toast
     * @param prompt
     */
    void setPrompt(Prompt prompt);

    // UiItem moveTo(int index);

    /**
     * 设置一个监听UiItem value值变化的监听器
     * @param watcher
     */
    void setUiItemValueWatcher(UiItemValueWatcher watcher);

    void setOnUiItemClickListener(UiItem.OnUiItemClickListener listener);

    /**
     * 设置一系列未执行(commit) 的操作的下标的意义 默认为 MODE_INDEX_DEPENDING_ON_SRC
     * @param mode
     */
    void setIndexMode(int mode);

    /*************************************针对不保留活动的**************************************/
    UiInteraction autoRestore();

    /**
     * 当界面被销毁,重新进入,items被恢复 时调用此方法, 需要调用commit来提交
     * @param items
     * @return
     */
    UiInteraction restoreItems(List<UiItem> items);

    boolean isThereAnyPendingOperation();
    void setTimeConsumingOperationWatcher(TimeConsumingOperationWatcher timeConsumingOperationWatcher);


编写注意

复用级别在UiItem,也就是说需要掌握UiItem及其相关类的创建方法

  • TestTestNewwayOfUi
  • TestNewwayOfUi
  • TestUiItemList
  1. 对外接口必须加入注释,譬如ViewMaker

猜你喜欢

转载自blog.csdn.net/fei20121106/article/details/86598070