【Android View事件(三)】Scroll類(lèi)源碼分析與應(yīng)用


【本文出自大圣代的技術(shù)專(zhuān)欄 http://blog.csdn.net/qq_23191031
【禁止任何商業(yè)活動(dòng)。轉(zhuǎn)載煩請(qǐng)注明出處】

學(xué)前準(zhǔn)備

詳解Android控件體系與常用坐標(biāo)系
Android常用觸控類(lèi)分析:MotionEvent 般妙、 ViewConfiguration虚茶、VelocityTracker

前言

在前面的幾篇文章俐末,我向大家介紹的都是單一View事件芥永,從這篇文章開(kāi)始,我將向大家介紹連續(xù)的事件 —— 滑動(dòng)杰妓≡逯危滑動(dòng)是移動(dòng)端設(shè)備提供的重要功能,正是由于強(qiáng)大的滑動(dòng)事件讓我們小巧的屏幕可以展現(xiàn)無(wú)限的數(shù)據(jù)巷挥。而滑動(dòng)事件沖突卻常常困擾著廣大開(kāi)發(fā)者桩卵。孫子云:知己知彼,百戰(zhàn)不殆。想更好的協(xié)調(diào)滑動(dòng)事件雏节,不知道其中原理的確困難重重胜嗓。當(dāng)你學(xué)習(xí)本篇文章之后你會(huì)發(fā)現(xiàn)其實(shí)Scroll很簡(jiǎn)單,你只是被各種文章與圖書(shū)弄糊涂了钩乍。

在真正講解之前辞州,我們需要掌握Android坐標(biāo)系與觸控事件相關(guān)知識(shí),對(duì)此不太明確的同學(xué)請(qǐng)參見(jiàn)上文的 學(xué)前準(zhǔn)備

View滑動(dòng)產(chǎn)生的原理

從原理上講View滑動(dòng)的本質(zhì)就是隨著手指的運(yùn)動(dòng)不斷地改變坐標(biāo)寥粹。當(dāng)觸摸事件傳到View時(shí)变过,系統(tǒng)記下觸摸點(diǎn)的坐標(biāo),手指移動(dòng)時(shí)系統(tǒng)記下移動(dòng)后的觸摸的坐標(biāo)并算出偏移量涝涤,并通過(guò)偏移量來(lái)修改View的坐標(biāo)媚狰,不斷的重復(fù)這樣的過(guò)程,從而實(shí)現(xiàn)滑動(dòng)過(guò)程阔拳。

1 scrollTo 與 scrollBy

說(shuō)到Scroll就不得不提到scrollTo()與scrollBy()這兩個(gè)方法崭孤。

1.1 scrollTo

首先我們要知道Android每一個(gè)控件都有滾動(dòng)條,只不過(guò)系統(tǒng)對(duì)我們隱藏了衫生,所以我們看不見(jiàn)裳瘪。
對(duì)于控件來(lái)說(shuō)它的大小是有限的土浸,(例如我們指定了大小罪针、屏幕尺寸的束縛等),系統(tǒng)在繪制圖像的時(shí)候只會(huì)在這個(gè)有限的控件內(nèi)繪制黄伊,但是內(nèi)容(content)的載體Canvas在本質(zhì)上是無(wú)限的泪酱,例如我們的開(kāi)篇圖片,控件仿佛就是一個(gè)窗口我們只能通過(guò)它看到這塊畫(huà)布还最。

    /**
     * Set the scrolled position of your view. This will cause a call to
     * {@link #onScrollChanged(int, int, int, int)} and the view will be
     * invalidated.
     * @param x the x position to scroll to
     * @param y the y position to scroll to
     */
    public void scrollTo(int x, int y) {//滾動(dòng)到目標(biāo)位置
        if (mScrollX != x || mScrollY != y) {
            int oldX = mScrollX; // 已經(jīng)滾動(dòng)到的X
            int oldY = mScrollY; //已經(jīng)滾動(dòng)到的Y
            mScrollX = x;
            mScrollY = y;
            invalidateParentCaches();
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);//回調(diào)方法墓阀,通知狀態(tài)改變
            if (!awakenScrollBars()) {
                postInvalidateOnAnimation(); //重新繪制
            }
        }
    }

通過(guò)注釋Set the scrolled position of your view我們可以清楚的得知 scrollTo(x,y)的作用就是將View滾動(dòng)到(x,y)這個(gè)點(diǎn),注意是滾動(dòng)(scroll本意滾動(dòng)拓轻,滑動(dòng)是translate)斯撮。

