Android - Fragment

Fragment

  1. Fragment,碎片,是 Android 3.0 開始引入的,其目的在于同時兼容手機和平板的開發,能讓程序更合理地利用大屏幕的空間;
  2. Fragment 是可以嵌入到 Activity 中的 UI 片段,也必須嵌入到 Activity 中,它可以包含布局,也有自己的生命周期(其生命周期受到其宿主 Activity 的影響),是一個輕量型的 Activity;
  3. 一個 Activity 中可以包含多個 Fragment,一個 Fragment 也可以在多個 Activity 中復用;
  4. 下圖取自官方文檔,很好地解釋了 Fragment 的應用:


Fragment 的使用

Fragment 有兩種使用方式,一種是是靜態添加到 Activity 中,另一種是動態添加 Activity 中。

靜態添加

  1. 首先在一個 Activity 中添加兩個 Fragment,這兩個 Fragment 是水平分布,首先是左側的 Fragment 布局文件,fragment_left.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:background="#0000ff"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="左側Fragment按鈕" />

</LinearLayout>

我們將左側的 Fragment 背景顏色設為藍色,在里面放了一個 Button,并讓它水平居中。

  1. 然后是右側的 Fragment 的布局文件,fragment_right.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:background="#FF0000"
    android:orientation="vertical" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="右側Fragment"
        android:textSize="36sp" />

</LinearLayout>

我們將右側的 Fragment 背景顏色設為紅色,在里面放了一個 TextView,并讓它水平居中。

  1. 左側的 XML 布局文件對應的 Java 代碼,LeftFragment.java
public class LeftFragment extends Fragment {

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

}
  1. 右側的 XML 布局文件對應的 Java 代碼,RightFragment.java
public class RightFragment extends Fragment {

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

上面分別新建了兩個類,并讓它們繼承自 Fragment 類,代表左右側 Fragment,并將各自對應的布局傳進去。

  1. 接下來是 activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<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">

    <fragment
        android:id="@+id/fragment_left"
        android:name="net.monkeychan.fragmenttest.LeftFragment"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />

    <fragment
        android:id="@+id/fragment_right"
        android:name="net.monkeychan.fragmenttest.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

</LinearLayout>

在 Activity 的布局中,我們定義了兩個 fragment 控件,并讓它們水平排列,其中右側的 Fragment 充滿了剩下的空間。
注意: fragment 控件中有個屬性 android:name,其值為 包名.類名,包名不能省略。

  1. MainActivity 的代碼,默認即可,MainActivity.java:
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

}
  1. 效果演示:

總結:靜態添加 Fragment 的步驟

  1. 新建 Fragment 的布局;
  2. 新建一個繼承自 Fragment 類 的類,并將對應的布局傳進去;
  3. 在 Activity 布局中定義 fragment 控件,將 fragment 控件的 *android:name * 屬性的值設為 包名.類名

動態添加

上面的程序中我們定義了一個按鈕,但沒有給它添加點擊事件,現在我們把給它增加一個功能,使按鈕被按下時,把右側的 Fragment 更換成另一個 Fragment。

  1. 新建 fragment_right_2.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:background="#00FF00"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="右側第二個Fragment"
        android:textSize="36sp" />

</LinearLayout>

上面只是在原來 fragment_right.xml 上修改了背景顏色和 TextView 的顏色。

  1. SecondRightFragment.java:
public class SecondRightFragment extends Fragment {

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

}
  1. activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<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">

    <fragment
        android:id="@+id/fragment_left"
        android:name="net.monkeychan.fragmenttest.LeftFragment"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />

    <FrameLayout
        android:id="@+id/layout_right"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1">

        <fragment
            android:id="@+id/fragment_right"
            android:name="net.monkeychan.fragmenttest.RightFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </FrameLayout>
    
</LinearLayout>

