極簡(jiǎn)MVP設(shè)計(jì)模式 搭配UI框架實(shí)現(xiàn)數(shù)據(jù)顯示

極簡(jiǎn)的MVP設(shè)計(jì)模式配合UI框架

MVP可以實(shí)現(xiàn)視圖和數(shù)據(jù)的解耦

image

如圖所示 MVP使M與V分離 實(shí)現(xiàn)了解耦

定義BaseView 所有視圖的通用接口(activity fragment)

public interface BaseView {

    /**
     * 初始化數(shù)據(jù)
     */
    void loadData();

    /**
     * 更新View的狀態(tài)
     * @param state
     */
    void updateState(int state);
}
updateState整個(gè)方法是由我們來(lái)根據(jù)網(wǎng)絡(luò)返回的數(shù)據(jù)或者來(lái)判斷整個(gè)View的狀態(tài),定義了三個(gè)狀態(tài)值,由此來(lái)展示loading頁(yè)面 或者 error的頁(yè)面
    //加載狀態(tài)
    public static final int STATUS_LOADING = 100;
    //成功狀態(tài)
    public static final int STATUS_SUCCESS = 101;
    //失敗狀態(tài)
    public static final int STATUS_ERROR = 102;

接著去定義通用的接口BaseViewImpl

public interface BaseViewImpl<D> extends BaseView{
    
    void attachPre();

    void showData(List<D> list);
}
attachPre 由View來(lái)關(guān)聯(lián)相應(yīng)的Presenter 可以在定義的BaseActivity里調(diào)用綁定Presenter

BasePresenter

public interface BasePresenter<V> {
    /**
     * 綁定View
     * @param view
     */
    void attachView(V view);

    /**
     * 解綁View
     */
    void detachView();
}

BasePresenter的實(shí)現(xiàn)類 基類

public abstract class BasePresenterImpl<V extends BaseView> implements BasePresenter<V>{

    public Context mContext;
    
    public V mBaseView;

    private CompositeSubscription mCompositeSubscription;

    public BasePresenterImpl(Context context){
        mContext = context;
    }

    @Override
    public void attachView(V view) {
        mBaseView = view;
    }

    @Override
    public void detachView() {
        this.mBaseView = null;
        onUnsubscribe(); //解綁
    }

    /**
     * 加載數(shù)據(jù)
     */
    public abstract void loadData();

    //RXjava取消注冊(cè)谐宙,以避免內(nèi)存泄露
    public void onUnsubscribe() {
        if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
            mCompositeSubscription.unsubscribe();
        }
    }

    public void addSubscription(Observable observable, Subscriber subscriber) {
        if (mCompositeSubscription == null) {
            mCompositeSubscription = new CompositeSubscription();
        }
        mCompositeSubscription.add(subscriber);
    }
}
在BasePresenterImpl里綁定View ,由此子類可以根據(jù)View來(lái)更新?tīng)顟B(tài)

MVP就是基類的實(shí)現(xiàn)就是這么簡(jiǎn)單,現(xiàn)在來(lái)創(chuàng)建activity來(lái)試試手, 首先創(chuàng)建一個(gè)BaseActivity,抽取一些公共的代碼

public abstract class BaseActivity<T extends BasePresenter,D> extends AppCompatActivity implements BaseViewImpl<D>{
    
    public T mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /**
         * 綁定Presenter
         */
        attachPre();
        View view = initView(LayoutInflater.from(this));
        setContentView(view);
        ButterKnife.bind(this);
        findView(view);
        loadData();
    }

    protected abstract View initView(LayoutInflater from);

    public abstract void findView(View view);

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mPresenter != null)
            mPresenter.detachView();
        ButterKnife.unbind(this);
    }

    /**
     * error的統(tǒng)一處理
     * @param error
     */
    public void error(Throwable error){
        if(error instanceof NetworkErrorException){
            Toast.makeText(this,"網(wǎng)絡(luò)錯(cuò)誤", Toast.LENGTH_SHORT).show();
        }
    }

}