在初始時(shí) mScrollX 與mScrollY均為0,表示著View中展示的是從畫(huà)布左上角開(kāi)始的內(nèi)容(如圖 1)扶叉,當(dāng)調(diào)用scrollTo(100,100)時(shí)相當(dāng)于將View的坐標(biāo)原點(diǎn)滾動(dòng)到(100,100)這個(gè)位置勿锅,展示畫(huà)布上從(100,100)開(kāi)始的內(nèi)容(如圖2),但是事實(shí)上View是靜止不動(dòng)的枣氧,所以最終的效果是View的內(nèi)容平移了(-100,-100)的偏移量(如圖3)

image.png

1.2 scrollBy

/** 
    * Move the scrolled position of your view. This will cause a call to 
    * {@link #onScrollChanged(int, int, int, int)} and the view will be 
    * invalidated. 
    * @param x the amount of pixels to scroll by horizontally 
    * @param y the amount of pixels to scroll by vertically 
    */  
   public void scrollBy(int x, int y) {  
       scrollTo(mScrollX + x, mScrollY + y);  
   }  

學(xué)習(xí)scrollTo在學(xué)習(xí)scrollBy就簡(jiǎn)單了溢十,通過(guò)源碼可以看到它里面調(diào)用了ScrollTo(),傳入的參數(shù)是mScrollX+x,也就是說(shuō)這次x是一個(gè)增量达吞,所以scrollBy實(shí)現(xiàn)的效果就是张弛,在當(dāng)前位置上,再偏移x距離
這是ScrollTo()和ScrollBy()的重要區(qū)別。

1.3 小結(jié):

  1. scrollTo與scrollBy都會(huì)另View立即重繪吞鸭,所以移動(dòng)是瞬間發(fā)生的
  2. scrollTo(x,y):指哪打哪寺董,效果為View的左上角滾動(dòng)到(x,y)位置,但由于View相對(duì)與父View是靜止的所以最終轉(zhuǎn)換為相對(duì)的View的內(nèi)容滑動(dòng)到(-x,-y)的位置瞒大。
  3. scrollBy(x,y): 此時(shí)的x,y為偏移量螃征,既在原有的基礎(chǔ)上再次滾動(dòng)
  4. scrollTo與scrollBy的最用效果會(huì)作用到View的內(nèi)容,所以要是想滑動(dòng)當(dāng)前View,就需要對(duì)其父View調(diào)用二者透敌。也可以在當(dāng)前View中使用((View)getParent).scrollXX(x,y)達(dá)到同樣目的盯滚。

2 Scroller

OK,通過(guò)上面的學(xué)習(xí)我們知道scrollTo與scrollBy可以實(shí)現(xiàn)滑動(dòng)的效果酗电,但是滑動(dòng)的效果都是瞬間完成的魄藕,在事件執(zhí)行的時(shí)候平移就已經(jīng)完成了,這樣的效果會(huì)讓人感覺(jué)突兀撵术,Google建議使用自然過(guò)渡的動(dòng)畫(huà)來(lái)實(shí)現(xiàn)移動(dòng)效果背率。因此,Scroller類(lèi)這樣應(yīng)運(yùn)而生了嫩与。

2.1 簡(jiǎn)單實(shí)例

舉一個(gè)簡(jiǎn)單的實(shí)例方便大家的理解與學(xué)習(xí) Scroller

主要代碼

public class CustomScrollerView extends LinearLayout {
    private Scroller mScroller;

    private View mLeftView;
    private View mRightView;

    private float mInitX, mInitY;
    private float mOffsetX, mOffsetY;

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

    public CustomScrollerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomScrollerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        this.setOrientation(LinearLayout.HORIZONTAL);

        mScroller = new Scroller(getContext(), null, true);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        if (getChildCount() != 2) {
            throw new RuntimeException("Only need two child view! Please check you xml file!");
        }

