Android——ListView與數(shù)據(jù)庫(kù)的結(jié)合

前言

ListView與數(shù)據(jù)庫(kù)結(jié)合使用也是一種非常常見(jiàn)的手段氧急。例如歌單的ListView,在我們重新啟動(dòng)應(yīng)用的時(shí)候數(shù)據(jù)依舊存在,如下圖:

具體實(shí)現(xiàn)

一麻掸、準(zhǔn)備Activity和ListView

  • Activity要繼承ListActivity或AppCompatActivity(推薦后者)
  • ListView不需要先準(zhǔn)備適配器Adapter,只需要初始化好ListView就行了

二、創(chuàng)建數(shù)據(jù)庫(kù)

  • 新建一個(gè)類MySQLite繼承自SQLiteOpenHelper,然后去實(shí)現(xiàn)未實(shí)現(xiàn)的3個(gè)方法屯碴,分別是:
    1、構(gòu)造方法
MySQLite(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        //this.mContext = context;    //自定義變量mContext 
}

其中形式參數(shù)的意義分別是:

  • context:上下文膊存,即當(dāng)前Activity的對(duì)象 this
  • name:即將創(chuàng)建的數(shù)據(jù)庫(kù)的表的名字导而,例如:"user.db"、"music_msg.db"
  • factory:游標(biāo)隔崎,一般都是 null
  • version:數(shù)據(jù)庫(kù)版本號(hào)今艺,用于更新數(shù)據(jù)庫(kù)版本, 例如:1

所以也可以寫成

MySQLite(Context context) {
      super(context, "music_msg.db", null, 1);
      this.mContext = context;
}

2爵卒、onCreate方法洼滚,用于創(chuàng)建數(shù)據(jù)庫(kù)的表

@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
      sqLiteDatabase.execSQL("create table music_msg(_id integer primary key autoincrement, singer varchar, songname varchar, url varchar)");
}

通過(guò)execSQL()方法執(zhí)行SQL語(yǔ)句創(chuàng)建數(shù)據(jù)庫(kù)的表
如果你不懂?dāng)?shù)據(jù)庫(kù),那你只需要改變下圖框出的部分技潘,第一個(gè)是表名,第二個(gè)是變量名和數(shù)據(jù)類型


3千康、onUpgrade方法享幽,用于升級(jí)軟件時(shí)更新數(shù)據(jù)庫(kù)表結(jié)構(gòu),此方法在數(shù)據(jù)庫(kù)的版本發(fā)生變化時(shí)會(huì)被調(diào)用

@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
    Toast.makeText(mContext, "onUpgrade", Toast.LENGTH_SHORT).show();
}

該方法這里不做演示分析拾弃,只是彈個(gè)Toast值桩,想具體了解的可自行谷歌百度

三、關(guān)聯(lián)ListView與數(shù)據(jù)庫(kù)

  • 關(guān)聯(lián)的關(guān)鍵就是ListView的適配器SimpleCursorAdapter豪椿,構(gòu)造方法與SimpleAdapter有些類似
SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags)

其中形式參數(shù)的意義分別是:

  • context:上下文奔坟,即當(dāng)前Activity的對(duì)象 this

  • layout:ListView對(duì)應(yīng)的布局文件

  • c:游標(biāo)對(duì)象携栋,此處可以設(shè)為 null

  • from:想從數(shù)據(jù)庫(kù)表中拿的數(shù)據(jù)的列名,放在new出來(lái)的String數(shù)組中

  • to:將 layout 里面的控件的 id 寫進(jìn)new出來(lái)的 int 數(shù)組中咳秉,分別對(duì)應(yīng) from 里的數(shù)據(jù)

  • flags:標(biāo)志用于確定適配器的行為婉支,用常量CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER

  • 代碼如下:

