实现一个轮播控件并封装起来

作者: 夏至,欢迎转载,但请保留这段申明,谢谢
http://blog.csdn.net/u011418943/article/details/77370490

需求,在首页开发中,我们经常会使用轮播图片的方法,来达到广告栏的效果,而它的实现方式呢,而比较简单,就是一个 viewpager ,然后让它循环起来就可以了。

而它的编写是比较繁琐的,因为除了图片和文字说明外,还需要底部的圆点指示器(你可以写成其他相撞),所以,封装起来,还是可以省很多事情的。

当然,github 有一大堆 轮子可以使用,但为何不自己也搞一个呢?

首先看一下今天要实现的效果:


这里写图片描述

首先,先看一下普通方式应该怎么实现。

布局文件:

<?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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:background="#22e12222"
    tools:context="com.rachel.activitytest.MainActivity">
    <android.support.v4.view.ViewPager
        android:layout_marginTop="20dp"
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:clipChildren="false"
        android:clipToPadding="false"
  />
</LinearLayout>

这里就不讲 viewpager 的配置什么的了,因为讲了估计你也不想看,这里讲讲轮播的实现把。

1、首尾填充实现循环

什么意思呢 ? 就是假如我们要轮播的是 三张图片,那么实际上,填充的是 五张 图片,如下面这张图:


这里写图片描述

即在初始化的时候,1 的前面,填充3,3,的后面填充1,然后再用 viewpager.setCurrentItem(),不动声色的直接设置过去,达到一个移花接木的效果,这样看起来就实现了轮播的效果了。
所以,数据的初始化,如下面所示:

 private List<View>  initImg() {
        for (int i = 0; i < images.length + 2; i++) {
            View view = LayoutInflater.from(this).inflate(R.layout.viewpager_item,null);
            ImageView imageView = (ImageView) view.findViewById(R.id.viewpager_item_img);
            if (i == 0){ //第一张的时候,把最后一张填充到第一张的前一张
                imageView.setImageResource(images[images.length - 1]);
            }else if (i == images.length + 1){
                imageView.setImageResource(images[0]);
            }else {
                imageView.setImageResource(images[i - 1]);
            }
            mViews.add(view);
            mViews2.add(view);
        }
        return mViews;
    }

数据初始化之后,我们还需要在滑动的时候,不动声色的 切换,这样才能有一个轮播的效果,所以,切换的代码如下:

 mViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }
            @Override
            public void onPageSelected(int position) {
            }
            @Override
            public void onPageScrollStateChanged(int state) {
                if (state == ViewPager.SCROLL_STATE_IDLE){
                    int currentpageid = mViewpager.getCurrentItem();
                    if (currentpageid == 0){
                        mViewpager.setCurrentItem(images.length,false);
                    } else if (currentpageid == (images.length +1)){
                        mViewpager.setCurrentItem(1,false);
                    }
                }
            }
        });

完整代码如下:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "zsr";
    //展示三张,其实是有五张
    int[] images = new int[]{R.mipmap.beauty,R.mipmap.beuty1,R.mipmap.beauty2};
    private ViewPager mViewpager;
    private List<View> mViews = new ArrayList<>();
    private List<View> mViews2 = new ArrayList<>();
    private int lastPage;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       mViewpager = (ViewPager) findViewById(R.id.viewpager);
        initImg();
        mViewpager.setAdapter(new CusTomAdapter());
       // mViewpager.setPageTransformer(false,new customTransFromer());
        mViewpager.setOffscreenPageLimit(3);
        mViewpager.setCurrentItem((images.length + 2)/2); //取中间的位置
        mViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }
            @Override
            public void onPageSelected(int position) {
            }
            @Override
            public void onPageScrollStateChanged(int state) {
                if (state == ViewPager.SCROLL_STATE_IDLE){
                    int currentpageid = mViewpager.getCurrentItem();
                    if (currentpageid == 0){
                        mViewpager.setCurrentItem(images.length,false);
                    } else if (currentpageid == (images.length +1)){
                        mViewpager.setCurrentItem(1,false);
                    }
                }
            }
        });
       }      
  class  CusTomAdapter extends PagerAdapter{
        @Override
        public int getCount() {
            return mViews.size();
        }
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            //return super.instantiateItem(container, position);
            container.addView(mViews.get(position));
            return mViews.get(position);
        }
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(mViews.get(position));
        }
    }
    private List<View>  initImg() {
        for (int i = 0; i < images.length + 2; i++) {
            View view = LayoutInflater.from(this).inflate(R.layout.viewpager_item,null);
            ImageView imageView = (ImageView) view.findViewById(R.id.viewpager_item_img);
            if (i == 0){ //第一张的时候,把最后一张填充到第一张的前一张
                imageView.setImageResource(images[images.length - 1]);
            }else if (i == images.length + 1){
                imageView.setImageResource(images[0]);
            }else {
                imageView.setImageResource(images[i - 1]);
            }
            mViews.add(view);
            mViews2.add(view);
        }
        return mViews;
    }
}