        mLeftView = getChildAt(0);
        mRightView = getChildAt(1);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mInitX = ev.getX();
                mInitY = ev.getY();
                super.dispatchTouchEvent(ev);
                return true;
            case MotionEvent.ACTION_MOVE:
                //>0為手勢(shì)向右下
                mOffsetX = ev.getX() - mInitX;
                mOffsetY = ev.getY() - mInitY;
                //橫向手勢(shì)跟隨移動(dòng)
                if (Math.abs(mOffsetX) - Math.abs(mOffsetY) > ViewConfiguration.getTouchSlop()) {
                    int offset = (int) -mOffsetX;
                    if (getScrollX() + offset > mRightView.getWidth() || getScrollX() + offset < 0) {
                        return true;
                    }
                    this.scrollBy(offset, 0);
                    mInitX = ev.getX();
                    mInitY = ev.getY();
                    return true;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                //松手時(shí)刻滑動(dòng)
                int offset = ((getScrollX() / (float) mRightView.getWidth()) > 0.5) ? mRightView.getWidth() : 0;
//                this.scrollTo(offset, 0);
                mScroller.startScroll(this.getScrollX(), this.getScrollY(), offset - this.getScrollX(), 0);
                invalidate();
                mInitX = 0;
                mInitY = 0;
                mOffsetX = 0;
                mOffsetY = 0;
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            this.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate(); //允許在非主線程中出發(fā)重繪寝姿,它的出現(xiàn)就是簡(jiǎn)化我們?cè)诜荱I線程更新view的步驟
        }
    }
}

主要布局

    <com.im_dsd.blogdemo.CustomScrollerView
        android:layout_width="200sp"
        android:layout_height="200sp"
        android:layout_centerInParent="true"
        android:orientation="horizontal"
        >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/holo_blue_light"/>

        <TextView
            android:layout_width="100sp"
            android:layout_height="match_parent"
            android:background="@android:color/holo_green_light"/>
    </com.im_dsd.blogdemo.CustomScrollerView>
項(xiàng)目效果

通過(guò)上面實(shí)例我們可以發(fā)現(xiàn)在自定義View的過(guò)程中使用Scroller的流程如下圖所示:

下面我們就按照這個(gè)流程進(jìn)行源碼分析吧

2.2 源碼分析

對(duì)于Scroller類(lèi) Google給出的如下解釋?zhuān)?/p>

This class encapsulates scrolling. You can use scrollers ( Scroller or OverScroller) to collect the data you need to produce a scrolling animation
for example, in response to a fling gesture. Scrollers track scroll offsets for you over time, but they don't automatically apply those positions to your view. It's your responsibility to get and apply new coordinates at a rate that will make the scrolling animation look smooth.

我們中可以看出:Scroller 是一個(gè)工具類(lèi),它只是產(chǎn)生一些坐標(biāo)數(shù)據(jù)划滋,而真正讓View平滑的滾動(dòng)起來(lái)還需要我們自行處理饵筑。我們使用的處理工具就是—— scrollTo與scrollBy

2.2.1 構(gòu)造方法分析

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

public Scroller(Context context, Interpolator interpolator) {
    this(context, interpolator,
        context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
}

public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
    mFinished = true;
    if (interpolator == null) {
        mInterpolator = new ViscousFluidInterpolator();
    } else {
        mInterpolator = interpolator;
    }
    mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
    //摩擦力計(jì)算單位時(shí)間減速度
    mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
    mFlywheel = flywheel;

    mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning
}

Scroller的構(gòu)造方法沒(méi)啥特殊的地方只不過(guò)第二個(gè)參數(shù)interpolator是插值器,不同的插值器實(shí)現(xiàn)不同的動(dòng)畫(huà)算法(這里不是重點(diǎn)不做展開(kāi),以后重點(diǎn)講解)处坪,如果我們不傳根资,則默認(rèn)使用ViscousFluidInterpolator()插值器。

2.2.2 startScroll與fling

/** 
     * 使用默認(rèn)滑動(dòng)時(shí)間完成滑動(dòng) 
     */  
    public void startScroll(int startX, int startY, int dx, int dy) {  
        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);  
    }  
  
    /** 
     * 在我們想要滾動(dòng)的地方調(diào)運(yùn)同窘,準(zhǔn)備開(kāi)始滾動(dòng)玄帕,手動(dòng)設(shè)置滾動(dòng)時(shí)間
     *  
     * @param startX  滑動(dòng)起始X坐標(biāo) 
     * @param startY     滑動(dòng)起始Y坐標(biāo) 
     * @param dx    X方向滑動(dòng)距離 
     * @param dy   Y方向滑動(dòng)距離 
     * @param duration  完成滑動(dòng)所需的時(shí)間      
     */  
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {  
        mMode = SCROLL_MODE;  
        mFinished = false;  
        mDuration = duration;  
        mStartTime = AnimationUtils.currentAnimationTimeMillis();//獲取當(dāng)前時(shí)間作為滑動(dòng)的起始時(shí)間  
        mStartX = startX;  
        mStartY = startY;  
        mFinalX = startX + dx;  
        mFinalY = startY + dy;  
        mDeltaX = dx;  
        mDeltaY = dy;  
        mDurationReciprocal = 1.0f / (float) mDuration;  
    }  

    /** 
     * 開(kāi)始基于滑動(dòng)手勢(shì)的滑動(dòng)。根據(jù)初始的滑動(dòng)手勢(shì)速度想邦,決定滑動(dòng)的距離(滑動(dòng)的距離裤纹,不能大于設(shè)定的最大值,不能小于設(shè)定的最小值)  
     */
