Android主題切換(Theme)實(shí)現(xiàn)日夜間功能


前言

隨著一款A(yù)PP應(yīng)用功能的不斷完善,用戶群體的不斷增多,APP的更新也就不僅僅局限于功能需求,如何做好良好的用戶體驗(yàn),讓用戶傳播良好的體驗(yàn)口碑,顯得尤為重要,而用戶體驗(yàn)一塊日夜間模式儼然成為了標(biāo)配。其實(shí),日夜間功能就是換膚的一種,關(guān)于換膚功能的實(shí)現(xiàn),也是眾說紛紜,總的來講分為兩類:主題換膚(Theme)和插件換膚(APK換膚)。

插件換膚 插件換膚的實(shí)現(xiàn)原理就是主APK根據(jù)當(dāng)前環(huán)境需求,解析指定目錄下對(duì)應(yīng)的插件APK,獲得其中同名的資源文件并動(dòng)態(tài)替換到主APK的應(yīng)用程序中。插件APK并不需要安裝,只需要放置在指定目錄下即可。

  • 優(yōu)點(diǎn): 能夠?qū)崿F(xiàn)各種主題樣式的加載,比較靈活,需要增添新的主題只要新建一個(gè)插件APK,并配置好相關(guān)的資源,放置到指定的文件目錄下就行,很方便。
  • 缺點(diǎn): 需要對(duì)控件進(jìn)行適配修改,實(shí)現(xiàn)換膚功能,對(duì)于自定義控件,也需要在適配上花點(diǎn)時(shí)間。而且放置在文件夾中的插件APK也可能會(huì)因?yàn)楸徽`刪或是損壞而造成資源獲取不到,導(dǎo)致?lián)Q膚失敗。

主題換膚 主題換膚的實(shí)現(xiàn)原理就是在主apk配置多套主題,每套主題對(duì)同一個(gè)屬性使用相應(yīng)的資源。

  • 優(yōu)點(diǎn): 相比插件換膚來說更容易上手,理解起來也會(huì)更容易。
  • 缺點(diǎn): 增添新的主題樣式必須要發(fā)布新版本。全部資源文件都放在APK中,APK會(huì)顯得十分臃腫,特別是圖片資源,因此個(gè)人推薦純色線條的圖標(biāo),并通過著色來實(shí)現(xiàn)不同主題下?lián)Q膚的可能。


因?yàn)榻裉斓闹黝}是日夜間模式,考慮到并不會(huì)涉及主題樣式增添的可能,所以權(quán)衡之下還是選擇使用主題換膚來實(shí)現(xiàn)日夜間模式,老套路,效果預(yù)覽(文末將附上高清地址入口)

預(yù)覽效果第一季槍版
預(yù)覽效果第二季槍版




準(zhǔn)備相關(guān)的屬性樣式及主題:

自定義attr屬性:

主題換膚和插件換膚原理其實(shí)一樣,就是控制不同模式下加載對(duì)應(yīng)的資源文件,只是實(shí)現(xiàn)的方式不同而已。以往我們?cè)趯憍ml布局文件的時(shí)候,默認(rèn)的屬性賦值都是絕對(duì)的,即android:background="#FFFFFF"android:background="@color/white"。
而一旦屬性被這樣賦值,默認(rèn)的資源加載就被限制,倘若有需求需要視圖在加載時(shí)能夠根據(jù)當(dāng)前環(huán)境配置特定的資源,那就只能在Java程序代碼中動(dòng)態(tài)修改,繁瑣程度可想而知。那么是否一個(gè)辦法能夠使xml屬性的賦值能夠動(dòng)態(tài)的根據(jù)當(dāng)前主題樣式的改變而去加載默認(rèn)的資源呢 ?
有,那就是今天的腕兒:自定義屬性。在我看來自定義屬性在主題換膚中充當(dāng)著占位符的角色,它會(huì)告訴系統(tǒng)這是一個(gè)相對(duì)的引用,真正的資源引用是當(dāng)前上下文環(huán)境所對(duì)應(yīng)的主題樣式屬性列表中,對(duì)這個(gè)自定義屬性的賦值。

1.在res-value目錄下新建attr屬性的資源文件,例如:custom_theme_attrs.xml
2.在custom_theme_attrs.xml文件中新建自定義屬性。

格式:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="自定義屬性名稱" format="資源引用格式(color、dimen、reference...)" />
</resources>

示例:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 控制app背景色 format:顏色值、資源引用 -->
    <attr name="custom_attr_app_bg" format="color|reference" />
    <!-- 控制app標(biāo)題欄背景色 format:顏色值、資源引用 -->
    <attr name="custom_attr_app_title_layout_bg" format="color|reference" />
    <!-- 用戶頭像顯示占位Drawable format:顏色值、資源引用 -->
    <attr name="custom_attr_user_photo_place_holder" format="color|reference" />
    <!-- 用戶昵稱字體顏色 format:顏色值、資源引用 -->
    <attr name="custom_attr_nickname_text_color" format="color|reference" />
    <!-- 用戶備注字體顏色 format:顏色值、資源引用 -->
    <attr name="custom_attr_remark_text_color" format="color|reference" />
    <!-- 用戶頭像顯示的透明度 format:尺寸值、資源引用 -->
    <attr name="custom_attr_user_photo_alpha" format="dimension|reference" />
</resources>

寫過自定義View的朋友一定不會(huì)陌生,不就是自定義屬性嘛。區(qū)別就是這些屬性值沒有包裹在styleable中,至于為啥我就不班門弄斧,有需要的朋友可以了解簡(jiǎn)書作者楚云之南寫的《深入理解Android 自定義attr Style styleable以及其應(yīng)用》,感覺寫的不錯(cuò),感謝分享 ??!

自定義theme主題:

Style想必并不陌生,在需要寫很多類似的代碼塊時(shí),我們通常會(huì)提取其中共有部分,配置在Style中,直接在xml中的style屬性中引用即可,非常方便。這里所說的主題其實(shí)也是Style樣式中的一種,只是它不僅僅局限于控件樣式屬性的賦值,常常還涉及到window窗口相關(guān),就是樣式屬性的一個(gè)集合。既然是通過切換主題來切換應(yīng)用UI樣式,所以在定義Style主題樣式的時(shí)候,需要準(zhǔn)備多套主題樣式。

1.在res-value目錄下新建style屬性的資源文件,例如:custom_theme_styles.xml。
2.在custom_theme_styles.xml文件中新建自定義主題,并對(duì)特定的系統(tǒng)、自定義屬性進(jìn)行賦值操作。

格式:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="自定義主題樣式的名稱" parent="繼承的主題,可以是自定義主題樣式也可以是系統(tǒng)主題樣式">
        <item name="屬性名稱">賦值的對(duì)應(yīng)資源</item>
    </style>
</resources>

示例:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="MarioTheme" parent="Theme.AppCompat.Light.DarkActionBar" >
        <!-- 隱藏Activity窗口的ActionBar -->
        <item name="windowActionBar">false</item>
        <!-- 隱藏Activity窗口的Title標(biāo)題欄 -->
        <item name="windowNoTitle">true</item>
    </style>
    
    <style name="MarioTheme.Day" >
        <!-- 日間模式下 "custom_attr_app_bg" 的賦值為#FFFFFF -->
        <item name="custom_attr_app_bg">#FFFFFF</item>
        ...
    </style>
    
    <style name="MarioTheme.Night" >
        <!-- 夜間模式下 "custom_attr_app_bg" 的賦值為#1F1F1F -->
        <item name="custom_attr_app_bg">#1F1F1F</item>
        ...
    </style>
</resources>

如上述示例所示,首先是新建一個(gè)繼承自系統(tǒng)Theme.AppCompat.Light.DarkActionBar樣式的自定義主題MarioTheme算是一個(gè)主題的Base基礎(chǔ)主題,在這個(gè)基礎(chǔ)主題中,可以對(duì)一些通用的屬性進(jìn)行賦值,比如一些全局性的窗口樣式,當(dāng)然這些賦值上去的屬性也是可以被后來繼承的子類主題覆蓋。
然后又新建了兩個(gè)繼承自這個(gè)基礎(chǔ)主題的MarioTheme.DayMarioTheme.Night分別作為日間和夜間的主題,而且分別在兩個(gè)主題中對(duì)自定義屬性custom_attr_app_bg進(jìn)行了賦值。

其實(shí)通過上述兩個(gè)步驟:[自定義屬性 --> 自定義主題,并在主題中對(duì)自定義屬性進(jìn)行相應(yīng)的賦值],主題換膚的準(zhǔn)備工作可以說是已經(jīng)完成。但是為了項(xiàng)目的可維護(hù)性更高,尚且有不少可以優(yōu)化的地方,如上#1F1F1F顏色值直接出現(xiàn)在style中。這是我非常反對(duì)的一種操作方式,在使用主題換膚的應(yīng)用中,隨著應(yīng)用功能的強(qiáng)大,自定義屬性的數(shù)量一定會(huì)越來越多,而且我覺得自定義屬性定義的越精細(xì)越好,所以一定會(huì)有一個(gè)龐大數(shù)量的屬性列表需要去維護(hù)。其中也有可能大部分是可以被重復(fù)使用的,何不將它們整理到統(tǒng)一的文件中,倘若到時(shí)候需求變化,資源引用需要修改,也不至于全局搜索挨個(gè)去改,何必給自己增加這么多沒有必要的工作量呢 ! 所以我還要講講自定義屬性 。




自定義resource資源:

同類型的資源新建在對(duì)應(yīng)的目錄下,尺寸資源定義在values-dimens目錄下,顏色資源定義再values-colors目錄下,drawable資源定義在values目錄下對(duì)應(yīng)的drawable目錄下... 并且每一種資源都應(yīng)該根據(jù)不同主題樣式配置多套。


自定義color:

1.在res-value目錄下新建color屬性的資源文件,例如:custom_theme_colors.xml
2.在custom_theme_colors.xml文件中新建自定義color顏色。

格式:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="自定義color名稱_day">對(duì)應(yīng)的日間顏色值</color>
    <color name="自定義color名稱_night">對(duì)應(yīng)的夜間顏色值</color>
</resources>

定義app背景色為例:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 日間模式下app背景色 -->
    <color name="custom_color_app_bg_day">#FFFFFF</color>
    <!-- 日間模式下app標(biāo)題欄背景色 -->
    <color name="custom_color_app_title_layout_bg_day">#FF2F3A4C</color>        
    <!-- 夜間模式下app背景色 -->
    <color name="custom_color_app_bg_night">#1F1F1F</color>
    <!-- 夜間模式下app標(biāo)題欄背景色 -->  
    <color name="custom_color_app_title_layout_bg_night">#FF1D1D1D</color> 
    ... 
</resources>


自定義drawable:

1.在res目錄下新建drawable文件夾。
1.在res-drawable目錄下新建drawable資源文件。

定義圓形圖片的占位drawable,示例:

custom_drawable_user_photo_place_holder_day.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/custom_color_user_photo_place_holder_bg_day" />
    <corners android:radius="32dp" />
</shape>

custom_drawable_user_photo_place_holder_night.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/custom_color_user_photo_place_holder_bg_night" />
    <corners android:radius="32dp" />
</shape>

自定義drawable中使用到的顏色值推薦也統(tǒng)一整理到custom_theme_colors.xml文件中。

<!-- 用戶頭像占位drawable背景顏色 -->
<color name="custom_color_user_photo_place_holder_bg_day">#29303B</color>
<color name="custom_color_user_photo_place_holder_bg_night">#171717</color>


自定義colorStateList:

同SelectorDrawable一樣,color也可以設(shè)置Selector選擇器。
1.value目錄下新建color.xml文件。
2.在res-color.xml目錄下新建color資源文件。

示例:

custom_selector_text_day.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/custom_color_text_pressed_day" android:state_pressed="true" />
    <item android:color="@color/custom_color_text_day" />
</selector>

custom_selector_text_night.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/custom_color_text_pressed_night" android:state_pressed="true" />
    <item android:color="@color/custom_color_text_night" />
</selector>

同理,自定義colorStateList中使用到的顏色值推薦也統(tǒng)一整理到custom_theme_colors.xml文件中。


在不同的主題樣式下為自定義屬性賦值:

示例:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    // 日間相關(guān)屬性集
    <style name="MarioTheme.Day" >
        <item name="custom_attr_app_bg">@color/custom_color_app_bg_day</item>
        <item name="custom_attr_app_title_layout_bg">@color/custom_color_app_title_layout_bg_day</item>
        <item name="custom_attr_user_photo_place_holder">@drawable/custom_drawable_user_photo_place_holder_day</item>
    </style>
    // 夜間相關(guān)屬性集
    <style name="MarioTheme.Night" >
        <item name="custom_attr_app_bg">@color/custom_color_app_bg_night</item>
        <item name="custom_attr_app_title_layout_bg">@color/custom_color_app_title_layout_bg_night</item>
        <item name="custom_attr_user_photo_place_holder">@drawable/custom_drawable_user_photo_place_holder_night</item>
    </style>
</resources>

到這里就完成了相關(guān)的準(zhǔn)備工作。因?yàn)樵谌找归g模式切換中基本不太會(huì)涉及字符串、尺寸的資源樣式的修改,實(shí)現(xiàn)的方式是一樣的,因此不做過多的贅述,有需要的朋友可以自定義去嘗試。






在XML布局文件中使用自定義屬性:

只要前期準(zhǔn)備工作做好了使用起來其實(shí)是非常簡(jiǎn)單的,就是在屬性賦值的時(shí)候不再使用絕對(duì)的資源引用,而是引用已經(jīng)完成賦值的自定義的屬性:

android:需要修改的屬性="?attr/自定義屬性名稱" 

這樣的話只要設(shè)置自定義屬性的View控件的Context上下文環(huán)境設(shè)置了對(duì)應(yīng)的Theme主題樣式,且對(duì)我們的自定義樣式進(jìn)行了相應(yīng)的賦值,則樣式的使用就會(huì)奏效,切記,項(xiàng)目中使用到的屬性一定要在使用的主題樣式下賦值,否則應(yīng)用運(yùn)行的時(shí)候會(huì)報(bào)錯(cuò)。當(dāng)然為了更好的開發(fā)體驗(yàn),我們可以在預(yù)覽模式下設(shè)置對(duì)應(yīng)的主題預(yù)覽我們?cè)O(shè)置的樣式效果是否起效,效果怎么樣。 ?

Demo部分布局代碼展示,示例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:background="?attr/custom_attr_app_bg"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="vertical">

    <View
        android:background="?attr/custom_attr_app_title_layout_bg"
        android:id="@id/custom_id_title_status_bar"
        android:layout_width="match_parent"
        android:layout_height="0dp" />

    <RelativeLayout
        android:background="?attr/custom_attr_app_title_layout_bg"
        android:id="@id/custom_id_title_layout"
        android:layout_width="match_parent"
        android:layout_height="136dp"
        android:paddingBottom="16dp"
        android:paddingRight="12dp"
        android:paddingLeft="12dp"
        android:paddingTop="8dp" >

        <ImageView
            android:padding="3dp"
            android:layout_width="72dp"
            android:layout_height="72dp"
            android:id="@+id/theme_user_photo"
            android:layout_alignParentLeft="true"
            android:layout_alignParentBottom="true"
            android:alpha="?attr/custom_attr_user_photo_alpha"
            tools:src="?attr/custom_attr_user_photo_place_holder" />

        <LinearLayout
            android:layout_toRightOf="@+id/theme_user_photo"
            android:layout_alignTop="@+id/theme_user_photo"
            android:layout_width="wrap_content"
            android:gravity="center_vertical"
            android:layout_marginLeft="12dp"
            android:orientation="vertical"
            android:layout_height="72dp" >

            <TextView
                android:textColor="?attr/custom_attr_nickname_text_color"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:id="@+id/theme_nickname"
                android:text="@string/nickname"
                android:textSize="19dp" />

            <TextView
                android:textColor="?attr/custom_attr_remark_text_color"
                android:text="@string/remark"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:layout_marginTop="3dp"
                android:id="@+id/theme_remark"
                android:textSize="12dp" />
        </LinearLayout>
    </RelativeLayout>
</LinearLayout>
日間預(yù)覽
夜間預(yù)覽

預(yù)覽中的兩個(gè)主題MarioTheme.Day.PreviewMarioTheme.Night.Preview分別繼承之MarioTheme.DayMarioTheme.Night,并沒有在項(xiàng)目中使用起來,主要用來控制狀態(tài)欄的顏色,個(gè)人用于編輯器狀態(tài)欄沉浸效果的一個(gè)預(yù)覽效果。

到這一步,在Activity中使用setTheme()就能加載對(duì)應(yīng)主題的視圖啦 ??! 有沒有很贊 ?
需要注意的一點(diǎn)是 setTheme()方法必須要在系統(tǒng)調(diào)用setContentView()方法前調(diào)用,個(gè)人推薦統(tǒng)一寫到基類BaseActivity的onCreate()方法中。而我們需要做的就是在本地SharePreference中配置一個(gè)tag控制BaseActivity設(shè)置不同的主題就行啦 !
也許看到這里你已經(jīng)躍躍欲試,或者你已經(jīng)一步一步照著寫到這里,但是應(yīng)用跑起來卻發(fā)現(xiàn),在Activity中點(diǎn)擊按鈕調(diào)用setTheme()方法,Activity并不會(huì)發(fā)生變化,或者返回上一個(gè)Activity也是沒有變化。并不是setTheme()方法沒有奏效,setTheme()方法確實(shí)起到應(yīng)有的效果了(可以調(diào)用getTheme()方法查看,當(dāng)前主題確實(shí)已經(jīng)改變)。那又是什么原因呢? 那是因?yàn)檫@些視圖都是已經(jīng)加載完成,設(shè)置主題并不會(huì)觸發(fā)系統(tǒng)去刷新UI,因此需要我們手動(dòng)去觸發(fā)。

而更改主題后的UI刷新我推薦兩種:

  • 重新創(chuàng)建Activity 關(guān)于重新創(chuàng)建Activity,只需要調(diào)用Activity的recreate()方法就行,普通不復(fù)雜的UI,用這個(gè)方法基本可以滿足,其中主要涉onSaveInstanceState()應(yīng)用狀態(tài)的保存,而使用這種方法重新創(chuàng)建Activity也是Google官方比較推崇的,有興趣可以了解一下。
  • 手動(dòng)加載當(dāng)前主題下的應(yīng)用資源 這是我這里需要重點(diǎn)講一下的。由于UI的復(fù)雜性和特殊性,并不是所有應(yīng)用的Activity都可以通過onSaveInstanceState()來保存當(dāng)前的應(yīng)用狀態(tài)的,因此了解如何從當(dāng)前主題獲取需要的屬性資源顯得尤為重要。


獲得當(dāng)前主題自定義屬性指定的資源:

其實(shí)獲取這個(gè)資源也很簡(jiǎn)單,也就兩步:

  • Step-01 獲取TypedValue

      TypedValue typedValue = new TypedValue(); 
      Resources.Theme theme = getTheme(); 
      try {
          theme.resolveAttribute(R.attr.自定義屬性, typedValue, true);
      } catch (Exception e) {
          e.printStackTrace();
      }
    

首先定義一個(gè)TypedValue用于承載Resource資源屬性,然后獲取當(dāng)前上下文對(duì)應(yīng)的Theme主題,再是通過resolveAttribute()方法獲取當(dāng)前主題下給定屬性ID對(duì)應(yīng)的資源信息并賦值給定義好的typedValue。因?yàn)榭赡艽嬖诮o定屬性對(duì)應(yīng)的資源信息獲取不到而拋出的異常,所以建議try&catch一下,捕獲可能存在的異常情況


  • Step-02 根據(jù)獲取的TypedValue所包含的資源信息獲取對(duì)應(yīng)的資源

      Resources resources = getResources();
          try {
              int color = ResourcesCompat.getColor(resources, typedValue.resourceId, null); // 獲取顏色值
              Drawable drawable = ResourcesCompat.getDrawable(resources, typedValue.resourceId, null); // 獲取Drawable對(duì)象
              String string = resources.getString(typedValue.resourceId); // 獲取字符串
          } catch (Exception e) {
              e.printStackTrace();
          }
    

TypedValue最重要的一個(gè)屬性就是resourceId,只要確定獲取的typedValue不為null。我們就可以通過typedValue.resourceId獲取資源的id,就好比知道了一個(gè)顏色資源的ID是R.color.black,讓你去獲取顏色值,知道一個(gè)Drawable資源的ID是R.drawable.ic_luncher,讓你去獲取Drawable對(duì)象,想想就簡(jiǎn)單(捂臉.jpg)。需要注意的是在獲取對(duì)應(yīng)資源的時(shí)候?yàn)楸苊赓Y源獲取失敗拋出的異常,各種獲取資源的方法還是建議用try&catch包裹一下。關(guān)于資源獲取,文末給出的Demo中有一個(gè)MarioResourceHelper的輔助類,該類對(duì)資源獲取一塊進(jìn)行了一個(gè)小封裝,用起來會(huì)更加方便。

而接下來需要做的就是對(duì)特定的資源進(jìn)行替換就好了。

補(bǔ)充一點(diǎn):

關(guān)于前文提到主題換膚缺點(diǎn)時(shí),其中一點(diǎn)就是所有資源文件都需要放置在主APK文件中打包發(fā)布,也許不同的主題就會(huì)有多套圖片資源,在Android有限內(nèi)存的條件下,這是一種非常糟糕的情況。
而在前文我也提及,應(yīng)對(duì)這種現(xiàn)象,我們開發(fā)能做的就是使用drawable著色的方式,盡量用一套圖片資源實(shí)現(xiàn)多種主題。切記! 著色的圖片要求純色且背景透明的PNG,因?yàn)橹⒉荒軈^(qū)分色彩,而是對(duì)所有非透明區(qū)域統(tǒng)一著色上指定的顏色。著色細(xì)節(jié)不做贅述,線上《Drawable著色的后向兼容》一文闡述的比較詳細(xì)了吧,感謝作者分享 ??!而我們要做的就是將需要的著色上去的顏色值定義在不同的主題下,不同主題獲取對(duì)應(yīng)的顏色值,并對(duì)特定的drawable進(jìn)行著色即可。 而MarioResourceHelper輔助類也會(huì)對(duì)drawable的著色方法做相應(yīng)的封裝。

? Github項(xiàng)目源碼地址 ?

最后附上前文兩張動(dòng)圖的原版錄制視頻,觀看效果更佳 !!

預(yù)覽效果第一季藍(lán)光
預(yù)覽效果第二季藍(lán)光

作者申明:如果文中有表述不當(dāng)或闡述錯(cuò)誤的地方,還望正在看文章的您可以幫忙指出,有疑惑也可以在評(píng)論區(qū)提問或者私信,期待您的意見和建議,歡迎關(guān)注交流,轉(zhuǎn)載請(qǐng)注明出處 !

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容