從 MVP學習代碼封裝 (4) - 給 MVP 添加 stateView狀態(tài)頁功能

本系列文章集合:從 MVP學習代碼封裝 (1) - 綜述

大家在做項目的時候絕對要面對一個需求,stateView - 缺省頁,也有叫狀態(tài)頁豌汇,我比較喜歡叫狀態(tài)頁借卧,這里我們今天要把 stateView 這個功能集成到我們的 MVP 基礎框架中。為啥,你傻啊瘾境,這 stateView 可是基礎功能歧杏,明顯要整合到基礎業(yè)務框架中的呀,難道你要傻傻的和筆者一樣迷守,要每個頁面都寫一遍嗎H蕖!兑凿!呵呵凯力,筆者在工作的第一年還真就干了這 SB 事,現(xiàn)在回頭想想也是感謝過去的自己冒傻氣礼华。當時產品每天對這個 statview 改來改去的咐鹤,真是要死的心都有啊,那滿滿的怨念現(xiàn)在都還記憶猶新啊圣絮。也是有了澤洋深刻的經歷祈惶,我對代碼封裝這塊才開始上心,真是不經歷不知道啊扮匠,這也才有了我的 MVP 這個系列的文章捧请。

我的 stateView

stateView 的實現(xiàn)方案很多,和 MVP 一樣棒搜,一個公司有自己的一套疹蛉,開源的庫也是有不少的,貌似這個 stateView 還真是個經久不衰的話題力麸,不時的總是可以在各種分享場合看到關于 stateView 的文章和庫氧吐,畢竟太典型了,而且實現(xiàn)上還真是很有味道啊末盔,先寫好不容易啊筑舅。

先來看看我寫的例子,我寫的效果很簡單陨舱,主要目的還是向大家表述 我寫 stateView 過程翠拣,而不是絢麗的效果:


ezgif.com-video-to-gif.gif

我寫的例子在顯示上還是很靈活的:

  • 在基類中設定 stateview 每個頁面的 layoutID
  • 然后你可以在具體的某個 activity/fragment/viewGroup 上設定某個類型狀態(tài)頁對應的 layoutID
  • 默認是在 activity/fragment/viewGroup 的根布局上顯示 stateview ,也可以設定 stateview 顯示在哪個布局上游盲。

上面說了下我簡單封裝的這個功能误墓,目前我看到的做 stateview 這個功能的實現(xiàn)思路有3種:

  • 最簡單的, 在每個 activity/fragment/viewGroup 中的 xml 布局中 include 相關的狀態(tài)頁的 xml 進來益缎,這種方法最簡單谜慌,減少了我們很多 xml 布局中的代碼,但是 java 類中的代碼是沒有經過封裝的莺奔,還是有大量重復代碼
  • 在 activity/fragment 所屬的 window 上 addView 欣范,這樣的思路就是開始從根本上封裝代碼了,這也應該是我們在寫代碼時首先應該想到怎么去封裝這個功能。這個在 window 上 addView 的方案恼琼,還是很有一些開源庫方案在用的妨蛹,但是我覺得很實際的, 就是這個思路不甚靈活晴竞,因為 stateview 的頁面只能顯示在 整個屏幕上蛙卤,那么自定義的 viewGroup 怎么辦,我們要是想 stateview 顯示在屏幕中的某一部分怎么辦呢噩死。我覺得靈活性和可維護性颤难,簡單易用性是代碼封裝的第一要務。
  • 承接上個思路已维,還是 addView 行嗤,這次不是寫死在 window 上了, 而是由用戶決定衣摩,我們在 base 基類中可以默認給一種方案昂验,我想這也是大部分的需求了,然后在具體的 activity/fragment/viewGroup中艾扮,可以根據(jù)具體的業(yè)務需求指定不同的布局以容納 stateview