public void fling(int startX, int startY, int velocityX, int velocityY,
    int minX, int maxX, int minY, int maxY) {
    ......
    mMode = FLING_MODE;
    mFinished = false;
    ......
    mStartX = startX;
    mStartY = startY;
    ......
    mDistance = (int) (totalDistance * Math.signum(velocity));

    mMinX = minX;
    mMaxX = maxX;
    mMinY = minY;
    mMaxY = maxY;
    ......
    mFinalY = Math.min(mFinalY, mMaxY);
    mFinalY = Math.max(mFinalY, mMinY);
}

在這兩個(gè)方法中丧没,都是一些全局變量的賦值鹰椒,果真沒(méi)有實(shí)現(xiàn)滾動(dòng)的方法,也佐證了Scroller是一個(gè)工具的解讀骂铁。而要實(shí)現(xiàn)滑動(dòng)還是要依靠我們手動(dòng)調(diào)用View的invalidated()方法觸發(fā)computeScroll()方法吹零。

   @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            this.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
             postInvalidate(); //允許在非主線程中出發(fā)重繪,它的出現(xiàn)就是簡(jiǎn)化我們?cè)诜荱I線程更新view的步驟
        }
    }

一旦觸發(fā)成功就會(huì)調(diào)用Scroller.computeScrollOffset()方法拉庵,返回結(jié)果如果為true表示當(dāng)前的滑動(dòng)尚未結(jié)束灿椅,如果返回false表示滑動(dòng)完成。
在Scroller類(lèi)中,最最重要的就是這個(gè)computeScrollOffset方法茫蛹,看上去只是返回了一個(gè)boolean類(lèi)型操刀,但他卻是Scroller的核心,所有的坐標(biāo)與滑動(dòng)時(shí)間都由它計(jì)算完成婴洼。他將原本瞬間的滑動(dòng)拆分成連續(xù)平滑的過(guò)程骨坑。

/** 
     * Call this when you want to know the new location.  If it returns true, 
     * the animation is not yet finished.  loc will be altered to provide the 
     * new location. 
     * 調(diào)用這個(gè)函數(shù)獲得新的位置坐標(biāo)(滑動(dòng)過(guò)程中)。如果它返回true柬采,說(shuō)明滑動(dòng)沒(méi)有結(jié)束欢唾。 
     * getCurX(),getCurY()方法就可以獲得計(jì)算后的值。 
     */   
    public boolean computeScrollOffset() {  
        if (mFinished) {//是否結(jié)束  
            return false;  
        }  
  
        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);//滑動(dòng)開(kāi)始粉捻,經(jīng)過(guò)了多長(zhǎng)時(shí)間  
      
        if (timePassed < mDuration) {//如果經(jīng)過(guò)的時(shí)間小于動(dòng)畫(huà)完成所需時(shí)間  
            switch (mMode) {  
            case SCROLL_MODE:  
                float x = timePassed * mDurationReciprocal;  
      
                if (mInterpolator == null)//如果沒(méi)有設(shè)置插值器礁遣,利用默認(rèn)算法  
                    x = viscousFluid(x);   
                else//否則利用插值器定義的算法  
                    x = mInterpolator.getInterpolation(x);  
      
                mCurrX = mStartX + Math.round(x * mDeltaX);//計(jì)算當(dāng)前X坐標(biāo)  
                mCurrY = mStartY + Math.round(x * mDeltaY);//計(jì)算當(dāng)前Y坐標(biāo)  
                break;  
            case FLING_MODE:  
                final float t = (float) timePassed / mDuration;  
                final int index = (int) (NB_SAMPLES * t);  
                final float t_inf = (float) index / NB_SAMPLES;  
                final float t_sup = (float) (index + 1) / NB_SAMPLES;  
                final float d_inf = SPLINE[index];  
                final float d_sup = SPLINE[index + 1];  
                final float distanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf);  
                  
                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));  
                // Pin to mMinX <= mCurrX <= mMaxX  
                mCurrX = Math.min(mCurrX, mMaxX);  
                mCurrX = Math.max(mCurrX, mMinX);  
                  
                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));  
                // Pin to mMinY <= mCurrY <= mMaxY  
                mCurrY = Math.min(mCurrY, mMaxY);  
                mCurrY = Math.max(mCurrY, mMinY);  
  
                if (mCurrX == mFinalX && mCurrY == mFinalY) {  
                    mFinished = true;  
                }  
  
                break;  
            }  
        }  
        else {  
            mCurrX = mFinalX;  
            mCurrY = mFinalY;  
            mFinished = true;  
        }  
        return true;  
    }  

