RxJava+Retrofit+Material Design極簡新聞App

快速完成一個新聞APP

本Demo主要使用的技術(shù):

  • 看標題就知道了
  • Material Design
  • 聚合數(shù)據(jù)

效果

直接點吧,先看下效果


這里寫圖片描述

Demo架構(gòu)

老司機們一看就知道界面是由ViewPager+Fragment組成,還是比較簡單的嘶伟。新聞詳情頁面主要是采用了design包下的CoordinatorLayout作為父布局赊豌,因為要做出那個下拉折疊效果嘛壤蚜。然后點擊新聞列表時會有一個轉(zhuǎn)場動畫邻悬,不知道細心的朋友們有木有看出來撞蚕,上拉刷新是采用的官方的SwipeRefreshLayout
一切都追求原滋原味验庙。
整個Demo的網(wǎng)絡(luò)請求是通過RxJava+Retrofit來實現(xiàn)的顶吮,為什么用這對基友組合呢?
三個字 “太爽了”
OK粪薛,后面會有相關(guān)的介紹悴了。
聚合數(shù)據(jù)的key請自己申請,我的已經(jīng)過期了违寿,在Config里面配置下就好了湃交。

代碼分析

封裝BaseActivity 
雖然整個Demo就兩個Activity,那我們還是封裝一下藤巢,因為我喜歡追求代碼簡潔(與后面可能會有些出入)額額搞莺,,
先上波代碼吧

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initContentView(savedInstanceState);
        initStatusBar();
    }

    protected abstract void initContentView(Bundle savedInstanceState);
    /**
     * 初始化沉浸式狀態(tài)欄
     */
    private void initStatusBar(){
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){//4.4 全透明狀態(tài)欄
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//5.0 全透明實現(xiàn)
            Window window = getWindow();
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);//calculateStatusColor(Color.WHITE, (int) alphaValue)
        }
    }
    
    @SuppressWarnings("unchecked")
    public final <E extends View> E findView(int id){
        try {
            return (E) findViewById(id);
        }catch (ClassCastException e){
            throw  e;
        }
    }
}

其實也沒什么亮點掂咒,就是封裝下共同的方法才沧,一個是沉浸式狀態(tài)欄,一個是我為了偷懶绍刮,不想在findviewById的時候加個強制類型轉(zhuǎn)換温圆。當然也可以通過框架注入,和databinding來解決這個孩革,但這次的重點不是他們岁歉。

創(chuàng)建實體類
對了,這些數(shù)據(jù)都是從聚合數(shù)據(jù)獲取下來的膝蜈,具體細節(jié)就不多說了锅移,就是申請個key熔掺,填寫一些參數(shù),然后它會返回一串json數(shù)據(jù)非剃。我們就通過這些json數(shù)據(jù)去生成對應(yīng)的實體類瞬女,用一個良心之作的工具 GsonFormat,具體操作可以自行Google或者百度努潘。
 使用這個工具一是為了偷懶诽偷,二是為了配合gson來對json解析。

創(chuàng)建配置文件
項目中可能會遇到一些很多地方都會用到的常量疯坤,比如說聚合數(shù)據(jù)的key等等报慕,我們可以創(chuàng)建一個接口
將這些數(shù)據(jù)寫到這個接口里面,這樣的話压怠,哪里要用就直接繼承這個接口就OK了眠冈。

public interface Config {
     String[] ARRYTITLES ={"頭條","社會","科技","國內(nèi)","國際","娛樂","時尚","軍事","體育","財經(jīng)"};
     String KEY_POSTION="key_postion";
     String[] ARRYTYPE={"top","shehui","keji","guonei","guoji","yule","shishang","junshi","tiyu","caijing"};
     String KEY_IMG_URL="imgurl";
     String KEY_CONTENT_URL="contenturl";
     String KEY_TYPE="type";
     String KEY_JUHE="0489bcea378ce792facda791d0f1e188";

}

因為請求不同的類型的新聞,參數(shù)不一樣菌瘫,所以弄個數(shù)組蜗顽,把參數(shù)存入進去,記得與類型對應(yīng)雨让。

主Activity編寫
這里因為創(chuàng)建項目的時候手賤了下雇盖,點了那個有側(cè)滑的activity,所以一些生成了很多沒什么卵用的代碼(至少這個項目里面沒什么用)

