[轉(zhuǎn)]自定義LyricView實(shí)現(xiàn)歌詞顯示控件

原文地址 | 源碼地址 | 加入“小知”Tower協(xié)作團(tuán)隊(duì)

注:原文的代碼閱讀和拷貝起來(lái)不太方便阁苞,我已經(jīng)摘錄出來(lái)惭缰。

前言

通過(guò)自定義View便锨,實(shí)現(xiàn)的進(jìn)階版LyricView ,能夠?qū)崿F(xiàn)歌詞滑動(dòng)查看民假,當(dāng)前播放位置高亮顯示可免,滑動(dòng)到指定位置并播放等等抓于,大致和網(wǎng)易云音樂(lè)的歌詞顯示效果一樣。

歌詞文件的組成

[ti:一個(gè)人的北京]
[ar:好妹妹樂(lè)隊(duì)]
[al:南北]
[by:]
[offset:0]
[00:00.10]一個(gè)人的北京 - 好妹妹樂(lè)隊(duì)
[00:00.20]詞:秦昊
[00:00.30]曲:秦昊
[00:00.40]
[00:30.16]你有多久沒(méi)有看到 滿天的繁星
[00:37.34]城市夜晚虛偽的光明 遮住你的眼睛
......
[04:48.87]離開(kāi)了這里 在晴朗的天氣
[04:55.08]
[04:56.27]讓我擁抱你 在晴朗的天氣

歌詞文件(*.lrc)都是以一個(gè)標(biāo)準(zhǔn)來(lái)進(jìn)行制作的浇借。

