view 繪制流程

performTravseral.png
  • DecorView 是界面上的頂層view. ViewRoot是連接WindowManager和DecorView的紐帶.view的繪制流程從ViewRoot的perfromTraversals開始.
  • ViewRoot作為頂級View,通常包括一個豎直方向的LinerLayout.這個LinerLayout分兩部分.上邊是標(biāo)題欄.就是屏幕顯示時間的那一行,下邊是內(nèi)容欄.id為content,我們平時設(shè)置的setContentView(),就是在想contentView里添加子類.通過ViewGroup content =(ViewGroup)findViewById(android.R.id.content);拿到contentView. content.getChildAt(0);拿到我們設(shè)置的View;
  • MeasureSpace 用一個32位的int代表測量的數(shù)據(jù)和類型,高二位代表SpecMode,表示測量模式,低30位 SpecSize表示測量大小.
  • MeasureSpace的三種模式
    • UNSPECIFIED:父容器不做限制,通常用于系統(tǒng)內(nèi)部.
    • EXACTLY 指定大小,view此時的大小就是SpecSize的值.它對應(yīng)于LayoutParams中的march_parent和具體指定的值(多少dp,sp).
    • At_MOST 父容器指定一個可用大小即SpecSize,View不能大于這個值.對于wrap_content.
  • MeasureSpace 決定view的寬高,但MeasureSpace由父類和view自己的layoutParams共同決定.
  • DecorView的MeasureSpace決定方式
3.png

2.png

-接著看vp如何測設(shè)計子類的MeasureSpace

 //parentWidthMeasureSpec,parentHeightMeasureSpec 是vp自己的measureSpace寬高值
    //widthUsed,heightUsed 是被vp額外用掉的寬高值
    protected void measureChildWithMargins(View child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {

        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
        
        //viewgroup自己的measureSpace,第二個參數(shù)是vp所使用的值
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
                        
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);
        //調(diào)用子類進(jìn)行測量
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    //得到子類的MeasureSpace 通過父類的specMode和子類Layoutparams的組合得到子類最終的MeasureSpace
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        vp的MeasureSpec
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);
        
        //vp自己的尺寸減去自己的padding.margin等 最后得到子類能用的最大尺寸
        int size = Math.max(0, specSize - padding);
        
        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY: //vp是精確模式
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST: //父類是最大模式
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:  //父類是不確定模式
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
     }

-最終的匹配結(jié)果如下,parentSize值最終父類可用大小,既getChildMeasureSpec中的 size

4.png

-measure 過程

-view 的measures過程

view 的measure方法是final,既子類無法重寫該方法,在measure 中又調(diào)用了onMeasure方法,只需要看onMeasure方法就可以.

//widthMeasureSpec,heightMeasureSpec為父類vp傳過來的子類的MeasureSpace 寬高值
//getSuggestedMinimumWidth()  是建議的最小寬度,一般為view背景的大小和屬性android:minWidth大小的最大值.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
//子類可以重寫這個方法來決定view的measure大小.但是必須調(diào)用setMeasuredDimension()方法,不然會拋出異常.
setMeasuredDimension()方法會設(shè)置view寬高的測量值.

public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
    }
    return result;
}
//UNSPECIFIED的情況可以忽略.看剩下兩種情況.所以 getDefaultSize的取值其實就是vp傳過來的measureSpace的specSize的值.
  • 經(jīng)過以上代碼我們得出結(jié)論:直接繼承View的自定義空間要重寫onMeasure方法并設(shè)置wrap_content時自身大小,不然設(shè)置wrap_content和設(shè)置march_parent的效果一樣.
  • 因為view 設(shè)置為wrap_content, 對應(yīng)的MeasureSpec 為SpecMode=AT_MOST,SpecSize=parentSize,在onMeasure中就會把父類可用空間的值設(shè)置給view.解決辦法如下
5.png