創(chuàng)建HomeActivity去訪問(wèn)網(wǎng)絡(luò)以及加載數(shù)據(jù)

public class HomeActivity extends BaseActivity<HomePresenter, NewsList.NewsEntity> {

    @Bind(R.id.recy)
    RecyclerView mRecy;
    private NewsListAdapter mNewsListAdapter;
    private LoadStuatus mLoadStuatus;

    @Override
    public View initView(LayoutInflater inflater) {
        /**
         * UI框架
         */
        mLoadStuatus = new LoadStuatus(this);
        /**
         * 實(shí)現(xiàn)成功頁(yè)面的View
         */
        mLoadStuatus.addSuccessView(inflater.inflate(R.layout.activity_home, null));
        /**
         * 綁定BaseView來(lái)實(shí)現(xiàn)頁(yè)面點(diǎn)擊加載
         */
        mLoadStuatus.bindView(this);
        return mLoadStuatus;
    }

    @Override
    public void findView(View view) {
        mRecy.setLayoutManager(new LinearLayoutManager(this));
        mNewsListAdapter = new NewsListAdapter(this);
        mRecy.setAdapter(mNewsListAdapter);
    }

    @Override
    public void attachPre() {
        mPresenter = new HomePresenter(this);
        mPresenter.attachView(this);
    }

    @Override
    public void showData(List<NewsList.NewsEntity> list) {
        mNewsListAdapter.flush(list);
    }

    @Override
    public void loadData() {
        mPresenter.loadData();
    }

    /**
     * 更新頁(yè)面
     */
    @Override
    public void updateState(int state) {
        mLoadStuatus.updateView(state);
    }
}

這里的loadStuatus是一個(gè)幀布局用來(lái)存放我們的三個(gè)頁(yè)面

errorPage,LoadPage,SuccessPage都是根據(jù)P層的回調(diào)來(lái)實(shí)現(xiàn)

在來(lái)看一下P層的實(shí)現(xiàn)

public class HomePresenter extends BasePresenterImpl<HomeActivity>{

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

    @Override
    public void loadData() {
        Subscription subscribe = RetrofitUtils.getinstance(mContext).buildNews().getNews()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(bean -> mBaseView.showData(bean.getNews()), //onNext() 去更新頁(yè)面
                        error -> {
                            mBaseView.error(error); // 請(qǐng)求網(wǎng)絡(luò)失敗 在BaseActivity定義了公共的處理方法
                            mBaseView.updateState(LoadStuatus.STATUS_ERROR); //更新頁(yè)面 顯示失敗的頁(yè)面
                        },
                        () -> mBaseView.updateState(LoadStuatus.STATUS_SUCCESS));// onComplete 完成頁(yè)面更新,顯示成功的頁(yè)面
        addSubscription(subscribe);
    }
}
P層很簡(jiǎn)單 就是訪問(wèn)網(wǎng)絡(luò) 當(dāng)請(qǐng)求

接下來(lái)看一下我們的UI框架 定義了統(tǒng)一處理頁(yè)面狀態(tài)的狀態(tài)碼以及如何去正確的顯示頁(yè)面

public class LoadStuatus extends FrameLayout {
    private static final String TAG = "TAG";
    BaseView nView;
    @Bind(R.id.error)
    View mErrorView;
    @Bind(R.id.loading)
    View mLoadingView;

    View successView;
    //加載狀態(tài)
    public static final int STATUS_LOADING = 100;
    //成功狀態(tài)
    public static final int STATUS_SUCCESS = 101;
    //失敗狀態(tài)
    public static final int STATUS_ERROR = 102;

    public LoadStuatus(Context context) {
        this(context, null);
    }

