MVVM+DataBinding 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的加載新聞列表Demo

**PS:這是我第一次寫文章荤西,小弟才疏學(xué)淺邪锌,如果有寫的不好請(qǐng)見諒噢秃流,與此同時(shí)有什么好的建議和意見都可以下面留言噢概说,謝謝大家!文章代碼有點(diǎn)多萍丐,本想只貼關(guān)鍵代碼的逝变,為了能讓大家看得明白就把全部代碼貼出來了壳影!

先給大家展示下咱們要實(shí)現(xiàn)效果:

實(shí)現(xiàn)效果展示.jpg

1. MVVM的簡(jiǎn)單介紹:

說起實(shí)現(xiàn)mvvm這個(gè)架構(gòu)可以說是最近比較火了,mvvm可以說是mvp的升級(jí)版掺栅,M是Model,V是View(Activity氧卧,F(xiàn)ragment 假抄,xml等等)宿饱,VM就是ViewModel谬以。

要想學(xué)會(huì)使用mvvm架構(gòu)由桌,就必須要了解DataBinding铭乾,DataBinding的入門教程炕檩,我就暫時(shí)轉(zhuǎn)載下郭霖大神分享的文章吧:——> 點(diǎn)我 ,不會(huì)的一定要看噢泉沾!

2. 簡(jiǎn)單說下mvvm中各個(gè)模塊的作用:

  1. Model是用來獲取本地或者網(wǎng)絡(luò)的數(shù)據(jù);
  2. ViewModel是來操作Model獲取的數(shù)據(jù)俊马;
  3. View當(dāng)然就是用來顯示UI的啦;

而mvvm****最主要的宗旨理念就是數(shù)據(jù)驅(qū)動(dòng)UI肩杈。大致意思是ViewModel操作Model的數(shù)據(jù)柴我,當(dāng)數(shù)據(jù)變化時(shí)會(huì)自動(dòng)同步到View的UI上(單向綁定)

掌握這些基本理念就差不多了,開始實(shí)戰(zhàn)吧锋恬!

3. 實(shí)戰(zhàn):

1. 項(xiàng)目結(jié)構(gòu):

項(xiàng)目結(jié)構(gòu).jpg

2. 添加項(xiàng)目所需要的依賴

implementation 'com.android.support:design:28.0.0'

implementation 'com.squareup.retrofit2:retrofit:2.5.0'

implementation 'com.squareup.retrofit2:retrofit-converters:2.5.0'

implementation 'com.squareup.retrofit2:converter-gson:2.5.0'

implementation 'com.github.bumptech.glide:glide:4.9.0'

3. 主要界面編寫

我這里是使用了ViewPager+TabLayout+Fragment實(shí)現(xiàn)的,我就暫時(shí)貼上NewsFragment的界面吧编丘!而NewsFragment布局中使用了RecyclerView,那么順便也貼下news_item布局与学。

<!--NewsFrgament布局-->

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable
            name="NewViewModel"
            type="com.bunny.swipelayoutdemo.viewmodel.NewsViewModel"/>
    </data>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".view.fragment.NewsFragment">

        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/rv_sl"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <android.support.v7.widget.RecyclerView
                android:id="@+id/rv_news"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
            </android.support.v7.widget.RecyclerView>
        </android.support.v4.widget.SwipeRefreshLayout>
    </LinearLayout>
</layout>

注意咱們?cè)贜ewsItem布局里設(shè)置的數(shù)據(jù)的時(shí)候使用了@{NewsViewModel.xxx};