效果呢,如下所示:


但这种方式,在快速滚动的时候,不是很利索,原因就是切换那里,我们是在滚动结束的时候在 切过去的,如果你在快速滚动,就有一种最后一张切到第一张,有卡顿的感觉。

2、getCount 中返回一个很大的数字,实现轮播

这种方式,就是在 pagerveiw 的 getCount 中,返回一个很大的数字,如下:

  @Override
        public int getCount() {
            //设置一个很大的数,用来实现轮播效果,实际上可以不用这么大
             return Integer.MAX_VALUE;
        }

为了在 adapter 中获取到正确的值,把 position 取余即可。然后为了方便左右都能滑动,在初始化的时候,把 setcurrentitem 的值设置成中间即可。为了方便,这里新建一个类,继承 Viewpager:

public class BannerViewPager extends ViewPager {
    private static final String TAG = "BannerViewPager";
    private BannerCustomAdapter mBannerCustomAdapter;
    public BannerViewPager(Context context) {
        super(context);
    }
    public BannerViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public void setAdapter(List<View> views) {
        setAdapter(new BannerViewAdapter(views));
    }
    class BannerViewAdapter extends PagerAdapter{
        List<View> views;
        public BannerViewAdapter(List<View> views) {
           this.views = views;
        }
        @Override
        public int getCount() {
            //设置一个很大的数,用来实现轮播效果
             return Integer.MAX_VALUE;
        }
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            //自己适配类型,比如imageview
            View view = null;
            if (this.views != null){
                //对Viewpager页号求模去除View列表中要显示的项
                position %= this.views.size();
               
                //如果View已经在之前添加到了一个父组件,则必须先remove,否则会抛出IllegalStateException。
                view = this.views.get(position);
                ViewParent viewParent = view.getParent();
                if (viewParent!=null){
                    
                    parent.removeAllView(view);
                }
            }
            container.addView(view);
            return view;
        }
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
          //上面已经对 item remove了,这里如果再次remove,则实现不出来
            //  container.removeView(this.views.get(position % this.views.size()));
           
        }
    }
}

然后,替换一下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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:background="#22e12222"
    tools:context="com.rachel.activitytest.MainActivity">
    <com.rachel.commonsdk.custom.bannerview.BannerViewPager
        android:layout_marginTop="20dp"
        android:id="@+id/custom_viewpager"
        android:layout_width="match_parent"
        android:layout_height="200dp"/>
</LinearLayout>

主函数这样写:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "zsr";
    //展示三张,其实是有五张
    int[] images = new int[]{R.mipmap.beauty,R.mipmap.beuty1,R.mipmap.beauty2};
    private ViewPager mViewpager;
    private List<View> mViews = new ArrayList<>();
  
    private int lastPage;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initImg();

        BannerViewPager bannerViewPager = (BannerViewPager) findViewById(R.id.custom_viewpager);
        bannerViewPager.setAdapter(mViews);
        bannerViewPager.setPageTransformer(false,new customTransFromer());
        bannerViewPager.setCurrentItem(900); //取中间或者大一点的数都可以,保证左边有item
    }
      private List<View>  initImg() {
        for (int i = 0; i < images.length + 2; i++) {
            View view = LayoutInflater.from(this).inflate(R.layout.viewpager_item,null);
            ImageView imageView = (ImageView) view.findViewById(R.id.viewpager_item_img);
            mViews.add(view);
  
        }
        return mViews;
    }
}

这样也实现了上面一样的效果。

当然,你可以加写 viewpager 的动画,比如魅族的 bannerview:

  /**
     * 魅族效果
     */
    class ZoomOutPageTransformer implements ViewPager.PageTransformer {
        private static final float MAX_SCALE = 1.0f;
        private static final float MIN_SCALE = 0.9f;//0.85f
        @Override
        public void transformPage(View view, float position) {
            //setScaleY只支持api11以上
            if (position < -1) {
                // view.setScaleX(MIN_SCALE);
                view.setScaleY(MIN_SCALE);
            } else if (position <= 1) //a页滑动至b页 ; a页从 0.0 -1 ;b页从1 ~ 0.0
            { // [-1,1]
//              Log.e("TAG", view + " , " + position + "");
                float scaleFactor = MIN_SCALE + (1 - Math.abs(position)) * (MAX_SCALE - MIN_SCALE);
                //  view.setScaleX(scaleFactor);
                //每次滑动后进行微小的移动目的是为了防止在三星的某些手机上出现两边的页面为显示的情况
                view.setScaleY(scaleFactor);
            } else { // (1,+Infinity]
                // view.setScaleX(MIN_SCALE);
                view.setScaleY(MIN_SCALE);
            }
        }
    }

