XCoreRedux框架:Android UI組件化與Redux實踐

XCoreRedux框架:Android UI組件化與Redux實踐

@author: 莫川 https://github.com/nuptboyzhb/

XCoreRedux源碼+Demo:https://github.com/nuptboyzhb/XCoreRedux

使用android studio打開該項目。

目錄結構

  • demo

    基于xcore框架寫的一個小demo
  • xcore

    XCoreRedux核心代碼庫
  • pics

    文檔的pic資源

前言

  • Android開發當中的Code Architecture思考

    最近看了很多前端的框架,React、Flux、Redux等,React和Redux都是前端比較流行的框架。而android方面,Google官方貌似不太Care此事,業內也沒有公認的優秀Architecture。與前端類似,在Android開發中,同樣也面臨著復雜的數據state管理的問題。在理解Store、Reducer和Action的基礎上,最終,基于Redux+React的思想,提出了一個基于Android平臺Redux框架,我給它起名叫作:XCoreRedux。本倉庫就是XCoreRedux+UIComponent框架的實現。它表達的是一種思想,希望大家能夠提出更好的意見。

XCoreRedux框架介紹

與前端的Redux框架類似,XCoreRedux框架的圖示如下:

redux20160930

Action

Action 是把數據傳到 store 的有效載體。它是store的唯一數據來源。我們一般是通過 store.dispatch()將action傳到store中。Action一般需要兩個參數:type類型和data數據。在XCoreRedux框架下,我們定義Action如下:


public class XCoreAction {//Action的類型public final String type;//Action攜帶的value,可為空public final Object value;public XCoreAction(String type, Object value) {this.type = type;this.value = value;}public XCoreAction(String type) {this(type, null);}@Overridepublic boolean equals(Object object) {...}@Overridepublic int hashCode() {...}
}

為了統一的管理Action,你可以實現一個ActionCreator,比如,demo中創建了一個聯系人業務的Creator:


/*** @version mochuan.zhb on 16/9/28.* @Author Zheng Haibo* @Blog github.com/nuptboyzhb* @Company Alibaba Group* @Description 聯系人 ActionCreator*/
public class ContactsActionCreator {public static final String ADD_ITEM = "AddContacts";public static final String ADD_TITLE = "addCategory";public static final String DELETE_LAST = "deleteLast";public static final String CHECK_BOX = "contactsCheck";public static XCoreAction addContacts(Contacts contacts) {return new XCoreAction(ADD_ITEM, contacts);}public static XCoreAction addCategory(Title title) {return new XCoreAction(ADD_TITLE, title);}public static XCoreAction deleteLast() {return new XCoreAction(DELETE_LAST);}public static XCoreAction checkBoxClick(ContactsWrapper contactsWrapper) {return new XCoreAction(CHECK_BOX, contactsWrapper);}
}

Action的概念比較好理解,下面我們看一下Reducer

Reducer

reducer的字面意思就是“減速器”。Action描述了事件,Reducer是決定如何根據Action更新狀態(state),而這正是reducer要做的事情。Reducer的接口定義如下:


public interface IXCoreReducer<State> {State reduce(State state, XCoreAction xcoreAction);
}

就是根據輸入的Action和當前的state,處理得到新的state。


(previousState, action) => newState

說的更直白一點,Reducer就是一些列 純函數 的集合。如Demo中的項目所示:


public class ContactsReducer implements IXCoreReducer<List<XCoreRecyclerAdapter.IDataWrapper>> {/*** 添加聯系人** @param contactsWrappers* @param contacts* @return*/private List<XCoreRecyclerAdapter.IDataWrapper> addOneContacts(List<XCoreRecyclerAdapter.IDataWrapper> contactsWrappers, Contacts contacts) {......return wrappers;}/*** 添加標題** @param contactsWrappers* @param value* @return*/private List<XCoreRecyclerAdapter.IDataWrapper> addOneTitle(List<XCoreRecyclerAdapter.IDataWrapper> contactsWrappers, Title value) {......return wrappers;}/*** 刪除最后一個** @param contactsWrappers* @return*/private List<XCoreRecyclerAdapter.IDataWrapper> deleteLast(List<XCoreRecyclerAdapter.IDataWrapper> contactsWrappers) {List<XCoreRecyclerAdapter.IDataWrapper> wrappers = new ArrayList<>(contactsWrappers);if (wrappers.size() > 0) {wrappers.remove(wrappers.size() - 1);}return wrappers;}/*** 設置選擇狀態** @param contactsWrappers* @param value* @return*/private List<XCoreRecyclerAdapter.IDataWrapper> changeCheckBoxStatus(List<XCoreRecyclerAdapter.IDataWrapper> contactsWrappers, ContactsWrapper value) {value.isChecked = !value.isChecked;return contactsWrappers;}@Overridepublic List<XCoreRecyclerAdapter.IDataWrapper> reduce(List<XCoreRecyclerAdapter.IDataWrapper> contactsWrappers, XCoreAction xcoreAction) {switch (xcoreAction.type) {case ContactsActionCreator.ADD_ITEM:return addOneContacts(contactsWrappers, (Contacts) xcoreAction.value);case ContactsActionCreator.ADD_TITLE:return addOneTitle(contactsWrappers, (Title) xcoreAction.value);case ContactsActionCreator.DELETE_LAST:return deleteLast(contactsWrappers);case ContactsActionCreator.CHECK_BOX:return changeCheckBoxStatus(contactsWrappers, (ContactsWrapper) xcoreAction.value);...}return contactsWrappers;}
}

通過上面的Reducer實現,我們可以看出,Reducer就是一些列函數的集合,其中一個關鍵函數reduce,它按照action的不同type執行不同的方法處理。

Store

store字面意思是存儲。在Redux框架下,Store和DataBase,File沒有關系,它可不是持久化存儲的意思。Store是負責管理數據源的狀態,負責把Action和Reducer聯系到一起。Store的職責為:

  • 1.保存數據源的當前狀態state
  • 2.對外提供dispatch方法,更新state
  • 3.通過subscribe注冊監聽器,當state變化時,通知觀察者
  • 4.提供getState方法,獲取當前的state

Store的Java實現:


public class XCoreStore<State> {private final IXCoreReducer<State> mIXCoreReducer;//數據處理器-reducerprivate final List<IStateChangeListener<State>> listeners = new ArrayList<>();//觀察者private volatile State state;//Store存儲的數據private XCoreStore(IXCoreReducer<State> mIXCoreReducer, State state) {this.mIXCoreReducer = mIXCoreReducer;this.state = state;}/*** 內部dispatch** @param xCoreAction*/private void dispatchAction(final XCoreAction xCoreAction) throws Throwable {synchronized (this) {state = mIXCoreReducer.reduce(state, xCoreAction);}for (IStateChangeListener<State> listener : listeners) {listener.onStateChanged(state);}}/*** 創建Store** @param reducer* @param initialState* @param <S>* @return*/public static <S> XCoreStore<S> create(IXCoreReducer<S> reducer, S initialState) {return new XCoreStore<>(reducer, initialState);}public State getState() {return state;}public void dispatch(final XCoreAction action) {try {dispatchAction(action);} catch (Throwable e) {e.printStackTrace();}}/*** 注冊接口;添加觀察者,當state改變時,通知觀察者** @param listener*/public void subscribe(final IStateChangeListener<State> listener) {listeners.add(listener);}/*** 注銷** @param listener*/public void unSubscribe(final IStateChangeListener<State> listener) {listeners.remove(listener);}/*** 狀態改變的回調接口** @param <S> 狀態*/public interface IStateChangeListener<S> {void onStateChanged(S state);}}

在Android中,一個Redux頁面(Fragment或者Activity) 只有一個單一的 store。當需要拆分數據處理邏輯時,應該使用 reducer組合,而不是創建多個Store。

搭配UIComponent

與前端的Redux搭配React類似,XCoreRedux搭配UIComponent。

UI組件化(UIComponent)

在前段的React框架下,我們常常聽說組件的概念:‘UI組件’。那么什么是UI組件呢?以下圖為例:

xcoreredux_demo
紅色的區域為“普通組件”,綠色的區域為兩種不同類型的“Item組件”。因此,在UIComponent里,組件分兩種:普通組件和item組件(或稱為cell組件)。

普通組件

