自定義BannerViewPager

雖然網(wǎng)上這種自定義ViewPager實現(xiàn)的banner挺多纳寂,但還是自己動手寫下會好很多。權(quán)當(dāng)練手吧圃伶。
一堤如、效果預(yù)覽

image.png

二、需求分析

1.重寫ViewPager控件實現(xiàn)bannerView
2.需要定時輪播
3.需要無限輪播
4.內(nèi)存優(yōu)化
5.ViewPager自身切換速率太快窒朋,需要重新設(shè)置

三搀罢、自定義View套路代碼
直接繼承自ViewPager

public class BannerViewPager extends ViewPager {
    //內(nèi)存優(yōu)化,復(fù)用的View.
    private SparseArray<View> mConvertViews;

    public BannerViewPager(Context context) {
        this(context, null);
    }

    public BannerViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        //內(nèi)存優(yōu)化侥猩,緩存頁面數(shù)據(jù)删性,下面會講到
        mConvertViews = new SparseArray<>();
    }
}

四蛙婴、采用Adapter設(shè)計模式
原始的ViewPager就是采用了adapter設(shè)計模式奢入,方便用戶使用展辞,所以這里我們也仿照源碼思想,采用adapter設(shè)計模式划提。
新建一個BannerAdapter類:

public abstract class BannerAdapter {
    //Observable枫弟,觀察者模式,下面講到
    private BannerViewPager.AdapterDataObservable mObservable = new BannerViewPager.AdapterDataObservable();
    //返回頁面的View
    public abstract View getView(int position);
    //返回BannerView的頁面數(shù)量鹏往,如果是無限輪播淡诗,記住要返回真是頁面數(shù)量。
    public abstract int getCount();
    //ViewPager頁面切換特效伊履,方便自定義袜漩,返回null采用默認(rèn)特效
    public Transformer getTransformer() {
        return null;
    }

    //************************** 觀察者設(shè)計模式 **************************
    public void registerAdapterDataObserver(BannerViewPager.AdapterDataObserver observer) {
        mObservable.registerObserver(observer);
    }

    public void unregisterAdapterDataObserver(BannerViewPager.AdapterDataObserver observer) {
        mObservable.unregisterObserver(observer);
    }

    public final void notifyDataSetChanged() {
        mObservable.notifyChanged();
    }
}

BannerViewPager中定義setAdapter方法:

public void setAdapter(BannerAdapter adapter) {
        if (mBannerAdapter != null) {
            mBannerAdapter.unregisterAdapterDataObserver(mObserver);
        }
        this.mBannerAdapter = adapter;
        if (mBannerAdapter == null) {
            throw new IllegalArgumentException("BannerAdapter不能為null");
        }
        mBannerAdapter.registerAdapterDataObserver(mObserver);
        //設(shè)置切換動畫
        if (mBannerAdapter.getTransformer() != null) {
            mTransformer = mBannerAdapter.getTransformer();
            mTransformer.bind(this);
        }
        mBannerPagerAdapter = new BannerPagerAdapter();
        setAdapter(mBannerPagerAdapter);
        setCurrentItem(mBannerAdapter.getCount());
    }

在這個方法中,我們主要做了這幾件事:

1.觀察者設(shè)計模式的處理湾碎。
2.bannerViewPager翻頁動畫。
3.給ViewPager設(shè)置真實的adapter:BannerPagerAdapter奠货,下面就看下這個Adapter內(nèi)部類介褥。

在BannerViewPager中定義BannerPagerAdapter類:

private class BannerPagerAdapter extends PagerAdapter {
        @Override
        public int getCount() {
            return mBannerAdapter.getCount() == 1 ? 1 : Integer.MAX_VALUE;
        }
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            //先轉(zhuǎn)化為實際position
            final int realPosition = position % mBannerAdapter.getCount();
            View bannerItemView = mBannerAdapter.getView(realPosition);
            container.addView(bannerItemView);
            //點擊事件
            bannerItemView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mItemClickListener != null) {
                        mItemClickListener.onBannerItemClick(realPosition);
                    }
                }
            });
            return bannerItemView;
        }
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }
    }