public class MainActivity extends BaseActivity
        implements NavigationView.OnNavigationItemSelectedListener,Config {
    private TabLayout mTabLayout;
    private ViewPager mViewPager;
    private ViewPagerAdapter mAdapter;
    private List<String> mTitles=new ArrayList<>();

    @Override
    protected void initContentView(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findView(R.id.toolbar);
        setSupportActionBar(toolbar);
        mTabLayout=findView(R.id.tab_layout);
        mViewPager=findView(R.id.viewpager);

        DrawerLayout drawer = findView(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = findView(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        initTitle();
        mAdapter=new ViewPagerAdapter(getSupportFragmentManager(),mTitles);
        mViewPager.setAdapter(mAdapter);
        mTabLayout.setupWithViewPager(mViewPager);
        mTabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
    }


    private void initTitle(){
        for (int i=0;i<ARRYTITLES.length;i++){
            mTitles.add(ARRYTITLES[i]);
        }

    }
 }

這里就是進行初始化一些view栖忠,將fragment添加進去崔挖,viewpager+tablayout基友組合。

創(chuàng)建Fragment的適配器
先說說適配器吧庵寞,因為這里要用的fragment比較多狸相,所以繼承FragmentStatePagerAdapter,Why捐川?

內(nèi)存優(yōu)化

因為一個Fragment占的內(nèi)存還是比較大脓鹃,一旦fragment數(shù)量比較多了,后果你懂的古沥。當頁面不可見時瘸右, 對應(yīng)的Fragment實例可能會被銷毀,但是Fragment的狀態(tài)會被保存渐白,所以一些提高了我們的app性能尊浓。

public class ViewPagerAdapter extends FragmentStatePagerAdapter {
    private List<String> mTitles;


    public ViewPagerAdapter(FragmentManager fm, List<String> mTitles) {
        super(fm);
        this.mTitles = mTitles;

    }

    @Override
    public Fragment getItem(int position) {

        return ContentFragment.instance(position);
    }

    @Override
    public int getCount(){
        return mTitles.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mTitles.get(position);
    }
}

還是比較簡單的,可能最后一個方法或許有些陌生纯衍,用過TabLayout的朋友應(yīng)該懂,就是設(shè)置Tab的標題苗胀,因為我們的ViewPager是要與TabLayout進行關(guān)聯(lián)的襟诸。

編寫網(wǎng)絡(luò)工具類
好了重頭戲來了瓦堵,也是本項目唯一的特色,RxJava+Retrofit歌亲。
記得別忘了引入這些框架

    compile 'io.reactivex:rxjava:1.0.14'
    compile 'io.reactivex:rxandroid:1.0.1'
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    compile 'com.google.code.gson:gson:2.6.2'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'

這里我不做過多關(guān)于RxJava和Retrofit的描述菇用,因為相關(guān)資料網(wǎng)上一堆堆。RxJava說到底就是異步陷揪,這是它整個流程非常簡潔明了惋鸥,而且方便線程切換。Retrofit是講OKHttp更好的封裝下悍缠,簡化我們的網(wǎng)絡(luò)請求卦绣。
首先編寫Retrofit接口

public interface NewService {
    String BASE_URL="http://v.juhe.cn/";

    @GET("toutiao/index?")
    Observable<News> getNews(@QueryMap Map<String,String> map);
}

好像News 少了個s

接下來編寫我們的請求工具類

 private static final int DEFAULT_TIMEOUT = 5;
    private Retrofit retrofit;
    private NewService newService;

    private RetrofitUtil(){
        //手動創(chuàng)建一個OkHttpClient并設(shè)置超時時間
        OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        Gson gson = new GsonBuilder()
                //配置Gson
                .setDateFormat("yyyy-MM-dd hh:mm:ss")
                .create();
        retrofit=new Retrofit.Builder()
                .baseUrl(NewService.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        newService=retrofit.create(NewService.class);

    }

    //在訪問HttpMethods時創(chuàng)建單例
    private static class SingletonHolder{
        private static final RetrofitUtil INSTANCE = new RetrofitUtil();
    }

    public static RetrofitUtil getInstance(){
        return SingletonHolder.INSTANCE;
    }

    public void getNews(Subscriber<News> newsSubscriber,int type){
        newService.getNews(getParams(type))
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(newsSubscriber);

    }

    private Map<String,String> getParams(int type){
        Map<String,String> map=new HashMap<>();
        map.put("type",ARRYTYPE[type]);
        map.put("key",KEY_JUHE);
        return map;
    }
}

我們在外面只用調(diào)用getNews就行了,傳一個Subscriber和類型就行了飞蚓。邏輯都不是很復(fù)雜吧滤港。里面的精髓就是線程切換 .subscribeOn(Schedulers.io()) ,RxJava給我嗎提供了五個選擇趴拧,這里因為我們是請求網(wǎng)絡(luò)溅漾,所以就用io的,最后切換到主線程 .observeOn(AndroidSchedulers.mainThread())

編寫Fragment

既然網(wǎng)絡(luò)工具類寫好了著榴,那么就寫個fragment來把這些數(shù)據(jù)展示出來吧添履!

public class ContentFragment extends Fragment implements Config {

    private int mType;
    private List<News.ResultBean.DataBean> mData;
    private SwipeRefreshLayout mRefreshLayout;
    private RecyclerView mShowNews;
    private NewsAdapter mAdapter;
    private int mSpacingInPixels;//
    private int mCount=0;


    public static Fragment instance(int postion){
        ContentFragment fragment=new ContentFragment();
        Bundle bundle = new Bundle() ;
        bundle.putInt(KEY_POSTION,postion);

        fragment.setArguments(bundle);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.fragment_content,container,false);
        return view;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Bundle bundle = getArguments() ;
        Log.e("dandy","pos "+bundle.getInt(KEY_POSTION));
        initViews(view);
        mType=bundle.getInt(KEY_POSTION);

        getData();
    }

    private void initViews(View view) {
        mRefreshLayout= (SwipeRefreshLayout) view.findViewById(R.id.refresh_layout);
        mShowNews= (RecyclerView) view.findViewById(R.id.news_recyclerview);
        mRefreshLayout.setColorSchemeResources(R.color.colorPrimary,R.color.tab_select_text_color,R.color.refresh_color,R.color.colorAccent);
        mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                getData();
            }
        });
        mSpacingInPixels= getResources().getDimensionPixelSize(R.dimen.item_space);
        mShowNews.setHasFixedSize(true);
    }

    private void getData(){
        mRefreshLayout.setRefreshing(true);
        Subscriber<News> subscriber=new Subscriber<News>() {
            @Override
            public void onCompleted() {

            }
            //出現(xiàn)異常回調(diào)
            @Override
            public void onError(Throwable e) {
                Log.e("smile","獲取失敗");
            }
            //獲取數(shù)據(jù)成功后回調(diào)
            @Override
            public void onNext(News news) {
                Log.e("smile","獲取出來的"+news.getResult().getData().size());
                //mData=news.getResult().getData();
                setData(news);
            }
        };
        RetrofitUtil.getInstance().getNews(subscriber,mType);
    }

    private void setData(News data){

        mData=data.getResult().getData();
        mRefreshLayout.setRefreshing(false);
        mAdapter=new NewsAdapter(getContext(),data);
        mShowNews.setLayoutManager(new LinearLayoutManager(getContext()));
        //避免重復(fù)添加間距
        if (mCount==0){
            mShowNews.addItemDecoration(new SpacesItemDecoration(mSpacingInPixels));
        }

        mShowNews.setAdapter(mAdapter);
       // mAdapter.setOnScrollListener(mShowNews);
        mAdapter.setOnItemClickListener(new NewsAdapter.OnItemClickListener() {

            @Override
            public void onItemClick(View view, int position) {
                startDetailActivity(view,mData.get(position));
            }

            @Override
            public void onItemLongClick(View view, int position) {

            }
        }) ;
        mCount++;

    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void startDetailActivity(View view, News.ResultBean.DataBean bean){
        Intent intent=new Intent(getActivity(), NewsDetailActivity.class);
        Bundle bundle=new Bundle();
        bundle.putString(KEY_IMG_URL,bean.getThumbnail_pic_s());
        bundle.putString(KEY_CONTENT_URL,bean.getUrl());
        bundle.putString(KEY_TYPE,ARRYTITLES[mType]);
        intent.putExtras(bundle);
        ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),view.findViewById(R.id.item_news_img),"photos");
        getContext().startActivity( intent, options.toBundle());
    }
}

