View 繪制體系知識梳理(8) - obtainStyledAttributes 詳解

一、基本概念

1.1 資源

Android使用xml文件來描述各種資源熟嫩,包括字符串秦踪、顏色、主題邦危、布局等等洋侨。資源分為兩個部分,及 屬性倦蚪。

1.1.1 屬性

App開發(fā)的過程中希坚,如果需要為自定義View聲明一個新的屬性,那么我們會在res/values/attr.xml文件中進行定義陵且。

<resources>
    <declare-styleable name="AttrTextView">
        <attr name="attrTvName" format="string"/>
        <attr name="attrTvColor" format="color"/>
    </declare-styleable>
</resources>

其中declare-styleable相當于一個屬性的集合裁僧,而attr則是其內(nèi)部的屬性个束,在R.java文件中declare-styleable對應(yīng)一個int[]數(shù)組。

declare-styleable 對應(yīng)的 int[] 數(shù)組

需要注意的是:

  • 如果attr后面僅有一個name:聊疲,那么這就是 引用茬底,其聲明在別的styleable中。
  • 如果attr后面不僅有name获洲,還有format阱表,那就是 聲明,不能在別的styleable中再次聲明贡珊。

1.1.2 值

常見的值存放在以下幾個位置:

  • 字符串最爬、顏色、樣式门岔,主題等爱致,在res/values/xxx.xml下,文件名可以為strings/colors/styles/theme等寒随。
  • drawable糠悯,在res/drawable/xxx.xml
  • layout,在res/layout/xxx.xml

對于值的類型分為兩種妻往,一種是基本類型互艾,例如integerstring讯泣、boolean等忘朝;另一種是引用類型,例如reference判帮。

1.2 資源解析

資源解析涉及到兩個類,AttributeSetTypedArray溉箕。

1.2.1 AttributeSet

AttributeSetxml文件解析時會返回的對象晦墙,它包含了 解析元素的所有屬性及屬性值AttributeSet提供了一組接口可以根據(jù)attr.xml中已有的名稱獲取相應(yīng)的值肴茄。

  • 操作特定屬性
  • 操作通用屬性
  • 獲取特定類型的值

1.2.2 TypedArray

TypedArray是對AttributeSet數(shù)據(jù)類的某種抽象晌畅,對于下面在控件的xml自定義的屬性而言:

browser:image_border_width="@dimen/light_app_icon_border_width"

如果采用AttributeSet的方法,那么僅僅可以獲取@dimen/light_app_icon_border_width寡痰。

如果想要獲取light_app_icon_border_width對應(yīng)的值抗楔,那么可以通過contextobtainStyledAttributesAttributeSet作為參數(shù)構(gòu)造TypedArray對象,從而直接獲取TypeArray的值拦坠。

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundedCornerImageView, defStyle, 0);

二连躏、實例講解

下面我們來看以下,如何在控件中使用自定義的屬性贞滨,實現(xiàn)的核心就是obtainStyledAttributes函數(shù)入热。

2.1 直接在 View 的 xml 中指定具體的屬性

這是最直觀也最常見的方式,我們需要做的有以下三步:

  • res/values/attr.xml中定義屬性名稱,為了方便管理勺良,我們一般都會將一個控件中的所有自定義屬性通過<declare-styleable>標簽包裹起來绰播,里面的每一個<attr>代表一個自定義屬性。
  • layout布局中尚困,指定對應(yīng)的自定義屬性的屬性值蠢箩。
  • View的構(gòu)造方法中,通過obtainStyledAttributes得到TypedArray對象事甜,obtainStyledAttributes的第一個參數(shù)傳入構(gòu)造函數(shù)中AttributeSet對象谬泌,它包含了該控件在xml中聲明的屬性及其值,第二個參數(shù)就是我們在第一步中定義的styleable讳侨。得到該TypedArray對象后呵萨,再通過getXXX獲得對應(yīng)的屬性值,從而實現(xiàn)通過xml自定義控件屬性的效果跨跨。