當然上面我都簡單介紹過我的實現(xiàn)了既琴,會跟明顯的我就是使用的第三種。這種方式我是沒看有第三方庫用泡嘴,可以全當是我自己想出來的甫恩,這里說下,筆者也是見識有限酌予,要是各位有不同意見請見諒磺箕,畢竟在沒見過相同實現(xiàn)的情況下,我自己寫出了就可以當做我自己的實現(xiàn)了不是嘛抛虫。


實現(xiàn)思路

記住核心的是:我們是在現(xiàn)有 MVP 框架上添加一個新功能 stateview

那么根據(jù)上一節(jié)我們抽象封裝聲明周期的做法松靡,使用組合的思路,給外部提供一個 stateview 的控制器建椰,而不是具體的邏輯代碼雕欺。那么首先我要思考下,MVP 中哪個角色應該持有這個 stateview 控制器棉姐,顯然應該是 V 層屠列,而不是 P 層,V 層關心的是具體顯示伞矩,那么 stateview 作為顯示的一部分笛洛,有 V 層來持有控制是合理的。

想明白這個功能加在誰的身上后乃坤,我們來思考下苛让,這個功能應該如何合理的對外表達沟蔑,顯然 stateview 是要給 view 添加各種意外狀態(tài)的頁面,那么對于具體的 view 使用者來說蝌诡,這些添加各種意外狀態(tài)的頁面的功能是有 view 提供的溉贿,而不關心 view 是如何實現(xiàn)的枫吧。好了浦旱,說到這里我們應該清楚了,這是一種代理模式的思路九杂,我們抽象出 stateview 的相關功能接口颁湖,然后 view 去實現(xiàn)接口,由 view 內部持有的具體的 stateview 實現(xiàn)類來實現(xiàn)相關方法例隆。特點是 view 會實現(xiàn) stateview 的功能接口甥捺,而不是拋出一個 stateview 對象,這是對外隱藏邏輯實現(xiàn)的一種做法镀层。我們當然可以對外提供 這個stateview 對象的方法镰禾,但是這只是為了照顧一些低度需求,而主要的對外使用上我們要按照代理模式來唱逢。

上面啰嗦了幾句吴侦,是因為我們在一開始在這點上猶豫了下, 思考這個 stateview 功能應該以何種形式對外服務坞古,是按照代理模式的思路來备韧,還是對外直接把這個 stateview 對象拋出來。想了想痪枫,結合 MVP 的封裝思路织堂,還是代理模式的思路更合適。

總體結構:

Snip20171220_7.png

一共涉及到這幾個類:

  • IStateView:對外功能接口
  • DefaultStateView:外層控制器奶陈,對外提供給功能
  • IStateViewProvide:內部提供 各種view 資源的接口易阳,視為一個內部子系統(tǒng)
  • BaseStateViewProvide:這個子系統(tǒng)的 abs 基類,這里考慮要擴展吃粒,未來號維護抽象出一個功能基類出來
  • DefaultStateViewProvide:具體的子系統(tǒng)實現(xiàn)類潦俺,這個是默認實現(xiàn),有其他實現(xiàn)可以繼承 DefaultStateViewProvide 声搁,或是 BaseStateViewProvide
  • StateCodes:各種 view 對應的識別碼

看到這里黑竞,整個結構應該很清晰了,畢竟是個小系統(tǒng)疏旨,沒多少類很魂,但是很適合練手啊,想寫好這個小功能檐涝,各方面考慮到位也是很困難的遏匆。這幾個類分以下幾個角色:

  • 外層功能接口:IStateView
  • 頂層主控制器:DefaultStateView
  • 子系統(tǒng):IStateViewProvide / BaseStateViewProvide / DefaultStateViewProvide
  • 公共資源:StateCodes

不管多大型法挨,多小型的框架,功能系統(tǒng)幅聘,都是由上面這幾個角色組合而成的:

  • 外層功能接口封裝了我們對外提供哪些方法功能
  • 頂層主控制器是實現(xiàn)了這個外層功能接口凡纳,是提供給調用者使用的,要求使用簡單帝蒿,擴展方便荐糜,內部包含 N 多子系統(tǒng)提供具體功能,頂層主控制器值關心核心主邏輯實現(xiàn)葛超。
  • 子系統(tǒng)是從主邏輯中抽象分離出的功能暴氏,封裝好了提供給頂層主控制器使用,這里我只是抽象出了 提供各種 view 對象的子系統(tǒng)出來绣张。
  • 公共資源答渔,這個就不用說了。