[ti:  標(biāo)題
[ar:  歌手
[al:  專輯
[by: 制作
[offset: 時(shí)間偏移量
[mm:ss.ms] 歌詞信息:由 開(kāi)始時(shí)間(分:秒.毫秒)和 歌詞內(nèi)容 兩部分組成

解析歌詞文件

首先獲取*.lrc歌詞文件的二進(jìn)制流 InputStream捉撮,再又轉(zhuǎn)換成字符流(注意:轉(zhuǎn)化成字符流的時(shí)候需要選擇編碼,比如QQ音樂(lè)的歌詞文件需要用”GBK”解碼)妇垢。

  • 準(zhǔn)備兩個(gè)類主要用于歌詞解析結(jié)果的緩存:LyricInfo和 LineInfo():

LyricInfo 歌詞信息:包含標(biāo)題巾遭、歌手、專輯等等

class LyricInfo {
    List<LineInfo> song_lines; 
    String song_artist;//歌手
    String song_title;//標(biāo)題
    String song_album;//專輯
    long song_offset;//偏移量
}

LineInfo 歌詞行信息:包含行開(kāi)始時(shí)間和歌詞行內(nèi)容

class LineInfo {
    String content;//歌詞內(nèi)容
    long start;//開(kāi)始時(shí)間
}

解析歌詞文件源碼:

/**
 * 初始化歌詞信息
 * @param inputStream  歌詞文件的流信息
 * */
private void setupLyricResource(InputStream inputStream, String charsetName) {
    if(inputStream != null) {
        try {
            LyricInfo lyricInfo = new LyricInfo();
            lyricInfo.song_lines = new ArrayList<>();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charsetName);
            BufferedReader reader = new BufferedReader(inputStreamReader);
            String line = null;
            while((line = reader.readLine()) != null) {
                analyzeLyric(lyricInfo, line);
            }
            reader.close();
            inputStream.close();
            inputStreamReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    } else {
        // 暫無(wú)歌詞
    }
}

/**
 * 逐行解析歌詞內(nèi)容
 * */
private void analyzeLyric(LyricInfo lyricInfo, String line) {
    int index = line.lastIndexOf("]");
    if(line != null && line.startsWith("[offset:")) {
        // 時(shí)間偏移量
        String string = line.substring(8, index).trim();
        lyricInfo.song_offset = Long.parseLong(string);
        return;
    }
    if(line != null && line.startsWith("[ti:")) {
        // title 標(biāo)題
        String string = line.substring(4, index).trim();
        lyricInfo.song_title = string;
        return;
    }
    if(line != null && line.startsWith("[ar:")) {
        // artist 作者
        String string = line.substring(4, index).trim();
        lyricInfo.song_artist = string;
        return;
    }
    if(line != null && line.startsWith("[al:")) {
        // album 所屬專輯
        String string = line.substring(4, index).trim();
        lyricInfo.song_album = string;
        return;
    }
    if(line != null && line.startsWith("[by:")) {
        return;
    }
    if(line != null && index == 9 && line.trim().length() > 10) {
        // 歌詞內(nèi)容
        LineInfo lineInfo = new LineInfo();
        lineInfo.content = line.substring(10, line.length());
        lineInfo.start = measureStartTimeMillis(line.substring(0, 10));
        lyricInfo.song_lines.add(lineInfo);
    }
}

/**
 * 從字符串中獲得時(shí)間值
 * */
private long measureStartTimeMillis(String str) {
    long minute = Long.parseLong(str.substring(1, 3));
    long second = Long.parseLong(str.substring(4, 6));
    long millisecond = Long.parseLong(str.substring(7, 9));
    return millisecond + second * 1000 + minute * 60 * 1000;
}

驗(yàn)證解析效果

完成歌詞解析闯估,接下來(lái)就是驗(yàn)證歌詞解析的一個(gè)實(shí)際效果的時(shí)候了:

File file = new File(Constant.lyricPath + "一個(gè)人的北京 - 好妹妹樂(lè)隊(duì).lrc");
if (file != null && file.exists()) {
    try {
        setupLyricResource(new FileInputStream(file), "GBK");
        StringBuffer stringBuffer = new StringBuffer();
        if(lyricInfo != null && lyricInfo.song_lines != null) {
            int size = lyricInfo.song_lines.size();
            for (int i = 0; i < size; i ++) {
                stringBuffer.append(lyricInfo.song_lines.get(i).content + "\n");
            }
            text.setText(stringBuffer.toString());
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}

效果圖:


Paste_Image.png

就這樣灼舍,一個(gè)簡(jiǎn)單的歌詞顯示功能也就實(shí)現(xiàn)了。 但是涨薪,如何才能夠讓自己寫(xiě)的音樂(lè)播放器在歌詞顯示模塊能夠顯得高大上骑素,并且能夠像很多當(dāng)前應(yīng)用市場(chǎng)上流行的音樂(lè)播放器那樣,實(shí)現(xiàn)當(dāng)前播放高亮顯示刚夺、歌詞回彈效果献丑、歌詞淡入淡出效果以及滑動(dòng)歌詞快速播放等功能呢? 請(qǐng)接著往下讀…..

上面有提及到在早些前我有用 ScrollView 嵌套 TextView 的方式實(shí)現(xiàn)過(guò)自定義 LyricView侠姑,但是创橄,由于體驗(yàn)效果和功能拓展上的不足,我并沒(méi)有公開(kāi)分享莽红。既然通過(guò) ScrollView 嵌套 TextView 的方式 不能滿足 我們的設(shè)計(jì)需求妥畏,那是不是能夠通過(guò) 自定義View 的方式實(shí)現(xiàn) LyricView?既有如 TextView 那樣的 LineHeigh(行高)安吁、LineCount(總行數(shù)) 的概念醉蚁,也有如 ScrollView 那樣的 ScrollY(Y方向的偏移量) 的概念。那是必須的柳畔,說(shuō)干就干馍管。

LyricView實(shí)現(xiàn)

解析*.lrc歌詞文件,生成歌詞集合列表薪韩,獲得行總數(shù)

上面我已經(jīng)講過(guò)解析歌詞了确沸,而在 LyricView 中,我們需要做的是將逐行解析出來(lái)的歌詞信息添加到 集合mLyricInfo 中俘陷,而 總行數(shù)mLineCount 也就等于 List集合 的大小 mLineCount = mLyricInfo.song_lines.size()罗捎。

計(jì)算歌詞單行高度,獲得歌詞繪制區(qū)域總高度

寫(xiě)過(guò) 自定義View 的朋友應(yīng)該都會(huì)知道拉盾,在 自定義View 中如果涉及文字的繪制桨菜,為了能夠精準(zhǔn)的繪制文字的位置,我們需要獲取需要繪制的文字的矩形區(qū)域捉偏,通過(guò)畫(huà)筆 Paint 的 getTextBounds(String text, int start, int end, Rect bounds)方法 則可以幫助我們輕松獲得一個(gè)需要繪制文字的一個(gè)矩形倒得。

當(dāng)然,繪制文字矩形區(qū)域的大小還與文字的大小相關(guān)夭禽,我們還可以通過(guò)畫(huà)筆 Paint 的 setTextSize(float textSize)方法 設(shè)置繪制文字大小霞掺,也就是常說(shuō)的 TextSize。

在 LyricView 中讹躯,行高可不僅僅就只是文字矩形區(qū)域的高度菩彬,行高還包括兩行之間的行間距,如下圖所示潮梯。既然歌詞總行數(shù)和歌詞單行高度我們都已取得骗灶,那么獲取歌詞繪制區(qū)域的總高度也就so easy了:

/**
 * 計(jì)算行高度
 * */
private void measureLineHeight() {
    Rect lineBound = new Rect();
    mTextPaint.getTextBounds(mDefaultHint, 0, mDefaultHint.length(), lineBound);
    mLineHeight = lineBound.height() + mLineSpace;
}

計(jì)算行高度

定義 scrollY,并通過(guò)當(dāng)前歌曲播放進(jìn)度的時(shí)間戳計(jì)算 scrollY

既然 LyricView 能夠?qū)崿F(xiàn)滑動(dòng)功能秉馏,那么引入 scrollY值 記錄滑動(dòng)偏移量耙旦,并控制視圖繪制效果也就順理成章。 需要明確一點(diǎn)萝究,當(dāng)偏移量 scrollY 的值為零的時(shí)候母廷,歌詞的首行將顯示在整個(gè) LyricView 的正中間 。

我們知道每一句歌詞中都包含著開(kāi)始時(shí)間糊肤,而我們也就可以通過(guò)當(dāng)前歌曲播放進(jìn)度匹配當(dāng)前播放的行數(shù) mCurrentPlayLine琴昆,并通過(guò)當(dāng)前播放所在行,計(jì)算偏移量 scrollY 的值馆揉,控制歌詞播放滾動(dòng)和當(dāng)前播放位置的高亮顯示业舍。

for(int i = 0, size = mLineCount; i < size; i ++) {
    LineInfo lineInfo = mLyricInfo.song_lines.get(i);
    if(lineInfo != null && lineInfo.start > time) {
        position = i;
        break;
    }
    if(i == mLineCount - 1) {
        position = mLineCount;
    }
}

匹配當(dāng)前播放行數(shù) mCurrentPlayLine

/**
 * Input current showing line to measure the view's current scroll Y
 * @param line  當(dāng)前指定行號(hào)
 * */
private float measureCurrentScrollY(int line) {
    return (line - 1) * mLineHeight;
}

計(jì)算偏移量scrollY

理論基礎(chǔ)已經(jīng)實(shí)現(xiàn),初步嘗試?yán)L圖 onDraw:

for(int i = 0, size = mLineCount; i < size; i ++) {
    float x = getMeasuredWidth() * 0.5f;
    float y = getMeasuredHeight() * 0.5f + (i + 0.5f) * mLineHeight - 6 - mLineSpace * 0.5f - mScrollY;
    if(y + mLineHeight * 0.5f < 0) {
        continue;
    }
    if(y - mLineHeight * 0.5f > getMeasuredHeight()) {
        break;
    }
    if(i == mCurrentPlayLine - 1) {
        mTextPaint.setColor(mHighLightColor);
    } else {
        if(mIndicatorShow && i == mCurrentShowLine - 1) {
            mTextPaint.setColor(mCurrentShowColor);
        }else {
            mTextPaint.setColor(mDefaultColor);
        }
    }
    if(y > getMeasuredHeight() - mShaderWidth || y < mShaderWidth) {
        if(y < mShaderWidth) {
            mTextPaint.setAlpha(26 + (int) (23000.0f * y / mShaderWidth * 0.01f));
        } else {
            mTextPaint.setAlpha(26 + (int) (23000.0f * (getMeasuredHeight() - y) / mShaderWidth * 0.01f));
        }
    } else {
        mTextPaint.setAlpha(255);
    }
    canvas.drawText(mLyricInfo.song_lines.get(i).content, x, y, mTextPaint);
}

Bingo ! 歌詞確實(shí)能夠在屏幕上繪制出來(lái)升酣,細(xì)心的朋友也許會(huì)發(fā)現(xiàn)其中的幾個(gè)特別的地方舷暮,分別是當(dāng)前播放位置高亮顯示和歌詞淡入淡出效果實(shí)現(xiàn)的代碼:

//實(shí)現(xiàn)當(dāng)前位置高亮顯示
if(i == mCurrentPlayLine - 1) {
    mTextPaint.setColor(mHighLightColor);
}
//歌詞淡入淡出效果實(shí)現(xiàn)
if(y > getMeasuredHeight() - mShaderWidth || y < mShaderWidth) {
    if(y < mShaderWidth) {
        mTextPaint.setAlpha(26 + (int) (23000.0f * y / mShaderWidth * 0.01f));
    } else {
        mTextPaint.setAlpha(26 + (int) (23000.0f * (getMeasuredHeight() - y) / mShaderWidth * 0.01f));
    }
} else {
    mTextPaint.setAlpha(255);
}

但是,僅僅只是實(shí)現(xiàn)顯示功能噩茄,并且超出范圍的歌詞內(nèi)容還不能通過(guò)滑動(dòng)來(lái)查看下面。哈哈~ 別著急啊,騷年绩聘,坐下來(lái)和我涼茶沥割,聽(tīng)我細(xì)細(xì)道來(lái)耗啦。

重寫(xiě) onTouchEvent,實(shí)現(xiàn)歌詞滑動(dòng)查看机杜,并實(shí)現(xiàn) overScroll 回彈效果

僅僅需要實(shí)現(xiàn)滑動(dòng)視圖的功能的話帜讲,說(shuō)實(shí)話,非常簡(jiǎn)單椒拗,只需要記錄 ACTION_DOWN 時(shí)的 y值似将,并比較 ACTION_MOVE 過(guò)程中的 y值 計(jì)算兩者的差值,生成新的偏移量 scrollY蚀苛,再刷新視圖在验,就可以搞定 !

要是就這么簡(jiǎn)簡(jiǎn)單單了事的話,怎么也不符合我個(gè)人對(duì)完美設(shè)計(jì)的要求堵未。要是我們無(wú)限滑動(dòng)的話腋舌,整個(gè)歌詞內(nèi)容區(qū)域就會(huì)滑動(dòng)出我們的可視區(qū)域,也就是常說(shuō)的 overScroll兴溜,如果不加以限制將會(huì)是一種非常差的用戶體驗(yàn)侦厚。

當(dāng)然,不同的開(kāi)發(fā)對(duì)解決這個(gè)問(wèn)題有不同的方法拙徽,有些播放器的歌詞顯示控件刨沦,當(dāng)滑動(dòng)事件出現(xiàn) overScroll 時(shí),將不再視圖繼續(xù)滑動(dòng)膘怕。當(dāng)然想诅,也有當(dāng)滑動(dòng)事件出現(xiàn) overScroll 時(shí),視圖依舊能夠繼續(xù)滑動(dòng)岛心,但與正忱雌疲滑動(dòng)時(shí)有所區(qū)別,這個(gè)時(shí)候的滑動(dòng)會(huì)有一種 阻尼效果忘古,也就是實(shí)際滑動(dòng)距離和視圖的滾動(dòng)距離并不相等徘禁,而且隨著 overScroll 的值越大,阻力越大髓堪,滑動(dòng)越艱難送朱,并在用戶手指離開(kāi)屏幕后回到 overScrol l的值為零的位置。當(dāng)然干旁,我本人更喜歡后者的用戶體驗(yàn)驶沼,為了實(shí)現(xiàn)這個(gè)功能,那么就必須要在重寫(xiě) onTouchEvent 的方法中”做點(diǎn)手腳”了争群。

/**
 * 手勢(shì)移動(dòng)執(zhí)行事件
 * @param event
 * */
private void actionMove(MotionEvent event) {
    if(scrollable()) {
        final VelocityTracker tracker = mVelocityTracker;
        tracker.computeCurrentVelocity(1000, maximumFlingVelocity);
        float scrollY = mLastScrollY + mDownY - event.getY();   // 102  -2  58  42
        float value01 = scrollY - (mLineCount * mLineHeight * 0.5f);   // 52  -52  8  -8
        float value02 = ((Math.abs(value01) - (mLineCount * mLineHeight * 0.5f)));   // 2  2  -42  -42
        mScrollY = value02 > 0 ? scrollY - (measureDampingDistance(value02) * value01 / Math.abs(value01)) : scrollY;   //   value01 / Math.abs(value01)  控制滑動(dòng)方向
        mVelocity = tracker.getYVelocity();
        measureCurrentLine();
    }
}

ACTION_MOVE

/**
 * 計(jì)算阻尼效果的大小
 * */
private final int mMaxDampingDistance = 360;
private float measureDampingDistance(float value02) {
    return value02 > mMaxDampingDistance ? (mMaxDampingDistance * 0.6f + (value02 - mMaxDampingDistance) * 0.72f) : value02 * 0.6f;
}

阻尼大小計(jì)算

通過(guò)我一次一次對(duì)代碼的細(xì)化回怜,只要這么簡(jiǎn)單的兩個(gè)方法,就完成了滑動(dòng)時(shí)偏移量 scrollY 的計(jì)算换薄,包括 overScroll 和 非overScroll玉雾,是的翔试,只要這么兩個(gè)方法。

到了這一步抹凳,歌詞的顯示遏餐、滑動(dòng)查看都已經(jīng)完成伦腐。但這還沒(méi)完赢底,我是不是還說(shuō)過(guò)我的 LyricView 能夠?qū)崿F(xiàn)像網(wǎng)易云音樂(lè)歌詞顯示控件那樣的指示器效果,哈哈哈 ~ 對(duì)于我這個(gè)完美主義者而言柏蘑,這個(gè)功能必須實(shí)現(xiàn)幸冻。

Paste_Image.png

歌詞指示器效果圖

實(shí)現(xiàn)歌詞指示器效果,”屌絲”蛻變”高富帥”

其實(shí)咳焚,指示器效果實(shí)現(xiàn)起來(lái)也不是很難洽损,其實(shí)指示器左側(cè)的按鈕完全可以用繪制 Bitmap 的方式其實(shí)現(xiàn),但是革半,考慮到 LyricView 的靈活性碑定,同時(shí),我們程序猿不都是能夠用代碼繪制的決不在工程中添加圖片的嘛 又官!更何況就一個(gè)簡(jiǎn)單的播放按鈕延刘,隨便畫(huà)畫(huà),哈哈 ~ 至于六敬,右側(cè)的時(shí)間指示碘赖,則是通過(guò)當(dāng)前偏移量 scrollY 的值計(jì)算得來(lái)的當(dāng)前控件正中間位置顯示歌詞的開(kāi)始時(shí)間。


/**
 * 繪制左側(cè)的播放按鈕
 * @param canvas
 * */
private void drawPlayer(Canvas canvas) {
    mBtnBound = new Rect(mDefaultMargin, (int) (getMeasuredHeight() * 0.5f - mBtnWidth * 0.5f), mBtnWidth + mDefaultMargin, (int) (getMeasuredHeight() * 0.5f + mBtnWidth * 0.5f));

    Path path = new Path();
    float radio = mBtnBound.width() * 0.3f;
    float value = (float) Math.sqrt(Math.pow(radio, 2) - Math.pow(radio * 0.5f, 2));
    path.moveTo(mBtnBound.centerX() - radio * 0.5f, mBtnBound.centerY() - value);
    path.lineTo(mBtnBound.centerX() - radio * 0.5f, mBtnBound.centerY() + value);
    path.lineTo(mBtnBound.centerX() + radio, mBtnBound.centerY());
    path.lineTo(mBtnBound.centerX() - radio * 0.5f, mBtnBound.centerY() - value);
    mBtnPaint.setAlpha(128);
    canvas.drawPath(path, mBtnPaint);  // 繪制播放按鈕的三角形
    canvas.drawCircle(mBtnBound.centerX(), mBtnBound.centerY(), mBtnBound. width() * 0.48f, mBtnPaint);  // 繪制圓環(huán)
}

繪制指示器左側(cè)播放按鈕

/**
 * 繪制指示器
 * @param canvas
 * */
private void drawIndicator(Canvas canvas) {
    mIndicatorPaint.setColor(mIndicatorColor);
    mIndicatorPaint.setAlpha(128);
    mIndicatorPaint.setStyle(Paint.Style.FILL);
    canvas.drawText(measureCurrentTime(), getMeasuredWidth() - mTimerBound.width(), (getMeasuredHeight() + mTimerBound.height() - 6) * 0.5f, mIndicatorPaint);

    Path path = new Path();
    mIndicatorPaint.setStrokeWidth(2.0f);
    mIndicatorPaint.setStyle(Paint.Style.STROKE);
    mIndicatorPaint.setPathEffect(new DashPathEffect(new float[]{20, 10}, 0));
    path.moveTo(mPlayable ? mBtnBound.right + 24 : 24 , getMeasuredHeight() * 0.5f);
    path.lineTo(getMeasuredWidth() - mTimerBound.width() - mTimerBound.width() - 36, getMeasuredHeight() * 0.5f);
    canvas.drawPath(path , mIndicatorPaint);
}

繪制指示器分割線和時(shí)間

既然設(shè)計(jì)播放按鈕外构,當(dāng)然播放按鈕就要實(shí)現(xiàn)點(diǎn)擊事件捌张荨:

/**
 * 判斷當(dāng)前點(diǎn)擊事件是否落在播放按鈕觸摸區(qū)域范圍內(nèi)
 * @param event  觸摸事件
 * */
private boolean clickPlayer(MotionEvent event) {
    if(mBtnBound != null &&  mDownX > (mBtnBound.left - mDefaultMargin) && mDownX < (mBtnBound.right + mDefaultMargin) && mDownY > (mBtnBound.top - mDefaultMargin) && mDownY < (mBtnBound.bottom + mDefaultMargin)) {
        float upX = event.getX();   float upY = event.getY();
        return upX > (mBtnBound.left - mDefaultMargin) && upX < (mBtnBound.right + mDefaultMargin) && upY > (mBtnBound.top - mDefaultMargin) && upY < (mBtnBound.bottom + mDefaultMargin);
    }
    return false;
}

播放按鈕點(diǎn)擊位置判斷

if(mIndicatorShow && clickPlayer(event)) {
    if(mCurrentShowLine != mCurrentPlayLine) {
        mIndicatorShow = false;
        if(mClickListener != null) {
            mClickListener.onPlayerClicked(
                mLyricInfo.song_lines.get(mCurrentShowLine - 1).start,
                mLyricInfo.song_lines.get(mCurrentShowLine - 1).content);
        }
    }
}

到這一步,我們的自定義 LyricView 設(shè)計(jì)介紹也就告一段落咯 审编! 當(dāng)然撼班,功能遠(yuǎn)不止這些,還有 設(shè)置字體大小垒酬、設(shè)置行間距 以及 結(jié)合速度追蹤器實(shí)現(xiàn)滑行效果 等等砰嘁。所謂”授人以魚(yú)不如授人以漁”,我想要和大家分享的是我的一個(gè)設(shè)計(jì)思路伤溉,大家可以根據(jù)需求設(shè)計(jì)不通的功能般码,因此在這里我也不做過(guò)多介紹,對(duì)小阿飛的 LyricView 感興趣的朋友可以去我的gitHub下載研究:
https://github.com/WuLiFei/LyricViewDemo

效果圖

Paste_Image.png

overScroll效果展示

Paste_Image.png

字體顏色設(shè)置效果展示

Paste_Image.png

字體大小設(shè)置效果展示

Paste_Image.png

行間距設(shè)置效果展示

Paste_Image.png

指示器和播放按鈕效果展示

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乱顾,一起剝皮案震驚了整個(gè)濱河市板祝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌走净,老刑警劉巖券时,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孤里,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡橘洞,警方通過(guò)查閱死者的電腦和手機(jī)捌袜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)炸枣,“玉大人虏等,你說(shuō)我怎么就攤上這事∈食Γ” “怎么了霍衫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)侯养。 經(jīng)常有香客問(wèn)我敦跌,道長(zhǎng),這世上最難降的妖魔是什么逛揩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任柠傍,我火速辦了婚禮,結(jié)果婚禮上辩稽,老公的妹妹穿的比我還像新娘惧笛。我一直安慰自己,他們只是感情好搂誉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布徐紧。 她就那樣靜靜地躺著,像睡著了一般炭懊。 火紅的嫁衣襯著肌膚如雪并级。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,785評(píng)論 1 290
  • 那天侮腹,我揣著相機(jī)與錄音嘲碧,去河邊找鬼。 笑死父阻,一個(gè)胖子當(dāng)著我的面吹牛愈涩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播加矛,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼履婉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了斟览?” 一聲冷哼從身側(cè)響起毁腿,我...
    開(kāi)封第一講書(shū)人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后已烤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體鸠窗,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年胯究,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了稍计。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡裕循,死狀恐怖臣嚣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情费韭,我是刑警寧澤茧球,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布庭瑰,位于F島的核電站星持,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏弹灭。R本人自食惡果不足惜督暂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望穷吮。 院中可真熱鬧逻翁,春花似錦、人聲如沸捡鱼。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)驾诈。三九已至缠诅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乍迄,已是汗流浹背管引。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闯两,地道東北人褥伴。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像漾狼,于是被迫代替她去往敵國(guó)和親重慢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

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