2.1.1 代碼

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="AttrTextView">
        <attr name="attrTvName" format="string"/>
        <attr name="attrTvColor" format="color"/>
    </declare-styleable>
</resources>
public class AttrTextView extends TextView {

    public AttrTextView(Context context) {
        super(context);
    }

    public AttrTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initAttr(context, attrs);
    }

    public AttrTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttr(context, attrs);
    }

    private void initAttr(Context context, @Nullable AttributeSet attrs) {
        //attrs 類型為 AttributeSet潮峦,其包含了在 xml 中指定的屬性和值。
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AttrTextView, 0, 0);
        if (typedArray != null) {
            String text = typedArray.getString(R.styleable.AttrTextView_attrTvName);
            int color = typedArray.getColor(R.styleable.AttrTextView_attrTvColor, 0);
            setText(text);
            setTextColor(color);
            typedArray.recycle();
        }
    }
    
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.demo.lizejun.attrdemo.AttrTextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:attrTvColor="@color/attrColorXmlDirect"
        app:attrTvName="直接在 View 的 xml 中指定具體的屬性"/> <!-- 直接通過屬性來指定 -->

</FrameLayout>

2.1.2 運行結(jié)果

2.2 在 View 的 xml 中聲明 style勇婴,通過 style 間接指定屬性

2.1中直接指定的方式忱嘹,優(yōu)點是 簡單且直觀,但是也有它的局限性耕渴,就是每次使用該控件的時候都需要對屬性進行定義拘悦,假如我們有 多個相同的控件要使用相同的樣式 時,就可以采用指定相同@style的方式進行復用橱脸。

對于自定義的控件的Java代碼础米,和2.1的方式是相同的,區(qū)別在于在xml中不指定具體的屬性值添诉,而通過style="@style/xxx"的方式屁桑,在style中再指定屬性。

2.2.1 代碼

public class AttrTextView extends TextView {

    public AttrTextView(Context context) {
        super(context);
    }

    public AttrTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initAttr(context, attrs);
    }

    public AttrTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttr(context, attrs);
    }

    private void initAttr(Context context, @Nullable AttributeSet attrs) {
        //attrs 類型為 AttributeSet栏赴,其包含了在 xml 中指定的屬性和值蘑斧。
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AttrTextView, 0, 0);
        if (typedArray != null) {
            String text = typedArray.getString(R.styleable.AttrTextView_attrTvName);
            int color = typedArray.getColor(R.styleable.AttrTextView_attrTvColor, 0);
            setText(text);
            setTextColor(color);
            typedArray.recycle();
        }
    }
    
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.demo.lizejun.attrdemo.AttrTextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:attrTvColor="@color/attrColorXmlDirect"
        style="@style/AttrUseXmlStyle"/>

</FrameLayout>
<resources>

    <style name="AttrUseXmlStyle">
        <item name="attrTvName">"在 View 的 xml 中聲明 style,通過 style 間接指定屬性"</item>
    </style>

</resources>

2.2.2 運行結(jié)果

2.3 在主題中指定某個 attr 的 style须眷,并將該 attr 作為第三個參數(shù)傳給 obtainStyledAttributes

除了在xml中通過@style的方式進行復用竖瘾,還可以采用定義主題的方式,這樣我們就可以不必在每個xml中都指定@style花颗,系統(tǒng)會自動去主題當中尋找對應(yīng)的屬性值捕传,實現(xiàn)這種方案需要做以下三步:

  • res/values/attr.xml中聲明一個新的AttrThemeStyle屬性,其類型為reference扩劝。
  • res/values/style.xml中定義一個新的style乐横,命名為AttrUseThemeStyle求橄,它包含了自定義控件的屬性,將該AttrUseThemeStyle在應(yīng)用的主題AppTheme中葡公,指定給第一步定義的AttrThemeStyle屬性罐农。
  • 在自定義控件中,將R.attr.AttrThemeStyle作為obtainStyledAttributes方法的第三個參數(shù)催什。

