深入了解ScrollView

做android開發(fā)也有很長(zhǎng)一段時(shí)間類批什,一直沒有仔細(xì)想過ScrollView是怎么實(shí)現(xiàn)的哆窿,如何實(shí)現(xiàn)滾動(dòng)的链烈,所以就去研究類一下其源碼,順便做一下筆記挚躯,望日后好查閱强衡。俗話說好記性不如爛筆頭嘛。小弟不才码荔,哪里理解錯(cuò)了還望大神指教漩勤,再此先謝過。

理論上弄清楚源碼是怎么做的缩搅,我們按照這個(gè)邏輯也可以寫出一個(gè)的ScrollView的越败,所有我也寫了一個(gè)ScrollView,留作參考硼瓣。這個(gè)ScrollView對(duì)于滑動(dòng)到邊界的處理究飞,只做了回彈的處理。所以支持邊界阻尼回彈的ScrollView堂鲤。

原理請(qǐng)參考:實(shí)現(xiàn)一個(gè)ScrollView

項(xiàng)目地址:https://github.com/cyuanyang/ScrollView.git

FillViewport

眾所周知ScrollView有一個(gè)FillViewport屬性亿傅,而他的實(shí)現(xiàn)也很簡(jiǎn)單,下面是源碼瘟栖,注釋是依照我的理解自己加上去的葵擎。

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //1.如果是false 按照父視圖的測(cè)量方式測(cè)量ScrollView的子View的寬高 
        // 即使你的子View設(shè)置math_parant 也只當(dāng)者wrap_content處理
        if (!mFillViewport) {
            return;
        }

        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode == MeasureSpec.UNSPECIFIED) {
            return;
        }

        //2.如果設(shè)置mFillViewport=true 則會(huì)走這里開始測(cè)量子View的寬高
        if (getChildCount() > 0) {
            //3.因?yàn)镾crollView有且只有一個(gè)子View所以直接取第一個(gè)
            final View child = getChildAt(0);
            final int widthPadding;
            final int heightPadding;
            final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
            //4.拿到布局參數(shù)
            final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
            //5.計(jì)算padding
            if (targetSdkVersion >= VERSION_CODES.M) {
                widthPadding = mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin;
                heightPadding = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin;
            } else {
                widthPadding = mPaddingLeft + mPaddingRight;
                heightPadding = mPaddingTop + mPaddingBottom;
            }

            //6. desiredHeight 是scrollView的高度減去上下margin剩下的高度 如果child的高度小于這個(gè)才去測(cè)量 
            // 如果大于的話已經(jīng)充滿里沒必要再折騰一次 源碼的水平還是很有質(zhì)量的
            final int desiredHeight = getMeasuredHeight() - heightPadding;
            if (child.getMeasuredHeight() < desiredHeight) {
                //7. 計(jì)算寬高 調(diào)用child的measure  完成
                final int childWidthMeasureSpec = getChildMeasureSpec(
                        widthMeasureSpec, widthPadding, lp.width);
                final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                        desiredHeight, MeasureSpec.EXACTLY);
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }

這里細(xì)心的人可能會(huì)有疑問,scrollView是FrameLayout的子類半哟,而mFillViewport=false酬滤,會(huì)調(diào)用 super.onMeasure()測(cè)量子View的寬高,這樣我們也會(huì)得到一個(gè)正確的值的镜沽。事實(shí)并不是這么簡(jiǎn)單的敏晤,再FrameLayout的測(cè)量View的方法中,測(cè)量child是有一個(gè)額外條件

if (mMeasureAllChildren || child.getVisibility() != GONE)

mMeasureAllChildren再mFillViewport為false的時(shí)候就是false

onInterceptTouchEvent

這個(gè)方法對(duì)于ScrollView是很關(guān)鍵的缅茉。如果想要滑動(dòng)嘴脾,肯定得返回true的,但是又不能全部返回true要不子View就接受不到事件了。這個(gè)方法就是處理何時(shí)該攔截事件译打。還是拿關(guān)鍵的源碼說話耗拓。如果不懂mScroller或者VelocityTracker請(qǐng)參考實(shí)現(xiàn)一個(gè)ScrollView