SimpleCursorAdapter mSimpleCursorAdapter = new SimpleCursorAdapter(MyListviewActivity.this, R.layout.listview_sql_item, null,
                new String[]{"songname", "singer"}, new int[]{R.id.songname, R.id.singer}, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);

  mListView.setAdapter(mSimpleCursorAdapter);     //給ListView設(shè)置適配器
  refreshListview();      //自定義的方法,用于當(dāng)數(shù)據(jù)列表改變時(shí)刷新ListView

四澜建、刷新ListView(很關(guān)鍵)

  • 當(dāng)我們對(duì)數(shù)據(jù)進(jìn)行增刪改的時(shí)候向挖,就需要將ListView刷新顯示
  • 自定義一個(gè)方法refreshListview(),里面關(guān)鍵的方法是適配器對(duì)象mSimpleCursorAdapter的changeCursor(Cursor cursor)方法炕舵,因?yàn)樾枰獋鬟f一個(gè)參數(shù)cursor何之,所以我們需要得到這個(gè)Cursor對(duì)象,通過(guò)數(shù)據(jù)庫(kù)對(duì)象的查詢方法query就能返回一個(gè)Cursor對(duì)象咽筋,query方法如下圖:


    這里我們暫時(shí)只需要設(shè)置table參數(shù)溶推,即數(shù)據(jù)庫(kù)的表名,先不管另外6個(gè)參數(shù)是什么(后面會(huì)再詳細(xì)說(shuō)明)奸攻,此處全部設(shè)置為null就行了蒜危,即:

Cursor mCursor = mDbWriter.query("music_msg", null, null, null, null, null, null);

上面說(shuō)到要用數(shù)據(jù)庫(kù)的對(duì)象去調(diào)用query方法,所以我們就得先拿到數(shù)據(jù)庫(kù)的對(duì)象舞箍。數(shù)據(jù)庫(kù)對(duì)象的獲取有兩個(gè)方法——getWritableDatabase() 和 getReadableDatabase()

  • SQLiteDatabase getWritableDatabase() :以讀寫的方式打開(kāi)數(shù)據(jù)庫(kù)對(duì)應(yīng)的SQLiteDatabase對(duì)象舰褪,如果打開(kāi)的數(shù)據(jù)庫(kù)磁盤滿了,此時(shí)只能讀不能寫疏橄,此時(shí)調(diào)用了 getWritableDatabase 的實(shí)例占拍,那么將會(huì)發(fā)生錯(cuò)誤(異常)
  • SQLiteDatabase getReadableDatabase():以讀寫的方式打開(kāi)數(shù)據(jù)庫(kù)對(duì)應(yīng)的SQLiteDatabase對(duì)象,如果數(shù)據(jù)庫(kù)的磁盤空間滿了捎迫,就會(huì)打開(kāi)失敗晃酒,當(dāng)打開(kāi)失敗后會(huì)繼續(xù)嘗試以只讀方式打開(kāi)數(shù)據(jù)庫(kù)。如果該問(wèn)題成功解決窄绒,則只讀數(shù)據(jù)庫(kù)對(duì)象就會(huì)關(guān)閉贝次,然后返回一個(gè)可讀寫的數(shù)據(jù)庫(kù)對(duì)象

要調(diào)用這兩個(gè)方法,先再Activity的onCreate()方法里實(shí)例化之前自定義的MySQLite類彰导,代碼如下:

MySQLite mMySQLite = new MySQLite(this);
SQLiteDatabase mDbWriter = mMySQLite.getWritableDatabase();
SQLiteDatabase mDbReader = mMySQLite.getReadableDatabase();
  • 接著就可以寫出refreshListview()方法了蛔翅,代碼如下:
//刷新數(shù)據(jù)列表
public void refreshListview() {
      Cursor mCursor = mDbWriter.query("music_msg", null, null, null, null, null, null);
      mSimpleCursorAdapter.changeCursor(mCursor);
}