2.3.1 代碼

public class AttrTextView extends TextView {

    public AttrTextView(Context context) {
        super(context);
    }

    public AttrTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initAttr(context, attrs);
    }

    public AttrTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttr(context, attrs);
    }

    private void initAttr(Context context, @Nullable AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AttrTextView, R.attr.AttrThemeStyle, 0);
        if (typedArray != null) {
            String text = typedArray.getString(R.styleable.AttrTextView_attrTvName);
            int color = typedArray.getColor(R.styleable.AttrTextView_attrTvColor, 0);
            setText(text);
            setTextColor(color);
            typedArray.recycle();
        }
    }

}
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="AttrThemeStyle" format="reference"/>
    <declare-styleable name="AttrTextView">
        <attr name="attrTvName" format="string"/>
        <attr name="attrTvColor" format="color"/>
    </declare-styleable>
</resources>
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="AttrThemeStyle">@style/AttrUseThemeStyle</item>
    </style>

    <style name="AttrUseThemeStyle">
        <item name="attrTvName">"在主題中指定某個 attr 的 style涵亏,并將該 attr 作為第三個參數(shù)傳給 obtainStyledAttributes"</item>
    </style>

</resources>

2.3.2 運行結(jié)果

2.4 通過 obtainStyledAttributes 的第四個參數(shù)直接傳入一個 style,該 style 中的 item 指定字符串

最后一種方法最簡單(給所有用到這個控件的地方都設(shè)置一個默認值)蒲凶,做法就是定義一個新的style气筋,在該style中指定控件的自定義屬性,作為obtainStyledAttributes的第四個參數(shù)旋圆。

2.4.1 代碼

public class AttrTextView extends TextView {

    public AttrTextView(Context context) {
        super(context);
    }

    public AttrTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initAttr(context, attrs);
    }

    public AttrTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttr(context, attrs);
    }

    private void initAttr(Context context, @Nullable AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AttrTextView, 0, R.style.AttrUseDirectStyle);
        if (typedArray != null) {
            String text = typedArray.getString(R.styleable.AttrTextView_attrTvName);
            int color = typedArray.getColor(R.styleable.AttrTextView_attrTvColor, 0);
            setText(text);
            setTextColor(color);
            typedArray.recycle();
        }
    }

}
<resources>

    <style name="AttrUseDirectStyle">
        <item name="attrTvName">"通過 obtainStyledAttributes 的第四個參數(shù)直接傳入一個 style宠默,該 style 中的 item 指定字符串"</item>
    </style>

</resources>

2.4.2 運行結(jié)果

2.5 小結(jié)

對于以上四種處理方式,如果 重復指定了同一個屬性的不同值灵巧,那么將會按照優(yōu)先級的順序進行匹配搀矫,優(yōu)先級按照2.1-2.4的順序依次遞減,最終以高優(yōu)先級指定的值為準刻肄。

也就是說瓤球,如果通過2.1的方式直接指定了attrTvName,那么即使通過2.2的方式在style中指定attrTvName的屬性敏弃,也會以2.1為準卦羡,使用下來我們可以發(fā)現(xiàn),它的粒度其實是依次增大的:

  • 單一xml的屬性
  • 多個xml復用@style
  • 應(yīng)用或者Activity的主題復用@style
  • 全局默認@style

三麦到、夜間模式

下面绿饵,通過一個項目中的例子來介紹一下obtainStyledAttributes的應(yīng)用,其最終效果就是通過Attr的方式來實現(xiàn)不重啟的夜間模式切換瓶颠,項目的地址為 AttrDemo蝴罪。

該方案的原理就是給控件聲明一個屬性dayNightAttr,該屬性指向了一個style步清,該style中又包含兩個新的attr,分別指向白天和夜間模式的style虏肾,在切換的時候廓啊,通過遍歷對應(yīng)模式的style中的屬性,將其屬性值應(yīng)用到對應(yīng)的控件當中封豪。

3.1 使用方法