case MotionEvent.ACTION_DOWN: {
                // 1. 如果按下的位置在不在 子View上
                final int y = (int) ev.getY();
                if (!inChild((int) ev.getX(), (int) y)) {
                    mIsBeingDragged = false;
                    recycleVelocityTracker();
                    break;
                }

                /*
                 * 2. 記住down事件 取第一個(gè)手指
                 * Remember location of down touch.
                 * ACTION_DOWN always refers to pointer index 0.
                 */
                mLastMotionY = y;
                mActivePointerId = ev.getPointerId(0);

                initOrResetVelocityTracker();
                //3. 這個(gè)是計(jì)算速率的 主要用來計(jì)算手指離開后的fling的速率
                mVelocityTracker.addMovement(ev);
                /*
                 * If being flinged and user touches the screen, initiate drag;
                 * otherwise don't. mScroller.isFinished should be false when
                 * being flinged. We need to call computeScrollOffset() first so that
                 * isFinished() is correct.
                 *
                */
                //4. 下面是如何區(qū)分是點(diǎn)擊子View還是拖動(dòng)ScrollView 原因上面源碼注釋也很清楚
                //如果mScroller再滾動(dòng) 即認(rèn)為是拖動(dòng) 直接賦值true
                mScroller.computeScrollOffset();
                mIsBeingDragged = !mScroller.isFinished();
                if (mIsBeingDragged && mScrollStrictSpan == null) {
                    mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
                }
                startNestedScroll(SCROLL_AXIS_VERTICAL);
                break;
            }