activity_main.xml 中我們添加了在 LinearLayout 中嵌套了一個 FrameLayout 布局,并讓它與左側的 fragment 控件水平排列,后面我們將在代碼中用另一個 Fragment 來取代 FrameLayout 里的內容,FrameLayout 在這里起占位作用,FrameLayout 也可以換成其他布局。

  1. MainActivity.java:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(this);

    }

    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn:
                replaceFragment();
                break;
            default:
                break;
        }
    }

    private void replaceFragment() {

        // 1. 創建需要添加的 Fragment 的對象
        SecondRightFragment fragment = new SecondRightFragment();

        // 2. 獲取 FragmentManager,在  Activity 中直接調用 getFragmentManager() 方法獲取
        FragmentManager fm = getFragmentManager();

        // 3. 開啟一個事務,通過調用 beginTransaction() 方法開啟
        FragmentTransaction transaction = fm.beginTransaction();

        // 4. 向布局中添加 Fragment,調用 replace() 方法實現,需要傳入布局的 id 和待添加的 Fragment 的對象
        transaction.replace(R.id.layout_right, fragment);

        // 5. 提交事務,調用 commit() 方法來完成
        transaction.commit();

    }
    
}
  1. 效果演示:

點擊按鈕之后,變成下面的界面:

此時點擊返回鍵,會發現直接退出程序。如果想要點擊返回鍵不退出程序,而是返回上一個 Fragment 時,可以在調用 FragmentTransaction 的 commit() 方法之前調用 addToBackStack() 方法,并給它傳入 null,之后按下返回鍵將不會直接退出程序,而是返回上一個 Fragment。

總結:動態添加 Fragment 的步驟

  1. 創建需要添加的 Fragment 的對象;
  2. 獲取 FragmentManager,在 Activity 中直接調用 getFragmentManager() 方法獲取;
  3. 開啟一個事務,通過調用 FragmentManager 對象的 beginTransaction() 方法開啟;
  4. 向布局中添加 Fragment,調用 FragmentTransaction 的 replace() 方法實現,需要傳入布局的 id 和待添加的 Fragment 的對象;
  5. 提交事務,調用 FragmentTransaction 的 commit() 方法來完成。

Fragment 的生命周期

  1. 先通過一張圖片來了解 Fragment 的生命周期,以下圖片取自《第一行代碼》:


  • onAttach(): 當 Fragment 和 Activity 第一次建立聯系時調用,之后不再調用;
  • onCreate(): onAttach() 方法之后立刻調用;
  • onCreateView(): 加載布局時調用;
  • onActivityCreated(): 與 Fragment 關聯的 Activity 創建之后調用;
  • onStart(): Fragment 可見時調用;
  • onResume(): onStart() 執行之后調用;
  • onPause(): 當另一個 Activity 在前臺并獲得焦點,但該 Activity 并沒有占滿整個屏幕,而 Fragment 的 宿主 Activity (即Fragment 所在的 Activity) 仍然可見時調用;
  • onStop(): Fragment 不可見時調用:當 Fragment 的宿主 Activity 處于 stopped 狀態時調用;當 Fragment 從宿主 Activity 中刪除 (調用 remove() 方法) 但沒有被添加到返回棧 (沒有調用 addToBackStack() 方法) 之中時調用。(處于 stopped 狀態的 Fragment 依然存活,它的所有狀態和信息會被系統保留起來,只是對于用戶而言是不可見的,當 宿主 Activity 被殺死時,Fragment 相應地也會被殺死);
  • onDestroyView(): 當與 Fragment 關聯的 View 被被移除時調用;
  • onDestroy(): 當 Fragment 不再被使用時調用,如按返回鍵時;
  • onDeatach(): 當 Fragment 與 Activity 解除關聯時調用。
    詳細內容請看這篇文章 Fragment 整個生命周期演示
  1. 接下來修改上面的 RightFragment.java 代碼,重寫圖片中的方法,重寫的規則是在每個方法里面增加一條 Log.d 語句,這樣我們就能在 logcat 里面看到每個方法在什么時候被調用:
public class RightFragment extends Fragment {

    private static final String TAG = "Lifecycle";

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.d(TAG,"onAttach");
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"onCreate");
    }

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

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.d(TAG,"onActivityCreated");
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG,"onStart");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG,"onResume");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG,"onPause");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.d(TAG,"onStop");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d(TAG,"onDestroyView");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"onDestroy");
    }

    @Override
    public void onDetach() {
        super.onDetach();
        Log.d(TAG,"onDestroy");
    }

}