首先來看一下使用的方法谴轮,首先在控件中對dayNightAttr進行指定:

xml 中的指定

接下來為NightModeTextViewStyle定義兩個style,分別指向白天和夜間模式:

style 的定義

NightModeTextView實現(xiàn)了INightMode接口吹埠,當需要切換的時候第步,調(diào)用該接口傳入當前的主題即可

切換方式

3.2 實現(xiàn)原理

首先看一下支持夜間模式的控件的實現(xiàn):

public class NightModeTextView extends AppCompatTextView implements INightMode {

    private HashMap<String, Integer> mTheme = new HashMap<>();
    private String mCurTheme = null;

    public NightModeTextView(Context context) {
        super(context);
    }

    public NightModeTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView(context, attrs, R.attr.dayNightAttr);
    }

    public NightModeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context, attrs, defStyleAttr);
    }

    private void initView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        int[] dayNightResId = DayNightHelper.getDayNightStyleId(context, attrs, defStyleAttr);
        if (dayNightResId[0] != 0) {
            mTheme.put(DayNightHelper.THEME_DAY, dayNightResId[0]);
        }
        if (dayNightResId[1] != 0) {
            mTheme.put(DayNightHelper.THEME_NIGHT, dayNightResId[1]);
        }
        applyTheme(DayNightHelper.getCurTheme(context));
    }

    @Override
    public void applyTheme(String theme) {
        if (TextUtils.equals(mCurTheme, theme)) {
            return;
        }
        Integer styleId = mTheme.get(theme);
        if (styleId != null) {
            DayNightHelper.applyTextView(this, styleId);
        }
    }
}

第一個關(guān)鍵的函數(shù)為DayNightHelper.getDayNightStyleId疮装,它會解析我們在3.1中定義的dayNightAttr的屬性值,得到白天和夜間模式主題的ID粘都,這里用到的方式就是我們前面看到的obtainStyledAttributes方法廓推。

第二個關(guān)鍵的函數(shù)是applyTextView,當?shù)玫搅藢?yīng)styleID后翩隧,我們會去遍歷它下面的所有attr樊展,然后與反射獲得的ID對比,找到對應(yīng)的屬性堆生,然后調(diào)用View對應(yīng)的方法去設(shè)置专缠。

public class DayNightHelper {

    public static final String SP_NAME = "sp";
    public static final String DAY_NIGHT_THEME_KEY = "theme_day_night";
    public static final String THEME_DAY = "theme_day";
    public static final String THEME_NIGHT = "theme_night";

    public static int[] getDayNightStyleId(Context context, @Nullable AttributeSet attrs, int defStyle) {
        int dayStyleId = 0;
        int nightStyleId = 0;
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThemeCommonStyleable, defStyle, 0);
        if (a != null) {
            int dayNightStyleId = a.getResourceId(R.styleable.ThemeCommonStyleable_dayNightAttr, 0);
            if (dayNightStyleId != 0) {
                TypedArray dayNightArray = context.getTheme().obtainStyledAttributes(dayNightStyleId, R.styleable.DayNightStyleable);
                if (dayNightArray != null) {
                    dayStyleId = dayNightArray.getResourceId(R.styleable.DayNightStyleable_themeDayAttr, 0);
                    nightStyleId = dayNightArray.getResourceId(R.styleable.DayNightStyleable_themeNightAttr, 0);
                    dayNightArray.recycle();
                }
            }
            a.recycle();
        }
        return new int[]{ dayStyleId, nightStyleId };
    }

    public static void applyTextView(TextView textView, int styleId) {
        TypedArray a = textView.getContext().getTheme().obtainStyledAttributes(styleId,
                ReflectHelper.Styleable.sTextView);
        if (a != null) {
            int indexCount = a.getIndexCount();
            for (int i = 0; i < indexCount; i++) {
                int attr = a.getIndex(i);
                if (attr == ReflectHelper.Styleable.sTextViewSize) {
                    int textSize = a.getDimensionPixelSize(attr, 0);
                    if (textSize != 0) {
                        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
                    }
                } else if (attr == ReflectHelper.Styleable.sTextViewColor) {
                    ColorStateList textColor = a.getColorStateList(attr);
                    textView.setTextColor(textColor);
                } else if (attr == ReflectHelper.Styleable.sText) {
                    String text = a.getString(attr);
                    textView.setText(text);
                }
            }
            a.recycle();
        }
    }

    public static String getCurTheme(Context context) {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        return sp.getString(DAY_NIGHT_THEME_KEY, THEME_DAY);
    }

    public static void switchTheme(Context context) {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        String curTheme = sp.getString(DAY_NIGHT_THEME_KEY, THEME_DAY);
        String nextTheme;
        if (TextUtils.equals(curTheme, THEME_DAY)) {
            nextTheme = THEME_NIGHT;
        } else {
            nextTheme = THEME_DAY;
        }
        sp.edit().putString(DAY_NIGHT_THEME_KEY, nextTheme).apply();
    }
}