其中有頁面點擊事件處理,很簡單的回調(diào)

    public BannerItemClickListener mItemClickListener;
    public interface BannerItemClickListener {
        void onBannerItemClick(int position);
    }
    /**
     * 條目點擊事件
     */
    public void setOnItemClickListener(BannerItemClickListener listener) {
        this.mItemClickListener = listener;
    }

這里說下無限輪播的思路,奧妙都在BannerPagerAdapter 這個類中柔滔,
getCount方法返回Integer.MAX_VALUE溢陪,代表ViewPager有2^31-1個頁面
instantiateItem方法中根據(jù)postion返回每個頁面的View,postion最大為Integer.MAX_VALUE-1睛廊,而實際頁面數(shù)只有有限的幾個形真,所以需要做下轉(zhuǎn)換final int realPosition = position % mBannerAdapter.getCount();
這樣處理后,假設(shè)有實際頁面ABC超全,最終的效果就是[position=0,頁面A] [position=1,頁面B] [position=2,頁面C] [position=3,頁面A] [position=4,頁面B] [position=5,頁面C] [position=6,頁面A] [position=7,頁面B] ...

五咆霜、自動輪播
自動輪播我們采用handler,handler.postDelayed(Runnable r, long delayMillis)嘶朱,同時傳入的runable的run方法里蛾坯,再次postDelayed調(diào)用自身runnable方法,形成一個遞歸疏遏。

//初始化handler
@SuppressLint("HandlerLeak")
    private void initHandler() {
        mHandler = new Handler();
    }
//定義一個runnable脉课,并在run方法內(nèi)再次mHandler.postDelayed自身
    private final Runnable task = new Runnable() {
        @Override
        public void run() {
            setCurrentItem(getCurrentItem() + 1);
            mHandler.postDelayed(task, mCutDownTime);
        }
    };

接著定義開始和停止?jié)L動的兩個方法,其實就是控制handler的執(zhí)行和停止任務(wù):

    public void startScroll() {
        if (mBannerAdapter == null) {
            return;
        }

        boolean scrollable = mBannerAdapter.getCount() != 1;
        if (scrollable && mHandler != null) {
            mHandler.postDelayed(task, mCutDownTime);
        }
    }

    public void stopScroll() {
        if (mBannerAdapter == null) {
            return;
        }
        if (mHandler != null) {
            mHandler.removeCallbacks(task);
        }
    }

mCutDownTime就是輪播間隔時間财异,當(dāng)然也得要提供給用戶api控制這個間隔

   private int mCutDownTime = 5000;
    /**
     * 設(shè)置每個條目切換時間
     */
    public void setCutDownTime(int millis) {
        this.mCutDownTime = millis;
    }

到此倘零,自動輪播處理完畢。

六戳寸、自定義Scroller改變ViewPager默認(rèn)切換速率
通過查看源碼我們發(fā)現(xiàn)呈驶,ViewPager的滾動內(nèi)部采用scroller控制,那么我們自己定義一個scroller將他自帶的替換掉即可庆揩。

public class BannerScroller extends Scroller {

    private int mDuration = 1000;

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

    public void setScrollerDuration(int duration) {
        this.mDuration = duration;
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy) {
        super.startScroll(startX, startY, dx, dy,mDuration);
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        super.startScroll(startX, startY, dx, dy, mDuration);
    }
}

mDuration即控制滑動時間的變量俐东。
在BannerViewPager的構(gòu)造方法中,替換默認(rèn)mScroller為我們自定義的Scroller订晌,不過遺憾的是ViewPager并沒有提供相關(guān)api虏辫,且mScroller為私有的,我們這里只能采取一些手段了:反射锈拨。

    public BannerViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);

        mConvertViews = new SparseArray<>();
        //反射修改ViewPager默認(rèn)的滾動速率
        try {
            final Field field = ViewPager.class.getDeclaredField("mScroller");
            mBannerScroller = new BannerScroller(context);
            field.setAccessible(true);
            field.set(this, mBannerScroller);
        } catch (Exception e) {
            e.printStackTrace();
        }
        initHandler();
    }

