元老控件之RelativeLayout源碼分析

再過幾個月就要30了,給自己定了一個小目標(biāo), 三年后會是怎樣呢......
1. 核心成員與標(biāo)記
1. LEFT_OF, RIGHT_OF,ABOVE,BELOW: 聲明某個子child相對于另外一個子child的位置。

2. ALIGN_BASELINE:某個child和另一個child的基準(zhǔn)線對齊。

3. ALIGN_LEFT,ALIGN_TOP,ALIGN_RIGHT,ALIGN_BOTTOM:某個child和另外一個child的對齊方式,左對齊,右對齊,上對齊,下對齊。

4. ALIGN_PARENT_LEFT,ALIGN_PARENT_TOP,ALIGN_PARENT_RIGHT,ALIGN_PARENT_BOTTOM:某個child和RelativeLayout的父容器的對齊方式。左,右,上,下對齊。

5. CENTER_IN_PARENT:child位于parent的正中間布局。

6. CENTER_HORIZONTAL:child位于parent的水平中間布局。

7. CENTER_VERTICAL:child位于parent的垂直中間布局。

8. START_OF,END_OF,ALIGN_START,ALIGN_END, ALIGN_PARENT_START,ALIGN_PARENT_END:相對于上面的left/end, align_left/align_right等考慮了相對方向,也就是從右到左的布局。

9. RULES_VERTICAL:包含了ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM,他的意思是子child之間互相依賴來定位的標(biāo)記。特指縱向哦。

10. RULES_HORIZONTAL:包含了LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END,他的意思是子child之間需互相依賴來定位的標(biāo)記,特指橫向哦。

11. gravity屬性:可以指定子view為left,top, right, bottom等位置擺放。