寫這個 stateView 小功能侥涵,處處體現(xiàn)出了組合的思路啊沼撕,不同的子系統(tǒng)組合在一起就是一個大系統(tǒng)。大系統(tǒng)臃腫就可以拆分成不同的小系統(tǒng)芜飘。

下面我們開始具體代碼部分:

這個 stateview 也算是一個小小的框架了务豺,作為練手非常適合。從頭開始編寫一個框架燃箭,都是先開始寫主干結構冲呢,然后再去填充一個個具體功能實現(xiàn)。主干寫清楚了招狸,首先我們可以清楚無誤的審視我們的結構是否合理敬拓,需要不需要再大的調整,要是都寫完了再去打動裙戏,那就是大給自己找事了乘凸。另外的好處就是我們不會混亂了,結構都有了累榜,剩下的照著寫就好了营勤,會杜絕我們寫著寫著突然發(fā)現(xiàn)某個地方寫錯了,發(fā)生結構錯誤壹罚,說實話可以大大節(jié)省我們的重復工作葛作。

  1. 我們先來抽象 stateview 功能接口
    stateview 是 view 對外展示功能的核心,view 實現(xiàn)哪個接口猖凛,調用者才能通過 view 去使用哪個接口的方法赂蠢,這里我們對外提供:顯示loading,網絡錯誤辨泳,數(shù)據(jù)異常虱岂,沒有數(shù)據(jù)玖院,顯示內容幾個功能
public interface IStateView {

    void showContent();

    void showDataError();

    void showDataEmpty();

    void shownNetError();

    void showLoading();
}
  1. 把 stateview 功能提供給 V 層對象,當然是在 MVP 的框架下第岖。

首先 V 層的跟接口 IBaseView 繼承 IStateView 接口难菌,這樣 MVP 框架下的多有 view 對象就都有 stateview 的功能了

public interface IBaseView extends IStateView {

    DefaultStateView getStateView();

}

然后我們在 V 層的 abs 抽象基類中實現(xiàn)相關的 IStateView 接口的方法,這里涉及到:V 層的 abs 抽象基類如何持有蔑滓,初始化 IStateView 接口具體實現(xiàn)類郊酒,如何添加顯示 stateview 功能的默認公共代碼。代碼只放了有關的代碼烫饼,其余的取掉了猎塞,DefaultStateView 是 IStateView 接口的具體實現(xiàn)類

public abstract class BaseActivity implements IBaseView {

    // 持有 IStateView 對象引用
    protected DefaultStateView mStateView;

    // 返回IStateView 對象引用
    @Override
    public DefaultStateView getStateView() {
        if (mStateView == null) {
            initStateView();
        }
        return mStateView;
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 初始化時間節(jié)點
        init();
    }

    // 初始化方法
    public void init() {
        initStateView();
    }

     // 初始化方法
    public void initStateView() {
        mStateView = createStateView();
        mStateView.setRootView((ViewGroup) getWindow().getDecorView());
    }

    public DefaultStateView createStateView() {
        return new DefaultStateView(this);
    }

  // 代理 stateview 功能實現(xiàn)
 @Override
    public void showContent() {
        getStateView().showContent();
    }

    @Override
    public void showDataError() {
        getStateView().showDataError();
    }

    @Override
    public void showDataEmpty() {
        getStateView().showDataEmpty();
    }

    @Override
    public void shownNetError() {
        getStateView().shownNetError();
    }

    @Override
    public void showLoading() {
        getStateView().showLoading();
    }

經過上面2部的編寫试读,我們就完成了 stateview 功能 在MVP 框架中的部分杠纵,下面就是 stateview 自身的實現(xiàn)了。