這個(gè)是move事件的處理

                //6.如果先點(diǎn)擊沒有滑動(dòng),攔截事件中為false奏司,ScrollView中的button也能接受到事件乔询,這是再根據(jù)滑動(dòng)的距離來決定是不是需要攔截事件
                //mTouchSlop(這個(gè)值是一個(gè)系統(tǒng)值,判斷滑動(dòng)的一個(gè)閾值)
                final int y = (int) ev.getY(pointerIndex);
                final int yDiff = Math.abs(y - mLastMotionY);
                if (yDiff > mTouchSlop && (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {
                    mIsBeingDragged = true;
                    //7.  賦值down后的y的位置
                    mLastMotionY = y;
                    //8. 初始化速率軌跡計(jì)算 主要用來計(jì)算手指離開后的fling的速率
                    initVelocityTrackerIfNotExists();
                    mVelocityTracker.addMovement(ev);
                    mNestedYOffset = 0;
                    if (mScrollStrictSpan == null) {
                        mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
                    }
                    final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }

onTouchEvent

這個(gè)是ScrollView最關(guān)鍵韵洋,最關(guān)鍵竿刁,最關(guān)鍵的地方,重要的話說三遍搪缨。理解這個(gè)地方后自己就可以寫出一個(gè)ScrollView了食拜。還是拿代碼說話吧

    //代碼不必要每一步都懂 只需要理解關(guān)鍵的地方即可,畢竟android是一個(gè)系統(tǒng)副编,考慮的很多很多负甸,我們沒有必要理解每一句代碼的含義
    //所以這里列舉一下關(guān)鍵的地方
    public boolean onTouchEvent(MotionEvent ev) {
        //1. 如果沒有初始化速率軌跡 初始化它,這個(gè)還是用于手指離開后計(jì)算fling的
        initVelocityTrackerIfNotExists();

        MotionEvent vtev = MotionEvent.obtain(ev);

        final int actionMasked = ev.getActionMasked();

        if (actionMasked == MotionEvent.ACTION_DOWN) {
            mNestedYOffset = 0;
        }
        vtev.offsetLocation(0, mNestedYOffset);

        switch (actionMasked) {
            case MotionEvent.ACTION_DOWN: {
                if (getChildCount() == 0) {
                    return false;
                }
                //2.請(qǐng)求父視圖不要攔截
                if ((mIsBeingDragged = !mScroller.isFinished())) {
                    final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }

                /*
                 * If being flinged and user touches, stop the fling. isFinished
                 * will be false if being flinged.
                 */
                //3. 如果當(dāng)前在fling 就是mScroller還沒有完成就觸摸了
                //立刻放棄當(dāng)前的滾動(dòng)
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                    if (mFlingStrictSpan != null) {
                        mFlingStrictSpan.finish();
                        mFlingStrictSpan = null;
                    }
                }

                // Remember where the motion event started
                //4. 記住觸摸的位置 mLastMotionY 這個(gè)值在move的時(shí)候用來計(jì)算手指移動(dòng)的變化量痹届,然后用來計(jì)算需要滾動(dòng)的距離
                mLastMotionY = (int) ev.getY();
                mActivePointerId = ev.getPointerId(0);
                //5. 這個(gè)是處理內(nèi)部滾動(dòng) 可以先不用管這個(gè)
                //涉及到Nested的都可以先不用管它  這個(gè)好像是為了支持v4包內(nèi)的某個(gè)功能做的處理
                startNestedScroll(SCROLL_AXIS_VERTICAL);
                break;
            }
            case MotionEvent.ACTION_MOVE:
                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                if (activePointerIndex == -1) {
                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
                    break;
                }

                final int y = (int) ev.getY(activePointerIndex);
                //6. deltaY 計(jì)算手指移動(dòng)的距離 在4中記錄的 同時(shí)下面還會(huì)更新這個(gè)值 8中會(huì)用到這個(gè)值來計(jì)算需要滾動(dòng)的距離
                int deltaY = mLastMotionY - y;
                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
                    deltaY -= mScrollConsumed[1];
                    vtev.offsetLocation(0, mScrollOffset[1]);
                    mNestedYOffset += mScrollOffset[1];
                }
                //7. 如果先點(diǎn)擊沒有滑動(dòng)呻待,攔截事件中為false,ScrollView中的button也能接受到事件队腐,這是再根據(jù)滑動(dòng)的距離來決定是不是需要攔截事件
                if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
                    final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                    mIsBeingDragged = true;
                    if (deltaY > 0) {
                        deltaY -= mTouchSlop;
                    } else {
                        deltaY += mTouchSlop;
                    }
                }
                if (mIsBeingDragged) {
                    // Scroll to follow the motion event
                    //更新mLastMotionY 這個(gè)很關(guān)鍵 否則根本滑不懂
                    mLastMotionY = y - mScrollOffset[1];

                    final int oldY = mScrollY;
                    final int range = getScrollRange();
                    final int overscrollMode = getOverScrollMode();
                    boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
                            (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);

                    // Calling overScrollBy will call onOverScrolled, which
                    // calls onScrollChanged if applicable.
                    //8. 調(diào)用overScrollBy方法計(jì)算滾動(dòng) 這個(gè)方法就是計(jì)算一下滾動(dòng)的距離然后回調(diào)給onOverScrolled()在這里調(diào)用scrollTo方法
                    // 到這里的時(shí)候 ScrollView還不會(huì)滾動(dòng)蚕捉,滾動(dòng)的代碼在onOverScrolled()中,緊接著下面會(huì)出現(xiàn)
                    // 這里返回true表示滑動(dòng)超出了內(nèi)容區(qū)域 像滑倒頂部會(huì)有阻尼的那種效果就可以用這個(gè)實(shí)現(xiàn)
                    // 這個(gè)是最關(guān)鍵的地方 關(guān)鍵的源碼都有注釋 厲害了word
                    if (overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true)
                            && !hasNestedScrollingParent()) {
                        // Break our velocity if we hit a scroll barrier.
                        mVelocityTracker.clear();
                    }

                    //9.下面就沒必要仔細(xì)去研究了 這里處理一下滑到邊界出的效果
                    final int scrolledDeltaY = mScrollY - oldY;
                    final int unconsumedY = deltaY - scrolledDeltaY;
                    if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {
                        mLastMotionY -= mScrollOffset[1];
                        vtev.offsetLocation(0, mScrollOffset[1]);
                        mNestedYOffset += mScrollOffset[1];
                    } else if (canOverscroll) {
                        final int pulledToY = oldY + deltaY;
                        if (pulledToY < 0) {
                            mEdgeGlowTop.onPull((float) deltaY / getHeight(),
                                    ev.getX(activePointerIndex) / getWidth());
                            if (!mEdgeGlowBottom.isFinished()) {
                                mEdgeGlowBottom.onRelease();
                            }
                        } else if (pulledToY > range) {
                            mEdgeGlowBottom.onPull((float) deltaY / getHeight(),
                                    1.f - ev.getX(activePointerIndex) / getWidth());
                            if (!mEdgeGlowTop.isFinished()) {
                                mEdgeGlowTop.onRelease();
                            }
                        }
                        if (mEdgeGlowTop != null
                                && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
                            postInvalidateOnAnimation();
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mIsBeingDragged) {
                    //10. 速率軌跡終于要大顯神威了
                    // up后 8中的計(jì)算滾動(dòng)就會(huì)停止柴淘,但是實(shí)際上ScrollView還會(huì)滾動(dòng)一段距離
                    // 這里根據(jù) VelocityTracker 得到手指離開這一瞬間的Velocity
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);

                    //11. 速錄很大 則會(huì)認(rèn)為是一個(gè)fling 動(dòng)作
                    // flingWithNestedDispatch()方法內(nèi)部就是執(zhí)行了mScroller.fling()方法
                    //else if 含義:速錄很小鱼冀,例如我們滑動(dòng)最后停下來,然后手指離開屏幕悠就,這時(shí)的速率可能為0,就不需要fling
                    //但是若滑動(dòng)到頂部就需要回彈動(dòng)畫 充易,直接動(dòng)用 mScroller.springBack()即可
                    if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
                        flingWithNestedDispatch(-initialVelocity);
                    } else if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0,
                            getScrollRange())) {
                        postInvalidateOnAnimation();
                    }

                    mActivePointerId = INVALID_POINTER;
                    endDrag();
                }
                break;
            //12. 取消事件的處理 類似于up事件 理解上面的下面的多個(gè)觸摸點(diǎn)的處理就很簡(jiǎn)單了
            case MotionEvent.ACTION_CANCEL:
                if (mIsBeingDragged && getChildCount() > 0) {
                    if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
                        postInvalidateOnAnimation();
                    }
                    mActivePointerId = INVALID_POINTER;
                    endDrag();
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN: {
                final int index = ev.getActionIndex();
                mLastMotionY = (int) ev.getY(index);
                mActivePointerId = ev.getPointerId(index);
                break;
            }
            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
                break;
        }