最后看一下反射獲取ID值的代碼:

public class ReflectHelper {

    private static final String TAG = ReflectHelper.class.getSimpleName();

    public static class Styleable {

        private static final Class<?> CLASS_STYLEABLE = getStyleableClass();

        public static int[] sTextView;
        public static int sTextViewSize;
        public static int sTextViewColor;
        public static int sText;

        static {

            try {
                sTextView = (int[]) CLASS_STYLEABLE.getField("TextView").get(null);
            } catch (Exception e) {
                Log.w(TAG, "", e);
            }

            try {
                sTextViewSize = CLASS_STYLEABLE.getField("TextView_textSize").getInt(null);
            } catch (Exception e) {
                Log.w(TAG, "", e);
            }

            try {
                sTextViewColor = CLASS_STYLEABLE.getField("TextView_textColor").getInt(null);
            } catch (Exception e) {
                Log.w(TAG, "", e);
            }

            try {
                sText = CLASS_STYLEABLE.getField("TextView_text").getInt(null);
            } catch (Exception e) {
                Log.w(TAG, "", e);
            }
        }

        private static Class<?> getStyleableClass() {
            try {
                Class<?> clz = Class.forName("com.android.internal.R$styleable");
                return clz;
            } catch (Exception e) {
                Log.w(TAG, "", e);
            }
            return null;
        }
    }
}

四、參考文獻

Android 中 View 自定義 XML 屬性詳解以及 R.attr 與 R.styleable 的區(qū)別

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末淑仆,一起剝皮案震驚了整個濱河市涝婉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔗怠,老刑警劉巖墩弯,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蟀淮,居然都是意外死亡最住,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門怠惶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涨缚,“玉大人,你說我怎么就攤上這事策治∨海” “怎么了?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵通惫,是天一觀的道長茂翔。 經(jīng)常有香客問我,道長履腋,這世上最難降的妖魔是什么珊燎? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮遵湖,結(jié)果婚禮上悔政,老公的妹妹穿的比我還像新娘。我一直安慰自己延旧,他們只是感情好谋国,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著迁沫,像睡著了一般芦瘾。 火紅的嫁衣襯著肌膚如雪捌蚊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天近弟,我揣著相機與錄音缅糟,去河邊找鬼。 笑死藐吮,一個胖子當著我的面吹牛溺拱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谣辞,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼迫摔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了泥从?” 一聲冷哼從身側(cè)響起句占,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎躯嫉,沒想到半個月后纱烘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡祈餐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年擂啥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帆阳。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡哺壶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜒谤,到底是詐尸還是另有隱情山宾,我是刑警寧澤,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布鳍徽,位于F島的核電站资锰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏阶祭。R本人自食惡果不足惜绷杜,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望濒募。 院中可真熱鬧鞭盟,春花似錦、人聲如沸萨咳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽培他。三九已至鹃两,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舀凛,已是汗流浹背俊扳。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留猛遍,地道東北人馋记。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像懊烤,于是被迫代替她去往敵國和親梯醒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359