Data-Mediator專題之屬性回撥

LightSun發表於2017-12-01

目前框架支援的屬性回撥有3種

  • 1,預設的屬性回撥 含義: 其實就是某個屬性變化了, 通知你的意思
  • 2, List屬性回撥 含義: 就是監聽List屬性的 增刪改。
  • 3, SparseArray屬性回撥。 含義: 就是監聽SparseArray屬性的 增刪改。

詳解

  • 屬性回撥

  • 實際場景:

    • 在工作中,有的時候我們想監聽某些資料/ui 的變化。以完成統計功能, 以前在專案中遇到過。
      比如新聞類的ui, 頂部有很多tab, 下面是資料流列表。 這時候有這樣一個需求:
      要求使用者點選了哪個item,屬於哪個tab. 什麼型別, 在列表中的索引, 這個時候我發現,搞統計還是挺麻煩的。
      即使最後我設計了一套通用的框架, 還是感覺不完美。
    • 所以後面我想到了資料中介者data-mediator這個框架. 後面會支援這些更加複雜的功能。
  • 屬性回撥,不一定非要繫結到view的屬性上(比如setText, setTextColor, setBackground等等)。 它也可以是事件點選的屬性, 後面也會支援。
    換句話說, 以後它可以做很多後臺的一些操作。

  • 迴歸正題, 如何使用屬性回撥?
    在demo中有這樣一個簡單的例子。

    public class TestPropertyChangeActivity extends BaseActivity {
   
       @BindView(R.id.tv_desc)
       TextView mTv_desc;
   
       @BindView(R.id.bt_set_text_on_TextView)
       Button mBt_changeProperty;
       @BindView(R.id.bt_set_text_on_mediator)
       Button mBt_temp;
   
       DataMediator<StudentModule> mMediator;
   
       @Override
       protected int getLayoutId() {
           return R.layout.ac_test_double_bind;
       }
   
       @Override
       protected void onInit(Context context, Bundle savedInstanceState) {
           mBt_changeProperty.setText("click this to change property");
           mBt_temp.setVisibility(View.GONE);
   
           //為資料模型建立  中介者。
           mMediator = DataMediatorFactory.createDataMediator(StudentModule.class);
           //新增屬性callback
           mMediator.addDataMediatorCallback(new DataMediatorCallback<StudentModule>() {
               @Override
               public void onPropertyValueChanged(StudentModule data, Property prop, Object oldValue, Object newValue) {
                   Logger.w("TestPropertyChangeActivity","onPropertyValueChanged","prop = "
                           + prop.getName() + " ,oldValue = " + oldValue + " ,newValue = " + newValue);
                  //改變文字         
                   mTv_desc.setText(String.valueOf(newValue));
               }
           });
           mMediator.getDataProxy().setName("heaven7");
       }
   
       @OnClick(R.id.bt_set_text_on_TextView)
       public void onClickSetTextOnTextView(View v){
           mMediator.getDataProxy().setName("time: " + System.currentTimeMillis());
       }
   }

複製程式碼