其實仔細一看也不是很復(fù)雜脑又,就是調(diào)用我們開始寫的getNews而已缝龄,請求成功后會回調(diào)onNext方法。對了這里有個轉(zhuǎn)場動畫挂谍,就是最后一個方法叔壤,這里的動畫效果是共享元素,所以指定你要共享的元素就行口叙,然后設(shè)置下它的 android:transitionName="photos"迎变。

編寫RecyclerView適配器
再見ListView蕉世,你好RecyclerView
RecyclerView的優(yōu)點就不多說了,就是自由,任性
適配器堕义,我就不貼代碼了,累羊娃,而且沒什么特色翼馆。

編寫詳情頁面
這里主要都是用了Design里面的一些控件

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="250dp">
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:minHeight="?attr/actionBarSize"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:statusBarScrim="?attr/colorAccent"

            app:collapsedTitleGravity="left"
            app:expandedTitleGravity="center_horizontal|bottom"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
            <ImageView
                android:id="@+id/news_img"
                android:src="@mipmap/ic_launcher"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax"
                android:transitionName="photos"
                app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                />
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                app:layout_collapseMode="pin"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:navigationIcon="?attr/homeAsUpIndicator"
                app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                style="@style/ToolbarTheme"
                android:titleTextColor="@color/white"

               />
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <com.dandy.smilenews.ui.MyNestedScrollView
        android:id="@+id/news_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <WebView
            android:id="@+id/news_web"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></WebView>
    </com.dandy.smilenews.ui.MyNestedScrollView>