  1. 實現(xiàn) stateview 控制類

這個 stateview 控制類用于對外提供相關功能調用钩骇,多說點這個也可以叫門板類比藻,是外觀設計模式的概念,外觀設計模式把一個復雜的框架或是功能稱為:系統(tǒng)倘屹。這里我們的 stateview 功能也是可以稱之為一個系統(tǒng)银亲,不管這個系統(tǒng)內部實現(xiàn)有多么的復雜,對調用者來說我只要能簡單的調用就好了纽匙,才不關心你怎么實現(xiàn)的务蝠,那么這個門板類就是給調用者使用的,簡要求簡單易用烛缔,不要拖泥帶水馏段。

DefaultStateView 這個是我們的控制類,實現(xiàn) IStateView 接口

public class DefaultStateView implements IStateView {

    public Context context;

    private ViewGroup rootView;
    private IStateViewProvide mStateViewProvide;

    public DefaultStateView(Context context) {
        this.context = context;
        init();
    }

    private void init() {
        initStateViewProvide(context);
    }

    private void initStateViewProvide(Context context) {
        mStateViewProvide = new DefaultStateViewProvide(context);
    }

    public IStateViewProvide getStateViewProvide() {
        return mStateViewProvide;
    }

    public void setContext(Context context) {
        this.context = context;
        invalidata(context);
    }

    @Override
    public void showContent() {
        if (rootView == null) {
            return;
        }
        if (StateCodes.CONTENT.code != mStateViewProvide.getCurrentStateViewCode()) {
            cleanCurrentStateView();
        }
    }

    @Override
    public void showDataError() {
        if (rootView == null) {
            return;
        }
        if (StateCodes.DATA_ERROR.code != mStateViewProvide.getCurrentStateViewCode()) {
            addDataErrorStateView();
        }
    }

    @Override
    public void showDataEmpty() {
        if (rootView == null) {
            return;
        }
        if (StateCodes.DATA_EMPTY.code != mStateViewProvide.getCurrentStateViewCode()) {
            addDataEmptyStateView();
        }
    }

    @Override
    public void shownNetError() {
        if (rootView == null) {
            return;
        }
        if (StateCodes.NET_ERROR.code != mStateViewProvide.getCurrentStateViewCode()) {
            addNetErrorStateView();
        }
    }

    @Override
    public void showLoading() {
        if (rootView == null) {
            return;
        }
        if (StateCodes.LOADING.code != mStateViewProvide.getCurrentStateViewCode()) {
            addloaingStateView();
        }
    }

    public void cleanCurrentStateView() {

        View currentStateView = mStateViewProvide.getCurrentStateView();
        if (currentStateView == null || rootView == null) {
            return;
        }
        rootView.removeView(currentStateView);
        mStateViewProvide.setCurrentStateViewCode(StateCodes.CONTENT.code);
    }

    public void setRootView(ViewGroup rootView) {
        this.rootView = rootView;
    }

    private void addDataErrorStateView() {
        cleanCurrentStateView();
        addStateViewByStateViewCode(StateCodes.DATA_ERROR.code);
    }

    private void addDataEmptyStateView() {
        cleanCurrentStateView();
        addStateViewByStateViewCode(StateCodes.DATA_EMPTY.code);
    }

    private void addNetErrorStateView() {
        cleanCurrentStateView();
        addStateViewByStateViewCode(StateCodes.NET_ERROR.code);
    }

    private void addloaingStateView() {
        cleanCurrentStateView();
        addStateViewByStateViewCode(StateCodes.LOADING.code);
    }

    private void addStateViewByStateViewCode(int stateViewCode) {
        View view = mStateViewProvide.getStateViewByCode(stateViewCode);
        if (view == null || rootView == null) {
            return;
        }
        rootView.addView(view);
        mStateViewProvide.setCurrentStateViewCode(stateViewCode);
    }