2. 內(nèi)部類
  • RelativeLayout.LayoutParams

    這是一個給RelativeLayout的子child提供的布局參數(shù)。

    • mRules: 在LayoutParams的構(gòu)造函數(shù)中記錄了left, right, align, start等各種標(biāo)記過的view元素的id.
    • resolveRules(): 將設(shè)定的start, end等布局方向相關(guān)的屬性轉(zhuǎn)換成對應(yīng)的lef, right。注意的是如果start和left同時設(shè)定了,那么優(yōu)先考慮start.end如right同理。這里還考慮到targetversion是低于17, 那么start, end等類似的屬性是無效的,將他們直接轉(zhuǎn)換成left, right.施加的布局方向是沒有作用的呢。
    • resolveLayoutDirection:給LayoutParams設(shè)定布局方向,同時他還會調(diào)用上面的resolveRules重新解析Relativelayout的布局策略。
  • RelativeLayout.DependencyGraph

    主要目的是為RelativeLayout確定view的定位布局的順序,即先布局哪個,后布局哪個元素。將這些確定順序的view放置到一個容器中,這里面有依賴的關(guān)系線。

    • mNodes:記錄了所有的view。

    • mKeyNodes: 和上面的一樣記錄了所有的view, 但是以key-value的方式,key為view的id。

    • mRoots: 本意是記錄一些不需要依賴其他view的位置來定位自己的view. 經(jīng)過內(nèi)部處理后和依賴關(guān)系的移除之后,里面就是mNodes中所有的view了。

    • DependencyGraph.findRoots方法

      該方法的意圖是根據(jù)縱向或者橫向的依賴標(biāo)記來找出不依賴其他child定位的child

        private ArrayDeque<Node> findRoots(int[] rulesFilter) {
                  final SparseArray<Node> keyNodes = mKeyNodes;
                  final ArrayList<Node> nodes = mNodes;
                  final int count = nodes.size();
      
                  // Find roots can be invoked several times, so make sure to clear
                  // all dependents and dependencies before running the algorithm
                  for (int i = 0; i < count; i++) {
                      final Node node = nodes.get(i);
                      node.dependents.clear();
                      node.dependencies.clear();
                  }
      
                  // Builds up the dependents and dependencies for each node of the graph
                  for (int i = 0; i < count; i++) {
                      final Node node = nodes.get(i);
      
                      final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
                      final int[] rules = layoutParams.mRules;
                      final int rulesCount = rulesFilter.length;
      
                      //1.這里是關(guān)鍵的地方,根據(jù)rulesFilter選擇的標(biāo)記,來找到當(dāng)前view依賴的child-view。找到了依賴之后。把當(dāng)前view添加到他的附屬集合里面去,把依賴view添加到當(dāng)前view的附主集合里面去。
                      for (int j = 0; j < rulesCount; j++) {
                          final int rule = rules[rulesFilter[j]];
                          if (rule > 0) {
                              // The node this node depends on
                              final Node dependency = keyNodes.get(rule);
                              // Skip unknowns and self dependencies
                              if (dependency == null || dependency == node) {
                                  continue;
                              }
                              // Add the current node as a dependent
                              dependency.dependents.put(node, this);
                              // Add a dependency to the current node
                              node.dependencies.put(rule, dependency);
                          }
                      }
                  }
      
                  final ArrayDeque<Node> roots = mRoots;
                  roots.clear();
                  //遍歷所有的view, 把那些沒有附主依賴項的添加到root集合。也就是說這些view可以是根view一樣,可以首先定位。
                  // Finds all the roots in the graph: all nodes with no dependencies
                  for (int i = 0; i < count; i++) {
                      final Node node = nodes.get(i);
                      if (node.dependencies.size() == 0) roots.addLast(node);
                  }
      
                  return roots;
              }
      
      
      
      
  • DependencyGraph.getSortedViews方法

    填充mRoots集合,他會將那些依賴其他兄弟view定位的view的陸續(xù)填入進去,它會先將被依賴的view放入到root中表示被依賴方已經(jīng)穩(wěn)定定位, 然后放入自己,確定這樣的定位順序。如果知道拓撲排序的話,就很容易理解這個過程啦, 拓撲排序就是按照依賴關(guān)系去排序,讓排在前面的內(nèi)容不會依賴排在后面的內(nèi)容,這和我們的mRoot之中的依賴圖譜是一致的哦.

     void getSortedViews(View[] sorted, int... rules) {
                //找到第一批不需要依賴其他child定位的view集合。
                final ArrayDeque<Node> roots = findRoots(rules);
                int index = 0;
    
                Node node;
                // 遍歷root.
                while ((node = roots.pollLast()) != null) {
                    final View view = node.view;
                    final int key = view.getId();
                    //root的節(jié)點直接添加進去,他們是無依賴的。
                    sorted[index++] = view;
                    //獲取當(dāng)前節(jié)點的附屬項,假如B,C依賴A定位。那么A的的dependents就是B,C
                    final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
                    final int count = dependents.size();
                    //遍歷B, C附屬。
                    for (int i = 0; i < count; i++) {
                        final Node dependent = dependents.keyAt(i);
                        //獲取B/C的附主方,這里就是A.
                        final SparseArray<Node> dependencies = dependent.dependencies;
                        //移除A附主方,因為A已經(jīng)sort排好序了,這樣B/C就可以說依賴的項就已知了。
                        dependencies.remove(key);
                        //如果這里的附主方清0了,那么就可以作為root節(jié)點添加到root里面了。
                        //比如B除了依賴A, 可能還依賴D,那么要等下一次定位D之后才能將A-add進去哦。
                        if (dependencies.size() == 0) {
                            roots.add(dependent);
                        }
                    }
                }
    
    
  •  graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
     graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
      
    - 這兩行代碼意圖是將所有的子child從橫縱兩個方向來計算view的定位順序,也即mSortedHorizontalChildren, mSortedVerticalChildren。他里面的元素是按照定位順序來排列的。
    
  • Node
    • dependents: 附屬內(nèi)容,如果b依賴a定位, 那么b是a的附屬。a里面的dependents就是b這類元素。
    • dependencies: 附主內(nèi)容,如果b依賴a定位,那么a是b的附主。這個屬性集合里面就是b所有依賴的附主方。
    • View: 就是當(dāng)前的節(jié)點的view。
3. 核心方法分解
  • onLayout: 因為RelativeLayout都是子child相對性地布局,所以只要計算出子child的坐標(biāo), 就可以知道child放置在什么位置。

  • sortChildren:生成兩個集合,分別是縱行和橫向的view集。他們都是RelativeLayout的子child全集,按照定位的順序,排列成一個集合。后面的測量就從中取出一個個地測量啦。

  • onMeasue

    RelativeLayout的核心算法,它分橫向,縱行兩個方向來測量RelativeLayout和子view, 這也是為什么說relativeLayout相對比較LinearLayout耗費性能, 它的邏輯概括起來分為四步:

    1. 橫向的測量:定位,測量,計算最終位置;
    2. 縱向的測量: 定位,測量,計算最終位置;
    3. isWrapContentHeight, 修正高度
    4. isWrapContentWidth,修正寬度

    注意:MeasureSpec.UNSPECIFIED,這個是測量模式在系統(tǒng)中使用構(gòu)建的時候,一般和0一起組合使用,他表明當(dāng)前view的測量規(guī)格是未知的。父容器沒有給我明確的限制所以是UNSPECIFIED,我自己也不知道自己該多大所以是0,具體我的大小到我的具體實現(xiàn)中再去計算吧。這在relativelayout橫豎測量的時候,對測量另外一個相對的數(shù)值有作用。

    • if (params.width == LayoutParams.MATCH_PARENT) {
          //這里解釋了為什么當(dāng)RelativeLayout是wrap的時候,子child是match的時候,得到的高度是精確模式,且尺寸為父容器最大。
                  childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
      } else {
          childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
      }
      
    • onMeasue方法主體

       protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
              if (mDirtyHierarchy) {
                  mDirtyHierarchy = false;
                  //計算縱橫兩個集合。
                  sortChildren();
              }
              
              ......  
                 
              //橫向測量
              View[] views = mSortedHorizontalChildren;
              int count = views.length;
      
              for (int i = 0; i < count; i++) {
                  View child = views[i];
                  if (child.getVisibility() != GONE) {
                      LayoutParams params = (LayoutParams) child.getLayoutParams();
                      int[] rules = params.getRules(layoutDirection);
      
                      applyHorizontalSizeRules(params, myWidth, rules);
                      measureChildHorizontal(child, params, myWidth, myHeight);
      
                      if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
                          offsetHorizontalAxis = true;
                      }
                  }
              }
      
           //縱向測量
              views = mSortedVerticalChildren;
              count = views.length;
              final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
      
              for (int i = 0; i < count; i++) {
                  View child = views[i];
                  if (child.getVisibility() != GONE) {
                      LayoutParams params = (LayoutParams) child.getLayoutParams();
                      //應(yīng)用規(guī)則,找到child的top或者bottom位置。
                      applyVerticalSizeRules(params, myHeight);
                      //測量child的寬與高。
                      measureChild(child, params, myWidth, myHeight);
                      if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
                          offsetVerticalAxis = true;
                      }
                      //如果是wrap的寬,計算出RelativeLayout的最大寬度1。
                      if (isWrapContentWidth) {
                          if (isLayoutRtl()) {
                              if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                                  width = Math.max(width, myWidth - params.mLeft);
                              } else {
                                  width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
                              }
                          } else {
                              if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                                  width = Math.max(width, params.mRight);
                              } else {
                                  width = Math.max(width, params.mRight + params.rightMargin);
                              }
                          }
                      }
                      //如果是wrap的高,計算出RelativeLayout的最大高度1。
                      if (isWrapContentHeight) {
                          if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                              height = Math.max(height, params.mBottom);
                          } else {
                              height = Math.max(height, params.mBottom + params.bottomMargin);
                          }
                      }
      
                      if (child != ignore || verticalGravity) {
                          left = Math.min(left, params.mLeft - params.leftMargin);
                          top = Math.min(top, params.mTop - params.topMargin);
                      }
      
                      if (child != ignore || horizontalGravity) {
                          right = Math.max(right, params.mRight + params.rightMargin);
                          bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
                      }
                  }
              }
      
              .......
                  //wrap的寬,矯正前面positionChildHorizontal返回true的child的left,right屬性。
              if (isWrapContentWidth) {
                  // Width already has left padding in it since it was calculated by looking at
                  // the right of each child view
                  width += mPaddingRight;
      
                  if (mLayoutParams != null && mLayoutParams.width >= 0) {
                      width = Math.max(width, mLayoutParams.width);
                  }
      
                  width = Math.max(width, getSuggestedMinimumWidth());
                  width = resolveSize(width, widthMeasureSpec);
      
                  if (offsetHorizontalAxis) {
                      for (int i = 0; i < count; i++) {
                          View child = getChildAt(i);
                          if (child.getVisibility() != GONE) {
                              LayoutParams params = (LayoutParams) child.getLayoutParams();
                              final int[] rules = params.getRules(layoutDirection);
                              if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
                                  centerHorizontal(child, params, width);
                              } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
                                  final int childWidth = child.getMeasuredWidth();
                                  params.mLeft = width - mPaddingRight - childWidth;
                                  params.mRight = params.mLeft + childWidth;
                              }
                          }
                      }
                  }
              }
               //wrap的高,矯正前面positionChildVertical返回true的child的top,bottom屬性。
              if (isWrapContentHeight) {
                  // Height already has top padding in it since it was calculated by looking at
                  // the bottom of each child view
                  height += mPaddingBottom;
      
                  if (mLayoutParams != null && mLayoutParams.height >= 0) {
                      height = Math.max(height, mLayoutParams.height);
                  }
      
                  height = Math.max(height, getSuggestedMinimumHeight());
                  height = resolveSize(height, heightMeasureSpec);
      
                  if (offsetVerticalAxis) {
                      for (int i = 0; i < count; i++) {
                          View child = getChildAt(i);
                          if (child.getVisibility() != GONE) {
                              LayoutParams params = (LayoutParams) child.getLayoutParams();
                              final int[] rules = params.getRules(layoutDirection);
                              if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
                                  centerVertical(child, params, height);
                              } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
                                  final int childHeight = child.getMeasuredHeight();
                                  params.mTop = height - mPaddingBottom - childHeight;
                                  params.mBottom = params.mTop + childHeight;
                              }
                          }
                      }
                  }
              }
      
              //默認這個都是false,但是如果relativelayout設(shè)置了Gravity.END, Gravity.Bottom,
           //那么就會將內(nèi)容右邊或者底部頂邊,這時候就需要矯正child的left, child的right, top, bottom屬性呢。因為前面的left, right,top, bottom屬性都是從start, top這個頂邊位置開始計算的。
           
              if (horizontalGravity || verticalGravity) {
                  final Rect selfBounds = mSelfBounds;
                  selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
                          height - mPaddingBottom);
      
                  final Rect contentBounds = mContentBounds;
                  Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
                          layoutDirection);
      
                  final int horizontalOffset = contentBounds.left - left;
                  final int verticalOffset = contentBounds.top - top;
                  if (horizontalOffset != 0 || verticalOffset != 0) {
                      for (int i = 0; i < count; i++) {
                          View child = getChildAt(i);
                          if (child.getVisibility() != GONE && child != ignore) {
                              LayoutParams params = (LayoutParams) child.getLayoutParams();
                              if (horizontalGravity) {
                                  params.mLeft += horizontalOffset;
                                  params.mRight += horizontalOffset;
                              }
                              if (verticalGravity) {
                                  params.mTop += verticalOffset;
                                  params.mBottom += verticalOffset;
                              }
                          }
                      }
                  }
              }
      
              if (isLayoutRtl()) {
                  final int offsetWidth = myWidth - width;
                  for (int i = 0; i < count; i++) {
                      View child = getChildAt(i);
                      if (child.getVisibility() != GONE) {
                          LayoutParams params = (LayoutParams) child.getLayoutParams();
                          params.mLeft -= offsetWidth;
                          params.mRight -= offsetWidth;
                      }
                  }
      
              }
      
              //設(shè)定RelativeLayout的最終的寬與高。
              setMeasuredDimension(width, height);
          }
      
      
      
  • getChildMeasureSpec: 這個和viewGroup中的同名方法的實現(xiàn)不一樣哦,google工程師這里重新實現(xiàn)了一套邏輯。但是感覺這些邏輯有些亂啊,簡單總結(jié)下他的基本邏輯。

  • 如果childStart, childEnd都設(shè)定了,childSpecMode=EXACTLY, childSpecSize=childEnd-childStart

  • 否則,childSize>0, childSpecMode=EXACTLY, childSpecSize是maxAvailable和childSize中最小的一個。

  • 否則,childSize == LayoutParams.MATCH_PARENT:

    childSpecMode = MeasureSpec.EXACTLY;
    childSpecSize = maxAvailable;
    - 這里也可以看出不關(guān)心父容器本身的模式,只要我是MATCH_PARENT,結(jié)果就是EXACTLY,這個和viewGroup的計算是大不同的哦。
    
  • childSize == LayoutParams.WRAP_CONTENT:

    childSpecMode = MeasureSpec.AT_MOST;
    childSpecSize = maxAvailable;
    
  • applyHorizontalSizeRules:計算child的left或者right位置。

    - 找到它依賴定位的view,然后根據(jù)rulues規(guī)則計算出他的left, 或right的數(shù)值。
    
    - 當(dāng)規(guī)則是ALIGN_PARENT_RIGHT時候,計算出的childParams.mRight是根據(jù)myWidth來的,這個時候的myWidth并不一定是確定的,如果是RelativeLayout是Wrap_conent,那么在后面確定了width之后要重新再計算一次childParams.mRight。
    
    
  • measureChildHorizontal: 測量child的寬度,高度也測量但不是最終的結(jié)果。

    - 根據(jù)params.mLeft,params.mRight或者childWith來計算出child的寬度測量規(guī)格childWidthMeasureSpec
    
    - 根據(jù)myHeight來和params.width的模式得出高度的測量規(guī)格childHeightMeasureSpec
    注意:這時候的childHeightMeasureSpec時候不準(zhǔn)確的,只是為了方便測寬度,大概出的一個值。
    
    - 測量child.
    
    
    
  • positionChildHorizontal:根據(jù)left/right以及前面測量的寬度來計算child的位置。

    - 因為之前經(jīng)過了寬度的測量,那么寬度是已知的了,然后通過這個寬度來得出child的left, right的位置。方便后續(xù)的layout.
    - 如果是CENTER_IN_PARENT,CENTER_HORIZONTAL,ALIGN_PARENT_END的三種該方法返回true.表示left,right的位置計算依賴了不確定的父容器,所以等到父容器確定之后需要再定一次left,right.
    
    
  • applyVerticalSizeRules:計算child的top或者bottom位置。

    - 和measureChildHorizontal同理,計算出縱向的top, bottom
    
    
  • measureChild: 測量child的最終的寬與高。

    - 得出寬度的計算規(guī)格是childWidthMeasureSpec, 寬度其實前面已經(jīng)計算好了。
    - 得出高度的計算規(guī)格, 高度是childHeightMeasureSpec,經(jīng)過前面的定位輔助,以及考慮child的height.
    -  child.measure(childWidthMeasureSpec, childHeightMeasureSpec);最終的測量寬與高。
    
    
  • positionChildVertical:根據(jù)left/right以及前面測量的寬度來計算child的位置。

  • 有了前面的寬,高,然后就可以定位child, 可以獲取到他的top,bottom屬性。

    • 如果是CENTER_IN_PARENT,CENTER_HORIZONTAL,ALIGN_PARENT_END的三種該方法返回true.表示left,right的位置計算依賴了不確定的父容器,所以等到父容器確定之后需要再定一次top,bottom.
4. 總結(jié)
  • RelativeLayout的重點在測量處,缺點也是在這里。對于子控件都要橫豎測量兩遍,這是一個較為耗費性能的地方,所以平時如果可以盡量不要用這個控件哦, 用LinearLayout取代啦。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,643評論 2 380