五、增

  • 實(shí)現(xiàn)往數(shù)據(jù)庫(kù)中增加一條數(shù)據(jù)并在ListView中顯示
  • 先簡(jiǎn)單寫一個(gè)UI界面:在有ListView的Activity的底部增加兩個(gè)文本輸入框和一個(gè)按鈕位谋,如圖:



    這里的ListView沒(méi)有數(shù)據(jù)所以看不見(jiàn)山析,兩個(gè)EditText的對(duì)象分別為 mEt_songName 和 mEt_singer ,Button的對(duì)象為 btn_insert掏父,這三個(gè)對(duì)象后面要用到

  • 數(shù)據(jù)的存儲(chǔ)是通過(guò)ContentValues 類實(shí)現(xiàn)的笋轨,ContentValues 類跟Hashtable 類很相似,也是通過(guò)put(key, value) 方法暫存的,其中鍵值對(duì)的鍵key只能是String類型爵政,值value只能是基本類型仅讽。于是我們可以new出ContentValues 類的對(duì)象,然后將EditText上的數(shù)據(jù)作為值value存起來(lái)钾挟,再通過(guò)數(shù)據(jù)庫(kù)對(duì)象的 insert() 方法將數(shù)據(jù)插入數(shù)據(jù)庫(kù)表中洁灵,最后調(diào)用refreshListview()方法刷新數(shù)據(jù)列表,將數(shù)據(jù)在ListView中顯示出來(lái)等龙,代碼如下:
//增
 public void insertData() {
       ContentValues mContentValues = new ContentValues();
       mContentValues.put("songname", mEt_songName.getText().toString().trim());
       mContentValues.put("singer", mEt_singer.getText().toString().trim());
       mDbWriter.insert("music_msg", null, mContentValues);
       refreshListview();
 }

其中的insert()方法的3個(gè)參數(shù)


  • table:想插入的數(shù)據(jù)庫(kù)的表名

  • nullColunmHack:代表強(qiáng)行插入null值的數(shù)據(jù)列的列名处渣,這里設(shè)置為 null 就行

  • values:存放了數(shù)據(jù)的ContentValues 對(duì)象

  • 這里將其封裝成一個(gè)insertData()方法,然后在添加按鈕mBtn_insert的點(diǎn)擊事件中調(diào)用蛛砰,代碼如下:

mBtn_insert.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           insertData();
           mEt_songName.setText("");    //數(shù)據(jù)添加完成后罐栈,把文本輸入框清空,方便下次輸入
           mEt_singer.setText("");
       }
});
  • 效果如圖:


六泥畅、刪

  • 實(shí)現(xiàn)刪除ListView的一條數(shù)據(jù)并將數(shù)據(jù)庫(kù)對(duì)應(yīng)的數(shù)據(jù)也刪除
  • 這里我們實(shí)現(xiàn)長(zhǎng)按ListView的某一項(xiàng)item荠诬,然后彈出一個(gè)對(duì)話框,確認(rèn)是否刪除位仁,確定的話就刪除該數(shù)據(jù)柑贞,取消的話就不做任何處理
  • 要想刪除數(shù)據(jù)庫(kù)的某一條數(shù)據(jù),我們就得先知道是ListView的哪一項(xiàng)聂抢,所以我們先寫ListView的長(zhǎng)按事件監(jiān)聽(tīng)钧嘶,并且添加一個(gè)AlertDialog,代碼如圖:


    可以注意到上面有3個(gè)紅色的框框琳疏,第一個(gè)框框是 final關(guān)鍵字有决,因?yàn)槠湫揎椀膒osition變量在匿名內(nèi)部類中需要被使用到,所以需要在前面添加 final 關(guān)鍵字空盼。第二個(gè)框框是一個(gè)自定義的方法书幕,其作用是刪除LIstView的第 position 項(xiàng)數(shù)據(jù),后面會(huì)具體介紹揽趾。第三個(gè)框框是布爾變量台汇,返回true代表響應(yīng)該次監(jiān)聽(tīng)事件

  • 從長(zhǎng)按監(jiān)聽(tīng)中我們可以知道 position 就是我們要?jiǎng)h除項(xiàng)的位置,所以我們現(xiàn)在可以開(kāi)始寫刪除功能篱瞎。我們自定義一個(gè) deleteData(int positon) 方法苟呐,參數(shù)就是剛才那個(gè) position 。思路是這樣的俐筋,拿到數(shù)據(jù)庫(kù)表的游標(biāo)掠抬,讓它移動(dòng)到 position 的位置,然后拿到對(duì)應(yīng)的 _id 列的 id 校哎,最后用數(shù)據(jù)庫(kù)對(duì)象的 delete方法刪除該id的這一行數(shù)據(jù),代碼如下:
//刪
public void deleteData(int positon) {
        Cursor mCursor = mSimpleCursorAdapter.getCursor();
        mCursor.moveToPosition(positon);
        int itemId = mCursor.getInt(mCursor.getColumnIndex("_id"));
        mDbWriter.delete("music_msg", "_id=?", new String[]{itemId + ""});
        refreshListview();
}

這里主要說(shuō)一下數(shù)據(jù)庫(kù)對(duì)象的 delete方法的3個(gè)參數(shù)


  • table:數(shù)據(jù)庫(kù)的表名
  • whereClause:代表要?jiǎng)h除哪一列上的哪一行,這里出于安全性考慮闷哆,只告訴了“_id”列腰奋,然后用“=?”代替具體哪一行
  • whereArgs:該參數(shù)給出具體哪一行
  • 效果如圖:


七、改

  • 學(xué)會(huì)了增和刪抱怔,其實(shí)就會(huì)了改劣坊,所謂的改就是移動(dòng)游標(biāo)到需要更改數(shù)據(jù)的位置,然后用ContentValues 存新的數(shù)據(jù)屈留,最后用update方法進(jìn)行數(shù)據(jù)替換更改局冰,這里希望讀者自己試著靠自己去理解,代碼如下:
//改
public void updateData(int positon) {
        Cursor mCursor = mSimpleCursorAdapter.getCursor();
        mCursor.moveToPosition(positon);
        int itemId = mCursor.getInt(mCursor.getColumnIndex("_id"));
        ContentValues mContentValues = new ContentValues();
        mContentValues.put("songname", mEt_dialog_songName.getText().toString().trim());
        mContentValues.put("singer", mEt_dialog_singer.getText().toString().trim());
        mDbWriter.update("music_msg", mContentValues, "_id=?", new String[]{itemId + ""});
        refreshListview();
}
  • 具體我實(shí)現(xiàn)單擊ListView的某一項(xiàng)灌危,然后彈出一個(gè)AlertDialog進(jìn)行更改康二,代碼如下:
//單擊修改item
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, final View view, final int position, long l) {
                View mView = View.inflate(MyListviewActivity.this, R.layout.dialog_et_layout, null);       //將放置了兩個(gè)EditText的布局dialog_et_layout渲染成view對(duì)象
                mEt_dialog_songName = (EditText) mView.findViewById(R.id.et_dialog_songname);       //要用對(duì)應(yīng)布局的view對(duì)象去findViewById獲取控件對(duì)象
                mEt_dialog_singer = (EditText) mView.findViewById(R.id.et_dialog_singer);
                mEt_dialog_songName.setText(((TextView) view.findViewById(R.id.songname)).getText());   //獲取并顯示原來(lái)的歌曲
                mEt_dialog_singer.setText(((TextView) view.findViewById(R.id.singer)).getText());       //獲取并顯示原來(lái)的歌手
                new AlertDialog.Builder(MyListviewActivity.this)
                        .setTitle("提示")
                        .setMessage("是否修改數(shù)據(jù)")
                        .setView(mView)
                        .setPositiveButton("確定", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                updateData(position);
                            }
                        })
                        .setNegativeButton("取消", null)
                        .show();
        }
});
  • 效果如圖:


八、查

  • 實(shí)現(xiàn)輸入歌曲或歌手進(jìn)行查詢
  • 首先我們需要一個(gè)輸入框和一個(gè)查詢按鈕勇蝙,對(duì)象分別為 mEt_input 和 btn_query沫勿,像這樣的就行


  • 跟增刪改一樣,我們還是自定義一個(gè)方法 queryData()味混,然后首先獲取輸入框 mEt_input 的值产雹,代碼如下:
String mInput = mEt_input.getText().toString().trim();
  • 我們的思路是這樣的:由于不知道輸入進(jìn)來(lái)的 mInput 是歌曲還是歌手,我們需要進(jìn)行判斷翁锡,如果是歌曲蔓挖,那么就去數(shù)據(jù)庫(kù)中找該歌曲名字對(duì)應(yīng)的所有歌手,放在List<Map>里馆衔,最后用 AlertDialog 進(jìn)行對(duì)話框列表顯示瘟判;如果是歌手,也是用同樣的方法哈踱。
  • 那么如何判斷呢荒适?還記得我們?cè)俳榻B第四部分“刷新ListView”的時(shí)候用到的數(shù)據(jù)庫(kù)對(duì)象的 query() 方法嗎?當(dāng)時(shí)沒(méi)作具體參數(shù)分析开镣,現(xiàn)在就來(lái)介紹一下
  • table:代表想要查詢的數(shù)據(jù)庫(kù)表名
  • columns:代表想要查詢的結(jié)果的列(相當(dāng)于select語(yǔ)句的select關(guān)鍵字后面的部分)
  • selection:代表想要通過(guò)這些列作為查詢的條件(相當(dāng)于select語(yǔ)句的where關(guān)鍵字后面的部分刀诬,允許使用占位符“?”)
  • selectionArgs:與上面的 selection 參數(shù)有關(guān)邪财,用于為 selection 子句中的占位符傳入?yún)?shù)值陕壹,代表是這些列的具體的數(shù)據(jù),也就知道是哪些行了
  • groupBy:用于控制分組(相當(dāng)于select語(yǔ)句的group by關(guān)鍵字后面的部分)
  • having:用于對(duì)分組進(jìn)行過(guò)濾(相當(dāng)于select語(yǔ)句的having關(guān)鍵字后面的部分)
  • orderBy:用于對(duì)記錄進(jìn)行排序(相當(dāng)于select語(yǔ)句的order by關(guān)鍵字后面的部分)
  • 正如上面所說(shuō)的树埠,因?yàn)椴恢垒斎氲氖歉枨€是歌手糠馆,所以要查詢兩次
 Cursor mCursor1 = mDbReader.query("music_msg", new String[]{"singer"}, "songname=?", new String[]{mInput}, null, null, null);
 Cursor mCursor2 = mDbReader.query("music_msg", new String[]{"songname"}, "singer=?", new String[]{mInput}, null, null, null);