-ViewGroup 的measures過程

  • viewgroup 出來測量自己,還要遍歷子veiw測量大小.vp通過measureChildren方法來遍歷測量子類大小

    //Gone的子view不會被測量
     protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
        final int size = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < size; ++i) {
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }
    //這個方法和之前的 measureChildWithMarging 方法差不多,只是少了widthUsed,heighUsed參數(shù)
     protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {
        final LayoutParams lp = child.getLayoutParams();
    
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom, lp.height);
    
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
     }
    
  • viewgroup并沒有實現(xiàn)具體測量過程,因為他是一個抽象類,需要子類例如LinerLayout等自行實現(xiàn)測量過程.

  • 四種方法拿到view的寬高

    1. Activity/View#onWindowFocusChanged,這個方法的含義標(biāo)識窗口的交點變化時回調(diào),此時view已經(jīng)初始化完畢,寬高已經(jīng)準(zhǔn)備好了.此方法會回調(diào)多次
    public void onWindowFocusChanged(boolean hasFocus){
        super.onWindowFocusChanged(hasFocus);
        if(hasFocus){
            int width=view.getMeasuredWidth();
            int hei=view.getMeasuredHeigh();
        }
    }
    
    1. view.post(runnable) 發(fā)送一個消息到消息隊列的尾部,等Lopper調(diào)用此消息時,view已經(jīng)初始化完成

      view.post(new Runnable{

      @Override
      public void run(){
           int width=view.getMeasuredWidth();
           int hei=view.getMeasuredHeigh();
      }
      

      });

    2. ViewTreeObserver#onGlobalLayoutListener,當(dāng)view樹的狀態(tài)或者view樹的子view可見性發(fā)生改變時會回調(diào),方法會被回調(diào)多次

      ViewTreeObserver observer=view.getViewTreeObserver();
      observer.addonGlobalLayoutListener(new onGlobalLayoutListener(){

      publci void onGlobalLayout(){
          view.getViewTreeObserver().removeGlobalLayoutListener(this);//先移除監(jiān)聽
          int width=view.getMeasuredWidth();
          int hei=view.getMeasuredHeigh();
      }
      

      });

    3. view.measure(int,int);需要根據(jù)view 的layoutParam 來區(qū)分

      march_parent :此狀態(tài)無法得到結(jié)果,因為此時父view的測量寬高也不知道
      具體數(shù)值 :比如寬高為100

       int width =MeasureSpec.makeMeasureSpec(100,MeasursSpec.EACTLY)
       int heigh =MeasureSpec.makeMeasureSpec(100,MeasursSpec.EACTLY)
       view.measurd(width,heigh);
      

      wrap_content

       int width =MeasureSpec.makeMeasureSpec((1<<30)-1,MeasursSpec.EACTLY)
       int heigh =MeasureSpec.makeMeasureSpec((1<<30)-1,MeasursSpec.EACTLY)
       view.measurd(width,heigh);
      

      View 的最大尺寸為30位二進(jìn)制,既 2的30次方減1 既(1<<30)-1,在最大化模式下,我們用view理論上的最大值去構(gòu)造MeasureSpec是合理的.

-layout 過程

  • layout 方法確定view本身的位置,在onLayout方法則會確定所有子元素的位置.

  • viewGroup 在位置被確定后,會在onLayout中便利所有子類比調(diào)用其layout方法,在layout方法中onLayout方法又被調(diào)用.

  • view 的layout方法中, 通過setFrame(l,t,r,b)來設(shè)定view的四個定點的位置.父容器通過onLayout方法來確定子view的位置.

  • view和viewgroup均沒實現(xiàn)onLayout方法,需要看具體的layout.

  • view 的layout過程

      public void layout(int l, int t, int r, int b) {
          //如果需要在layout前進(jìn)行一次measure
          if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) { 
              onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
              mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
          }
          //之前的位置 左上右下
          int oldL = mLeft;
          int oldT = mTop;
          int oldB = mBottom;
          int oldR = mRight;
          //主要部分,設(shè)置view的左上右下位置.
          boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
          
          //通知監(jiān)聽器,界面layout發(fā)生變化
          if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
              onLayout(changed, l, t, r, b); //空實現(xiàn)
              mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
    
              ListenerInfo li = mListenerInfo;
              if (li != null && li.mOnLayoutChangeListeners != null) {
                  ArrayList<OnLayoutChangeListener> listenersCopy =
                          (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
                  int numListeners = listenersCopy.size();
                  for (int i = 0; i < numListeners; ++i) {
                      listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
                  }
              }
          }
    
          mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
          mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
      }
    

