Android GSYVideoPlayer

重拾播放器

近期有做播放器的需求,特此记录:GSYVideoPlayer 官方传送门


//依赖
api 'com.shuyu:GSYVideoPlayer:7.1.5'

对GSYVideo进行二次封装,方便自定义布局,修改样式

public class TyVideo extends StandardGSYVideoPlayer {

    private ImageView mCoverImage;

    public TyVideo(Context context, Boolean fullFlag) {
        super(context, fullFlag);
    }

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

    public TyVideo(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void init(Context context) {
        // 不调父类的 init
        // super.init(context);
        // GSYVideoView int
        if (getActivityContext() != null) {
            this.mContext = getActivityContext();
        } else {
            this.mContext = context;
        }

        initInflate(mContext);

        mTextureViewContainer = (ViewGroup) findViewById(R.id.surface_container);
        if (isInEditMode())
            return;
        mScreenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
        mScreenHeight = mContext.getResources().getDisplayMetrics().heightPixels;
        mAudioManager = (AudioManager) mContext.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);


        // GSYVideoControlView
        mLoadingProgressBar = findViewById(R.id.loading); //载入中动画

        mStartButton = findViewById(R.id.start); //播放按钮
        mBackButton = (ImageView) findViewById(R.id.back); //返回按键
        mLockScreen = (ImageView) findViewById(R.id.lock_screen); //锁定图标
        mFullscreenButton = (ImageView) findViewById(R.id.fullscreen); //全屏按钮

        mTitleTextView = (TextView) findViewById(R.id.title); //title
        mTotalTimeTextView = (TextView) findViewById(R.id.total); //总的时长
        mCurrentTimeTextView = (TextView) findViewById(R.id.current); //时间显示

        mBottomContainer = (ViewGroup) findViewById(R.id.layout_bottom); //顶部和底部区域
        mTopContainer = (ViewGroup) findViewById(R.id.layout_top); //顶部和底部区域

        mProgressBar = (SeekBar) findViewById(R.id.progress); //进度条
        mBottomProgressBar = (ProgressBar) findViewById(R.id.bottom_progressbar); //底部进度条

        mThumbImageViewLayout = (RelativeLayout) findViewById(R.id.thumb); //用来装封面的容器

        if (isInEditMode())
            return;

        if (mStartButton != null) {
            mStartButton.setOnClickListener(this);
        }

        if (mFullscreenButton != null) {
            mFullscreenButton.setOnClickListener(this);
            mFullscreenButton.setOnTouchListener(this);
        }

        if (mProgressBar != null) {
            mProgressBar.setOnSeekBarChangeListener(this);
        }

        if (mBottomContainer != null) {
            mBottomContainer.setOnClickListener(this);
        }

        if (mTextureViewContainer != null) {
            mTextureViewContainer.setOnClickListener(this);
            mTextureViewContainer.setOnTouchListener(this);
        }

        if (mProgressBar != null) {
            mProgressBar.setOnTouchListener(this);
        }

        if (mThumbImageViewLayout != null) {
            mThumbImageViewLayout.setVisibility(GONE);
            mThumbImageViewLayout.setOnClickListener(this);
        }
        if (mThumbImageView != null && !mIfCurrentIsFullscreen && mThumbImageViewLayout != null) {
            mThumbImageViewLayout.removeAllViews();
            resolveThumbImage(mThumbImageView);
        }

        if (mBackButton != null)
            mBackButton.setOnClickListener(this);

        if (mLockScreen != null) {
            mLockScreen.setVisibility(GONE);
            mLockScreen.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mCurrentState == CURRENT_STATE_AUTO_COMPLETE ||
                            mCurrentState == CURRENT_STATE_ERROR) {
                        return;
                    }
                    lockTouchLogic();
                    if (mLockClickListener != null) {
                        mLockClickListener.onClick(v, mLockCurScreen);
                    }
                }
            });
        }

        if (getActivityContext() != null) {
            mSeekEndOffset = CommonUtil.dip2px(getActivityContext(), 50);
        }

        // GSYBaseVideoPlayer
        mSmallClose = findViewById(R.id.small_close);

        //StandardGSYVideoPlayer
        if (mBottomProgressDrawable != null) {
            mBottomProgressBar.setProgressDrawable(mBottomProgressDrawable);
        }

        if (mBottomShowProgressDrawable != null) {
            mProgressBar.setProgressDrawable(mBottomProgressDrawable);
        }

        if (mBottomShowProgressThumbDrawable != null) {
            mProgressBar.setThumb(mBottomShowProgressThumbDrawable);
        }

        mCoverImage = new ImageView(getContext());
        mCoverImage.setScaleType(ImageView.ScaleType.CENTER_CROP);
        setThumbImageView(mCoverImage);
    }

    /**
     * 设置封面图片
     * <p>
     * GlideApp.with(context).load(imageUrl).into(player.getCoverView());
     *
     * @return
     */
    public ImageView getCoverView() {
        return mCoverImage;
    }

    @Override
    public int getLayoutId() {
        return R.layout.lib_video_layout;
    }

    /**
     * 开始播放
     */
    @Override
    public void startPrepare() {
        super.startPrepare();
    }

    /**
     * 自定义 开始/ 暂停/ 错误的图标
     */
    @Override
    protected void updateStartImage() {
        if (mStartButton instanceof ENPlayView) {
            ENPlayView enPlayView = (ENPlayView) mStartButton;
            enPlayView.setDuration(500);
            if (mCurrentState == CURRENT_STATE_PLAYING) {
                enPlayView.play();
            } else if (mCurrentState == CURRENT_STATE_ERROR) {
                enPlayView.pause();
            } else {
                enPlayView.pause();
            }
        } else if (mStartButton instanceof ImageView) {
            ImageView imageView = (ImageView) mStartButton;
            if (mCurrentState == CURRENT_STATE_PLAYING) {
                //暂停
                imageView.setImageResource(R.drawable.video_click_pause_selector);
            } else if (mCurrentState == CURRENT_STATE_ERROR) {
                //错误
                imageView.setImageResource(R.drawable.video_click_error_selector);
            } else {
                //播放
                imageView.setImageResource(R.drawable.video_click_play_selector);
            }
        }
    }

    /**
     * 是否处于暂停状态
     *
     * @return
     */
    public boolean isInInPause() {
        return (mCurrentState >= 0 && mCurrentState == CURRENT_STATE_PAUSE);
    }
}