<!--NewsItem布局-->

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable
            name="NewsViewModel"
            type="com.bunny.swipelayoutdemo.viewmodel.NewsViewModel"/>
    </data>
    
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:padding="10dp">

        <TextView
            android:id="@+id/title"
            android:textSize="18sp"
            app:layout_constraintEnd_toStartOf="@id/image"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:textColor="@android:color/black"
            android:layout_width="0dp"
            app:layout_constraintHorizontal_weight="7"
            android:text="@{NewsViewModel.title}"
            android:singleLine="true"
            android:layout_height="wrap_content" />

        <TextView
            android:id="@+id/content"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:maxLines="2"
            android:textSize="15sp"
            app:layout_constraintTop_toBottomOf="@id/title"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@id/image"
            app:layout_constraintHorizontal_weight="7"
            android:text="@{NewsViewModel.content}"
            android:ellipsize="end"/>



        <ImageView
            android:paddingTop="2dp"
            android:id="@+id/image"
            android:layout_width="0dp"
            app:imgUrl="@{NewsViewModel.imgUrl}"
            android:scaleType="fitXY"
            app:layout_constraintHorizontal_weight="3"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@id/title"
            android:layout_height="70dp" />

        <TextView
            android:layout_marginTop="3dp"
            android:text="@{NewsViewModel.author}"
            app:layout_constraintTop_toBottomOf="@id/content"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"/>

        <TextView
            android:layout_marginTop="3dp"
            android:text="@{NewsViewModel.time}"
            android:layout_width="0dp"
            app:layout_constraintTop_toBottomOf="@id/image"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_height="wrap_content" />
    </android.support.constraint.ConstraintLayout>
</layout>

其中ImageView在設(shè)置圖片的時(shí)候很特別,這個(gè)Demo里面使用了Glide圖片加載框架,因?yàn)榉?wù)器返回的是圖片的URL地址所以用Glide更簡(jiǎn)單。

public class BindAdapter {
    @BindingAdapter("imgUrl")
    public static void setImage(ImageView iv, String imgUrl){
        Glide.with(iv.getContext()).load(imgUrl).into(iv);
    }
}

@BinderAdapter注解的作用:相當(dāng)于一個(gè)綁定的數(shù)據(jù)源監(jiān)聽器,如果發(fā)現(xiàn)數(shù)據(jù)源改變蚓峦,那么他的函數(shù)體方法會(huì)被立即調(diào)用一汽;而是用@BinderAdapter注解其方法必須是public static類型沾谓;

舉個(gè)栗子:咱們?cè)诤竺娴腣iewModel中會(huì)設(shè)置ImageView的URL妇穴,那么BindAdapter下面的方法就會(huì)立即被調(diào)用死讹。

4. Model中通過Retrofit+OkHttp獲取網(wǎng)絡(luò)數(shù)據(jù):

這是咱們要使用Retrofit必須設(shè)置的,沒啥好說的世剖;

//Api:
public interface Api {
    public static final String BASE_URL="http://xxxx.xxxx.xxxx.xxx";

    //pi是頁數(shù)酬凳,ps是當(dāng)前服務(wù)器要返回的新聞條數(shù)
    @GET("CPAPI/V1/NewsList")
    Call<News> getNews(@Query("pi") int pi,@Query("ps") int ps);
}

通過枚舉單例創(chuàng)建一個(gè)Http請(qǐng)求的工具類台诗,里面封裝了Retrofit。當(dāng)然使用枚舉類單例能防止反射攻擊,避免序列化等等好處漫雷,大家具體可以百度下;

//Http工具類:通過枚舉單例創(chuàng)建
public enum HttpManagerSingleton {
    INSTANCE{
        public OkHttpClient getClient(){
            return new OkHttpClient.Builder()
                    .readTimeout(3, TimeUnit.SECONDS)
                    .connectTimeout(1,TimeUnit.SECONDS)
                    .retryOnConnectionFailure(true)
                    .build();
        }
        public Retrofit getRetrofit(OkHttpClient client){
            return new Retrofit.Builder()
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl(Api.BASE_URL)
                    .client(client)
                    .build();
        }
    };
    public abstract Retrofit getRetrofit(OkHttpClient client);
    public abstract OkHttpClient getClient();
    public static HttpManagerSingleton getInstance(){
        return INSTANCE;
    }
}

在此NewsModel用來通過網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)涡戳,通過回調(diào)將數(shù)據(jù)傳遞給ViewModel來處理推正;