    public LoadStuatus(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoadStuatus(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public void addSuccessView(View view){
        successView = view;
        addView(successView);
        successView.setVisibility(GONE);
    }
    public void bindView(BaseView view) {
        this.nView = view;
        mErrorView.setOnClickListener(v -> {
            if(nView == null)
                throw new NoBindViewException("沒(méi)有綁定View");
            nView.loadData();
        });
    }

    public void init() {
        View.inflate(getContext(), R.layout.status, this);
        ButterKnife.bind(this);
    }

    public void updateView(int currentStatus) {
        switch (currentStatus) {
            case STATUS_ERROR:
                successView.setVisibility(GONE);
                mErrorView.setVisibility(VISIBLE);
                mLoadingView.setVisibility(GONE);
                break;
            case STATUS_LOADING:
                successView.setVisibility(GONE);
                mErrorView.setVisibility(GONE);
                mLoadingView.setVisibility(VISIBLE);
                break;
            case STATUS_SUCCESS:
                successView.setVisibility(VISIBLE);
                mErrorView.setVisibility(GONE);
                mLoadingView.setVisibility(GONE);
                break;
        }
    }

    public class NoBindViewException extends RuntimeException{
        public NoBindViewException(String info){
            super(info);
        }
    }
}

github地址: 點(diǎn)此進(jìn)入

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市界弧,隨后出現(xiàn)的幾起案子凡蜻,更是在濱河造成了極大的恐慌,老刑警劉巖垢箕,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件划栓,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡条获,警方通過(guò)查閱死者的電腦和手機(jī)忠荞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)帅掘,“玉大人委煤,你說(shuō)我怎么就攤上這事⌒薜担” “怎么了碧绞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)萍悴。 經(jīng)常有香客問(wèn)我头遭,道長(zhǎng),這世上最難降的妖魔是什么癣诱? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮袜香,結(jié)果婚禮上撕予,老公的妹妹穿的比我還像新娘。我一直安慰自己蜈首,他們只是感情好实抡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布欠母。 她就那樣靜靜地躺著,像睡著了一般吆寨。 火紅的嫁衣襯著肌膚如雪赏淌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天啄清,我揣著相機(jī)與錄音六水,去河邊找鬼。 笑死辣卒,一個(gè)胖子當(dāng)著我的面吹牛掷贾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荣茫,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼想帅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了啡莉?” 一聲冷哼從身側(cè)響起港准,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咧欣,沒(méi)想到半個(gè)月后叉趣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡该押,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年夺姑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窿撬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖店枣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情掺冠,我是刑警寧澤来颤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站囤躁,受9級(jí)特大地震影響冀痕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狸演,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一言蛇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宵距,春花似錦腊尚、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)劝篷。三九已至,卻和暖如春民宿,著一層夾襖步出監(jiān)牢的瞬間娇妓,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工活鹰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哈恰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓华望,卻偏偏與公主長(zhǎng)得像蕊蝗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赖舟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評(píng)論 25 707
  • 作者:李旺成 時(shí)間:2016年4月3日 “Android MVP 詳解(下)”已經(jīng)發(fā)布蓬戚,歡迎大家提建議。 MVP ...
    diygreen閱讀 128,870評(píng)論 86 1,321
  • 項(xiàng)目啟發(fā)來(lái)自谷歌的同類框架項(xiàng)目 android-architecture利用一個(gè)相同的項(xiàng)目宾抓,使用不同的框架實(shí)現(xiàn)之...
    boredream閱讀 4,210評(píng)論 9 67
  • 最近看了網(wǎng)上特別流行的年齡分布圖子漩,才知道,原來(lái)80后已經(jīng)步入奔三的行列很久了石洗,真的沒(méi)想到幢泼,一轉(zhuǎn)眼,還是孩子的80后...
    疾行者閱讀 423評(píng)論 0 1
  • 寫(xiě)在前面: 為了開(kāi)發(fā)屬于自己的 APP,也算是總結(jié)兩年了工作內(nèi)容, 沒(méi)有后臺(tái)支撐真是很麻煩,數(shù)據(jù)沒(méi)有,更不用談測(cè)...
    IMSong閱讀 1,509評(píng)論 0 1