剛剛在8中調(diào)用overScrollBy用來計(jì)算滾動(dòng)的距離然后回調(diào)給onOverScrolled來處理是否需要滾動(dòng)梗脾,這里就是處理邏輯

    @Override
    protected void onOverScrolled(int scrollX, int scrollY,
                                  boolean clampedX, boolean clampedY) {
        // Treat animating scrolls differently; see #computeScroll() for why.
        //這個(gè)if是用來區(qū)別mScroller滾動(dòng)調(diào)用的還是手指拖動(dòng)滾動(dòng)的
        //mScroller.isFinished()為true 就是手指拖動(dòng)引起的滾動(dòng) 直接調(diào)用super.scrollTo,這樣就完成了滾動(dòng)  完美
        //if代碼塊其實(shí)就是一個(gè)和scrollTo的代碼差不多盹靴,這里并沒有直接調(diào)用我也不知道為什么炸茧,看注解也沒太明白,哪位大神知道麻煩告訴我一下稿静,謝謝梭冠。
        if (!mScroller.isFinished()) {
            final int oldX = mScrollX;
            final int oldY = mScrollY;
            mScrollX = scrollX;
            mScrollY = scrollY;
            invalidateParentIfNeeded();
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            if (clampedY) {
                mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange());
            }
        } else {
            super.scrollTo(scrollX, scrollY);
        }
        //顯示滾動(dòng)條 滾動(dòng)條是View的方法,其實(shí)每個(gè)View都有滾動(dòng)的功能的改备。
        awakenScrollBars();
    }

到此控漠,ScrollView就能滾動(dòng)了。

總結(jié)

瀏覽源碼不是為了去寫一個(gè)ScrollView,而是在看完之后我們學(xué)到了啥盐捷。就像小時(shí)候?qū)W校組織看電影一樣偶翅,學(xué)校單純的只想讓你看完電影就算了,一般都會(huì)讓我們寫一篇讀后感碉渡。haha聚谁。。滞诺。

