Android: Camera相機(jī)開發(fā)詳解(中) ——實(shí)現(xiàn)預(yù)覽竹海、拍照慕蔚、保存照片等功能

android.jpg

前言

  • 在上一篇文章中給小伙伴們介紹了進(jìn)行Camera開發(fā)需要了解的知識(shí)點(diǎn),如果你還沒(méi)有看過(guò)的話斋配,建議先去看上一篇文章《Android: Camera相機(jī)開發(fā)詳解(上) —— 知識(shí)儲(chǔ)備》

  • 本篇文章會(huì)帶著小伙伴們一步一步實(shí)現(xiàn)自己的Camera孔飒,并在實(shí)現(xiàn)的過(guò)程中驗(yàn)證上一篇中所講解的結(jié)論


實(shí)現(xiàn)思路:

  1. 在xml布局中定義一個(gè)SurfaceView,用于預(yù)覽相機(jī)采集的數(shù)據(jù)

  2. 給SurfaceHolder添加回調(diào)艰争,在surfaceCreated(holder: SurfaceHolder?)回調(diào)中打開相機(jī)

  3. 成功打開相機(jī)后坏瞄,設(shè)置相機(jī)參數(shù)。比如:對(duì)焦模式甩卓,預(yù)覽大小鸠匀,照片保存大小等等

  4. 設(shè)置相機(jī)預(yù)覽時(shí)的旋轉(zhuǎn)角度,然后調(diào)用startPreview()開始預(yù)覽

  5. 調(diào)用takePicture方法拍照 或者 是在Camera的預(yù)覽回調(diào)中 保存照片

  6. 對(duì)保存的照片進(jìn)行旋轉(zhuǎn)處理逾柿,使其為"自然方向"

  7. 關(guān)閉頁(yè)面缀棍,釋放相機(jī)資源

具體實(shí)現(xiàn)步驟:

一丶申請(qǐng)權(quán)限

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

二、在xml布局文件中定義一個(gè)SurfaceView

 <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

三机错、創(chuàng)建一個(gè)CameraHelper類

class CameraHelper(activity: Activity, surfaceView: SurfaceView) : Camera.PreviewCallback {
    private var mCamera: Camera? = null                   //Camera對(duì)象
    private lateinit var mParameters: Camera.Parameters   //Camera對(duì)象的參數(shù)
    private var mSurfaceView: SurfaceView = surfaceView   //用于預(yù)覽的SurfaceView對(duì)象
    var mSurfaceHolder: SurfaceHolder                     //SurfaceHolder對(duì)象

    private var mActivity: Activity = activity
    private var mCallBack: CallBack? = null   //自定義的回調(diào)

    var mCameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK  //攝像頭方向
    var mDisplayOrientation: Int = 0    //預(yù)覽旋轉(zhuǎn)的角度

    private var picWidth = 2160        //保存圖片的寬
    private var picHeight = 3840       //保存圖片的高

}

由于對(duì)Camera的操作等代碼比較多爬范,本著各司其職的原則,創(chuàng)建了一個(gè)CameraHelper類來(lái)處理Camera相關(guān)的操作弱匪,如果放在Activity中對(duì)Camera操作會(huì)使Activity臃腫復(fù)雜

CameraHelper的構(gòu)造方法有兩個(gè)青瀑,一個(gè)是Activity對(duì)象,一個(gè)是SurfaceView對(duì)象(就是xml文件里定義的SurfaceView)

四萧诫、給SurfaceView對(duì)象添加回調(diào)函數(shù)斥难,并初始化相機(jī)

    private fun init() {
        mSurfaceHolder.addCallback(object : SurfaceHolder.Callback {
            override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
            }

            override fun surfaceDestroyed(holder: SurfaceHolder?) {
                releaseCamera() //釋放相機(jī)資源
            }

            override fun surfaceCreated(holder: SurfaceHolder?) { //surface創(chuàng)建
                if (mCamera == null) {
                    openCamera(mCameraFacing)  //打開相機(jī)
                }
                startPreview()  //開始預(yù)覽
            }
        })
    }

    //打開相機(jī)
    private fun openCamera(cameraFacing: Int = Camera.CameraInfo.CAMERA_FACING_BACK): Boolean {
        var supportCameraFacing = supportCameraFacing(cameraFacing)   //判斷手機(jī)是否支持前置/后置攝像頭
        if (supportCameraFacing) {
            try {
                mCamera = Camera.open(cameraFacing)
                initParameters(mCamera!!)          //初始化相機(jī)配置信息
                mCamera?.setPreviewCallback(this)
            } catch (e: Exception) {
                e.printStackTrace()
                toast("打開相機(jī)失敗!")
                return false
            }
        }
        return supportCameraFacing
    }

    //判斷是否支持某個(gè)相機(jī)
    private fun supportCameraFacing(cameraFacing: Int): Boolean {
        var info = Camera.CameraInfo()
        for (i in 0 until Camera.getNumberOfCameras()) {
            Camera.getCameraInfo(i, info)
            if (info.facing == cameraFacing) return true
        }
        return false
    }