    private void invalidata(Context context) {
        if (context == null) {
            return;
        }
        mStateViewProvide = new DefaultStateViewProvide(context);
    }

}
  1. 實現(xiàn) stateview 相關頁面提供器

看過第三部的践瓷,可以看到有一個關鍵功能類: mStateViewProvide院喜,這是提供相關 view 的功能類,這里覺得應該把 view 提供的功能從控制器類分離出來晕翠,再抽象出一個功能類來喷舀,因為外層控制器封裝的是整個系統(tǒng)的邏輯,而期中較為復雜的代碼片段可以再次封裝成為一個這個系統(tǒng)中一個子系統(tǒng)淋肾,這樣代碼結構可以更清晰硫麻,編寫頁簡單容易一些,不容易亂樊卓。這里 mStateViewProvide 這個對象就是對外提供 各種狀態(tài) view 的子系統(tǒng)對象拿愧。

考慮到這里以后會有擴展,所以靈活一些简识,封裝出一個 abs 基類赶掖,放一些基礎功能代碼

public abstract class BaseStateViewProvide implements IStateViewProvide {

    protected Context context;

    protected int curState = StateCodes.CONTENT.code;
    protected Map<Integer, Integer> stateIDs;
    protected Map<Integer, View> stateViews;

    public BaseStateViewProvide(Context context) {
        this.context = context;
        init();
    }

    @Override
    public void setContext(Context context) {
        this.context = context;
    }

    public int getCurState() {
        return curState;
    }

    public Map<Integer, Integer> getStateIDs() {
        if (stateIDs == null) {
            initMap();
        }
        return stateIDs;
    }

    public Map<Integer, View> getStateViews() {
        if (stateViews == null) {
            initMap();
        }
        return stateViews;
    }

    public View getStateView(int stateViewCode) {

        if (stateViews == null) {
            init();
        }
        if (stateViews == null || stateViews.size() == 0) {
            return null;
        }
        return stateViews.get(stateViewCode);
    }

    public int getStateViewID(int stateViewCode) {

        if (stateIDs == null) {
            init();
        }
        if (stateIDs == null || stateIDs.size() == 0) {
            return 0;
        }
        return stateIDs.get(stateViewCode);
    }

    public View inflateView(int stateViewCode) {

        if (context == null) {
            return null;
        }
        int stateViewCode2 = stateViewCode;
        Integer integer = getStateIDs().get(stateViewCode);
        View view = LayoutInflater.from(context).inflate(integer, null, false);

        if (view == null) {
            return null;
        }

        getStateViews().put(stateViewCode, view);
        return view;
    }

    private void init() {
        initMap();
        initData();
    }

    private void initMap() {
        if (stateIDs == null) {
            stateIDs = new HashMap<>();
        }
        if (stateViews == null) {
            stateViews = new HashMap<>();
        }
    }

    protected abstract void initData();

    protected void invalidateData() {
        if (stateViews == null || stateIDs == null) {
            initMap();
        }
        stateViews.clear();
        stateIDs.clear();
        initData();
    }

    protected void setStateVIewIDAndCleanStateView(int stateVIewCode,int stateVIewID) {
        getStateIDs().remove(stateVIewCode);
        getStateIDs().put(stateVIewCode, stateVIewID);
        getStateViews().remove(stateVIewCode);
    }

然后具體實現(xiàn)這個功能 DefaultStateViewProvide 感猛,核心就在于 重寫 initData() 這個方法,在這個方法里綁定各種 狀態(tài) view 的 layoutID奢赂,未來擴展時可以考慮繼承這個 DefaultStateViewProvide 類陪白,重寫 initData() ,也可以繼承 BaseStateViewProvide 這個 abs 基類提供更多的功能膳灶,這里涉及到重寫 外層控制器了咱士,要不也不會設計到寫個新的功能類了

public class DefaultStateViewProvide extends BaseStateViewProvide {

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

    @Override
    protected void initData() {
        getStateIDs().put(StateCodes.LOADING.code, R.layout.layout_stateview_loading);
        getStateIDs().put(StateCodes.DATA_ERROR.code, R.layout.layout_stateview_dataerror);
        getStateIDs().put(StateCodes.DATA_EMPTY.code, R.layout.layout_stateview_dataempty);
        getStateIDs().put(StateCodes.NET_ERROR.code, R.layout.layout_stateview_neterror);
    }