重写布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black">


    <!-- 这个用来装我们视频播放器的不要删除,可以改变大小。-->
    <RelativeLayout
        android:id="@+id/surface_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center">

    </RelativeLayout>

    <!-- 这个是用来装封面的容器 -->
    <RelativeLayout
        android:id="@+id/thumb"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:background="#000000"
        android:scaleType="fitCenter" />

    <LinearLayout
        android:id="@+id/layout_bottom"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_alignParentBottom="true"
        android:background="#99000000"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:visibility="invisible">

        <TextView
            android:id="@+id/current"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16dp"
            android:text="00:00"
            android:textColor="#ffffff" />


        <!--可以拖动快进的 进度条-->
        <SeekBar
            android:id="@+id/progress"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_weight="1.0"
            android:background="@null"
            android:max="100"
            android:maxHeight="4dp"
            android:minHeight="4dp"
            android:paddingBottom="8dp"
            android:paddingTop="8dp"
            android:progressDrawable="@drawable/video_seek_progress"
            android:thumb="@drawable/video_seek_thumb" />

        <!-- 总的时长 -->
        <TextView
            android:id="@+id/total"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="16dp"
            android:text="00:00"
            android:textColor="#ffffff" />

        <ImageView
            android:id="@+id/fullscreen"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:paddingRight="16dp"
            android:scaleType="center"
            android:src="@drawable/video_enlarge" />
    </LinearLayout>


    <!--视屏底部进度条,不可以拖动-->
    <ProgressBar
        android:id="@+id/bottom_progressbar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="1.5dp"
        android:layout_alignParentBottom="true"
        android:max="100"
        android:progressDrawable="@drawable/video_progress" />

    <ImageView
        android:id="@+id/back_tiny"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginLeft="6dp"
        android:layout_marginTop="6dp"
        android:visibility="gone" />

    <LinearLayout
        android:id="@+id/layout_top"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:background="@drawable/video_title_bg"
        android:gravity="center_vertical">

        <ImageView
            android:id="@+id/back"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:paddingLeft="10dp"
            android:scaleType="centerInside"
            android:src="@drawable/video_back" />

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="10dp"
            android:textColor="@android:color/white"
            android:textSize="18sp" />
    </LinearLayout>

    <moe.codeest.enviews.ENDownloadView
        android:id="@+id/loading"
        android:layout_width="28dp"
        android:layout_height="28dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:visibility="invisible" />

    <ImageView
        android:id="@+id/start"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:layout_gravity="center_vertical" />


    <ImageView
        android:id="@+id/small_close"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:paddingLeft="10dp"
        android:paddingTop="10dp"
        android:scaleType="centerInside"
        android:src="@drawable/video_small_close"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/lock_screen"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="50dp"
        android:visibility="gone"
        android:scaleType="centerInside"
        android:src="@drawable/unlock" />

</RelativeLayout>

视频管理类 --->

对于封面的播放按钮点击事件和播放完成单独做了接口回调,为的是点击开始按钮时隐藏掉封面上展示的播放量之类的控件,视频播放完之后显示,并释放资源。

/**
 * @describe:视频播放
 * @author:TY
 */
class VideoPlayingManger(private val gsyVideoOptionBuilder: GSYVideoOptionBuilder) {