-vieGroup layout過程

public final void layout(int l, int t, int r, int b) {
        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
            if (mTransition != null) {
                mTransition.layoutChange(this);
            }
            super.layout(l, t, r, b); //其實還是調(diào)用了父類,最終調(diào)用view 的layout
        } else {
            // record the fact that we noop'd it; request layout when transition finishes
            mLayoutCalledWhileSuppressed = true;
        }
}
  • getwidth getMeasuredWidth 區(qū)別

  • getMeasuredWith 形成于measure過程.getWidth 形成于layout過程.

  • 一般情況兩者是相同的.除非如下情況

    public void layout (int l,int t,int r,int b){
        super.layout(l,t,r+100,b+100);
    }     
    

-draw 過程

1罐柳、對View的背景進(jìn)行繪制
2、保存當(dāng)前的圖層信息(可跳過)
3老客、繪制View的內(nèi)容
4、對View的子View進(jìn)行繪制(如果有子View)
5、繪制View的褪色的邊緣蛛壳,類似于陰影效果(可跳過)
6吸占、繪制View的裝飾(例如:滾動條)

public void draw(Canvas canvas) {
 final int privateFlags = mPrivateFlags;
 final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
   (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
 mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

 /*
  * Draw traversal performs several drawing steps which must be executed
  * in the appropriate order:
  *
  *  1. Draw the background
  *  2. If necessary, save the canvas' layers to prepare for fading
  *  3. Draw view's content
  *  4. Draw children
  *  5. If necessary, draw the fading edges and restore layers
  *  6. Draw decorations (scrollbars for instance)
  */
 
 // Step 1, draw the background, if needed
 int saveCount;
 
 if (!dirtyOpaque) {
  drawBackground(canvas); //繪制背景
 }
 
 // skip step 2 & 5 if possible (common case)
 final int viewFlags = mViewFlags;
 boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
 boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
 if (!verticalEdges && !horizontalEdges) {
  // Step 3, draw the content
  if (!dirtyOpaque) onDraw(canvas); //空實現(xiàn),需要子類實現(xiàn)
 
  // Step 4, draw the children
  dispatchDraw(canvas);
 
  // Overlay is part of the content and draws beneath Foreground
  if (mOverlay != null && !mOverlay.isEmpty()) {
   mOverlay.getOverlayView().dispatchDraw(canvas);
  }
 
  // Step 6, draw decorations (foreground, scrollbars)
  onDrawForeground(canvas);
 
  // we're done...
  return;
 }
 ...
}


分布講解
1繪制背景
private void drawBackground(Canvas canvas) {

     //mBackground是該View的背景參數(shù)馁筐,比如背景顏色,沒有背景就不繪制
     final Drawable background = mBackground;
     if (background == null) {
      return;
     }
     
     //根據(jù)View四個布局參數(shù)來確定背景的邊界 mBackground.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
     setBackgroundBounds();
     
     ...
     
     //獲取當(dāng)前View的mScrollX和mScrollY值
     final int scrollX = mScrollX;
     final int scrollY = mScrollY;
     if ((scrollX | scrollY) == 0) {
      background.draw(canvas);
     } else {
      //如果scrollX和scrollY有值调限,則對canvas的坐標(biāo)進(jìn)行偏移舟陆,再繪制背景
      canvas.translate(scrollX, scrollY);
      background.draw(canvas);
      canvas.translate(-scrollX, -scrollY);
     }
}
    3.繪制子View,view中這個方法為空,viewgroup實現(xiàn)了這個方法
    protected void dispatchDraw(Canvas canvas) {
        boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
        final int childrenCount = mChildrenCount;
        final View[] children = mChildren;
        int flags = mGroupFlags;
            
        for (int i = 0; i < childrenCount; i++) { 
            while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
                final View transientChild = mTransientViews.get(transientIndex);
                if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                transientChild.getAnimation() != null) {
                    more |= drawChild(canvas, transientChild, drawingTime); //這句是重點
                }
                transientIndex++;
                if (transientIndex >= transientCount) {
                    transientIndex = -1;
                }
            }
            int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
            final View child = (preorderedList == null)? children[childIndex] : preorderedList.get(childIndex);
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);//這句是重點
            }
        }
            //省略...
    }

    drawChild(canvas, transientChild, drawingTime)的實現(xiàn),調(diào)用了子view的繪制方法
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
         return child.draw(canvas, this, drawingTime);
    }
  • view 的draw(Canvas canvas, ViewGroup parent, long drawingTime)
  • 我們主要來看核心部分,首先判斷是否已經(jīng)有緩存旧噪,即之前是否已經(jīng)繪制過一次了吨娜,如果沒有脓匿,則會調(diào)用draw(canvas)方法淘钟,開始正常的繪制,即上面所說的六個步驟陪毡,否則利用緩存來顯示米母。
  • 這一步也可以歸納為ViewGroup繪制過程,它對子View進(jìn)行了繪制毡琉,而子View又會調(diào)用自身的draw方法來繪制自身铁瞒,這樣不斷遍歷子View及子View的不斷對自身的繪制,從而使得View樹完成繪制
  •   Skip 6 繪制裝飾,指View除了背景桅滋、內(nèi)容慧耍、子View的其余部分,例如滾動條等丐谋,我們看View#onDrawForeground:
      public void onDrawForeground(Canvas canvas) {
       onDrawScrollIndicators(canvas);
       onDrawScrollBars(canvas);
       
       final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
       if (foreground != null) {
        if (mForegroundInfo.mBoundsChanged) {
         mForegroundInfo.mBoundsChanged = false;
         final Rect selfBounds = mForegroundInfo.mSelfBounds;
         final Rect overlayBounds = mForegroundInfo.mOverlayBounds;
       
         if (mForegroundInfo.mInsidePadding) {
          selfBounds.set(0, 0, getWidth(), getHeight());
         } else {
          selfBounds.set(getPaddingLeft(), getPaddingTop(),
            getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
         }
       
         final int ld = getLayoutDirection();
         Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(),
           foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld);
         foreground.setBounds(overlayBounds);
        }
       
        foreground.draw(canvas);
       }
      }
    