在CameraHelper的創(chuàng)建后調(diào)用init()方法。在init()方法中财搁,我們首先對(duì)mSurfaceHolder添加了一個(gè)回調(diào)蘸炸,這個(gè)回調(diào)會(huì)告訴我們SurfaceView中surface的變化(在上一篇上有講解)

在surfaceCreated(holder: SurfaceHolder?) 回調(diào)中打開相機(jī)躬络。因?yàn)橄鄼C(jī)開始預(yù)覽的時(shí)候尖奔,如果SurfaceView中的surface還沒(méi)有創(chuàng)建,就回拋出異常,所以我們?cè)趕urface創(chuàng)建后再對(duì)相機(jī)進(jìn)行操作

我們調(diào)用相機(jī)的open()方法打開一個(gè)攝像頭提茁,在打開攝像頭之前判斷一下手機(jī)是否支持我們將要打開的攝像頭淹禾。

五、配置相機(jī)參數(shù)

    //配置相機(jī)參數(shù)
    private fun initParameters(camera: Camera) {
        try {
            mParameters = camera.parameters
            mParameters.previewFormat = ImageFormat.NV21   //設(shè)置預(yù)覽圖片的格式

            //獲取與指定寬高相等或最接近的尺寸
            //設(shè)置預(yù)覽尺寸
            val bestPreviewSize = getBestSize(mSurfaceView.width, mSurfaceView.height, mParameters.supportedPreviewSizes)
            bestPreviewSize?.let {
                mParameters.setPreviewSize(it.width, it.height)
            }
            //設(shè)置保存圖片尺寸
            val bestPicSize = getBestSize(picWidth, picHeight, mParameters.supportedPictureSizes)
            bestPicSize?.let {
                mParameters.setPictureSize(it.width, it.height)
            }
            //對(duì)焦模式
            if (isSupportFocus(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
                mParameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE

            camera.parameters = mParameters
        } catch (e: Exception) {
            e.printStackTrace()
            toast("相機(jī)初始化失敗!")
        }
    }

       //獲取與指定寬高相等或最接近的尺寸
    private fun getBestSize(targetWidth: Int, targetHeight: Int, sizeList: List<Camera.Size>): Camera.Size? {
        var bestSize: Camera.Size? = null
        var targetRatio = (targetHeight.toDouble() / targetWidth)  //目標(biāo)大小的寬高比
        var minDiff = targetRatio

        for (size in sizeList) {
            var supportedRatio = (size.width.toDouble() / size.height)
            log("系統(tǒng)支持的尺寸 : ${size.width} * ${size.height} ,    比例$supportedRatio")
        }

        for (size in sizeList) {
            if (size.width == targetHeight && size.height == targetWidth) {
                bestSize = size
                break
            }
            var supportedRatio = (size.width.toDouble() / size.height)
            if (Math.abs(supportedRatio - targetRatio) < minDiff) {
                minDiff = Math.abs(supportedRatio - targetRatio)
                bestSize = size
            }
        }
        log("目標(biāo)尺寸 :$targetWidth * $targetHeight 茴扁,   比例  $targetRatio")
        log("最優(yōu)尺寸 :${bestSize?.height} * ${bestSize?.width}")
        return bestSize
    }

我們對(duì)預(yù)覽大小和保存圖片大小進(jìn)行設(shè)置铃岔,在設(shè)置的時(shí)候,我們應(yīng)該獲取到與指定寬高相等或最接近的尺寸峭火,這樣的話才能保證圖片既不變形又能最接近我們指定的大小毁习。

下面是vivo x9的后置攝像頭支持的尺寸:

相機(jī)預(yù)覽大小.png
保存圖片的大小.png

六、開始預(yù)覽

   //開始預(yù)覽
    fun startPreview() {
        mCamera?.let {
            it.setPreviewDisplay(mSurfaceHolder)         //設(shè)置相機(jī)預(yù)覽對(duì)象
          //  setCameraDisplayOrientation(mActivity)    //設(shè)置預(yù)覽時(shí)相機(jī)旋轉(zhuǎn)的角度
            it.startPreview()
        }
    }

調(diào)用startPreview()方法開始預(yù)覽卖丸,我們先看一下預(yù)覽效果:

設(shè)置角度前預(yù)覽效果.jpg

我們可以看到纺且,畫面并不是"自然方向"而且被拉伸。這個(gè)在上一篇已經(jīng)講解過(guò)稍浆,下面通過(guò)setDisplayOrientation(int degree)方法载碌,使其正常顯示

    //設(shè)置預(yù)覽旋轉(zhuǎn)的角度
    private fun setCameraDisplayOrientation(activity: Activity) {
        var info = Camera.CameraInfo()
        Camera.getCameraInfo(mCameraFacing, info)
        val rotation = activity.windowManager.defaultDisplay.rotation

        var screenDegree = 0
        when (rotation) {
            Surface.ROTATION_0 -> screenDegree = 0
            Surface.ROTATION_90 -> screenDegree = 90
            Surface.ROTATION_180 -> screenDegree = 180
            Surface.ROTATION_270 -> screenDegree = 270
        }

        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            mDisplayOrientation = (info.orientation + screenDegree) % 360
            mDisplayOrientation = (360 - mDisplayOrientation) % 360          // compensate the mirror
        } else {
            mDisplayOrientation = (info.orientation - screenDegree + 360) % 360
        }
        mCamera?.setDisplayOrientation(mDisplayOrientation)

        log("屏幕的旋轉(zhuǎn)角度 : $rotation")
        log("setDisplayOrientation(result) : $mDisplayOrientation")
    }

設(shè)置后預(yù)覽效果如下:

設(shè)置角度后預(yù)覽效果.jpg

上一篇提到的相機(jī)的預(yù)覽方向:

相機(jī)預(yù)覽方向.png
后置攝像頭預(yù)覽旋轉(zhuǎn)角度.png
前置攝像頭預(yù)覽旋轉(zhuǎn)角度.png

通過(guò)日志我們看到,前后攝像頭的預(yù)覽旋轉(zhuǎn)角度都是90
前置攝像頭在進(jìn)行角度旋轉(zhuǎn)之前衅枫,圖像會(huì)進(jìn)行一個(gè)水平的鏡像翻轉(zhuǎn)嫁艇,所以前置攝像頭應(yīng)該設(shè)置的旋轉(zhuǎn)角度是 270 - 180 = 90

七、進(jìn)行拍照

拍照的話有兩種方式:

  • 調(diào)用takePicture(ShutterCallback shutter, PictureCallback raw,
    PictureCallback jpeg) 方法

  • 在相機(jī)的預(yù)覽回調(diào)里直接保存

1.調(diào)用takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg) 拍照
    //拍攝照片
    fun takePic() {
        mCamera?.let {
            it.takePicture({}, null, { data, _ ->
                it.startPreview()
                savePic(data)  //保存圖片
            })
        }
    }

   //保存照片
    private fun savePic(data: ByteArray?) {
        thread {
            try {
                val temp = System.currentTimeMillis()
                val picFile = FileUtil.createCameraFile()
                if (picFile != null && data != null) {
                   val rawBitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
                   Okio.buffer(Okio.sink(picFile)).write(BitmapUtils.toByteArray(resultBitmap)).close()
                    runOnUiThread {
                        toast("圖片已保存! ${picFile.absolutePath}")
                        log("圖片已保存! 耗時(shí):${System.currentTimeMillis() - temp}    路徑:  ${picFile.absolutePath}")
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
                runOnUiThread {
                    toast("保存圖片失斚伊谩步咪!")
                }
            }
        }
    }

takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg)方法有3個(gè)參數(shù),而且這3個(gè)參數(shù)都是抽象接口:

  • 第一個(gè)是點(diǎn)擊拍照時(shí)的回調(diào)益楼。
    如果傳null歧斟,則沒(méi)有任何效果
    如果寫一個(gè)空實(shí)現(xiàn),則在點(diǎn)擊拍照時(shí)會(huì)有"咔擦"聲

  • 第二個(gè)和第三個(gè)參數(shù)類型一樣偏形,PictureCallback 有一個(gè)抽象方法
    void onPictureTaken(byte[] data, Camera camera)
    data就是點(diǎn)擊拍照后相機(jī)返回的照片的byte數(shù)組静袖,用該數(shù)組創(chuàng)建一個(gè)bitmap保存下來(lái),就得到了拍攝的照片

2.在相機(jī)的預(yù)覽回調(diào)里直接保存
override fun onPreviewFrame(data: ByteArray?, camera: Camera?) {
    savePic(data)   //保存照片   
}

注意:實(shí)際上這個(gè)回調(diào)方法會(huì)一直一直的調(diào)用俊扭,如果要保存一張照片的話應(yīng)該加個(gè)字段進(jìn)行控制队橙,此處只是做演示

在保存圖片的時(shí)候,我們需要開啟一個(gè)子線程來(lái)進(jìn)行操作萨惑,通過(guò)日志輸出可以看到保存圖片所用時(shí)間和保存路徑:

保存圖片.png

八捐康、調(diào)整保存照片的方向

與預(yù)覽時(shí)方向類似,照片在保存時(shí)也有一個(gè)方向庸蔼。我們先看一下在上一步中保存的照片是什么樣的:

后置攝像頭:


后置攝像頭拍攝照片.png

前置攝像頭:


前置攝像頭拍攝照片.png

下面我們?cè)诒4鎴D片的時(shí)候解总,對(duì)照片進(jìn)行旋轉(zhuǎn)處理,保存照片的方法應(yīng)該如下:

   private fun savePic(data: ByteArray?) {
        thread {
            try {
                val temp = System.currentTimeMillis()
                val picFile = FileUtil.createCameraFile()
                if (picFile != null && data != null) {
                    val rawBitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
                    val resultBitmap = if (mCameraHelper.mCameraFacing == Camera.CameraInfo.CAMERA_FACING_FRONT)
                       BitmapUtils.rotate(rawBitmap, 270f)  //前置攝像頭旋轉(zhuǎn)270°
                    else
                        BitmapUtils.rotate(rawBitmap, 90f)  //后置攝像頭旋轉(zhuǎn)90°

                    Okio.buffer(Okio.sink(picFile)).write(BitmapUtils.toByteArray(resultBitmap)).close()
                    runOnUiThread {
                        toast("圖片已保存! ${picFile.absolutePath}")
                        log("圖片已保存! 耗時(shí):${System.currentTimeMillis() - temp}    路徑:  ${picFile.absolutePath}")
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
                runOnUiThread {
                    toast("保存圖片失斀憬觥花枫!")
                }
            }
        }
    }


//圖片工具類
object BitmapUtils {
    //水平鏡像翻轉(zhuǎn)
    fun mirror(rawBitmap: Bitmap): Bitmap {
        var matrix = Matrix()
        matrix.postScale(-1f, 1f)
        return Bitmap.createBitmap(rawBitmap, 0, 0, rawBitmap.width, rawBitmap.height, matrix, true)
    }
    //旋轉(zhuǎn)
    fun rotate(rawBitmap: Bitmap, degree: Float): Bitmap {
        var matrix = Matrix()
        matrix.postRotate(degree)
        return Bitmap.createBitmap(rawBitmap, 0, 0, rawBitmap.width, rawBitmap.height, matrix, true)
    }
|

然后我們?cè)谶M(jìn)行一次拍照:

后置攝像頭:


后置攝像頭拍攝照片旋轉(zhuǎn)后.png

前置攝像頭:


前置攝像頭拍攝照片旋轉(zhuǎn)后.png

對(duì)比一下上一篇文章所講的相機(jī)保存照片的方向:

圖六刻盐、采集的圖像方向.png

關(guān)于前置攝像頭所拍攝照片,需要注意的是劳翰,由于在setDisplayOrientation()設(shè)置相機(jī)預(yù)覽方向的時(shí)候系統(tǒng)默認(rèn)做了一個(gè)水平鏡面的翻轉(zhuǎn)敦锌,所以我們通過(guò)前置攝像頭保存來(lái)的照片并不是和預(yù)覽時(shí)看到的一樣,兩者是水平鏡像關(guān)系佳簸。所以乙墙,一般情況下我們不僅僅需要對(duì)前置攝像頭做旋轉(zhuǎn),還應(yīng)該做一個(gè)水平方向的鏡面翻轉(zhuǎn)處理生均。

在上面保存圖片的方法中判斷如果是前置攝像頭的話听想,代碼修改如下:

BitmapUtils.mirror(BitmapUtils.rotate(rawBitmap, 270f)) //旋轉(zhuǎn)270,然后水平鏡面翻轉(zhuǎn)

這樣的話马胧,就能保證所拍攝照片與在預(yù)覽時(shí)所呈現(xiàn)的畫面是一模一樣的哗魂,如下圖:

前置攝像頭預(yù)覽與保存一致.png

注:如果有小伙伴對(duì)這點(diǎn)還不太理解的話,墻裂建議自己用前置攝像頭自拍一張漓雅,然后在對(duì)比保存的照片與預(yù)覽時(shí)手機(jī)里顯示的畫面录别,就很容易理解了
不是我不愿意自己自拍來(lái)給小伙們演示,長(zhǎng)相實(shí)在是有點(diǎn)慘邻吞,所以大家還是自己親自驗(yàn)證吧o(╥﹏╥)o

九组题、釋放相機(jī)資源

在Activity銷毀前或者是關(guān)閉相機(jī)時(shí),應(yīng)當(dāng)釋放當(dāng)前相機(jī)資源

   //釋放相機(jī)
    fun releaseCamera() {
        if (mCamera != null) {
            mCamera?.stopPreview()
            mCamera?.setPreviewCallback(null)
            mCamera?.release()
            mCamera = null
        }
    }

完整效果如下:

拍照效果圖.gif

總結(jié)

本篇文章主要給小伙伴們介紹了實(shí)現(xiàn)Camera拍照功能的流程及步驟抱冷,并且用實(shí)際效果驗(yàn)證了上一篇文章中所講解的理論
下一篇文章將會(huì)給小伙伴們介紹如何實(shí)現(xiàn)人臉檢測(cè)功能崔列,敬請(qǐng)期待~~

完整代碼

https://github.com/smashinggit/Study

注:此工程包含多個(gè)module,本文所用代碼均在camerademo文件夾下

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旺遮,一起剝皮案震驚了整個(gè)濱河市赵讯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耿眉,老刑警劉巖边翼,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鸣剪,居然都是意外死亡组底,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門筐骇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)债鸡,“玉大人,你說(shuō)我怎么就攤上這事铛纬⊙峋” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵告唆,是天一觀的道長(zhǎng)棺弊。 經(jīng)常有香客問(wèn)我晶密,道長(zhǎng),這世上最難降的妖魔是什么镊屎? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮茄螃,結(jié)果婚禮上缝驳,老公的妹妹穿的比我還像新娘。我一直安慰自己归苍,他們只是感情好用狱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拼弃,像睡著了一般夏伊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吻氧,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天溺忧,我揣著相機(jī)與錄音,去河邊找鬼盯孙。 笑死鲁森,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的振惰。 我是一名探鬼主播歌溉,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼骑晶!你這毒婦竟也來(lái)了痛垛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤桶蛔,失蹤者是張志新(化名)和其女友劉穎匙头,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仔雷,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乾胶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了朽寞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片识窿。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖脑融,靈堂內(nèi)的尸體忽然破棺而出喻频,到底是詐尸還是另有隱情,我是刑警寧澤肘迎,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布甥温,位于F島的核電站锻煌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏姻蚓。R本人自食惡果不足惜宋梧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望狰挡。 院中可真熱鬧捂龄,春花似錦、人聲如沸加叁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)它匕。三九已至展融,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間豫柬,已是汗流浹背告希。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烧给,地道東北人暂雹。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像创夜,于是被迫代替她去往敵國(guó)和親杭跪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

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