僅僅,屬性改變的時候改變一下文字。很簡單吧。

  • List屬性編輯器

  • 當一個屬性是List型別時,會自動 生成beginXXXEditor的方法返回該編輯器, 它可以方便的操作list資料。 資料模型和代理均有。其中XXX是屬性的名稱
  • 一般用於繫結列表控制元件,比如android RecyclerView. 下面是一個demo:
      public class TestRecyclerListBindActivity extends BaseActivity {
    
        private static final Random sRan = new Random();
        
        @BindView(R.id.rv)
        RecyclerView mRv;
        
        private Binder<RecyclerListBindModule> mBinder;
        private TestRecyclerListAdapter<StudentModule> mAdapter;
        
        @Override
        protected int getLayoutId() {
            return R.layout.ac_test_recycler_list_bind;
        }
        
            @Override
            protected void onInit(Context context, Bundle savedInstanceState) {
         //初始化adapter
             initAdapter();
         //建立binder
             mBinder = DataMediatorFactory.createBinder(RecyclerListBindModule.class);
         //繫結列表
             onBindListItems(mBinder);
            }
        
            //新增一個item
            @OnClick(R.id.bt_add)
            public void onClickAddItem(View v){
                mBinder.getDataProxy().beginStudentsEditor()
                        .add(0, createItem());
            }
        
            //新增一組items
            @OnClick(R.id.bt_add_all)
            public void onClickAddItems(View v){
                List<StudentModule> list = createItems();
                mBinder.getDataProxy().beginStudentsEditor()
                        .addAll(list);
            }
        
            //刪除一個 item
            @OnClick(R.id.bt_remove)
            public void onClickRemoveItem(View v){
                mBinder.getDataProxy().beginStudentsEditor()
                        .remove(0);
            }
        
            //替換所有items
            @OnClick(R.id.bt_replace)
            public void onClickReplaceItem(View v){
                mBinder.getDataProxy().setStudents(createItems());
            }
        
            protected void onBindListItems(Binder<RecyclerListBindModule> mBinder) {
                //通用的繫結方法. 這裡用於繫結列表
                mBinder.bindList(RecyclerListBindModule.PROP_students,
                        mAdapter);
            }
        
            protected void initAdapter() {
                mRv.setLayoutManager(new LinearLayoutManager(this));
                mRv.setAdapter(mAdapter = new TestRecyclerListAdapter<StudentModule>(
                        R.layout.item_test_recycler_list, null) {
                    @Override
                    protected void onBindData(Context context, int position,
                                              StudentModule item, int itemLayoutId, ViewHelper helper) {
                        helper.setText(R.id.tv_name, item.getName())
                                .setText(R.id.tv_age, ""+item.getAge());
                    }
                });
            }
        
            private static StudentModule createItem(){
                StudentModule data = DataMediatorFactory.createData(StudentModule.class);
                data.setAge(sRan.nextInt(10001));
                data.setName("google__" + sRan.nextInt(100));
                return data;
            }
            @NonNull
            private static List<StudentModule> createItems() {
                List<StudentModule> list = new ArrayList<>();
                //just mock data
                final int count = sRan.nextInt(10) + 1;
                for (int i =0 ; i< count ; i++){
                    list.add(createItem());
                }
                return list;
            }
    
        private static abstract class TestRecyclerListAdapter<T extends ISelectable>
             extends QuickRecycleViewAdapter<T> implements
            BaseListPropertyCallback.IItemManager<T> {
    
            public TestRecyclerListAdapter(int layoutId, List<T> mDatas) {
                super(layoutId, mDatas);
            }
    
             @Override
            public void addItems(List<T> items) {
               //增加
                getAdapterManager().addItems(items);
            }
    
            @Override
            public void addItems(int index, List<T> items) {
            //增加
                getAdapterManager().addItems(index, items);
            }
    
            @Override
            public void removeItems(List<T> items) {
            //刪除
                getAdapterManager().removeItems(items);
            }
    
            @Override
            public void replaceItems(List<T> items) {
            //替換所有
                getAdapterManager().replaceAllItems(items);
            }
    
            @Override
            public void onItemChanged(int index, T oldItem, T newItem) {
            //改變元素
                getAdapterManager().setItem(index, newItem);
            }
        }
    }
複製程式碼

複雜麼? 不復雜,第一步繫結了列表。然後改變資料的時候回撥到了BaseListPropertyCallback.IItemManager

SparseArray屬性編輯器

  • 當屬性型別是SparseArray時,會自動生成SparseArray屬性編輯器: beginXXXEdiator, XXX是屬性名稱.
  • 下面是一個示例程式:
public class TestSparseArrayActivity extends BaseActivity {

  private static final String TAG = "TestSparseArray";
  @BindView(R.id.tv_sa)
  TextView mTv_sa;

  private DataMediator<TestBindModule> mDm;
  private Set<Integer> mIndexes = new HashSet<>();

  @Override
  protected int getLayoutId() {
      return R.layout.ac_test_sparse_array;
  }

  @Override
  protected void onInit(Context context, Bundle savedInstanceState) {
      mDm = DataMediatorFactory.createDataMediator(TestBindModule.class);
      // 這裡直接用屬性回撥。也可以用binder.bind(String property, SparseArrayPropertyCallback<? super T> callback)方法
      mDm.addDataMediatorCallback(DataMediatorCallback.createForSparse(
              TestBindModule.PROP_cityData2.getName(), new CallbackImpl()));

  }