3.?好了,接下來運行程序,并打開 logcat 窗口,看看發生了什么:

可以看到,在程序啟動時調用了 onCreate() 方法,接著在加載布局的時候調用了 onCreateView() 方法,緊接著又分別調用了 onActivityCeated() 、onStart() 、onResume() 這三個方法。哎?等等,好像有點不對,怎么一開始不是調用 onAttach() 方法?回到我們上面的代碼,我們在重寫 onAttach() 方法時,傳入的參數為類型為 Context,所以在一開始是不會調用的,將傳入的參數類型修改為 Activity 就可以了。另外,在 SDK API 23 的版本中,當我們傳入的參數類型為 Activity 時會提示該方法已過時。

  1. 將 onAttach() 方法傳入的參數類型改為 Activity,再次運行程序,此時執行的方法如下:

當我們點擊左側 Fragment 中的按鈕時,將會動態替換掉 RightFragment,此時執行的方法如下:

  1. 讓我們重新運行程序,此時執行的方法如下:

此時按下主頁鍵,執行的方法如下:

再按下多任務鍵,回到程序,此時執行的方法如下:

  1. 再重新運行程序,然后按下多任務鍵,此時執行的方法如下:

再回到程序,此時執行的方法如下:

  1. 再重新運行程序,然后按下返回鍵,此時執行的方法如下:

通過上面的一系列操作,我們對 Fragment 的生命周期已經有了一些了解了。

Fragment 與 Activity 之間的通信

  1. 在 Activity 里調用 Fragment 里的方法:
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

在 Activity 中調用 FragmentManager 對象的 findFragmentById() 方法,就能獲得與 Activity 關聯的 Fragment 的實例,進而通過該實例調用 Fragment 里的方法。

  1. 在 Fragment 里調用 Activity 里的方法:
MainActivity activity = (MainActivity) getActivity();

在 Fragment 中通過 getActivity() 方法,就能獲得與 Fragment 關聯的 Activity 的實例,進而通過該實例調用 Activity 里的方法。

  1. 下面來看一個實例:

左邊是一個 Activity 里的 ListView,右邊是一個 Fragment,當點擊左邊 ListView 的某一個子項時,在右邊顯示該子項的名字及大圖。

  1. 首先是 Activity 的布局,activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/list_view"
        android:layout_width="150dp"
        android:layout_height="match_parent"
        android:layout_marginTop="16dp" />

    <!-- FrameLayout 在此起占位作用,后面將用 Fragment 替代它里面的內容 -->
    <FrameLayout
        android:id="@+id/frame_layout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

</LinearLayout>
  1. 接下來是右邊 Fragment 的布局,fragment_large.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:gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_large"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:textColor="#FF7F00"
        android:textSize="24sp" />

    <ImageView
        android:id="@+id/iv_large"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:scaleType="centerCrop" />

</LinearLayout>
  1. 樣式文件,控制 ListView 的外觀,list_view_item.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="horizontal">

    <ImageView
        android:id="@+id/iv_small"
        android:layout_width="60dp"
        android:layout_height="60dp" />

    <TextView
        android:id="@+id/tv_small"
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:layout_marginLeft="10dp"
        android:gravity="center" />

</LinearLayout>
  1. 定義一個接口,接口里面定義了一個 send() 方法,此方法用于從 Activity 向 Fragment 發送數據,即當 Activity 里的 ListView 的某一子項被點擊時,將該子項的名字與圖片 id 傳給右邊的 Fragment,右邊的 Fragment 接收到數據之后,將數據在自己的布局中顯示出來。
    FragmentCallbackListener.java:
public interface FragmentCallbackListener {
    void send(String name, int imageId);
}
  1. MainActivity.java:
public class MainActivity extends AppCompatActivity {