  • 單組件,比如一個自定義的Widget,就是一樣View。比如自定義的CircleImageView等。
  • 容器組件,由ViewGroup派生出的組件。有FrameLayout、LinearLayout、RelativeLayout等。還有些常見的列表組件,比如ListView或者RecyclerView的組件等。

普通組件在XCore中是以FrameLayout的形式封裝的,編寫一個普通組件只需要實現如下方法:

  • 1.public int getLayoutResId()

    返回組件的布局資源Id
  • 2.public void onViewCreated(View view)

    View初始化
  • 3.實現XCoreStore中的IStateChangeListener接口,在onStateChanged中做數據綁定

    為了使UI組件能夠與Store進行關聯,UI組件可以實現IStateChangeListener接口,然后作為觀察者,觀察Store的state變化。然后在onStateChanged方法中做數據綁定。
    

Item組件(Cell組件)

對于前端來說,item組件和普通組件并沒有什么不同。但是對于Android或者iOS而言,item組件和普通組件是有本質區別的。以ReyclerView為例,Item組件在同一種類型下是會復用的。在XCoreRedux框架中,定義Item組件,需要繼承自XCoreItemUIComponent,它本身并不是一個View。它只需要實現的方法有:

  • View onCreateView(LayoutInflater inflater,ViewGroup container);
    與Fragment的onCreateView類似,它負責創建item的布局View
  • void onViewCreated(View view);
    與Fragment的onViewCreated類似,在此寫View的初始化
  • public String getViewType();
    Item組件對于數據源的類型
  • public void bindView(IXCoreComponent coreComponent,
    XCoreRecyclerAdapter coreRecyclerAdapter,
    XCoreRecyclerAdapter.IDataWrapper data,
    int pos);
    數據綁定,當Adapter調用bindViewHolder時,會回調bindView方法。

Item組件需要通過Adapter,與對應的列表組件聯系起來。針對Android常用的RecyclerView,XCoreRedux提供了插件式的通用XCoreRecyclerAdapter。

含列表組件下的XCoreRedux框架圖

XCoreRedux20161002

與之前的不同之處在于,這里把整個列表封裝成一個列表組件,對外提供注冊Item,比如XCoreRecyclerViewComponent組件源碼。


public class XCoreRecyclerViewComponent extends XCoreUIBaseComponent implements XCoreStore.IStateChangeListener<List<XCoreRecyclerAdapter.IDataWrapper>> {private SwipeRefreshLayout mSwipeRefreshLayout;private RecyclerView mRecyclerView;private RecyclerView.LayoutManager mLayoutManager;private XCoreRecyclerAdapter mXCoreRecyclerAdapter;public XCoreRecyclerViewComponent(Context context) {super(context);}public XCoreRecyclerViewComponent(Context context, AttributeSet attrs) {super(context, attrs);}public XCoreRecyclerViewComponent(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overridepublic final int getLayoutResId() {return R.layout.xcore_recyclerview_component;}@Overridepublic void onViewCreated(View view) {//初始化ViewmSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.xcore_refresh_layout);mSwipeRefreshLayout.setEnabled(false);mRecyclerView = (RecyclerView) findViewById(R.id.xcore_rv);//初始化RecyclerViewmLayoutManager = new LinearLayoutManager(getContext());mRecyclerView.setLayoutManager(mLayoutManager);mXCoreRecyclerAdapter = new XCoreRecyclerAdapter(this);mRecyclerView.setAdapter(mXCoreRecyclerAdapter);}public SwipeRefreshLayout getSwipeRefreshLayout() {return mSwipeRefreshLayout;}public RecyclerView getRecyclerView() {return mRecyclerView;}public RecyclerView.LayoutManager getLayoutManager() {return mLayoutManager;}public XCoreRecyclerAdapter getXCoreRecyclerAdapter() {return mXCoreRecyclerAdapter;}/*** 當狀態發生變化時,自動通知** @param status*/@Overridepublic void onStateChanged(List<XCoreRecyclerAdapter.IDataWrapper> status) {mXCoreRecyclerAdapter.setDataSet(status);mXCoreRecyclerAdapter.notifyDataSetChanged();}/*** 對外提供item組件的注冊** @param xCoreItemUIComponent* @return*/public XCoreRecyclerViewComponent registerItemComponent(XCoreItemUIComponent xCoreItemUIComponent) {mXCoreRecyclerAdapter.registerItemUIComponent(xCoreItemUIComponent);return this;}public void setRefreshEnable(boolean enable) {mSwipeRefreshLayout.setEnabled(enable);}
}

我們在使用該組件時,只需要:

1.在XML中添加組件


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><!-- 頭部組件--><com.example.haibozheng.myapplication.components.container.HeaderComponentandroid:id="@+id/recycler_view_header_component"android:layout_width="match_parent"android:layout_height="wrap_content" /><!-- 列表組件--><com.github.nuptboyzhb.xcore.components.impl.XCoreRecyclerViewComponentandroid:id="@+id/recycler_view_component"android:layout_width="match_parent"android:layout_height="match_parent" />
</LinearLayout>

2.初始化

        ...//創建數據源的storemContactsListXCoreStore = XCoreStore.create(new ContactsReducer(), new ArrayList<XCoreRecyclerAdapter.IDataWrapper>());//創建RecyclerView的UI組件mXCoreRecyclerViewComponent = (XCoreRecyclerViewComponent) findViewById(R.id.recycler_view_component);//注冊item組件模板mXCoreRecyclerViewComponent.registerItemComponent(new TextItemComponent()).registerItemComponent(new ImageItemComponent());//創建頭部組件mHeaderComponent = (HeaderComponent) findViewById(R.id.recycler_view_header_component);//添加觀察者mContactsListXCoreStore.subscribe(mXCoreRecyclerViewComponent);mContactsListXCoreStore.subscribe(mHeaderComponent);...

組件之間通信

Item組件與列表組件及普通組件之間的通信。在本Demo中使用的EventBus是輕量級的otto。每一個繼承自XCoreUIBaseComponent的組件,都已經在onCreate和onDestroy中分別進行了注冊和反注冊。使用時,只需要使用@Subscribe 注解來指定訂閱方法。因此,在任意地方都可以調用:

XCoreBus.getInstance().post(action);

小優化

對于數據綁定方面,做了兩個優化:
1.把數據通過Wrapper包裝
2.使用UIBinderHelper做流式綁定,比如:


public class ImageItemComponent extends XCoreItemUIComponent implements View.OnClickListener {private UIBinderHelperImpl mUIBinderHelperImpl;...@Overridepublic void bindView(IXCoreComponent coreComponent,XCoreRecyclerAdapter coreRecyclerAdapter,XCoreRecyclerAdapter.IDataWrapper data,int pos) {mContactsWrapper = (ContactsWrapper) data;mUIBinderHelperImpl.from(R.id.item_content_tv).setText(mContactsWrapper.bindContentText()).from(R.id.item_pic_iv).setImageUrl(mContactsWrapper.getAvatarUrl()).from(R.id.item_title_tv).setText(mContactsWrapper.bindItemTitle()).from(R.id.checkbox).setButtonDrawable(mContactsWrapper.isChecked ? R.mipmap.checkbox_checked : R.mipmap.checkbox_normal).setOnClickListener(this);}...
}

后續

  • 1.異步
  • 2.Middleware中間件
  • 3.與Rx結合

參考文獻

    1. Redux中文文檔
    1. Flux and Android
    1. AndroidFlux一覽

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/397557.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/397557.shtml
英文地址,請注明出處:http://en.pswp.cn/news/397557.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Gigaset ME/pure/pro體驗:就是這個德味

Gigaset是何方神圣&#xff1f;可能大多數人都沒有聽過。但如果說起西門子&#xff0c;那各位肯定就會“哦”地一聲明白了。實際上&#xff0c;Gigaset就是西門子旗下的手機品牌&#xff0c;當年世界上第一部數字無繩電話就是該品牌的產物&#xff0c;所以這次Gigaset在智能手機…

java獨步尋花,小班語言《江畔獨步尋花》

小班語言《江畔獨步尋花》活動目標&#xff1a;1、學習古詩&#xff0c;感知和理解古詩描繪的景象。2、感受古詩的文學語言。活動準備&#xff1a;1、古詩《江畔獨步尋花》PPT課件。2、柳條兩枝(一條葉子多的&#xff0c;一條葉子少的)活動過程&#xff1a;一、導入&#xff1a…

linux-shell——02

Linux命令的通用命令格式 :命令字 【選項】 【參數】 選項&#xff1a; 作用&#xff1a;用于調節命令的具體功能"-"引導短格式選項&#xff08;單個字符&#xff09; EX&#xff1a;“-l”"--"引導長格式選項&#xff08;多個字符&#xff09; EX: "…

IOS 資料備份

2019獨角獸企業重金招聘Python工程師標準>>> 利用本地服務器邊下載視頻邊播放 目前還沒有做好&#xff0c;下面是參考資料&#xff0c;做個備份&#xff1b; 參考資料&#xff1a; http://blog.csdn.net/wxw55/article/details/17557295 http://www.code4app.com/io…

BZOJ 1854: [Scoi2010]游戲( 二分圖最大匹配 )

匈牙利算法..從1~10000依次找增廣路, 找不到就停止, 輸出答案. ----------------------------------------------------------------------------#include<bits/stdc.h>using namespace std;const int MAXL 10009, MAXR 1000009;struct edge {int to;edge* next;} E[MA…

linux adduser mysql,linux獨享初始配置方法(ftp、apache、mysql)

在此我們對您購買的linux獨享服務器的配置方法進行簡單說明&#xff0c;內容涉及ftp、apache、mysql相關配置&#xff0c;希望給您使用中帶來方便。該文章為指導性說明。☆獨立服務器linux系統ftp帳戶的設置方法&#xff1a;1、首先服務器端已經安裝vsftp。2、您可以直接登陸服…

Android下文件的壓縮和解壓(Zip格式)

Zip文件結構 ZIP文件結構如下圖所示&#xff0c; File Entry表示一個文件實體,一個壓縮文件中有多個文件實體。 文件實體由一個頭部和文件數據組&#xff0c;Central Directory由多個File header組成&#xff0c;每個File header都保存一個文件實體的偏移&#xff0c;文件最后由…

快速理解和使用 ES7 await/async

await/async 是 ES7 最重要特性之一&#xff0c;它是目前為止 JS 最佳的異步解決方案了。雖然沒有在 ES2016 中錄入&#xff0c;但很快就到來&#xff0c;目前已經在 ES-Next Stage 4 階段。 直接上例子&#xff0c;比如我們需要按順序獲取&#xff1a;產品數據>用戶數據>…

jdeveloper優化:

D:\jdevstudio10133\jdev\bin\jdev.conf末尾加上下面的AddVMOption -Dsun.java2d.noddrawtrueAddVMOption -Dsun.java2d.ddoffscreenfalse 轉載于:https://www.cnblogs.com/sprinng/p/4780112.html

linux make java版本,告訴make是否在Windows或Linux上運行

更新請閱讀這個類似但更好的答案&#xff1a;https&#xff1a;//stackoverflow.com/a/14777895/938111make (和 gcc )可以使用Cygwin或MinGW在MS-Windows上輕松安裝 .正如ldigas所說&#xff0c; make 可以使用 UNAME:$(shell uname) 檢測平臺(命令 uname 也由Cygwin或MinGW安…

MPI多機器實現并行計算

最近使用一個系統的分布式版本搭建測試環境&#xff0c;該系統是基于MPI實現的并行計算&#xff0c;MPI是傳統基于msg的系統&#xff0c;這個框架非常靈活&#xff0c;對程序的結構沒有太多約束&#xff0c;高效實用簡單&#xff0c;下面是MPI在多臺機器上實現并行計算的過程。…

Jenkins_獲取源碼編譯并啟動服務(二)

一、創建Maven項目二、設置SVN信息三、設置構建觸發器四、設置Maven命令五、設置構建后發郵件信息&#xff08;參考文章一&#xff09;六、設置構建后拷貝文件到遠程機器并執行命令來自為知筆記(Wiz)

php 判斷頁面加載完,所有ajax執行完且頁面加載完判斷

jquery ajax&load 方法導致 js效果不顯示或顯示后由于加載后ajax 重新布局頁面導致效果錯誤。解決思路&#xff1a;需要在ajax get post 或 load 等執行完后再去執行方法就不會由于他們沒執行完導致的最終錯誤。那么首先看load 方法定義&#xff1a;jQuery ajax - load() 方…

正確理解ThreadLocal

想必很多朋友對 ThreadLocal并不陌生&#xff0c;今天我們就來一起探討下ThreadLocal的使用方法和實現原理。首先&#xff0c;本文先談一下對ThreadLocal的理 解&#xff0c;然后根據ThreadLocal類的源碼分析了其實現原理和使用需要注意的地方&#xff0c;最后給出了兩個應用場…

2018.7.10 個人博客文章=利用ORM創建分類和ORM的內置函數

昨天的注冊收尾工作 其實就差了和MySql聯系起來的部分&#xff0c;這部分很簡單&#xff0c;首先要做的就是保存用戶通過from傳送過來的頭像文件&#xff1a; """ 保存頭像文件 """ file request.FILES.get(avatar) file_path os.path.join(st…

python 列表與元組的操作簡介

上一篇&#xff1a;Python 序列通用操作介紹 列表 列表是可變的(mutable)——可以改變列表的內容&#xff0c;這不同于字符串和元組&#xff0c;字符串和元組都是不可變的。接下來討論一下列表所提供的方法。 list函數 可以使用list函數來創建列表&#xff1a; list(Hello) [H,…

mfc嵌入matlab繪圖窗口,將matlab的圖嵌入MFC

【實例簡介】VS調用matlab畫圖模塊編譯成的動態鏈接庫&#xff0c;并在MFC顯示。【實例截圖】【核心代碼】3b0582a3-4ea8-4a61-ba33-e448be563b88└── 將matlab的圖嵌入MFC├── matlab_2010b與VS2008_混合編程的實現.pdf├── TestWithData│ ├── Debug│ │ ├─…

python multiprocessing 和tcp

#用類方法 服務端from socket import *from multiprocessing import Processimport osclass Myprocess(Process): def __init__(self, conn): self.conn conn super().__init__() def run(self): conn self.conn start True whil…

matlab 畫三維花瓶,精美花瓶建模教程

1、首先&#xff0c;草圖單位為mm&#xff0c;進入前視圖繪制如圖草圖&#xff0c;花瓶的基本形狀輪廓2、然后對草圖進行旋轉3、旋轉出曲面后&#xff0c;在頂部邊線新建一個基準面4、繼續在前視圖繪制草圖&#xff0c;如圖繪制一弧線5、然后進行旋轉6、可以得到圖示的兩個曲面…

PKI系統相關知識點介紹

公鑰基礎設施&#xff08;Public Key Infrastructure&#xff0c;簡稱PKI&#xff09;是目前網絡安全建設的基礎與核心&#xff0c;是電子商務安全實施的基本保障&#xff0c;因此&#xff0c;對PKI技術的研究和開發成為目前信息安全領域的熱點。本文對PKI技術進行了全面的分析…