OverScroller

如果你要是想做一個(gè)滾動(dòng)的View形导,這個(gè)一定能幫助你實(shí)現(xiàn)夢(mèng)想。 自帶強(qiáng)大的滾動(dòng)技能习霹。一般配合VelocityTracker來計(jì)算fling滾動(dòng)朵耕。


如何優(yōu)雅的區(qū)分是點(diǎn)擊還是滑動(dòng)操作

當(dāng)我們做一個(gè)滑動(dòng)的容器組件的時(shí)候,當(dāng)我們快速的滑動(dòng)的時(shí)候序愚,并不想讓down事件傳遞下去憔披,但同時(shí)又不影響點(diǎn)擊容器內(nèi)的View。我們可以這么做爸吮。這是在onInterceptTouchEvent中哦芬膝!

 case MotionEvent.ACTION_DOWN:
       .....
       mScroller.computeScrollOffset();
       mIsBeingDragged = !mScroller.isFinished();
       ......
        break;
 return mIsBeingDragged;

可能會(huì)坑猿的地方

ScrollView會(huì)自動(dòng)滾動(dòng)到獲取焦點(diǎn)的View上面。例如我們?cè)赟crollView中放一個(gè)WebView形娇,就會(huì)發(fā)現(xiàn)總是會(huì)滾動(dòng)到WebView那里锰霜。筆者有一次用WebView來加載MathJax來渲染數(shù)學(xué)符號(hào)的時(shí)候就遇到這個(gè)坑。解決辦法有很多桐早。主要思路就是移除不必要的焦點(diǎn)癣缅。

scrollBy參數(shù)是Int 會(huì)丟失小數(shù)部分
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市哄酝,隨后出現(xiàn)的幾起案子友存,更是在濱河造成了極大的恐慌,老刑警劉巖陶衅,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屡立,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡搀军,警方通過查閱死者的電腦和手機(jī)膨俐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罩句,“玉大人焚刺,你說我怎么就攤上這事∶爬茫” “怎么了乳愉?”我有些...
    開封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我匾委,道長(zhǎng)拖叙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任赂乐,我火速辦了婚禮薯鳍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘挨措。我一直安慰自己挖滤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開白布浅役。 她就那樣靜靜地躺著斩松,像睡著了一般。 火紅的嫁衣襯著肌膚如雪觉既。 梳的紋絲不亂的頭發(fā)上惧盹,一...
    開封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音瞪讼,去河邊找鬼钧椰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛符欠,可吹牛的內(nèi)容都是我干的嫡霞。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼希柿,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼诊沪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起曾撤,我...
    開封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤端姚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后挤悉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寄锐,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年尖啡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剩膘。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡衅斩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出怠褐,到底是詐尸還是另有隱情畏梆,我是刑警寧澤,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站奠涌,受9級(jí)特大地震影響宪巨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜溜畅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一捏卓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧慈格,春花似錦怠晴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至选泻,卻和暖如春冲粤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背页眯。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工梯捕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人餐茵。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓科阎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親忿族。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锣笨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,318評(píng)論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件道批、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,124評(píng)論 4 61
  • 今天在使用storyboard時(shí)隆豹,直接拖拽一個(gè)scrollView到View上椭岩,設(shè)置好約束后,本來想這樣就OK了璃赡!...
    光明程輝閱讀 1,116評(píng)論 1 4
  • 偶然的機(jī)會(huì)喜歡上圓珠筆判哥,不需要削筆,也不怕弄壞顏色碉考,而且圓珠筆比較好好掌控一點(diǎn)塌计。 喜歡動(dòng)物多于...
    蝸怪獸閱讀 209評(píng)論 0 1
  • 作為一個(gè)普通人,你可能從來沒有接觸過這么一款游戲侯谁。 一個(gè)由沙丘锌仅、谷地章钾、雪山、云海和巨大的塔構(gòu)成的旅途热芹,兩個(gè)渺小的旅...
    路見不平伊森吼閱讀 14,828評(píng)論 5 19