從代碼可以看到,如果我們沒(méi)有設(shè)置插值器肩刃,就會(huì)調(diào)用內(nèi)部默認(rèn)算法祟霍。

/** 
     * 函數(shù)翻譯是粘性流體 
     * 估計(jì)是一種算法 
     */  
    static float viscousFluid(float x)  
    {  
        x *= sViscousFluidScale;  
        if (x < 1.0f) {  
            x -= (1.0f - (float)Math.exp(-x));  
        } else {  
            float start = 0.36787944117f;   // 1/e == exp(-1)  
            x = 1.0f - (float)Math.exp(1.0f - x);  
            x = start + x * (1.0f - start);  
        }  
        x *= sViscousFluidNormalize;  
        return x;  
    }  

接著是兩個(gè)重要的get方法

/** 
     * Returns the current X offset in the scroll.  
     *  
     * @return The new X offset as an absolute distance from the origin. 
     * 獲得當(dāng)前X方向偏移 
     */  
    public final int getCurrX() {  
        return mCurrX;  
    }  
      
    /** 
     * Returns the current Y offset in the scroll.  
     *  
     * @return The new Y offset as an absolute distance from the origin. 
     * 獲得當(dāng)前Y方向偏移 
     */  
    public final int getCurrY() {  
        return mCurrY;  
    }  

2.2.3 其他方法

public class Scroller  {
    ......
    public Scroller(Context context) {}
    public Scroller(Context context, Interpolator interpolator) {}
    public Scroller(Context context, Interpolator interpolator, boolean flywheel) {}
    //設(shè)置滾動(dòng)持續(xù)時(shí)間
    public final void setFriction(float friction) {}
    //返回滾動(dòng)是否結(jié)束
    public final boolean isFinished() {}
    //強(qiáng)制終止?jié)L動(dòng)
    public final void forceFinished(boolean finished) {}
        //返回滾動(dòng)持續(xù)時(shí)間
    public final int getDuration() {}
    //返回當(dāng)前滾動(dòng)的偏移量
    public final int getCurrX() {}
    public final int getCurrY() {}
    //返回當(dāng)前的速度
    public float getCurrVelocity() {}
    //返回滾動(dòng)起始點(diǎn)偏移量
    public final int getStartX() {}
    public final int getStartY() {}
        //返回滾動(dòng)結(jié)束偏移量
    public final int getFinalX() {}
    public final int getFinalY() {}
    //實(shí)時(shí)調(diào)用該方法獲取坐標(biāo)及判斷滑動(dòng)是否結(jié)束,返回true動(dòng)畫(huà)沒(méi)結(jié)束
    public boolean computeScrollOffset() {}
    //滑動(dòng)到指定位置
    public void startScroll(int startX, int startY, int dx, int dy) {}
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {}
    //快速滑動(dòng)松開(kāi)手勢(shì)慣性滑動(dòng)
    public void fling(int startX, int startY, int velocityX, int velocityY,
            int minX, int maxX, int minY, int maxY) {}
    //終止動(dòng)畫(huà)盈包,滾到最終的x沸呐、y位置
    public void abortAnimation() {}
    //延長(zhǎng)滾動(dòng)的時(shí)間
    public void extendDuration(int extend) {}
    //返回滾動(dòng)開(kāi)始經(jīng)過(guò)的時(shí)間
    public int timePassed() {}
    //設(shè)置終止時(shí)偏移量
    public void setFinalX(int newX) {}
    public void setFinalY(int newY) {}
}