    /**
     * @param TyVideo Video控件
     * @param videoUrl 视频 URL
     * @param imageUrl 封面图 URL
     * @param title 全屏下标题
     * @param adapterPosition 防止错位传适配器绝对位置 getAbsoluteAdapterPosition()
     * @param mPlayTag 防止tag错误 不能重复
     */
    fun playVideo(videoView: TyVideo, videoUrl: String, imageUrl: String, title: String, adapterPosition: Int, mPlayTag: String, onVideoPlayingMangerlistener: VideoPlayingMangerlistener) {
        gsyVideoOptionBuilder
                .setIsTouchWiget(true) //是否可以滑动界面改变进度,声音等 默认true
                .setUrl(videoUrl) //播放url
                .setCacheWithPlay(true) //是否边缓存,m3u8等无效
                .setRotateViewAuto(true) //是否开启自动旋转
                .setLockLand(true) //一全屏就锁屏横屏,默认false竖屏,可配合setRotateViewAuto使用
                .setPlayTag(mPlayTag) //播放tag防止错误,因为普通的url也可能重复 playTag - 保证不重复就好
                .setShowFullAnimation(true) //是否使用全屏动画效果
                .setNeedLockFull(false) //是否需要全屏锁定屏幕功能 如果单独使用请设置setIfCurrentIsFullscreen为true
                .setNeedShowWifiTip(true) //是否需要显示流量提示,默认true
                .setPlayPosition(adapterPosition) //设置播放位置防止错位 (获取绝对适配器位置)
                .setVideoAllCallBack(object : GSYSampleCallBack() {
                    /**
                     * 在进入全屏模式
                     * @param url
                     * @param objects
                     */
                    override fun onEnterFullscreen(url: String, vararg objects: Any) {
                        super.onEnterFullscreen(url, *objects)
                        //获取当前正在播放的播放控件,将title隐藏
                        videoView.currentPlayer.titleTextView.visibility = View.VISIBLE
                        videoView.currentPlayer.titleTextView.text = title
                    }

                    override fun onClickStartIcon(url: String?, vararg objects: Any?) {
                        super.onClickStartIcon(url, *objects)
                        onVideoPlayingMangerlistener.onClickStartIcon()
                    }

                    override fun onAutoComplete(url: String?, vararg objects: Any?) {
                        super.onAutoComplete(url, *objects)
                        onVideoPlayingMangerlistener.onAutoComplete()
                        //播放完之后释放资源
                        GSYVideoManager.releaseAllVideos()
                    }
                }).build(videoView)

        //增加title
        videoView.titleTextView.visibility = View.GONE

        //设置返回键
        videoView.backButton.visibility = View.GONE

        //设置全屏按键功能
        videoView.fullscreenButton.setOnClickListener {
            resolveFullBtn(videoView)
        }

        //设置封面图片
        Glide.with(videoView.context).load(imageUrl).into(videoView.coverView)
    }

    /**
     * 全屏幕按键处理
     */
    private fun resolveFullBtn(standardGSYVideoPlayer: StandardGSYVideoPlayer) {
        standardGSYVideoPlayer.startWindowFullscreen(standardGSYVideoPlayer.context, true, true)
    }

    interface VideoPlayingMangerlistener {
        /**
         * 点击了播放按钮
         */
        fun onClickStartIcon()

        /**
         * 播放完了
         */
        fun onAutoComplete()
    }
}

具体使用:AndroidManifest.xml

<!--  使用播放器更改配置并设置屏幕方向 有些类型视频无法播放 开启硬件加速  -->
<activity
    android:name=".xx.xx.XXXActivity"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
    android:hardwareAccelerated="true"
    android:screenOrientation="portrait" />
private val videoPlayingManger = VideoPlayingManger(GSYVideoOptionBuilder())

mBinding.apply {
    videoPlayingManger.playVideo(
            mBinding.video,
            "https://media.w3.org/2010/05/sintel/trailer.mp4",
            "https://t7.baidu.com/it/u=3601447414,1764260638&fm=193&f=GIF",
            "测试横屏显示",
            0,
            "0",
            object : VideoPlayingManger.VideoPlayingMangerlistener {
                override fun onClickStartIcon() {
                    //点击封面的播放按钮
                }

                override fun onAutoComplete() {
                    //播放完
                }
            }
    )
}

此外还有单独滚动自动播放,滑动不可见小屏播放辅助类
public interface TyAutoPlayVideoHolder {
    /**
     * 绑定播放器 TyVideo
     */
    TyVideo getVideoView();
}
/**
 * Video自动播放滚动 Helper
 */
public class TyVideoAutoPlayScrollHelper extends RecyclerView.OnScrollListener {

    int firstVisibleItem;
    int lastVisibleItem;
    LinearLayoutManager linearLayoutManager;