    public void setmDataErrorViewID(int mDataErrorViewID) {
        setStateVIewIDAndCleanStateView(StateCodes.DATA_ERROR.code, mDataErrorViewID);
    }

    public void setmDataEmptyViewID(int mDataEmptyViewID) {
        setStateVIewIDAndCleanStateView(StateCodes.DATA_EMPTY.code, mDataEmptyViewID);
    }

    public void setmNetErrorViewID(int mNetErrorViewID) {
        setStateVIewIDAndCleanStateView(StateCodes.NET_ERROR.code, mNetErrorViewID);
    }

    public void setmloadingViewID(int mLoadingViewID) {
        setStateVIewIDAndCleanStateView(StateCodes.LOADING.code, mLoadingViewID);
    }

    @Override
    public View getCurrentStateView() {
        return getStateViews().get(curState);
    }

    @Override
    public int getCurrentStateViewCode() {
        return curState;
    }

    @Override
    public void setCurrentStateViewCode(int stateViewCode) {
        curState = stateViewCode;
    }

    @Override
    public View getStateViewByCode(int stateViewCode) {

        View view = getStateViews().get(stateViewCode);
        if (view == null) {
            view = inflateView(stateViewCode);
        }

        return view;
    }

}

總結

最后了,也沒啥說的轧钓,大伙沒事多看看設計模式序厉,設計模式要靈活應用,不必分得按照設計模式的例子來毕箍,只要設計模式的恩那個給我們提供思路就行弛房。

項目地址: BW-MVPDemo / step2_2 這部分。


其他資料

這個單元寫完有些時間了而柑,又看到一些新的文章文捶,這里整理一下

  1. 有個朋友在總結自己的 stateview 時有一些感想很贊
  1. 找到一個開源項目和我的思路基本吻合,卻別在于管理 stateview 地方不太一樣媒咳,我的是在頁面 base 層提供管理的粹排,這位兄弟的是在頁面層里,想了想涩澡,各有利弊顽耳,應該在頁面層之外也能提供靈活優(yōu)雅的使用,這點之后會考慮重構一下

參考項目:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末妙同,一起剝皮案震驚了整個濱河市射富,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渐溶,老刑警劉巖辉浦,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異茎辐,居然都是意外死亡宪郊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門拖陆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弛槐,“玉大人,你說我怎么就攤上這事依啰『醮” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵速警,是天一觀的道長叹誉。 經常有香客問我鸯两,道長,這世上最難降的妖魔是什么长豁? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任钧唐,我火速辦了婚禮,結果婚禮上匠襟,老公的妹妹穿的比我還像新娘钝侠。我一直安慰自己,他們只是感情好酸舍,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布帅韧。 她就那樣靜靜地躺著,像睡著了一般啃勉。 火紅的嫁衣襯著肌膚如雪忽舟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天璧亮,我揣著相機與錄音萧诫,去河邊找鬼。 笑死枝嘶,一個胖子當著我的面吹牛,可吹牛的內容都是我干的哑诊。 我是一名探鬼主播群扶,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼镀裤!你這毒婦竟也來了竞阐?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤暑劝,失蹤者是張志新(化名)和其女友劉穎骆莹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體担猛,經...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡幕垦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了傅联。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片先改。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蒸走,靈堂內的尸體忽然破棺而出仇奶,到底是詐尸還是另有隱情,我是刑警寧澤比驻,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布该溯,位于F島的核電站岛抄,受9級特大地震影響,放射性物質發(fā)生泄漏狈茉。R本人自食惡果不足惜弦撩,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望论皆。 院中可真熱鬧益楼,春花似錦、人聲如沸点晴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粒督。三九已至陪竿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屠橄,已是汗流浹背族跛。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锐墙,地道東北人礁哄。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像溪北,于是被迫代替她去往敵國和親桐绒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內容