3 總結(jié):

  1. 滑動(dòng)的本質(zhì)就是View隨著手指的運(yùn)動(dòng)不斷地改變坐標(biāo)
  2. scrollTo(x,y)指的就是View滾動(dòng)到(x,y)這個(gè)位置,但是View 要相當(dāng)于父控件靜止不懂呢燥,所以相對(duì)的View的內(nèi)容就會(huì)滑動(dòng)到(-x, -y)的位置
  3. scrollTo崭添、scrollBy移動(dòng)是瞬間的
  4. 滑動(dòng)效果作用的對(duì)象是View內(nèi)容
  5. Scroller類(lèi)其實(shí)是一個(gè)工具類(lèi),生產(chǎn)滑動(dòng)過(guò)程的平滑坐標(biāo)疮茄,但最終的滑動(dòng)動(dòng)作還是需要我們自行處理
  6. Scroller類(lèi)的使用流程:

參考

《Android群英傳》
http://blog.csdn.net/crazy__chen/article/details/45896961
http://blog.csdn.net/yanbober/article/details/49904715

版權(quán)聲明:
禁止一切商業(yè)行為滥朱,轉(zhuǎn)載請(qǐng)著名出處 http://blog.csdn.net/qq_23191031根暑。作者: 大圣代
Copyright (c) 2017 代圣達(dá). All rights reserved.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末力试,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子排嫌,更是在濱河造成了極大的恐慌畸裳,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淳地,死亡現(xiàn)場(chǎng)離奇詭異怖糊,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)颇象,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)伍伤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人遣钳,你說(shuō)我怎么就攤上這事扰魂。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵劝评,是天一觀的道長(zhǎng)姐直。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蒋畜,這世上最難降的妖魔是什么声畏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮姻成,結(jié)果婚禮上插龄,老公的妹妹穿的比我還像新娘。我一直安慰自己科展,他們只是感情好辫狼,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著辛润,像睡著了一般膨处。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上砂竖,一...
    開(kāi)封第一講書(shū)人閱讀 49,985評(píng)論 1 291
  • 那天真椿,我揣著相機(jī)與錄音,去河邊找鬼乎澄。 笑死突硝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的置济。 我是一名探鬼主播解恰,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼浙于!你這毒婦竟也來(lái)了护盈?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤羞酗,失蹤者是張志新(化名)和其女友劉穎腐宋,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體檀轨,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胸竞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了参萄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卫枝。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖讹挎,靈堂內(nèi)的尸體忽然破棺而出校赤,到底是詐尸還是另有隱情腺占,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布痒谴,位于F島的核電站衰伯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏积蔚。R本人自食惡果不足惜意鲸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尽爆。 院中可真熱鬧怎顾,春花似錦、人聲如沸漱贱。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)幅狮。三九已至募强,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崇摄,已是汗流浹背擎值。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逐抑,地道東北人鸠儿。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像厕氨,于是被迫代替她去往敵國(guó)和親进每。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,849評(píng)論 25 707
  • 什么是View View 是 Android 中所有控件的基類(lèi)命斧。 View的位置參數(shù) View 的位置由它的四個(gè)頂...
    acc8226閱讀 1,156評(píng)論 0 7
  • 導(dǎo)語(yǔ) 滑動(dòng)算是Android比較常用的效果了田晚,滑動(dòng)的操作具有很好的用戶體驗(yàn)性。 主要內(nèi)容 滑動(dòng)效果是如何產(chǎn)生的 實(shí)...
    一個(gè)有故事的程序員閱讀 6,443評(píng)論 3 11
  • [轉(zhuǎn)載]被“辯證法”毒害的中國(guó)人 一個(gè)無(wú)敵句式——你要辯證的看問(wèn)題 無(wú)論你說(shuō)啥觀點(diǎn)冯丙,“辯證的看問(wèn)題”都能將你輕易擊...
    白鹿格桑閱讀 2,292評(píng)論 0 6
  • 第一天彩鉛畫(huà)櫻桃 給跪了肉瓦,完全不是那么回事啊遭京,覺(jué)比之前用中性筆畫(huà)畫(huà)難萬(wàn)倍胃惜。一種畫(huà)叫人家的,一種畫(huà)叫我畫(huà)的哪雕。 看來(lái)我...
    吉祥天的夏天閱讀 479評(píng)論 3 4