//Model類獲取數(shù)據(jù):
public class NewsModel {
    private static Retrofit getRetrofit(){
        HttpManagerSingleton singleton=HttpManagerSingleton.getInstance();
        return singleton.getRetrofit(singleton.getClient());
    }

    public static void getNews(int pi, int ps, final NewsCallBack newsCallBack){
        final Call<News> newsCall = getRetrofit().create(Api.class).getNews(pi, ps);
                newsCall.enqueue(new Callback<News>() {
                    @Override
                    public void onResponse(Call<News> call, Response<News> response) {
                        newsCallBack.onSuccess(response.body().getData());
                        newsCall.cancel();
                    }

                    @Override
                    public void onFailure(Call<News> call, Throwable t) {
                        newsCallBack.onError(t.getMessage());
                        newsCall.cancel();
                    }
                });
    }
}

5. ViewModel操作數(shù)據(jù):

在ViewModel中處理Model通過回調(diào)傳來的數(shù)據(jù),進(jìn)行操作;

public class NewsViewModel {
    public ObservableField<String> title=new ObservableField<>();
    public ObservableField<String> author=new ObservableField<>();
    public ObservableField<String> imgUrl=new ObservableField<>();
    public ObservableField<String> time=new ObservableField<>();
    public ObservableField<String> content=new ObservableField<>();
    public ObservableArrayList<News.DataBean> localList=new ObservableArrayList();
    //設(shè)置默認(rèn)加載第一頁8條數(shù)據(jù)
    public int pi=1,ps=8;
    //滑動(dòng)監(jiān)聽回調(diào)
    private FreshCallBack mFreshCallBack;
    
    
    /**
     *  將服務(wù)器中返回的時(shí)間戳轉(zhuǎn)換為日期格式:
     */

    private String parseTimeToString(long time,String format){
        Date date=new Date(time*1000);
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat(format);
        String form = simpleDateFormat.format(date);
        return form;
    }

    /**
     * 設(shè)置數(shù)據(jù):
     * @param result
     */
    public void setData(final News.DataBean result){
       new Thread(new Runnable() {
           @Override
           public void run() {
               if (result==null)
                   return;
               title.set(result.getTitle());
               content.set(result.getTitle());
               time.set(parseTimeToString(result.getTime(),"yyyy-MM-dd"));
               imgUrl.set(result.getPic());
               author.set(result.getAuthor());
           }
       }).start();
    }

    /**
     * 獲取新聞數(shù)據(jù):
     */
    public void loadNews(int pi, int ps, final FreshCallBack mFreshCallBack){
        NewsModel.getNews(pi, ps, new NewsCallBack() {
            @Override
            public void onSuccess(List<News.DataBean> newsList) {
                localList.addAll(newsList);
                mFreshCallBack.freshSuccess();
            }

            @Override
            public void onError(String error) {
                mFreshCallBack.freshError();
            }
        });
    }

    /**
     * 上拉加載更多
     */
    public void loadMoreNews(FreshCallBack mFreshCallBack){
        pi=pi+1;
        loadNews(pi,ps,mFreshCallBack);
    }

    /**
     * 下拉刷新
     */
    public void loadFreshNews(final FreshCallBack mFreshCallBack){
        pi=1;
        NewsModel.getNews(pi, ps, new NewsCallBack() {
            @Override
            public void onSuccess(List<News.DataBean> newsList) {
                localList.clear();
                localList.addAll(0,newsList);
                mFreshCallBack.freshSuccess();
            }

            @Override
            public void onError(String error) {
                mFreshCallBack.freshError();
            }
        });
    }
}

}

6. Adapter的編寫:

方法解釋:

  • 因?yàn)槭褂昧薕bservableArrayList所以我們?cè)赼dapter里的構(gòu)造方法中添加監(jiān)聽器這樣當(dāng)數(shù)據(jù)源發(fā)生改變的時(shí)候,我們就不需要在View中手動(dòng)通知適配器了;
  • 在onCreateViewHolder()里面使用DataBinding將news_item界面進(jìn)行綁定,從而再創(chuàng)建ViewHoder;
  • 在ViewHolder中我們只需要更改下構(gòu)造方法里的參數(shù)將Binding傳入進(jìn)去纹坐;
  • 在onBindViewHolder中讓Binding與ViewModel進(jìn)行綁定瓷马,這樣就調(diào)用里面的方法設(shè)置數(shù)據(jù);
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private ObservableArrayList<News.DataBean> newsList;

    public MyAdapter(ObservableArrayList<News.DataBean> newsList) {
        this.newsList = newsList;
        //這里使用ObservableArrayList的監(jiān)聽器蒋伦,這樣當(dāng)數(shù)據(jù)源發(fā)生改變的時(shí)候锤窑,我們就不需要在View中手動(dòng)通知適配器了
        newsList.addOnListChangedCallback(new ObservableList.OnListChangedCallback<ObservableList<News.DataBean>>() {
            @Override
            public void onChanged(ObservableList<News.DataBean> sender) {
                notifyDataSetChanged();
            }

            @Override
            public void onItemRangeChanged(ObservableList<News.DataBean> sender, int positionStart, int itemCount) {
                notifyItemChanged(positionStart,itemCount);
            }

            @Override
            public void onItemRangeInserted(ObservableList<News.DataBean> sender, int positionStart, int itemCount) {
                notifyItemRangeInserted(positionStart,itemCount);
            }

            @Override
            public void onItemRangeMoved(ObservableList<News.DataBean> sender, int fromPosition, int toPosition, int itemCount) {
                if (itemCount==1)
                    notifyItemMoved(fromPosition,toPosition);
                notifyDataSetChanged();
            }

            @Override
            public void onItemRangeRemoved(ObservableList<News.DataBean> sender, int positionStart, int itemCount) {
                notifyItemRangeRemoved(positionStart,itemCount);
            }
        });
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        NewsItemBinding newsItemBinding = DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.news_item, viewGroup, false);
        return new ViewHolder(newsItemBinding);
    }

    @Override
    public int getItemCount() {
        return newsList.size();
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
        NewsViewModel viewModel = new NewsViewModel();
        //將newsItemBinding和NewsModel綁定
        viewHolder.getBinding().setNewsViewModel(viewModel);
        //設(shè)置數(shù)據(jù):
        viewModel.setData(newsList.get(i));
        //避免RecyclerView界面閃爍
        viewHolder.getBinding().executePendingBindings();
    
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        NewsItemBinding binding;
        public ViewHolder(@NonNull NewsItemBinding binding ) {
            super(binding.getRoot());
            this.binding =binding;
        }
        public NewsItemBinding getBinding() {
            return binding;
        }
    }
}

7. 在View中(這里代表NewsFragment)與Model進(jìn)行綁定:

我們可以看到在onCreateView()方法里的bindViewModel()中綁定了ViewModel;這樣在后面我們要實(shí)了下拉刷新和上拉加載的時(shí)候就可以手動(dòng)設(shè)置下數(shù)據(jù);

public class NewsFragment extends Fragment {
    private MyAdapter mAdapter;
    private ProgressDialog mProgressDialog;
    private RecyclerView mRvNews;
    private SwipeRefreshLayout mSrl;