在 viewpager 设置一下:

 mViewpager.setPageTransformer(false,new ZoomOutPageTransformer());

当然,你需要在 xml中,设置一个 viewpager 左右的margin 和在根布局中添加 clipChildren = false:

<?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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:background="#22e12222"
    tools:context="com.rachel.activitytest.MainActivity">
    <android.support.v4.view.ViewPager
        android:layout_marginTop="20dp"
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
  />
</LinearLayout>

效果如下:


轮播与封装

这个就比较简单了,用一个 Handle 就可以了,需要注意的是,在按下的时候,需要取消消息,在抬起之后再取恢复;而上面的这种做法,其实还是有点问题的,用户除了要配置数据之外,还有额外的 viewpager 的配置,还有底部小圆圈的配置,这样是需要配置天多,那能不能我们先封装起来,用户再用的时候,只要配置数据就可以了?
当然可以,这里我们新建一个 xml,包含 viewpager 和 底部导航圆点 banner_layout.xml:

<?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:clipChildren="false"
    >
    <android.support.v4.view.ViewPager
        android:id="@+id/banner_viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
    <LinearLayout
        android:id="@+id/banner_indeticor"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal"
        android:gravity="center"
        android:layout_marginBottom="10dp"
        />
</RelativeLayout>

接着新建一个类,让它继承 releativelayout,然后把这个 banner_layout.xml ,加进来即可:

public class CusViewPager extends RelativeLayout implements ViewPager.OnPageChangeListener, View.OnTouchListener {
   
    private ViewPager mViewPager;
    private Context mContext;
    private LinearLayout mLinearLayout;
    
   
    public CusViewPager(Context context) {
        this(context,null);
    }
    public CusViewPager(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public CusViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        View view = LayoutInflater.from(context).inflate(R.layout.banner_layout,null);
        addView(view);  // 把包含 viewpager 和 底部 linearlayout的view 加给自身
        mViewPager = (ViewPager) view.findViewById(R.id.banner_viewpager);
        mLinearLayout = (LinearLayout) view.findViewById(R.id.banner_indeticor);
        mViewPager.setOnTouchListener(this);
    }
    ....
}

这样,我们在这里就可以操作 viewpager 和 底部小圆点的操作了。关于底部小圆点的,可以参考我的另一篇文章:
5分钟搞定开机引导界面

完整代码如下:

public class CusViewPager extends RelativeLayout implements ViewPager.OnPageChangeListener, View.OnTouchListener {
    private static final String TAG = "zsr";
    private ViewPager mViewPager;
    private Context mContext;
    private LinearLayout mLinearLayout;
    private static final int LOOP_COUNT = 5000;
    private static final int LOOP_START = 1;
    private static final int LOOP_TIME = 1000;
    private int mCurrentPageId;
    private List<View> mPointLists = new ArrayList<>();
    public CusViewPager(Context context) {
        this(context,null);
    }
    public CusViewPager(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public CusViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        View view = LayoutInflater.from(context).inflate(R.layout.banner_layout,null);
        addView(view);
        mViewPager = (ViewPager) view.findViewById(R.id.banner_viewpager);
        mLinearLayout = (LinearLayout) view.findViewById(R.id.banner_indeticor);
        mViewPager.setOnTouchListener(this);
    }
    Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case LOOP_START:
                    //这里重新获取一下 id,防止手动滑动而导致位置错乱
                    mCurrentPageId = mViewPager.getCurrentItem();
                    if (mCurrentPageId > LOOP_COUNT /2 ){
                        mViewPager.setCurrentItem(++mCurrentPageId);
                    }
                    if (mCurrentPageId > LOOP_COUNT){
                        mCurrentPageId = LOOP_COUNT+1;
                    }
                    mHandler.sendEmptyMessageDelayed(LOOP_START,LOOP_COUNT);
                    break;
            }
        }
    };
    public void setDatas(List<View> lists){
        if (mViewPager != null) {
            mViewPager.setAdapter(new AutoAdapter(lists));
            mViewPager.setCurrentItem(lists.size()+LOOP_COUNT/2);
            mCurrentPageId = lists.size()+LOOP_COUNT/2;
            initPoint(lists);
            mViewPager.addOnPageChangeListener(this);
           // setViewScrollTime(mContext,mViewPager,300);
            mHandler.sendEmptyMessageDelayed(LOOP_START,LOOP_TIME);
            Log.d(TAG, "handleMessage: "+System.currentTimeMillis());
        }
    }
    private void initPoint(List<View> lists) {
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        params.setMargins(15,0,0,0);
        for (int i = 0; i < lists.size(); i++) {
            ImageView imageView = new ImageView(mContext);
            imageView.setImageResource(R.drawable.pointselector);
            imageView.setLayoutParams(params);
            if (i == 0){
                imageView.setSelected(true);
            }else{
                imageView.setSelected(false);
            }
            mPointLists.add(imageView);
            mLinearLayout.addView(imageView);
            
        }
    }
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }
    @Override
    public void onPageSelected(int position) {
        for (int i = 0; i < mPointLists.size(); i++) {
            View view = mPointLists.get(i);
            if (i == (position % mPointLists.size())){
                view.setSelected(true);
            }else{
                view.setSelected(false);
            }
        }
    }
    @Override
    public void onPageScrollStateChanged(int state) {
    }
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mHandler.removeMessages(LOOP_START);
                break;
            case MotionEvent.ACTION_UP:
                //手指抬起3s后,仍没有再次按下,则启动轮播
                mHandler.sendEmptyMessageDelayed(LOOP_START,3000);
                break;
        }
        return false;
    }
    class AutoAdapter extends PagerAdapter {
        List<View> datas;
        public AutoAdapter(List<View> datas) {
            this.datas = datas;
        }
        @Override
        public int getCount() {
            return datas.size() + LOOP_COUNT;
        }
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View view = datas.get(position % datas.size());
            //Log.d(TAG, "instantiateItem: "+view);
            ViewGroup parent = (ViewGroup) view.getParent();
            if (parent != null) {
                parent.removeAllViews();
            }
            container.addView(view);
            return view;
        }
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }
    }
    /**
     * 魅族效果
     */
    class ZoomOutPageTransformer implements ViewPager.PageTransformer {
        private static final float MAX_SCALE = 1.0f;
        private static final float MIN_SCALE = 0.9f;//0.85f
        @Override
        public void transformPage(View view, float position) {
            //setScaleY只支持api11以上
            if (position < -1) {
                // view.setScaleX(MIN_SCALE);
                view.setScaleY(MIN_SCALE);
            } else if (position <= 1) //a页滑动至b页 ; a页从 0.0 -1 ;b页从1 ~ 0.0
            { // [-1,1]
//              Log.e("TAG", view + " , " + position + "");
                float scaleFactor = MIN_SCALE + (1 - Math.abs(position)) * (MAX_SCALE - MIN_SCALE);
                //  view.setScaleX(scaleFactor);
                //每次滑动后进行微小的移动目的是为了防止在三星的某些手机上出现两边的页面为显示的情况
                view.setScaleY(scaleFactor);
            } else { // (1,+Infinity]
                // view.setScaleX(MIN_SCALE);
                view.setScaleY(MIN_SCALE);
            }
        }
    }
    /**
     * 渐隐效果
     */
    class DepthPageTransformer implements ViewPager.PageTransformer {
        private static final float MIN_SCALE = 0.75f;
        public void transformPage(View view, float position) {
            int pageWidth = view.getWidth();
            if (position < -1) { // [-Infinity,-1)
                // This page is way off-screen to the left.
                view.setAlpha(0);
            } else if (position <= 0) { // [-1,0]
                // Use the default slide transition when moving to the left page
                view.setAlpha(1);
                view.setTranslationX(0);
                view.setScaleX(1);
                view.setScaleY(1);
            } else if (position <= 1) { // (0,1]
                // Fade the page out.
                view.setAlpha(1 - position);
                // Counteract the default slide transition
                view.setTranslationX(pageWidth * -position);
                // Scale the page down (between MIN_SCALE and 1)
                float scaleFactor = MIN_SCALE
                        + (1 - MIN_SCALE) * (1 - Math.abs(position));
                view.setScaleX(scaleFactor);
                view.setScaleY(scaleFactor);
            } else { // (1,+Infinity]
                // This page is way off-screen to the right.
                view.setAlpha(0);
            }
        }
    }
    private void setViewScrollTime(Context context,ViewPager viewpager,int time){
        FixedSpeedScroller mScroller = null;
        try {
            Field mField;
            mField = ViewPager.class.getDeclaredField("mScroller");
            mField.setAccessible(true);
            mScroller = new FixedSpeedScroller(context,
                    new AccelerateInterpolator());
            mScroller.setmDuration(time);
            mField.set(viewpager, mScroller);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

所以,在使用的时候,我们只要加载布局:

  <com.rachel.studyapp.view.CusViewPager
        android:id="@+id/autoview"
        android:layout_width="match_parent"
        android:layout_height="180dp"/>

在主函数中调用即可:

CusViewPager autoView = (CusViewPager) view.findViewById(R.id.autoview);
        autoView.setDatas(mDatas);

这样就省去了很多的步骤了。效果如下:


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

推荐阅读更多精彩内容