    /**
     * 自动播放滚动
     * <p>
     * <p>
     * mRecyclerView.addOnScrollListener(new TyVideoAutoPlayScrollHelper((LinearLayoutManager) mRecyclerView.getLayoutManager()));
     *
     * @param linearLayoutManager
     */
    public TyVideoAutoPlayScrollHelper(LinearLayoutManager linearLayoutManager) {
        this.linearLayoutManager = linearLayoutManager;
    }

    @Override
    public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition();
        lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
        //大于0说明有播放
        if (GSYVideoManager.instance().getPlayPosition() >= 0) {
            //当前播放的位置
            int position = GSYVideoManager.instance().getPlayPosition();
            //对应的播放列表TAG
            if ((position < firstVisibleItem || position > lastVisibleItem)) {

                //如果滑出去了上面和下面就是否,和今日头条一样
                //是否全屏
                if (!GSYVideoManager.isFullState((Activity) recyclerView.getContext())) {
                    GSYVideoManager.releaseAllVideos();
                    recyclerView.getAdapter().notifyItemChanged(position);
                }
            }
        }
    }

    @Override
    public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            play(recyclerView);

        }
    }


    /**
     * 从第一可见的position  到最后一个可见的position 挨个遍历
     * 通过 position 找到 对应的item view
     * 在通过 item view 找到 holder 对象
     * 如果这个holder 是一个视频广告的holder
     * 如果这个item 视频播放器全部可见 那么通过这个item view 找到 对应的播放器控件,然后调用start 播放
     */
    public void play(RecyclerView recyclerView) {
        // 遍历第一个可见 item 和 最后一个可见item 之间的 是否有 视频广告item
        for (int i = firstVisibleItem; i <= lastVisibleItem; i++) {

            View itemView = linearLayoutManager.findViewByPosition(i);// 根据 position  找到 item view;
            if (itemView == null) {
                return;
            }

            RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder(itemView); // 更具 item  view 找到对应 holder

            if (holder instanceof TyAutoPlayVideoHolder) { // 如果这个holder 实现了这个接口,意味着这个holder 对应的item 里面的视频会自动播放
                TyVideo video = ((TyAutoPlayVideoHolder) holder).getVideoView();

                int itemViewTop = itemView.getTop(); // item 在 recycler view 中的 top

                int videoHeight = video.getHeight();// 视频控件的高度

                int videoTopYInRecyclerView = itemViewTop + video.getTop(); // video view 在 recycler view (祖宗容器) 的 top 值

                if (videoTopYInRecyclerView < 0) { // 如果video view 顶部有一部分不在recycler view 里面
                    // 顶部不在 recycler  view 里面的部分小于制定的值,那么播放
                    if (Math.abs(videoTopYInRecyclerView) <= getPlayOrStopThreshold(videoHeight)) {
                        if (video.isInInPause()) {
                            video.onVideoResume(false);
                        } else if (!video.isInPlayingState()) {
                            video.startPrepare();
                        }
                        break;
                    } else {
                        if (video.isInPlayingState()) {
                            video.onVideoPause();
                        }
                    }
                } else {
                    int videoBottomYInRecyclerView = itemView.getTop() + video.getBottom(); // video view 在 recycler vie 中的 bot
                    int excess = videoBottomYInRecyclerView - recyclerView.getHeight(); // video view 在 recycler view 中超出部分
                    //  如果video view 整个都在recycler view  里面或者 video 有一部分已经超出了 recycler view 下面一部分,但是超出部分不足 video view 高度的三分之一
                    if (excess < getPlayOrStopThreshold(videoHeight)) {
                        if (video.isInInPause()) {
                            video.onVideoResume(false);
                        } else if (!video.isInPlayingState()) {
                            video.startPrepare();
                        }
                        break;
                    } else {
                        if (video.isInPlayingState()) {
                            video.onVideoPause();
                        }
                    }
                }
            }
        }
    }

    /**
     * 返回 video view 上边 或者 下边 垂直方向上在屏幕之外的距离,如果大于这个距离 就停止播放,小于这个距离就自动播放
     * 默认是高度的 1/3,   返回 0 表示,只有整个Video View 都在屏幕上可见是才播放。
     *
     * @param videoHeight
     * @return
     */
    public int getPlayOrStopThreshold(int videoHeight) {
        return 0;
    }

    /**
     * 看列表(RecycleView)是否有满足自动播放的 video
     * 如果有的话就将列表向下滑动 1 个像素
     * 使得 广告Video 自动播放
     * TyVideoAutoPlayScrollHelper.playIfNeed(mRecyclerView);
     */
    public static void playIfNeed(final RecyclerView recyclerView) {
        recyclerView.post(new Runnable() {
            @Override
            public void run() {
                recyclerView.smoothScrollBy(0, 1);
            }
        });
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容