到這里砌庄,自定義ViewPager也算勉強可以用了,不過還有不少值得優(yōu)化的地方奕枢。

七娄昆、內(nèi)存優(yōu)化
回到BannerPagerAdapter這個類中,instantiateItem方法缝彬,每次獲取頁面萌焰,直接調(diào)用mBannerAdapter.getView(realPosition);,我們的輪播圖條目數(shù)可以有Integer.MAX_VALUE-1個的谷浅,這個方法就要調(diào)用無數(shù)次扒俯,頁面就要初始化無數(shù)次奶卓,而實際的頁面只有有限的幾個,能不能優(yōu)化呢撼玄,將初始化后的頁面緩存起來即可夺姑!

//用SparseArray緩存,鍵為postion掌猛,真實的postion盏浙;值為頁面數(shù)據(jù)。
private SparseArray<View> mConvertViews;
public View getConvertView(int position) {
        final View convertView = mConvertViews.get(position, null);
        if (convertView == null || convertView.getParent() != null) {
            //健壯性判斷荔茬,如果緩存的convertView有它的parent废膘,那么返回null。
            return null;
        }
        return convertView;
    }
    //內(nèi)存優(yōu)化后的BannerPagerAdapter 
    private class BannerPagerAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return mBannerAdapter.getCount() == 1 ? 1 : Integer.MAX_VALUE;
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            //先轉(zhuǎn)化為實際position
            final int realPosition = position % mBannerAdapter.getCount();
            View bannerItemView = getConvertView(realPosition);
            //先從緩存中拿兔院,如果拿不到殖卑,那么就重新初始化
            if (bannerItemView == null) {
                bannerItemView = mBannerAdapter.getView(realPosition);
            }
            container.addView(bannerItemView);
            bannerItemView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mItemClickListener != null) {
                        mItemClickListener.onBannerItemClick(realPosition);
                    }
                }
            });
            return bannerItemView;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
            //頁面從viewpager移除的同時,緩存到mConvertViews中
            mConvertViews.put(position % mBannerAdapter.getCount(), (View) object);
        }
    }

八坊萝、bug修復(fù)
在使用中孵稽,存在這樣一個現(xiàn)象,比如嵌套在RecyclerView中使用十偶,如果該bannerViewPager被滑出屏幕再滑進(jìn)屏幕菩鲜,ViewPager的第一次切換沒有動畫,很生硬惦积。
查看源碼:

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mFirstLayout = true;
    }

這個onAttachedToWindow方法中接校,將mFirstLayout 置為true,在切換時狮崩,會先判斷該字段蛛勉,只有mFirstLayout =false時才啟用滑動動畫,所以重寫該方法即可睦柴,同樣mFirstLayout 為私有诽凌,采取反射

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        try {
            //緩存反射字段
            if (mFirstLayoutField == null) {
                //反射耗時,緩存一下
                mFirstLayoutField = ViewPager.class.getDeclaredField("mFirstLayout");
            }
            mFirstLayoutField.setAccessible(true);
            mFirstLayoutField.set(this,false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        startScroll();
    }
    //離開屏幕坦敌,暫停輪播侣诵。
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopScroll();
    }

九、處理點擊暫停
手指在觸摸或滑動bannerView的時候不應(yīng)自動輪播狱窘,需要停止杜顺。
開始我想重寫onTouchEvent或onInterceptTouchEvent,都沒能達(dá)到效果蘸炸,于是我就翻看了其他BannerView的處理躬络,發(fā)現(xiàn)他們都是在dispatchTouchEvent中處理的。

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
                || action == MotionEvent.ACTION_OUTSIDE) {
            startScroll();
        } else if (action == MotionEvent.ACTION_DOWN) {
            stopScroll();
        }
        return super.dispatchTouchEvent(ev);
    }

后來我分析了一下事件分發(fā):

