android 富文本设计

SpannableString:

This is the class for text whose content is immutable but to which markup objects can be attached and detached.

SpannableString构造的字符串后不可变了,标记对象可以添加和删除

SpannableStringBuilder:

This is the class for text whose content and markup can both be changed.

SpannableStringBuilder 构造的内容和标记对象都可更改

    //object what :对应的各种Span
    // int start:开始应用指定Span的位置,索引从0开始
    //int end:结束应用指定Span的位置,不包含
    //int flags   见Spanned中的描述
    public void setSpan(Object what, int start, int end, int flags) {
        setSpan(true, what, start, end, flags, true/*enforceParagraph*/);
    }

Spanned

This is the interface for text that has markup objects ranges of it.

public interface Spanned
extends CharSequence
{
    //在标志位【start,end)后添加文字,新添加的文字不会有任何设置的属性,前边边的添加的文字会带有设置的what属性
     public static final int SPAN_INCLUSIVE_EXCLUSIVE = SPAN_MARK_MARK;
     //在标志位【start,end)前后添加文字,新添加的文字会有设置的属性
     public static final int SPAN_INCLUSIVE_INCLUSIVE = SPAN_MARK_POINT;
     //在标志位【start,end)前后添加文字,新添加的文字不会有任何设置的属性
     public static final int SPAN_EXCLUSIVE_EXCLUSIVE = SPAN_POINT_MARK;
    //在标志位【start,end)前添加文字,新添加的文字不会有任何设置的属性,后边的添加的文字会带有设置的what属性
     public static final int SPAN_EXCLUSIVE_INCLUSIVE = SPAN_POINT_POINT;
}

Spannable

静态单例 实现工厂方法 返回的SpannableString,
This is the class for text whose content is immutable but to which markup objects can be attached and detached.
就是处理content内容不变。

/**
 * This is the interface for text to which markup objects can be
 * attached and detached.  Not all Spannable classes have mutable text;
 * see {@link Editable} for that.
 */
public interface Spannable
extends Spanned
{
    /**
     * Attach the specified markup object to the range <code>start&hellip;end</code>
     * of the text, or move the object to that range if it was already
     * attached elsewhere.  See {@link Spanned} for an explanation of
     * what the flags mean.  The object can be one that has meaning only
     * within your application, or it can be one that the text system will
     * use to affect text display or behavior.  Some noteworthy ones are
     * the subclasses of {@link android.text.style.CharacterStyle} and
     * {@link android.text.style.ParagraphStyle}, and
     * {@link android.text.TextWatcher} and
     * {@link android.text.SpanWatcher}.
     */
    public void setSpan(Object what, int start, int end, int flags);

    /**
     * Remove the specified object from the range of text to which it
     * was attached, if any.  It is OK to remove an object that was never
     * attached in the first place.
     */
    public void removeSpan(Object what);

    /**
     * Remove the specified object from the range of text to which it
     * was attached, if any.  It is OK to remove an object that was never
     * attached in the first place.
     *
     * See {@link Spanned} for an explanation of what the flags mean.
     *
     * @hide
     */
    default void removeSpan(Object what, int flags) {
        removeSpan(what);
    }

    /**
     * Factory used by TextView to create new {@link Spannable Spannables}. You can subclass
     * it to provide something other than {@link SpannableString}.
     *
     * @see android.widget.TextView#setSpannableFactory(Factory)
     */
    public static class Factory {
        private static Spannable.Factory sInstance = new Spannable.Factory();

        /**
         * Returns the standard Spannable Factory.
         */
        public static Spannable.Factory getInstance() {
            return sInstance;
        }

        /**
         * Returns a new SpannableString from the specified CharSequence.
         * You can override this to provide a different kind of Spannable.
         */
        public Spannable newSpannable(CharSequence source) {
            return new SpannableString(source);
        }
    }
}

Editable

静态单例 实现工厂方法 返回的SpannableStringBuilder,
This is the interface for text whose content and markup
can be changed

