TextInputLayout TextInputEditText

默认效果图

image.png

布局

        <com.google.android.material.textfield.TextInputLayout
            android:id="@+id/input_host"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/dialog_item_logo_txt_padding"
            android:layout_toRightOf="@+id/iv_host_logo"
            android:textColorHint="@color/item_divider_dark_gray"
            app:boxBackgroundColor="#00000000"
            app:hintTextAppearance="@style/OurTextInputHint"
            app:boxStrokeColor="@color/blue_gray_state_color">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/et_host"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:hint="@string/ID_BOOK_INFO_HOST"
                android:minWidth="@dimen/book_edit_text_width"
                android:singleLine="true"
                android:text="x"
                android:imeOptions="actionDone"
                android:textColor="@color/dialog_item_text_color"
                android:textSize="20sp" />
        </com.google.android.material.textfield.TextInputLayout>

上边那个hint文字的颜色设置textColorHint 这个是state状态的,选中状态可以变个深色

app:hintTextAppearance :hint的文字其他属性,比如大小,只能通过这个设置了

    <style name="OurTextInputHint" parent="TextAppearance.Design.Hint">
        <item name="android:textSize">16sp</item>
    </style>

可以看下源码,Layout的默认属性是这个,点进去看这个style就知道有啥了
private static final int DEF_STYLE_RES = R.style.Widget_Design_TextInputLayout;

  <style name="Widget.Design.TextInputLayout" parent="android:Widget">
    <item name="materialThemeOverlay">@style/ThemeOverlay.Design.TextInputEditText</item>
    <item name="enforceMaterialTheme">false</item>
    <item name="enforceTextAppearance">false</item>

    <item name="boxBackgroundMode">none</item>
    <item name="boxStrokeColor">@color/design_box_stroke_color</item>
    <item name="passwordToggleDrawable">@drawable/design_password_eye</item>
    <item name="passwordToggleTint">@color/design_icon_tint</item>
    <item name="passwordToggleContentDescription">@string/password_toggle_content_description</item>
    <item name="endIconTint">@color/design_icon_tint</item>
    <item name="startIconTint">@color/design_icon_tint</item>

    <item name="counterTextAppearance">@style/TextAppearance.Design.Counter</item>
    <item name="counterOverflowTextAppearance">@style/TextAppearance.Design.Counter.Overflow</item>
    <item name="errorTextAppearance">@style/TextAppearance.Design.Error</item>
    <item name="helperTextTextAppearance">@style/TextAppearance.Design.HelperText</item>
    <item name="hintTextAppearance">@style/TextAppearance.Design.Hint</item>

    <item name="counterTextColor">@null</item>
    <item name="counterOverflowTextColor">@null</item>
    <item name="errorTextColor">@null</item>
    <item name="helperTextTextColor">@null</item>
    <item name="hintTextColor">@null</item>

    <item name="shapeAppearance">@null</item>
    <item name="shapeAppearanceOverlay">@null</item>
  </style>

TextInputEditText

这个其实没太多的东西,就是和TextInputLayout交互下hint的值而已。
如果它自己没有设置hint,那就取容器也就是TextInputLayout的hint值。

    @Override
  public CharSequence getHint() {
    // Certain test frameworks expect the actionable element to expose its hint as a label. When
    // TextInputLayout is providing our hint, retrieve it from the parent layout.
    TextInputLayout layout = getTextInputLayout();
    if ((layout != null) && layout.isProvidingHint()) {
      return layout.getHint();
    }
    return super.getHint();
  }

  @Override
  public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
    final InputConnection ic = super.onCreateInputConnection(outAttrs);
    if (ic != null && outAttrs.hintText == null) {
      // If we don't have a hint and our parent is a TextInputLayout, use its hint for the
      // EditorInfo. This allows us to display a hint in 'extract mode'.
      outAttrs.hintText = getHintFromLayout();
    }
    return ic;
  }

  @Nullable
  private TextInputLayout getTextInputLayout() {
    ViewParent parent = getParent();
    while (parent instanceof View) {
      if (parent instanceof TextInputLayout) {
        return (TextInputLayout) parent;
      }
      parent = parent.getParent();
    }
    return null;
  }

  @Nullable
  private CharSequence getHintFromLayout() {
    TextInputLayout layout = getTextInputLayout();
    return (layout != null) ? layout.getHint() : null;
  }