對于onTouchEvent搭儒,
ACTION_DOWN 不經(jīng)過此方法洗鸵,向下傳遞到它的子view時越锈,子view的onTouchEvent返回true(bannerViewPager的子view設(shè)置了點擊事件),因此事件不再回傳給該BannerViewPager。
ACTION_MOVE膘滨,ACTION_UP經(jīng)過此方法,子view不處理這兩種事件稀拐,交給該BannerViewPager處理拖拽滑動事件火邓。
對于onInterceptTouchEvent,
ACTION_DOWN 經(jīng)過此方法
ACTION_MOVE ACTION_UP不經(jīng)過此方法德撬,因為在move和up的時候铲咨,onTouchEvent處理了拖拽滑動事件,此時mFirstTouchTarget被置為null, dispatchTouchEvent處理分發(fā)事件時蜓洪,就不走onInterceptTouchEvent方法了纤勒。

十、加入切換動畫
我使用了仿魅族商店的切換動畫隆檀,具體原理看這里
http://www.reibang.com/p/e67aa68d2766
算法什么的我就無恥的直接拿過來用了
定義接口:

public interface Transformer {
    void bind(BannerViewPager viewPager);
    int getChildDrawingOrder(int childCount, int n);
}

仿魅族切換動畫

public class MZTransformImpl implements Transformer{
    //中間放大系數(shù)
    private float mScaleMax = 1.0f;
    //兩邊縮小系數(shù)
    private float mScaleMin = 0.9f;
    //重疊部分
    private float mCoverWidth = 80f;

    private ArrayList<Integer> childCenterXAbs = new ArrayList<>();
    private SparseArray<Integer> childIndex = new SparseArray<>();

    private BannerViewPager mBannerViewPager;

    @Override
    public void bind(BannerViewPager viewPager) {
        this.mBannerViewPager = viewPager;
        mBannerViewPager.setPageTransformer(true, new SPageTransformer());//默認(rèn)調(diào)用了 setChildrenDrawingOrderEnabledCompat(true);使得getChildDrawingOrder起作用
        mBannerViewPager.setClipToPadding(false);
        mBannerViewPager.setOverScrollMode(ViewPager.OVER_SCROLL_NEVER);
    }

    public int getChildDrawingOrder(int childCount, int n) {
        if (n == 0 || childIndex.size() != childCount) {
            childCenterXAbs.clear();
            childIndex.clear();
            int viewCenterX = getViewCenterX(mBannerViewPager);
            for (int i = 0; i < childCount; ++i) {
                int indexAbs = Math.abs(viewCenterX - getViewCenterX(mBannerViewPager.getChildAt(i)));
                //兩個距離相同摇天,后來的那個做自增,從而保持abs不同
                if (childIndex.get(indexAbs) != null) {
                    ++indexAbs;
                }
                childCenterXAbs.add(indexAbs);
                childIndex.append(indexAbs, i);
            }
            Collections.sort(childCenterXAbs);//1,0,2  0,1,2
        }
        //那個item距離中心點遠(yuǎn)一些恐仑,就先draw它泉坐。(最近的就是中間放大的item,最后draw)
        return childIndex.get(childCenterXAbs.get(childCount - 1 - n));
    }

    private int getViewCenterX(View view) {
        int[] array = new int[2];
        view.getLocationOnScreen(array);
        return array[0] + view.getWidth() / 2;
    }

    class SPageTransformer implements ViewPager.PageTransformer {
        private float reduceX = 0.0f;
        private float itemWidth = 0;
        private float offsetPosition = 0f;