public interface Editable
extends CharSequence, GetChars, Spannable, Appendable
{
    /**
     * Replaces the specified range (<code>st&hellip;en</code>) of text in this
     * Editable with a copy of the slice <code>start&hellip;end</code> from
     * <code>source</code>.  The destination slice may be empty, in which case
     * the operation is an insertion, or the source slice may be empty,
     * in which case the operation is a deletion.
     * <p>
     * Before the change is committed, each filter that was set with
     * {@link #setFilters} is given the opportunity to modify the
     * <code>source</code> text.
     * <p>
     * If <code>source</code>
     * is Spanned, the spans from it are preserved into the Editable.
     * Existing spans within the Editable that entirely cover the replaced
     * range are retained, but any that were strictly within the range
     * that was replaced are removed. If the <code>source</code> contains a span
     * with {@link Spanned#SPAN_PARAGRAPH} flag, and it does not satisfy the
     * paragraph boundary constraint, it is not retained. As a special case, the
     * cursor position is preserved even when the entire range where it is located
     * is replaced.
     * @return  a reference to this object.
     *
     * @see Spanned#SPAN_PARAGRAPH
     */
    public Editable replace(int st, int en, CharSequence source, int start, int end);

    /**
     * Convenience for replace(st, en, text, 0, text.length())
     * @see #replace(int, int, CharSequence, int, int)
     */
    public Editable replace(int st, int en, CharSequence text);

    /**
     * Convenience for replace(where, where, text, start, end)
     * @see #replace(int, int, CharSequence, int, int)
     */
    public Editable insert(int where, CharSequence text, int start, int end);

    /**
     * Convenience for replace(where, where, text, 0, text.length());
     * @see #replace(int, int, CharSequence, int, int)
     */
    public Editable insert(int where, CharSequence text);

    /**
     * Convenience for replace(st, en, "", 0, 0)
     * @see #replace(int, int, CharSequence, int, int)
     */
    public Editable delete(int st, int en);

    /**
     * Convenience for replace(length(), length(), text, 0, text.length())
     * @see #replace(int, int, CharSequence, int, int)
     */
    public Editable append(CharSequence text);

    /**
     * Convenience for replace(length(), length(), text, start, end)
     * @see #replace(int, int, CharSequence, int, int)
     */
    public Editable append(CharSequence text, int start, int end);

    /**
     * Convenience for append(String.valueOf(text)).
     * @see #replace(int, int, CharSequence, int, int)
     */
    public Editable append(char text);

    /**
     * Convenience for replace(0, length(), "", 0, 0).
     * Note that this clears the text, not the spans;
     * use {@link #clearSpans} if you need that.
     * @see #replace(int, int, CharSequence, int, int)
     */
    public void clear();

    /**
     * Removes all spans from the Editable, as if by calling
     * {@link #removeSpan} on each of them.
     */
    public void clearSpans();

    /**
     * Sets the series of filters that will be called in succession
     * whenever the text of this Editable is changed, each of which has
     * the opportunity to limit or transform the text that is being inserted.
     */
    public void setFilters(InputFilter[] filters);

    /**
     * Returns the array of input filters that are currently applied
     * to changes to this Editable.
     */
    public InputFilter[] getFilters();

    /**
     * Factory used by TextView to create new {@link Editable Editables}. You can subclass
     * it to provide something other than {@link SpannableStringBuilder}.
     *
     * @see android.widget.TextView#setEditableFactory(Factory)
     */
    public static class Factory {
        private static Editable.Factory sInstance = new Editable.Factory();

        /**
         * Returns the standard Editable Factory.
         */
        public static Editable.Factory getInstance() {
            return sInstance;
        }

        /**
         * Returns a new SpannedStringBuilder from the specified
         * CharSequence.  You can override this to provide
         * a different kind of Spanned.
         */
        public Editable newEditable(CharSequence source) {
            return new SpannableStringBuilder(source);
        }
    }
}

object what 各种设置

1 AbsoluteSizeSpan 修改字体大小
//A span that changes the size of the text it's attached to.
public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan {

    /**
     * Set the text size to <code>size</code> physical pixels.
     */
    public AbsoluteSizeSpan(int size) {
        this(size, false);
    }

    /**
     * Set the text size to <code>size</code> physical pixels, or to <code>size</code>
     * device-independent pixels if <code>dip</code> is true.
     */
    public AbsoluteSizeSpan(int size, boolean dip) {
        mSize = size;
        mDip = dip;
    }

    /**
     * Creates an {@link AbsoluteSizeSpan} from a parcel.
     */
    public AbsoluteSizeSpan(@NonNull Parcel src) {
        mSize = src.readInt();
        mDip = src.readInt() != 0;
    }

}