先看一些参数的效果

  1. app:boxBackgroundColor , boxBackgroundMode
            <com.google.android.material.textfield.TextInputLayout
                app:boxBackgroundMode="filled">

            <com.google.android.material.textfield.TextInputLayout
                android:background="@mipmap/adp_logo"
                app:boxBackgroundColor="#E91E63"
                app:boxBackgroundMode="outline">

boxBackgroundMode:3种,默认的filled,还有outline以及none
为filled,有个灰色背景色,edittext 点击以后,下边是一条线,主题色
而且好像上边2个角是圆角
app:boxBackgroundColor 可以修改背景色。
android:background :会被boxBackgroundColor的颜色挡住,可能看不见


image.png

app:boxBackgroundMode:
为none的时候,背景直接没了,成透明的拉;
为outline的时候,hint是显示在文字顶部的,而且它的rect的范围background被剪切掉了,你看到的背景就是容器的背景,这次不是底部有条线了,而是成了边框,颜色是主题色


image.png
  1. 背景的圆角
    上边说了,默认的顶部2个角是圆角的,可以如下修改4个角
                app:boxCornerRadiusBottomEnd="10dp"
                app:boxCornerRadiusBottomStart="10dp"
                app:boxCornerRadiusTopStart="10dp"
                app:boxCornerRadiusTopEnd="10dp"
  1. 选中以后底部那条线【filled模式】,或者边框的颜色[outline模式]
app:boxStrokeColor="#009688"

strokeColor是支持state的,也就是聚焦和非聚焦不同的颜色,上边那种直接设置个颜色,那么就是foucus的颜色拉。
//boxStrokeColor 如果是单独的色值,非isStateful的情况,就走的else,也就是给了focusColor
//如果有state的话,对应设置下if里的3种state颜色就可以拉。

    ColorStateList boxStrokeColorStateList =
        MaterialResources.getColorStateList(context, a, R.styleable.TextInputLayout_boxStrokeColor);
    if (boxStrokeColorStateList != null && boxStrokeColorStateList.isStateful()) {
      defaultStrokeColor = boxStrokeColorStateList.getDefaultColor();
      disabledColor =
          boxStrokeColorStateList.getColorForState(new int[] {-android.R.attr.state_enabled}, -1);
      hoveredStrokeColor =
          boxStrokeColorStateList.getColorForState(new int[] {android.R.attr.state_hovered}, -1);
      focusedStrokeColor =
          boxStrokeColorStateList.getColorForState(new int[] {android.R.attr.state_focused}, -1);
    } else {
      // If attribute boxStrokeColor is not a color state list but only a single value, its value
      // will be applied to the box's focus state.
      focusedStrokeColor =
          a.getColor(R.styleable.TextInputLayout_boxStrokeColor, Color.TRANSPARENT);
      defaultStrokeColor =
          ContextCompat.getColor(context, R.color.mtrl_textinput_default_box_stroke_color);
      disabledColor = ContextCompat.getColor(context, R.color.mtrl_textinput_disabled_color);
      hoveredStrokeColor =
          ContextCompat.getColor(context, R.color.mtrl_textinput_hovered_box_stroke_color);
    }

app:boxStrokeWidth="5dp" 看到有个属性,设置了下,感觉没效果

  1. hint文字
    正确的合理做法是随便哪个其中一个设置hint就行
    Layout和EditText都设置了,那么就各是各的,如下图,
            <com.google.android.material.textfield.TextInputLayout
                android:hint="hint layout">
                <com.google.android.material.textfield.TextInputEditText
                    android:textColorHint="#FF5722"
             android:hint="hint">

image.png

2个都设置了,非聚焦状态可以看到2个重叠在一起了。


image.png
  1. hint文字距离顶部的距离
    设置太大就没意义了,高度超过Layout的高度就不可见了。
app:boxCollapsedPaddingTop="0dp"
image.png

5.hint 文字style设置
复杂的比如字体大小,需要用TextAppearance来设置
简单的修改颜色就用hintTextColor,这个颜色优先级比上边appearance高