        @Override
        public void transformPage(View view, float position) {
            if (offsetPosition == 0f) {
                float paddingLeft = mBannerViewPager.getPaddingLeft();
                float paddingRight = mBannerViewPager.getPaddingRight();
                float width = mBannerViewPager.getMeasuredWidth();
                offsetPosition = paddingLeft / (width - paddingLeft - paddingRight);
            }
            float currentPos = position - offsetPosition;
            if (itemWidth == 0) {
                itemWidth = view.getWidth();
                //由于左右邊的縮小而減小的x的大小的一半
                reduceX = (2.0f - mScaleMax - mScaleMin) * itemWidth / 2.0f;
            }
            if (currentPos <= -1.0f) {
                view.setTranslationX(reduceX + mCoverWidth);
                view.setScaleX(mScaleMin);
                view.setScaleY(mScaleMin);
            } else if (currentPos <= 1.0) {
                float scale = (mScaleMax - mScaleMin) * Math.abs(1.0f - Math.abs(currentPos));
                float translationX = currentPos * -reduceX;
                if (currentPos <= -0.5) {//兩個view中間的臨界,這時兩個view在同一層裳仆,左側(cè)View需要往X軸正方向移動覆蓋的值()
                    view.setTranslationX(translationX + mCoverWidth * Math.abs(Math.abs(currentPos) - 0.5f) / 0.5f);
                } else if (currentPos <= 0.0f) {
                    view.setTranslationX(translationX);
                } else if (currentPos >= 0.5) {//兩個view中間的臨界腕让,這時兩個view在同一層
                    view.setTranslationX(translationX - mCoverWidth * Math.abs(Math.abs(currentPos) - 0.5f) / 0.5f);
                } else {
                    view.setTranslationX(translationX);
                }
                view.setScaleX(scale + mScaleMin);
                view.setScaleY(scale + mScaleMin);
            } else {
                view.setScaleX(mScaleMin);
                view.setScaleY(mScaleMin);
                view.setTranslationX(-reduceX - mCoverWidth);
            }
        }
    }
}

回到BannerViewPager類中,setAdapter方法內(nèi)設(shè)置切換動畫

    public void setAdapter(BannerAdapter adapter) {
        ```
        //設(shè)置切換動畫
        if (mBannerAdapter.getTransformer() != null) {
            mTransformer = mBannerAdapter.getTransformer();
            mTransformer.bind(this);
        }
        ```
    }

重寫getChildDrawingOrder控制繪制順序

    /**
     * 控制子View的繪制順序
     */
    @Override
    protected int getChildDrawingOrder(int childCount, int i) {
        if (mTransformer != null) {
            return mTransformer.getChildDrawingOrder(childCount, i);
        }
        return super.getChildDrawingOrder(childCount, i);
    }

注意歧斟,如果要采用仿魅族切換樣式纯丸,在BannerViewpager需要設(shè)置paddingLeft和paddingRight,在xml中定義即可静袖。