2 BackgroundColorSpan 修改背景色

Changes the background color of the text to which the span is attached.

public class BackgroundColorSpan extends CharacterStyle
        implements UpdateAppearance, ParcelableSpan {


  /**
     * Creates a {@link BackgroundColorSpan} from a color integer.
     * <p>
     *
     * @param color color integer that defines the background color
     * @see android.content.res.Resources#getColor(int, Resources.Theme)
     */
    public BackgroundColorSpan(@ColorInt int color) {
        mColor = color;
    }

    /**
     * Creates a {@link BackgroundColorSpan} from a parcel.
     */
    public BackgroundColorSpan(@NonNull Parcel src) {
        mColor = src.readInt();
    }

}

3 BulletSpan

A span which styles paragraphs as bullet points (respecting layout direction).
设置小圆点似的符号
BulletSpans must be attached from the first character to the last character of a single paragraph. otherwise the bullet point will not be displayed but the first paragraph encountered will have a leading margin.

public class BulletSpan implements LeadingMarginSpan, ParcelableSpan {

 // Bullet is slightly bigger to avoid aliasing artifacts on mdpi devices.
    private static final int STANDARD_BULLET_RADIUS = 4;
    public static final int STANDARD_GAP_WIDTH = 2;
    private static final int STANDARD_COLOR = 0;


    @Px
    private final int mGapWidth;
    @Px
    private final int mBulletRadius;
    private Path mBulletPath = null;
    @ColorInt
    private final int mColor;
    private final boolean mWantColor;

    /**
     * Creates a {@link BulletSpan} with the default values.
     */
    public BulletSpan() {
        this(STANDARD_GAP_WIDTH, STANDARD_COLOR, false, STANDARD_BULLET_RADIUS);
    }

    /**
     * Creates a {@link BulletSpan} based on a gap width
     *
     * @param gapWidth the distance, in pixels, between the bullet point and the paragraph.
     */
    public BulletSpan(int gapWidth) {
        this(gapWidth, STANDARD_COLOR, false, STANDARD_BULLET_RADIUS);
    }

    /**
     * Creates a {@link BulletSpan} based on a gap width and a color integer.
     *
     * @param gapWidth the distance, in pixels, between the bullet point and the paragraph.
     * @param color    the bullet point color, as a color integer
     * @see android.content.res.Resources#getColor(int, Resources.Theme)
     */
    public BulletSpan(int gapWidth, @ColorInt int color) {
        this(gapWidth, color, true, STANDARD_BULLET_RADIUS);
    }

    /**
     * Creates a {@link BulletSpan} based on a gap width and a color integer.
     *
     * @param gapWidth     the distance, in pixels, between the bullet point and the paragraph.
     * @param color        the bullet point color, as a color integer.
     * @param bulletRadius the radius of the bullet point, in pixels.
     * @see android.content.res.Resources#getColor(int, Resources.Theme)
     */
    public BulletSpan(int gapWidth, @ColorInt int color, @IntRange(from = 0) int bulletRadius) {
        this(gapWidth, color, true, bulletRadius);
    }

    private BulletSpan(int gapWidth, @ColorInt int color, boolean wantColor,
            @IntRange(from = 0) int bulletRadius) {
        mGapWidth = gapWidth;
        mBulletRadius = bulletRadius;
        mColor = color;
        mWantColor = wantColor;
    }

    /**
     * Creates a {@link BulletSpan} from a parcel.
     */
    public BulletSpan(@NonNull Parcel src) {
        mGapWidth = src.readInt();
        mWantColor = src.readInt() != 0;
        mColor = src.readInt();
        mBulletRadius = src.readInt();
    }

}

4 ForegroundColorSpan 设置文字的颜色

Changes the color of the text to which the span is attached.