image.png
    <style name="hintText" parent="Widget.Design.TextInputLayout">
        <item name="android:textSize">16sp</item>
    </style>
  1. helperText 相关
                app:helperText="helper text"
                app:helperTextTextColor="#009688"
                app:helperTextEnabled="true"
                app:helperTextTextAppearance="@style/test"

默认的属性,enalbe默认也是true的

<item name="helperTextTextAppearance">@style/TextAppearance.Design.HelperText</item>
<item name="helperTextTextColor">@null</item>

效果图,java代码里也有对应的方法可以处理


image.png
  1. 字数统计
                app:counterEnabled="true"
                app:counterMaxLength="20"
                app:counterTextColor="@color/colorAccent"
                app:counterOverflowTextColor="@color/txt_black_white"
app:counterTextAppearance="@style/test"//字体还可以设置style,其他类似,后边不在说这种

默认是不可用的,2种颜色,
counterMaxLength的值来决定,
比这个大的就是overflow颜色,否则就是正常的app:counterTextColor
注意事项,这2个color都不能直接使用#000000这种,必须是一个color资源也就是@color/xxx

image.png

image.png

  1. endIcon
    看名字就猜到末尾有个icon
    主要就是看mode区分作用的,
    none:默认的,就是不显示icon,你设置了也没用。It will not display an end icon
    clear_text :点击一下文字清除
    password_toggle: 密码显示明文与否的开关
    custom: icon会显示,就是没点击效果而已。
                app:endIconDrawable="@mipmap/book_add"
                app:endIconMode="clear_text"

注意事项:password_toggle的时候,你里边的EditText的inputType需要是password才行

image.png

  1. error text
    布局里可以启用,以及设置颜色,显示的文字就需要代码里写了
    调用Layout的setError方法即可,
                app:errorEnabled="true"
                app:errorTextColor="#ff0000"

可以看到:
errorText会隐藏 helper text,字数太长会把后边的counter也就是字数统计,挤到看不见了。
说下,如果你设置了errorText很长,把counter挤没了,接着你设置errorText为空,counter也不会还原的,你需要设置errorEnable为false,counter才能还原。


image.png

helper text和error text一样的效果,把counter挤到看不见了,


image.png
  1. passwordToggle
    这个好像弃用了,就是endIcon里的mode为password_toggle的效果,建议用endIcon

  2. startIcon
    上边有图,前边显示的那个加号就是
    ···
    app:startIconDrawable="@mipmap/book_add"
    ···
    基本相关的属性都看完了,然后看下源码,了解下布局结构,也就方便理解上边的一些效果咋来的了

补充

看过一个ui密码显示按钮是在前边的,那咋办?
其实了,TextInputLayout前后两个icon都是可以点击的,而且可以有状态的
如下,Checkable设置为true

app:startIconCheckable="true"
app:startIconDrawable="@drawable/design_password_eye"

代码里就可以用了

        et_layout2.setStartIconOnClickListener {
            println("it=====$it")//listener里返回的view是CheckableImageButton【不对外使用的】
            if(it is Checkable){
                println("check=======${it.isChecked}")
                    // Store the current cursor position
                    val selection = editText!!.selectionEnd
//显示隐藏密码
                    this.editText?.transformationMethod = if(it.isChecked) PasswordTransformationMethod.getInstance() else  null
                    // And restore the cursor position
                    editText!!.setSelection(selection)
                it.toggle()//因为是checkable的,所以我们可以切换状态,也可以拿到当前状态来做处理,比如隐藏密码
            }
        }
image.png

源码解析

先打印下结构,没找到那个hint,难道不是个view?等待看完源码再说
至少发现,EditText被取出来放到一个FrameLayout里了.
helper text和error text这两个文本是在一个FrameLayout里的