    // 定義一個 int 型的數組,用于存放圖片資源的 id
    private int[] imageId = {R.drawable.aries, R.drawable.taurus, R.drawable.gemini, R.drawable.cancer,
            R.drawable.leo, R.drawable.virgo, R.drawable.libra, R.drawable.scorpio, R.drawable.sagittarius,
            R.drawable.capricorn, R.drawable.aquarius, R.drawable.pisces, R.drawable.fuck_off};

    // 定義一個 String 類型的數組,用于存放名稱
    private String[] titles = {"白羊座", "金牛座", "雙子座", "巨蟹座", "獅子座", "處女座",
            "天秤座", "天蝎座", "射手座", "摩羯座", "水瓶座", "雙魚座", "Surprise"};

    // 定義一個 FragmentCallbackListener 接口的引用
    private FragmentCallbackListener listener;

    public FragmentCallbackListener getListener() {
        return listener;
    }

    public void setListener(FragmentCallbackListener listener) {
        this.listener = listener;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        List<Map<String, Object>> listItems = new ArrayList<>();
        for (int i = 0; i < titles.length; i++) {
            Map<String, Object> listItem = new HashMap<>();
            listItem.put("imageId", imageId[i]);
            listItem.put("titles", titles[i]);
            listItems.add(listItem);
        }

        // 1. 實例化布局中的控件
        ListView listView = (ListView) findViewById(R.id.list_view);

        // 2. 創建一個 Adapter 對象,并進行設置
        SimpleAdapter adapter = new SimpleAdapter(this, listItems, R.layout.list_view_item,
                new String[]{"imageId", "titles"}, new int[]{R.id.iv_small, R.id.tv_small});

        // 3. 將 Adapter 設置到 AdapterView
        listView.setAdapter(adapter);

        // 4. 設置 AdapterView 的監聽事件
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                // 調用 FragmentCallbackListener 接口的 send() 方法,
                // 把用戶當前點擊的子項的名稱和圖片 id 傳給實現 FragmentCallbackListener 接口的地方
                listener.send(titles[i], imageId[i]);
            }
        });

        replaceFragment();
    }

    // 此方法用于動態添加 Fragment
    private void replaceFragment() {

        // 1. 創建一個待添加的 Fragment 的對象
        LargeViewFragment fragment = new LargeViewFragment();

        // 2. 獲取 FragmentManager,通過 getFragmentManager() 方法獲取
        FragmentManager fm = getFragmentManager();

        // 3. 開啟一個事務,通過 FragmentManager 對象的 beginTransaction() 方法開啟
        FragmentTransaction transaction = fm.beginTransaction();

        // 4. 向布局中添加 Fragment
        transaction.replace(R.id.frame_layout, fragment);

        // 5. 提交事務
        transaction.commit();
    }

}
  1. LargeViewFragment.java:
public class LargeViewFragment extends Fragment {

    private TextView tv_large;
    private ImageView iv_large;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        
        // 加載 Fragment 的布局
        View view = inflater.inflate(R.layout.fragment_large, container, false);
        
        // 實例化布局中的控件
        tv_large = (TextView) view.findViewById(R.id.tv_large);
        iv_large = (ImageView) view.findViewById(R.id.iv_large);

        // 獲取宿主 Activity 的實例
        MainActivity activity = (MainActivity) getActivity();

        // 調用 Activity 中的 setListener 方法,并實現 FragmentCallbackListener 接口
        activity.setListener(new FragmentCallbackListener() {
            @Override
            public void send(String name, int imageId) {
                // 更新 UI
                tv_large.setText(name);
                iv_large.setImageResource(imageId);
            }
        });
        return view;
    }
    
}

注意: 上面的代碼中使用了接口回調,在 MainActivity 中并不實現該接口,而是在 Fragment 中實現該接口;也就是說,發送數據的一方不實現接口,接收數據的一方負責實現接口。

  1. 演示效果:

Fragment 與 Fragment 之間的通信

在一個 Fragment 中可以得到與它關聯的 Activity,然后再通過這個 Activity 去獲取另外一個 Fragment 的實例,這樣也就實現了不同碎片之間的通信功能。——《第一行代碼》- 郭霖


參考資料:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容