SpannableString string = new SpannableString("Text with a foreground color span");
string.setSpan(new ForegroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
5 LeadingMarginSpan.Standard 缩进
6 QuoteSpan 文本左侧添加一条竖线
SpannableString string = new SpannableString("Text with quote span on a long line");
 string.setSpan(new QuoteSpan(Color.GREEN, 20, 40), 0, string.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

A span which styles paragraphs by adding a vertical stripe at the beginning of the text (respecting layout direction).

A QuoteSpan must be attached from the first character to the last character of a single paragraph, otherwise the span will not be displayed.

public class QuoteSpan implements LeadingMarginSpan, ParcelableSpan {

public QuoteSpan() {
        this(STANDARD_COLOR, STANDARD_STRIPE_WIDTH_PX, STANDARD_GAP_WIDTH_PX);
    }

 public QuoteSpan(@ColorInt int color) {
        this(color, STANDARD_STRIPE_WIDTH_PX, STANDARD_GAP_WIDTH_PX);
    }

  public QuoteSpan(@ColorInt int color, @IntRange(from = 0) int stripeWidth,
            @IntRange(from = 0) int gapWidth) {
        mColor = color;
        mStripeWidth = stripeWidth;
        mGapWidth = gapWidth;
    }

    public QuoteSpan(@NonNull Parcel src) {
        mColor = src.readInt();
        mStripeWidth = src.readInt();
        mGapWidth = src.readInt();
    }

}
7 RelativeSizeSpan 相对当前文本大小进行比例缩放

Uniformly scales the size of the text to which it's attached by a certain proportion.

SpannableString string = new SpannableString("Text with relative size span");
string.setSpan(new RelativeSizeSpan(1.5f), 10, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
public class RelativeSizeSpan extends MetricAffectingSpan implements ParcelableSpan {

  /**
     * Creates a {@link RelativeSizeSpan} based on a proportion.
     *
     * @param proportion the proportion with which the text is scaled.
     */
    public RelativeSizeSpan(@FloatRange(from = 0) float proportion) {
        mProportion = proportion;
    }

    /**
     * Creates a {@link RelativeSizeSpan} from a parcel.
     */
    public RelativeSizeSpan(@NonNull Parcel src) {
        mProportion = src.readFloat();
    }
}
8 ScaleXSpan 字体按比例水平方向缩放

Scales horizontally the size of the text to which it's attached by a certain factor.

Values > 1.0 will stretch the text wider. Values < 1.0 will stretch the text narrower.

SpannableString string = new SpannableString("Text with ScaleX span");
 string.setSpan(new ScaleXSpan(2f), 10, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
9 StrikethroughSpan 给对应的文本加入删除线

A span that strikes through the text it's attached to.

SpannableString string = new SpannableString("Text with strikethrough span");

string.setSpan(new StrikethroughSpan(), 10, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
10 StyleSpan 字体的样式

Span that allows setting the style of the text it's attached to.

Possible styles are: {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC} and
{@link Typeface#BOLD_ITALIC}.

SpannableString string = new SpannableString("Bold and italic text");

string.setSpan(new StyleSpan(Typeface.BOLD), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
string.setSpan(new StyleSpan(Typeface.ITALIC), 9, 15, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

11 SubscriptSpan 下标式的样式

The span that moves the position of the text baseline lower.

 SpannableString string = new SpannableString("☕- C8H10N4O2\n");
        string.setSpan(new SubscriptSpan(), 4, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        string.setSpan(new SubscriptSpan(), 6, 8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        string.setSpan(new SubscriptSpan(), 9, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        string.setSpan(new SubscriptSpan(), 11, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

12 SuperscriptSpan 上标样式

The span that moves the position of the text baseline higher.

SpannableString string = new SpannableString("1st example");
string.setSpan(new SuperscriptSpan(), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

12 TypefaceSpan 设置不同的字体

Span that updates the typeface of the text it's attached to

       Typeface myTypeface = Typeface.create(ResourcesCompat.getFont(context, R.font.acme),Typeface.BOLD);

        SpannableString string = new SpannableString("Text with typeface span.");
        string.setSpan(new TypefaceSpan(myTypeface), 1, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        string.setSpan(new TypefaceSpan("sans-serif"), 10, 18, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        string.setSpan(new TypefaceSpan("monospace"), 19, 22, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

13 UnderlineSpan 下划线

A span that underlines the text it's attached to.

SpannableString string = new SpannableString("Text with underline span");
        string.setSpan(new UnderlineSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

14 URLSpan 通过系统浏览器打开链接

Implementation of the {@link ClickableSpan} that allows setting a url string. When selecting and clicking on the text to which the span is attached, the <code>URLSpan</code> will try to open the url, by launching an an Activity with an {@link Intent#ACTION_VIEW} intent.

  SpannableString string = new SpannableString("Text with a url span");
  string.setSpan(new URLSpan("http://www.baidu.com"), 12, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

URLSpan 实现部分源码

public class URLSpan extends ClickableSpan implements ParcelableSpan{

 private final String mURL;
 public URLSpan(String url) {
        mURL = url;
    }
 public URLSpan(@NonNull Parcel src) {
        mURL = src.readString();
    }


    @Override
    public void onClick(View widget) {
        Uri uri = Uri.parse(getURL());
        Context context = widget.getContext();
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
        try {
            context.startActivity(intent);
        } catch (ActivityNotFoundException e) {
            Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString());
        }
    }

}

15 ClickableSpan 抽象类

If an object of this type is attached to the text of a TextView with a movement method of LinkMovementMethod, the affected spans of text can be selected. If selected and clicked, the {@link #onClick} method will be called.

也就是需要实现点击效果 必须
textView.setMovementMethod(LinkMovementMethod.getInstance());重写onClick.

支持下划线和颜色设置 重写 updateDrawState

public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance {
    private static int sIdCounter = 0;

    private int mId = sIdCounter++;

    /**
     * Performs the click action associated with this span.
     */
    public abstract void onClick(@NonNull View widget);

    /**
     * Makes the text underlined and in the link color.
     */
    @Override
    public void updateDrawState(@NonNull TextPaint ds) {
        ds.setColor(ds.linkColor);
        ds.setUnderlineText(true);
    }

    /**
     * Get the unique ID for this span.
     *
     * @return The unique ID.
     * @hide
     */
    public int getId() {
        return mId;
    }
}
16 DrawableMarginSpan

A span which adds a drawable and a padding to the paragraph it's attached to.

If the height of the drawable is bigger than the height of the line it's attached to then the line height is increased to fit the drawable. <code>DrawableMarginSpan</code> allows setting a padding between the drawable and the text. The default value is 0. The span must be set from the beginning of the text, otherwise either the span won't be rendered or it will be rendered incorrectly.

SpannableString string = new SpannableString("Text with a drawable.");

string.setSpan(new DrawableMarginSpan(drawable, 20), 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
17 IconMarginSpan

类似DrawableMarginSpan 只不过,不过是加入Bitmap

18 ImageSpan 图片样式,主要用于在文本中插入图片
19 MaskFilterSpan

Span that allows setting a {@link MaskFilter} to the text it's attached to.

文本滤镜 目前只有模糊效果 BlurMaskFilter 和浮雕效果EmbossMaskFilter

       MaskFilter blurMask = new BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL);
        SpannableString string = new SpannableString("Text with blur mask");

        string.setSpan(new MaskFilterSpan(blurMask), 10, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

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

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,258评论 0 10
  • 如今的社会环境下,越来越多的女生慢慢修炼成了女汉子,并以女汉子自居。固执倔强,霸道蛮横,得理不饶人这些均不是女汉子...
    果昊妈妈阅读 581评论 0 0
  • 最近儿子变化很大,作业能够静下来认真写了,虽然还不能坚持很久,但和以前比起来已经是很大进步了,上周老师留的...
    全顺楼阅读 400评论 0 1
  • 在多记录集Union时,发生了错误,由于排序集的不同而产生冲突。 一般说来数据库均会采用默认的排序集,因此不会产生...
    ConstZ阅读 1,092评论 0 0
  • 今天很开心 我们去看电影了 (使徒行者)看的电影是九点场的,有点晚。大概看到十点十几的时候我们就先离场了,因为他有...
    自落的木欣欣阅读 76评论 0 0