若mCursor1.getCount() > 0,表示輸入的是歌曲怎憋,并且有查詢到結(jié)果又碌,這時(shí)定義一個(gè)全局變量List<Map<String, String>> mStringList = new ArrayList<>();然后通過(guò)游標(biāo)的向下滑動(dòng)九昧,把歌曲和歌名都存進(jìn)List里面,最后別忘了關(guān)閉游標(biāo)毕匀。具體看代碼去理解:

 //查
 public void queryData() {
        String mInput = mEt_input.getText().toString().trim();
        //第二個(gè)參數(shù)是你需要查找的列
        //第三和第四個(gè)參數(shù)確定是從哪些行去查找第二個(gè)參數(shù)的列
        Cursor mCursor1 = mDbReader.query("music_msg", new String[]{"singer"}, "songname=?", new String[]{mInput}, null, null, null);
        Cursor mCursor2 = mDbReader.query("music_msg", new String[]{"songname"}, "singer=?", new String[]{mInput}, null, null, null);
        if (mCursor1.getCount() > 0) {
            mStringList.clear();        //清空List
            while (mCursor1.moveToNext()) {     //游標(biāo)總是在查詢到的上一行
                Map<String, String> mMap = new HashMap<>();
                String output_singer = mCursor1.getString(mCursor1.getColumnIndex("singer"));
                mMap.put("tv1", mInput);
                mMap.put("tv2", output_singer);
                mStringList.add(mMap);
            }
            mCursor1.close();
        } else if (mCursor2.getCount() > 0) {
            mStringList.clear();        //清空List
            while (mCursor2.moveToNext()) {     //游標(biāo)總是在查詢到的上一行
                Map<String, String> mMap = new HashMap<>();
                String output_songname = mCursor2.getString(mCursor2.getColumnIndex("songname"));
                mMap.put("tv1", output_songname);
                mMap.put("tv2", mInput);
                mStringList.add(mMap);
            }
            mCursor2.close();
        } else {
            mStringList.clear();        //清空List
            Map<String, String> mMap = new HashMap<>();
            mMap.put("tv1", "未能查詢到結(jié)果");
            mStringList.add(mMap);
        }
}
  • 最后通過(guò)查詢按鈕 btn_query 的點(diǎn)擊監(jiān)聽(tīng)事件去調(diào)用上面的 queryData()方法铸鹰,然后用AlertDialog 進(jìn)行對(duì)話框列表顯示郎仆,代碼如下:
 mBtn_query.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
              queryData();
              mEt_input.setText("");      //清空輸入框
              new AlertDialog.Builder(MyListviewActivity.this)
                      .setTitle("查詢結(jié)果")
                      .setAdapter(new SimpleAdapter(MyListviewActivity.this, mStringList, R.layout.dialog_tv_layout, new String[]{"tv1", "tv2"}, new int[]{R.id.tv1_dialog, R.id.tv2_dialog}), null)
                      .setPositiveButton("確定", null)
                      .show();
        }
});

其中AlertDialog的setAdapter方法跟ListView設(shè)置SimpleAdapter基本一樣喊括,只是最后那個(gè)點(diǎn)擊監(jiān)聽(tīng)設(shè)置為null咬腕。

  • 效果如圖:


后話

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旺坠,一起剝皮案震驚了整個(gè)濱河市堂淡,隨后出現(xiàn)的幾起案子教馆,更是在濱河造成了極大的恐慌逊谋,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件活玲,死亡現(xiàn)場(chǎng)離奇詭異涣狗,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)舒憾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門镀钓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人镀迂,你說(shuō)我怎么就攤上這事丁溅。” “怎么了探遵?”我有些...
    開(kāi)封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵窟赏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我箱季,道長(zhǎng)涯穷,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任藏雏,我火速辦了婚禮拷况,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掘殴。我一直安慰自己赚瘦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布奏寨。 她就那樣靜靜地躺著起意,像睡著了一般。 火紅的嫁衣襯著肌膚如雪病瞳。 梳的紋絲不亂的頭發(fā)上揽咕,一...
    開(kāi)封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天悲酷,我揣著相機(jī)與錄音,去河邊找鬼心褐。 笑死舔涎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的逗爹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嚎于,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼掘而!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起于购,我...
    開(kāi)封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤袍睡,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后肋僧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體斑胜,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年嫌吠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了止潘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辫诅,死狀恐怖凭戴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炕矮,我是刑警寧澤么夫,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站肤视,受9級(jí)特大地震影響档痪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜邢滑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一腐螟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧殊鞭,春花似錦遭垛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至趾盐,卻和暖如春庶喜,著一層夾襖步出監(jiān)牢的瞬間小腊,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工久窟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秩冈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓斥扛,卻偏偏與公主長(zhǎng)得像入问,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子稀颁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

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