十一觉鼻、最后講下觀察者設(shè)計模式
源碼中Adaper里很多到用到了觀察者模式,Adapter中數(shù)據(jù)變化勾徽,調(diào)用notifyDataSetChanged滑凉,那么對應(yīng)的View也會變化,這都是有一定模板套路的喘帚,也可參考上一篇http://www.reibang.com/p/d75edebb6c8f的講解畅姊。
這里就總結(jié)下套路:
首先要弄明白,誰是觀察者(observer)吹由,誰是被觀察者(observable)
記住一點:被觀察者發(fā)生變化若未,觀察者就會響應(yīng)變化。
舉個例子倾鲫,手機是被觀察者吧粗合,人就是觀察者萍嬉,手機來微信了,就是被觀察者發(fā)生變化隙疚,我們會很自然的去打開微信查看壤追,這就是觀察者響應(yīng)變化。
類比一下供屉,adapter就是被觀察者行冰,BannerViewpager就是觀察者,因為adapter數(shù)據(jù)變化伶丐,BannerViewpager得要響應(yīng)改變ui悼做。
在代碼世界里,這種響應(yīng)是如何做到的呢哗魂,為什么observer會響應(yīng)observable的變化肛走,說白了就是observable中持有了observer的引用,observable發(fā)生變化后录别,再主動調(diào)用observer相關(guān)變化的方法即可朽色。
開始上代碼,BannerViewPager中:

    private final BannerDataObserver mObserver = new BannerDataObserver();
    static class AdapterDataObservable extends Observable<AdapterDataObserver> {
        public void notifyChanged() {
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    public static abstract class AdapterDataObserver {
        public void onChanged() {

        }
    }

    private class BannerDataObserver extends AdapterDataObserver{
        @Override
        public void onChanged() {
            mBannerPagerAdapter.notifyDataSetChanged();
        }
    }

BannerAdapter中:

    private BannerViewPager.AdapterDataObservable mObservable = new BannerViewPager.AdapterDataObservable();
    //************************** 觀察者設(shè)計模式 **************************
    public void registerAdapterDataObserver(BannerViewPager.AdapterDataObserver observer) {
        mObservable.registerObserver(observer);
    }

    public void unregisterAdapterDataObserver(BannerViewPager.AdapterDataObserver observer) {
        mObservable.unregisterObserver(observer);
    }

    public final void notifyDataSetChanged() {
        mObservable.notifyChanged();
    }

在setAdapter(BannerAdapter adapter)中庶灿,將觀察者注冊給被觀察者就完事了纵搁,實質(zhì)就是依賴注入。

public void setAdapter(BannerAdapter adapter) {
        if (mBannerAdapter != null) {
            mBannerAdapter.unregisterAdapterDataObserver(mObserver);
        }
        this.mBannerAdapter = adapter;
        if (mBannerAdapter == null) {
            throw new IllegalArgumentException("BannerAdapter不能為null");
        }
        mBannerAdapter.registerAdapterDataObserver(mObserver);
       ...
    }

這樣往踢,調(diào)用BannerAdapter的notifyDataSetChanged方法就是調(diào)用 mObservable.notifyChanged();->AdapterDataObserver.onChanged()->mBannerPagerAdapter.notifyDataSetChanged();最終ui發(fā)生改變腾誉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市峻呕,隨后出現(xiàn)的幾起案子利职,更是在濱河造成了極大的恐慌,老刑警劉巖瘦癌,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猪贪,死亡現(xiàn)場離奇詭異,居然都是意外死亡讯私,警方通過查閱死者的電腦和手機热押,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斤寇,“玉大人桶癣,你說我怎么就攤上這事∧锼” “怎么了牙寞?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我间雀,道長悔详,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任惹挟,我火速辦了婚禮茄螃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匪煌。我一直安慰自己责蝠,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布萎庭。 她就那樣靜靜地躺著,像睡著了一般齿拂。 火紅的嫁衣襯著肌膚如雪驳规。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天署海,我揣著相機與錄音吗购,去河邊找鬼。 笑死砸狞,一個胖子當(dāng)著我的面吹牛捻勉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播刀森,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼踱启,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了研底?” 一聲冷哼從身側(cè)響起埠偿,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎榜晦,沒想到半個月后冠蒋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡乾胶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年抖剿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片识窿。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡斩郎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出腕扶,到底是詐尸還是另有隱情孽拷,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布半抱,位于F島的核電站脓恕,受9級特大地震影響膜宋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜炼幔,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一秋茫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乃秀,春花似錦肛著、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至刀脏,卻和暖如春局荚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背愈污。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工耀态, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人暂雹。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓首装,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杭跪。 傳聞我的和親對象是個殘疾皇子仙逻,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

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

  • 1.概述 這其實是我第一篇想寫的博客,可能是因為我遇到了太多的坑揍魂,那個時候剛?cè)胄邢铝撕芏郉emo發(fā)現(xiàn)怎么也改不動桨醋,...
    紅橙Darren閱讀 8,436評論 21 19
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,112評論 25 707
  • 一、實驗環(huán)境:centOS 二现斋、步驟 1喜最、使用yum安裝ffmpeg的依賴文件 此步驟一般不會有什么問題,就算以前...
    RookieSky閱讀 3,564評論 0 2
  • 明天你有空嗎? 小園是我初中同學(xué)了限书,是個喜歡披著頭發(fā)的女孩子虫蝶,我對她的印象也僅止于此了,高中曾經(jīng)見過幾次面倦西,大學(xué)之...
    折舍閱讀 255評論 0 0
  • 寓言大意 講了一個走出迷茫能真,恐懼,舒適區(qū)的寓言,在一個迷宮中粉铐,有兩只小鼠嗅嗅和匆匆和兩個小矮人哼哼和唧唧疼约。他們?nèi)粘?..
    小逸電影說閱讀 548評論 0 0