</android.support.design.widget.CoordinatorLayout>

折疊式效果就是通過編寫xml就能實現(xiàn)了,還是簡單的描述下那些屬性的含義
CollapsingToolbarLayout

//折疊后的背景色  -> setContentScrim(Drawable)
  app:contentScrim="?attr/colorPrimary"   
  // 必須設(shè)置透明狀態(tài)欄才有效  -> setStatusBarScrim(Drawable)     
  app:statusBarScrim="?attr/colorAccent"    
  // 標題  
  app:title="title"
  // 折疊后的標題位置
  app:collapsedTitleGravity="right"
  // 打開時的標題位置
  app:expandedTitleGravity="center_horizontal|bottom"

折疊效果

app:layout_collapseMode      
  有兩個可選:
       parallax ——  視差模式,就是上面的圖片的變化效果
       pin     —— 固定模式启具,在折疊的時候最后固定在頂端

  // 視差效果
  app:layout_collapseParallaxMultiplier   
  范圍[0.0,1.0]本讥,值越大視差越大

下面的MyNestedScrollView是我自定義的一個view,繼承的NestedScrollView,主要是為了解決事件沖突導(dǎo)致滑動卡頓拷沸,就是不讓攔截子view的觸摸事件色查。

class MyNestedScrollView extends NestedScrollView {
    private GestureDetector mGestureDetector;
    View.OnTouchListener mGestureListener;
    public MyNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

    public MyNestedScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mGestureDetector = new GestureDetector(context, new YScrollDetector());
        setFadingEdgeLength(0);
    }

    class YScrollDetector extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (Math.abs(distanceY) > Math.abs(distanceX)) {
                return true;
            }
            return false;
        }
    }

}

到這里差不多也要完工了,就可以看到開頭的效果了撞芍,貌似第一次寫這么長的博客秧了,,序无,
最后附上源碼 傳送門
喜歡就給個星星吧验毡!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市帝嗡,隨后出現(xiàn)的幾起案子晶通,更是在濱河造成了極大的恐慌,老刑警劉巖丈探,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件录择,死亡現(xiàn)場離奇詭異,居然都是意外死亡碗降,警方通過查閱死者的電腦和手機隘竭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來讼渊,“玉大人动看,你說我怎么就攤上這事∽茫” “怎么了菱皆?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挨稿。 經(jīng)常有香客問我仇轻,道長,這世上最難降的妖魔是什么奶甘? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任篷店,我火速辦了婚禮,結(jié)果婚禮上臭家,老公的妹妹穿的比我還像新娘疲陕。我一直安慰自己,他們只是感情好钉赁,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布蹄殃。 她就那樣靜靜地躺著,像睡著了一般你踩。 火紅的嫁衣襯著肌膚如雪诅岩。 梳的紋絲不亂的頭發(fā)上讳苦,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音按厘,去河邊找鬼医吊。 笑死钱慢,一個胖子當著我的面吹牛逮京,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播束莫,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼懒棉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了览绿?” 一聲冷哼從身側(cè)響起策严,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎饿敲,沒想到半個月后妻导,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡怀各,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年倔韭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓢对。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡寿酌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出硕蛹,到底是詐尸還是另有隱情醇疼,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布法焰,位于F島的核電站秧荆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏埃仪。R本人自食惡果不足惜乙濒,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贵试。 院中可真熱鬧琉兜,春花似錦、人聲如沸毙玻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桑滩。三九已至梧疲,卻和暖如春允睹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背幌氮。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工缭受, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人该互。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓米者,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宇智。 傳聞我的和親對象是個殘疾皇子蔓搞,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

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