  // put 操作
  @OnClick(R.id.bt_put)
  public void onClickPut(View v){
      final StudentModule stu = createStu(-1);
      mDm.getDataProxy().beginCityData2Editor()
              .put((int)stu.getId(), stu)
              .end();
  }

  // 移除操作(通過key)
  @OnClick(R.id.bt_remove_key)
  public void onClickRemoveByKey(View v){
      if(!mIndexes.isEmpty()){
          final Integer index = mIndexes.iterator().next();
          mDm.getDataProxy().beginCityData2Editor()
                  .remove(index);
          mIndexes.remove(index);
      }else{
          mTv_sa.setText("");
          Logger.w(TAG , "onClickRemoveByKey", "already empty");
      }
  }

  // 移除操作(通過value)
  @OnClick(R.id.bt_remove_value)
  public void onClickRemoveByValue(View v){
      if(!mIndexes.isEmpty()){
          final Integer index = mIndexes.iterator().next();
          mDm.getDataProxy().beginCityData2Editor()
                  .removeByValue(createStu(index));
          mIndexes.remove(index);
      }else{
          mTv_sa.setText("");
          Logger.w(TAG , "onClickRemoveByValue", "already empty");
      }
  }

  //清空操作
  @OnClick(R.id.bt_clear)
  public void onClickClear(View v){
      if(!mIndexes.isEmpty()){
          mDm.getDataProxy().beginCityData2Editor().clear();
          mIndexes.clear();
      }else{
          mTv_sa.setText("");
          Logger.w(TAG , "onClickClear", "already empty");
      }
  }
  private StudentModule createStu(int index) {
      if(index < 0){
          index = new Random().nextInt(5);
      }
      mIndexes.add(index);
      return DataMediatorFactory.createData(StudentModule.class)
              .setId(index).setName("google_" + index).setAge(index);
  }

  private void setLogText(String method, String msg){
      mTv_sa.setText(method + ": \n  " + msg + "\n\n now is: \n"
              + mDm.getData().getCityData2().toString());
  }

  private class CallbackImpl implements SparseArrayPropertyCallback<TestBindModule>{

      @Override
      public void onEntryValueChanged(TestBindModule data, Property prop, Integer key,
                                      Object oldValue, Object newValue) {
          final String msg = "oldValue = " + oldValue + " ,newValue = " + newValue;
          Logger.i(TAG , "onEntryValueChanged", msg);
          setLogText("onEntryValueChanged", msg);
      }
      @Override //新增key-value
      public void onAddEntry(TestBindModule data, Property prop, Integer key, Object value) {
          final String msg = "key = " + key + " ,value = " + value;
          Logger.i(TAG , "onAddEntry", msg);
          setLogText("onAddEntry", msg);
      }
      @Override //移除key-value
      public void onRemoveEntry(TestBindModule data, Property prop, Integer key, Object value) {
          final String msg = "key = " + key + " ,value = " + value;
          Logger.i(TAG , "onRemoveEntry", msg);
          setLogText("onRemoveEntry", msg);
      }
      @Override //清空
      public void onClearEntries(TestBindModule data, Property prop, Object entries) {
          final String msg = entries.toString(); //here entries is SparseArray
          Logger.i(TAG , "onClearEntries", msg);
          setLogText("onClearEntries", msg);
      }
      @Override //一般的屬性改變
      public void onPropertyValueChanged(TestBindModule data, Property prop,
                                         Object oldValue, Object newValue) {
          final String msg = "oldValue = " + oldValue + " ,newValue = " + newValue;
          Logger.i(TAG , "onPropertyValueChanged", msg);
          setLogText("onPropertyValueChanged", msg);
      }
      @Override
      public void onPropertyApplied(TestBindModule data, Property prop, Object value) {
          final String msg = "value = " + value;
          Logger.i(TAG , "onPropertyApplied", msg);
          setLogText("onPropertyApplied", msg);
      }
  }
}

複製程式碼

想要體驗最新的特性 ?

請到github/data-mediator體驗。 如果覺得不錯,請star支援下專案哈。

歡迎大家star, fork,contribute ,提issue. 它會越來越棒。

Thanks for reading !

技術源於分享!

相關文章