自定義view,寬高寫為wrap_content時,如果不處理,和寫成 march_parent是一樣的,處理規(guī)則如下,mWidth,mHeigh為自定義寬高

11.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芍碧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子号俐,更是在濱河造成了極大的恐慌泌豆,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吏饿,死亡現(xiàn)場離奇詭異踪危,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)猪落,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門贞远,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笨忌,你說我怎么就攤上這事蓝仲。” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵杂曲,是天一觀的道長庶艾。 經(jīng)常有香客問我,道長擎勘,這世上最難降的妖魔是什么咱揍? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮棚饵,結(jié)果婚禮上煤裙,老公的妹妹穿的比我還像新娘。我一直安慰自己噪漾,他們只是感情好硼砰,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著欣硼,像睡著了一般题翰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上诈胜,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天豹障,我揣著相機(jī)與錄音,去河邊找鬼焦匈。 笑死血公,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缓熟。 我是一名探鬼主播累魔,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼够滑!你這毒婦竟也來了垦写?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤版述,失蹤者是張志新(化名)和其女友劉穎梯澜,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渴析,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡晚伙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了俭茧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咆疗。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖母债,靈堂內(nèi)的尸體忽然破棺而出午磁,到底是詐尸還是另有隱情尝抖,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布迅皇,位于F島的核電站昧辽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏登颓。R本人自食惡果不足惜搅荞,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望框咙。 院中可真熱鬧咕痛,春花似錦、人聲如沸喇嘱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽者铜。三九已至腔丧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間王暗,已是汗流浹背悔据。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工庄敛, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留俗壹,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓藻烤,卻偏偏與公主長得像绷雏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子怖亭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

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