    private FragmentNewsBinding mBinding;
    private NewsViewModel mNewsViewModel;


    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mBinding=DataBindingUtil.inflate(inflater,R.layout.fragment_news, null, false);
        bindViewModel();
        initUi();
        return mBinding.getRoot();
    }

    private void bindViewModel() {
        mNewsViewModel=new NewsViewModel();
        mBinding.setNewViewModel(mNewsViewModel);
    }

    private void initUi() {
        mRvNews=mBinding.rvNews;
        mSrl=mBinding.rvSl;
        mProgressDialog=new ProgressDialog(getActivity());
        mProgressDialog.setMessage("正在加載中...");
        mProgressDialog.setCancelable(false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setRecyclerViewAdapter();
        autoLoad();
        loadMore();
        loadFreshNews();
    }

    private void setRecyclerViewAdapter() {
        mRvNews.setLayoutManager(new LinearLayoutManager(getActivity(),LinearLayoutManager.VERTICAL,false));
        mRvNews.addItemDecoration(new DividerItemDecoration(getActivity(),DividerItemDecoration.VERTICAL));
        mAdapter=new MyAdapter(mNewsViewModel.localList);
        mRvNews.setAdapter(mAdapter);
    }

    //首次進(jìn)入自動(dòng)刷新
    private void autoLoad() {
        mSrl.measure(0,0);
        mSrl.setRefreshing(true);
        fresh();
    }


    /**
     * 上拉加載
     */
    public void loadMore(){
        mRvNews.setOnScrollListener(new RecyclerView.OnScrollListener() {
            //判斷是否向最后一個(gè)item進(jìn)行滑動(dòng)
            boolean isLoading=false;
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                LinearLayoutManager manager = (LinearLayoutManager) mRvNews.getLayoutManager();
                switch (newState){
                    case RecyclerView.SCROLL_STATE_IDLE:
                        //獲取最后一個(gè)item的索引值
                        int lastPosition = manager.findLastCompletelyVisibleItemPosition();
                        //獲取item總數(shù)
                        int itemCount = manager.getItemCount();
                        if (lastPosition>=(itemCount-1) && isLoading){
                            mProgressDialog.show();
                            //加載更多
                            mNewsViewModel.loadMoreNews(new FreshCallBack() {
                                @Override
                                public void freshSuccess() {
                                    if (mProgressDialog.isShowing())
                                        mProgressDialog.dismiss();
                                }

                                @Override
                                public void freshError() {
                                    if (mProgressDialog.isShowing())
                                        mProgressDialog.dismiss();
                                        
                                    Toast.makeText(getActivity(), "數(shù)據(jù)加載失斊敲肌晃择!", Toast.LENGTH_SHORT).show();
                                }
                            });

                        }
                        break;
                }
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (dy>0){
                    isLoading=true;
                }else{
                    isLoading=false;
                }
            }
        });
    }

    /**
     * 下拉刷新
     */
    public void loadFreshNews(){
        mSrl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                fresh();
            }
        });
    }
    private void fresh(){
        mNewsViewModel.loadFreshNews(new FreshCallBack() {
            @Override
            public void freshSuccess() {
                if (mSrl.isRefreshing())
                    mSrl.setRefreshing(false);
            }

            @Override
            public void freshError() {
                if (mSrl.isRefreshing())
                    mSrl.setRefreshing(false);
                Toast.makeText(getActivity(), "數(shù)據(jù)加載失斀呋洹!", Toast.LENGTH_SHORT).show();
            }
        });
    }
  
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子栽烂,更是在濱河造成了極大的恐慌册倒,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恤左,“玉大人瓶您,你說我怎么就攤上這事∈鹉猓” “怎么了蟹腾?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵芬首,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)抖锥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮贾费,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘壳坪。我一直安慰自己九孩,他們只是感情好铣减,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布鸟廓。 她就那樣靜靜地躺著,像睡著了一般档玻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上楼雹,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音仰禀,去河邊找鬼裕坊。 笑死酱讶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的榛泛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼炭庙,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼淌哟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起遵蚜,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤友扰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后击喂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體懂昂,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铲敛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年奸鬓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捐下,死狀恐怖账锹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坷襟,我是刑警寧澤奸柬,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站婴程,受9級(jí)特大地震影響廓奕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜档叔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一桌粉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衙四,春花似錦铃肯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窍育。三九已至卡睦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間漱抓,已是汗流浹背表锻。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乞娄,地道東北人瞬逊。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像仪或,于是被迫代替她去往敵國(guó)和親确镊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359