image.png
public class TextInputLayout extends LinearLayout{
public TextInputLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    setOrientation(VERTICAL);//垂直方向的线性布局
    inputFrame = new FrameLayout(context);
    inputFrame.setAddStatesFromChildren(true);
    addView(inputFrame);//先加了一个帧布局进来
    startLayout = new LinearLayout(context);
    startLayout.setOrientation(HORIZONTAL);
    startLayout.setLayoutParams(
        new FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.MATCH_PARENT,
            Gravity.START | Gravity.LEFT));
    inputFrame.addView(startLayout);//frame里左侧添加一个startLayout
    endLayout = new LinearLayout(context);
    endLayout.setOrientation(HORIZONTAL);
    endLayout.setLayoutParams(
        new FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.MATCH_PARENT,
            Gravity.END | Gravity.RIGHT));
    inputFrame.addView(endLayout);//frame里右侧添加一个endLayout


    endIconFrame = new FrameLayout(context);
    endIconFrame.setLayoutParams(
        new FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));

//读取attr属性

    // Initialize error icon view.
    errorIconView =
        (CheckableImageButton)
            LayoutInflater.from(getContext())
                .inflate(R.layout.design_text_input_end_icon, endLayout, false);
    errorIconView.setVisibility(GONE);

    // Initialize start icon view.
    startIconView =
        (CheckableImageButton)
            LayoutInflater.from(getContext())
                .inflate(R.layout.design_text_input_start_icon, startLayout, false);
    startIconView.setVisibility(GONE);

    // Initialize end icon view.
    endIconView =
        (CheckableImageButton)
            LayoutInflater.from(getContext())
                .inflate(R.layout.design_text_input_end_icon, endIconFrame, false);
    endIconFrame.addView(endIconView);

    // Set up prefix view.
    prefixTextView = new AppCompatTextView(context);
    prefixTextView.setId(R.id.textinput_prefix_text);
    prefixTextView.setLayoutParams(
        new FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));

    startLayout.addView(startIconView);
    startLayout.addView(prefixTextView);


    // Set up suffix view.
    suffixTextView = new AppCompatTextView(context);
    suffixTextView.setId(R.id.textinput_suffix_text);
    suffixTextView.setLayoutParams(
        new FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT,
            Gravity.BOTTOM));
    ViewCompat.setAccessibilityLiveRegion(
        suffixTextView, ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);

    endLayout.addView(suffixTextView);
    endLayout.addView(errorIconView);
    endLayout.addView(endIconFrame);


}
//找到里边的EditText然后放到我们new出来的帧布局里
  public void addView(
      @NonNull View child, int index, @NonNull final ViewGroup.LayoutParams params) {
    if (child instanceof EditText) {
      // Make sure that the EditText is vertically at the bottom, so that it sits on the
      // EditText's underline
      FrameLayout.LayoutParams flp = new FrameLayout.LayoutParams(params);
      flp.gravity = Gravity.CENTER_VERTICAL | (flp.gravity & ~Gravity.VERTICAL_GRAVITY_MASK);
      inputFrame.addView(child, flp);

      // Now use the EditText's LayoutParams as our own and update them to make enough space
      // for the label
      inputFrame.setLayoutParams(params);
      updateInputLayoutMargins();

      setEditText((EditText) child);
    } else {
      // Carry on adding the View...
      super.addView(child, index, params);
    }
  }

hint部分简单看下源码
app:boxBackgroundMode="outline"
效果图


image.png

源码相关部分

//上图的就是collapse hint,所以会走下边的代码
  private void collapseHint(boolean animate) {
//代码省略
    if (cutoutEnabled()) {
      openCutout();
    }
}
  private boolean cutoutEnabled() {
    return hintEnabled && !TextUtils.isEmpty(hint) && boxBackground instanceof CutoutDrawable;
  }

  private void openCutout() {
    if (!cutoutEnabled()) {
      return;
    }
    final RectF cutoutBounds = tmpRectF;
    collapsingTextHelper.getCollapsedTextActualBounds(cutoutBounds);
    applyCutoutPadding(cutoutBounds);
    // Offset the cutout bounds by the TextInputLayout's left padding to ensure that the cutout is
    // inset relative to the TextInputLayout's bounds.
    cutoutBounds.offset(-getPaddingLeft(), 0);
//可以看到background被减掉一部分,就是显示展开的hint的那部分
    ((CutoutDrawable) boxBackground).setCutout(cutoutBounds);
  }

//通过helper获取rect
  public void getCollapsedTextActualBounds(@NonNull RectF bounds) {
    boolean isRtl = calculateIsRtl(text);

    bounds.left =
        !isRtl ? collapsedBounds.left : collapsedBounds.right - calculateCollapsedTextWidth();
    bounds.top = collapsedBounds.top;
    bounds.right = !isRtl ? bounds.left + calculateCollapsedTextWidth() : collapsedBounds.right;
    bounds.bottom = collapsedBounds.top + getCollapsedTextHeight();
  }

看下代码的初始化,肯定是CutoutDrawable的

      case BOX_BACKGROUND_OUTLINE:
        if (hintEnabled && !(boxBackground instanceof CutoutDrawable)) {
          boxBackground = new CutoutDrawable(shapeAppearanceModel);
        } else {
          boxBackground = new MaterialShapeDrawable(shapeAppearanceModel);
        }
        boxUnderline = null;
        break;

现在的studio有个layout inspector,我们可以用这个来分析这个view的结构

整体来看

分两部分,上边的 startIcon,prefix text, editText,suffix text,endIcon在一个FrameLayout里
下边的helper,error,count之类的在下边的一个线性布局里[水平方向的,所以help或者error 文本太长的话可能把counter挤到屏幕外看不见了]

image.png

image.png

其他知识

ui设计的时候不需要hint,而我们需要后边那个clear icon,结果会发现,那个文字和icon不对齐,icon默认是居中的,所以就是那个editTextView有问题了?


image.png

刚开始想到的是把那个endIcon的gravity设置成bottom,结果发现不太好看,理想的应该是和文字居中对齐的。
然后又继续去看源码,仔细的看布局里margin,高度咋设置的,首先看了edittextView的容器FrameLayout,在hint不可用的时候,它的margin已经是0了

  private int calculateLabelMarginTop() {
    if (!hintEnabled) {
      return 0;
    }

    switch (boxBackgroundMode) {
      case BOX_BACKGROUND_OUTLINE:
        return (int) (collapsingTextHelper.getCollapsedTextHeight() / 2);
      case BOX_BACKGROUND_FILLED:
      case BOX_BACKGROUND_NONE:
        return (int) collapsingTextHelper.getCollapsedTextHeight();
      default:
        return 0;
    }
  }

然后我用studio的工具layout inspect查看了下布局,发现EditTextView竟然有padding,4个方向都有,可我们xml里并没有给他设置.
我尝试在xml手动设置padding为0,然后发现 EditTextView高度果然变小了,既然不是我们设置的,那么必然是默认属性里的

  </style>
    <style name="ThemeOverlay.MaterialComponents.TextInputEditText.FilledBox">
    <item name="editTextStyle">@style/Widget.MaterialComponents.TextInputEditText.FilledBox</item>
  </style>
    <style name="ThemeOverlay.MaterialComponents.TextInputEditText.FilledBox.Dense">
    <item name="editTextStyle">@style/Widget.MaterialComponents.TextInputEditText.FilledBox.Dense
    </item>
  </style>
    <style name="ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox">
    <item name="editTextStyle">@style/Widget.MaterialComponents.TextInputEditText.OutlinedBox</item>
  </style>
    <style name="ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox.Dense">
    <item name="editTextStyle">@style/Widget.MaterialComponents.TextInputEditText.OutlinedBox.Dense
    </item>
  </style>

对应的值

    <style name="Widget.MaterialComponents.TextInputEditText.FilledBox" parent="Base.Widget.MaterialComponents.TextInputEditText">
    <item name="android:paddingTop">28dp</item>
    <item name="android:paddingBottom">12dp</item>
  </style>
    <style name="Widget.MaterialComponents.TextInputEditText.FilledBox.Dense">
    <item name="android:paddingTop">24dp</item>
    <item name="android:paddingBottom">8dp</item>
  </style>
    <style name="Widget.MaterialComponents.TextInputEditText.OutlinedBox" parent="Base.Widget.MaterialComponents.TextInputEditText"/>
    <style name="Widget.MaterialComponents.TextInputEditText.OutlinedBox.Dense">
    <item name="android:paddingTop">13dp</item>
    <item name="android:paddingBottom">13dp</item>
  </style>

解决办法
手动修改editTextView的padding

未完待续

突然发现我后边源码的部分咋丢了,我记得我写了啊,靠

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容