Android Material Design 兼容庫的使用詳解

Misty8089 8年前發布 | 72K 次閱讀 Android開發 移動開發 Android

眾所周知Material Design(材質設計)是Google在2014年I/O大會上發布的一種新的設計規范。一經推出就好評如潮,個人是非常喜歡這種風格的,由于他只支持5.0及其以上的設備,開發者也只是去嘗嘗鮮,并沒用在真實的項目中去,使得其在國內的市場并不是太好。隨后不久Google就退出了其兼容庫Android Design Support Library,兼容至2.1!這絕對是業界良心,極大的便利了我們這些開發者。這一兼容庫可以讓我們可以在自己的項目真實的去體驗一把。百度谷歌一下,其實現在已經有個很多介紹這個支持庫的文章,但是很多都并不太詳細,大部分讓人看了都似懂非懂,我之前也學習過這個庫的用法,很久沒看基本又忘了,最近我又重新開始學習了這個庫,所以本文也主要是對這次學習的一個總結,當然也參考了其他很多的文章,算是站在巨人的肩膀上吧,當然這篇文章也為后來的學習者有更多的參考資料。

先來幾張效果圖吧:

Android Material Design 兼容庫的使用詳解

圖1

Android Material Design 兼容庫的使用詳解

圖2

感興趣的也可以先看看,最后能做一個什么樣的App:MDDemo.apk

說到這個兼容庫,其實很多都是可以在Github去找到對應的效果的,其實我們也是可以去用Github上的那些開源項目,那么多的庫都要一個一個去引用,還是非常的麻煩,我猜可能Google也是看到這些東西經常被開發者用在了項目中,索性自己將其封裝成了一個庫,以方便開發者的使用。這個庫我們應該怎樣去使用呢?這是本文講解的重點。首先我們看看這個庫怎么去引用到我們的項目中:

compile 'com.android.support:design:23.1.1'

我們只需將其添加到項目的依賴中去,然后同步一下就OK了。
添加了依賴之后我們再來看看這個庫里面都有哪些控件吧,如下圖,紅色邊框矩形框出來的就是這個庫里面包含的控件,我們可以看到有AppBarLayout、CollapsingToolbarLayout、CoordinatorLayout,FloatingActionButton、NavigationView、Snackbar、TabLayout、TextInputLayout這八種,后面我會一個一個介紹用法,如果還沒學過這些控件使用的本文可以手把手教你學會怎么去使用,如果之前有了解過的或者對這個還比較熟悉的,也可以繼續看下去,就當做溫習一下,順便還可以幫我找出有表達不正確的地方。

Android Material Design 兼容庫的使用詳解

庫中包含的控件.png

1.Snackbar

SnackBar通過在屏幕底部展示簡潔的信息,為一個操作提供了一個輕量級的反饋,并且在Snackbar中還可以包含一個操作,在同一時間內,僅且只能顯示一個 Snackbar,它的顯示依賴于UI,不像Toast那樣可以脫離應用顯示。它的用法和Toast很相似,唯一不同的就是它的第一個參數不是傳入Context而是傳入它所依附的父視圖,但是他比Toast更強大。
我們來看看它的基本使用

Snackbar.make(mDrawerLayout, "SnackbarClicked", Snackbar.LENGTH_SHORT).show();

是不是和Toast很相似。我們來看看它的效果圖:

Android Material Design 兼容庫的使用詳解

SnackWithoutAction


是不是看著比Toast舒服多了

好了,我們再來看看一個包含Action的Snackbar怎么使用

  Snackbar.make(mDrawerLayout, "SnackbarClicked", Snackbar.LENGTH_SHORT).setAction("Action", new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                Toast.makeText(MainActivity.this, "I'm a Toast", Toast.LENGTH_SHORT).show();
                            }
                        }).setActionTextColor(Color.RED).show();

這里我給Snackbar設置了一個Action,設置其文字顏色為紅色,并帶有了一個點擊事件,在單擊這個Action后就彈出一個Toast,效果圖如下

Android Material Design 兼容庫的使用詳解

SnackbarWithAction

2.TextInputLayout

使用過EditText的同學肯定知道,有一個叫hint的屬性,它可以提示用戶此處應該輸入什么內容,然而當用戶輸入真實內容之后,hint的提示內容就消失了,用戶的體驗效果是十分不好的,TextInputLayout的出現解決了這個問題。

我們來看看這個控件的是怎么使用的

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context="burgess.com.materialdesignstudy.MDTextViewActivity">
    <!--TextInputLayout的顏色來自style中的colorAccent的顏色-->
    <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:id="@+id/edit_username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="用戶名/手機號"
            android:inputType="textEmailAddress" />
    </android.support.design.widget.TextInputLayout>
    <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:id="@+id/edit_pwd"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="密碼"
            android:inputType="textPassword" />
    </android.support.design.widget.TextInputLayout>
</LinearLayout>

其實這個控件的使用非常簡單,我們只需在EditText外面再嵌套一個TextInputLayout就行了。我們來看看效果

Android Material Design 兼容庫的使用詳解

TextInputLayout


還是很不錯的,當用戶在輸入的時候hint的內容就會跑到輸入內容的上邊去,其中TextInputLayout中字體的顏色是style文件中的colorAccent(關于colorAccent是什么,文末有一張圖看了就清楚了)的顏色。

3.FloatingActionButton

FloatingActionButton從名字可以看出它是一個浮動的按鈕,它是一個帶有陰影的圓形按鈕,可以通過fabSize來改變其大小,主要負責界面的基本操作,這個按鈕總體來說還是比較簡單的。
我們來看看它的一些屬性:

  • 默認FloatingActionButton 的背景色是應用主題的 colorAccent(其實MD中的控件主題默認基本都是應用的這個主題),可以通過app:backgroundTint 屬性或者setBackgroundTintList (ColorStateList tint)方法去改變背景顏色。
  • 上面提到 Floating action button 的大小尺寸,可以用過app:fabSize 屬性設置(normal or mini)
  • android:src 屬性改變 drawable
  • app:rippleColor設置點擊 button 時候的顏色(水波紋效果)
  • app:borderWidth設置 button 的邊框寬度
  • app:elevation設置普通狀態陰影的深度(默認是 6dp)
  • app:pressedTranslationZ設置點擊狀態的陰影深度(默認是 12dp)

怎么去使用:

<android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_search"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:src="@android:drawable/ic_dialog_email"
        app:borderWidth="2dp"
        app:fabSize="normal"
        app:rippleColor="#ff0000" />


其效果圖如下:

Android Material Design 兼容庫的使用詳解

FloatingActionButton


##4.CoordinatorLayout
我們來看看官方對他的描述:
- CoordinatorLayout is a super-poweredFrameLayout.
- CoordinatorLayout is intended for two primary use cases:
1.As a top-level application decor or chrome layout
2.As a container for a specific interaction with one or more child views
從這里我們可以知道它是一個增強版的FrameLayout,他可以協調其子View的交互,控制手勢機滾動技巧。這個控件十分的強大,用處也十分的廣泛。就比如剛才說的FloatingActionButton如果用CoordinatorLayout 作為FloatingActionButton的父布局,它將很好的協調Snackbar的顯示與FloatingActionButton。

Android Material Design 兼容庫的使用詳解

沒有使用CoordinatorLayout 作為父布局的FloatingActionButton顯示Snackbar

 

Android Material Design 兼容庫的使用詳解

使用了CoordinatorLayout 作為父布局的FloatingActionButton顯示Snackbar


這兩個動態圖來源于網上,效果是顯而易見的,原諒我用的手機測試,所以沒法錄制動態圖(如果有人知道怎么去錄制的請告知)。
還有它的另外一個比較重要的用處是控制協調內容區和AppBarLayout的滾動,這個后面會講到。這個控件還是比較復雜的,用多了就能慢慢體會到他的強大之處。我對這個控件的還不是那么的清楚,所以在這里提供一篇關于CoordinatorLayout 用法的譯文: 掌握 Coordinator Layout
##5.NavigationView
抽屜式的導航控件,有了他我們可以使得導航更簡單。我們可以直接通過菜單資源文件生成所需的元素。下圖左邊的即是NavigationView。

Android Material Design 兼容庫的使用詳解

NavigationView


下面我們來看看它是怎么實現的吧。

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
    <!--內容區-->
    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/main_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include
            android:id="@+id/appbar"
            layout="@layout/toolbar" />
        <FrameLayout
            android:id="@+id/frame_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@+id/appbar"
            android:scrollbars="none"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />
    </android.support.design.widget.CoordinatorLayout>
    <!--左側導航菜單-->
    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/navigation_header"
        app:menu="@menu/drawer" />
</android.support.v4.widget.DrawerLayout>

可以看到這里我們是以DrawerLayout作為其父布局,對于DrawLayout他可以實現一種抽屜式的側滑效果,這里不多做講解,這里只簡單說一點:DrawLayout中的第一個布局是內容布局,第二個是菜單布局。現在我們直接定位到NavigationView,我們看到這里有 app:headerLayout="@layout/navigation_header"app:menu="@menu/drawer"這兩行代碼,其中headerLayout是設置其頭部的布局,這個布局我們可以隨便寫,就和寫普通的布局文件一樣的。對于menu就是菜單項的配置了,其配置文件如下:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/navigation_item_home"
            android:icon="@drawable/iconfont_home"
            android:title="首頁" />
        <item
            android:id="@+id/navigation_item_blog"
            android:icon="@drawable/iconfont_blog"
            android:title="我的博客" />
        <item
            android:id="@+id/navigation_item_about"
            android:icon="@drawable/iconfont_about"
            android:title="關于" />
    </group>
</menu>

就這么簡單,我們要實現上述的效果就只要這些就足夠了。但是如果我們想要對Item添加一個點擊的事件怎么做呢?請看下面:

 private void setNavigationViewItemClickListener() {
        mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.navigation_item_home:
                        mToolbar.setTitle("首頁");
                        switchFragment("MainFragment");
                        break;
                    case R.id.navigation_item_blog:
                        mToolbar.setTitle("我的博客");
                        switchFragment("BlogFragment");
                        break;
                    case R.id.navigation_item_about:
                        mToolbar.setTitle("關于");
                        switchFragment("AboutFragment");
                        break;
                    default:
                        break;
                }
                item.setChecked(true);
                mDrawerLayout.closeDrawer(Gravity.LEFT);
                return false;
            }
        });
    }


上面的代碼很清楚了,就是為NavigationView添加了一個OnNavigationItemSelectedListener的監聽事件,然后我們就可以做我們想做的事了。
##6. AppBarLayout、CollapsingToolbarLayout、TabLayout
這三個控件我用一個例子一下講解了,在實際的使用中這三個控件還是經常會組合在一起的。
- AppBarLayout:其繼承于LinearLayout,使用AppBarLayout可以讓包含在其中的子控件能響應被標記了ScrollingViewBehavior的的滾動事件(要與CoordinatorLayout 一起使用),利用它我們可以很容易的去實現視差滾動效果,這樣所你可能還是不太懂,千言萬語不如一張圖來的直接爽快(這樣圖還是來源于網上,其藍色的部分就是AppBarLayout,內容區就是被標記了ScrollingViewBehavior的,可以看到效果是不是挺不錯的)。

Android Material Design 兼容庫的使用詳解


- CollapsingToolbarLayout :讓Toolbar可伸縮,在伸縮的時候決定ToolBar是移除屏幕和固定在最上面。由于Toolbar 只能固定到屏幕頂端并且不能做出好的動畫效果,所以才有了這個Layout的出現。
- TabLayout:這個其實和我們之前使用的第三方庫ViewPagerIndicator是很類似的,一般都和ViewPager一起使用,效果如下圖:

Android Material Design 兼容庫的使用詳解

動態圖來源于網上


基本知道他們是干什么用的了,下面就讓我們動手,看看到底怎么用吧。
先來幾張下面我們要實現的效果。由于我是真機測試,沒法錄制動態圖,這里就用幾張靜態圖代替了。

Android Material Design 兼容庫的使用詳解


布局文件(這個是很重要的,很多的效果都是直接在布局文件中配置的):

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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">
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginEnd="64dp"
            app:expandedTitleMarginStart="48dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
            <ImageView
                android:id="@+id/iv_book_image"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                android:scaleType="centerInside"
                android:transitionName="transition_book_img"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.7" />
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="10dp">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
                <TextView
                    android:id="@+id/tv_title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#292929"
                    android:textSize="18sp" />
                <TextView
                    android:id="@+id/tv_rating"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:layout_marginTop="2dp"
                    android:textColor="#FF6347"
                    android:textSize="14sp" />
            </LinearLayout>
            <TextView
                android:id="@+id/tv_msg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="2dp"
                android:textColor="#575757"
                android:textSize="12sp" />
        </LinearLayout>
        <android.support.design.widget.TabLayout
            android:id="@+id/sliding_tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabGravity="fill"
            app:tabMode="scrollable" />
        <android.support.v4.view.ViewPager
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
</android.support.design.widget.CoordinatorLayout>

我們來分析一下這個布局文件:
1.最外層的布局用的是CoordinatorLayout,因為這里面有很多的動畫,CoordinatorLayout可以很好的去協調里面的動畫。在android.support.design.widget.AppBarLayout下面的那個LinearLayout被標記了appbar_scrolling_view_behavior,這樣一來AppBarLayout就能響應LinearLayout中的滾動事件。
2.再來看看CollapsingToolbarLayout,其中contentScrim是設置其內容區的顏色,layout_scrollFlags取了scroll和exitUntilCollapsed兩個值。
layout_scrollFlags的Flag包括:

  • scroll: 所有想滾動出屏幕的view都需要設置這個flag,沒有設置這個flag的view將被固定在屏幕頂部。
  • enterAlways: 這個flag讓任意向下的滾動都會導致該view變為可見,當向上滑的時候Toolbar就隱藏,下滑的時候顯示。
  • enterAlwaysCollapsed: 顧名思義,這個flag定義的是何時進入(已經消失之后何時再次顯示)。假設你定義了一個最小高度(minHeight)同時enterAlways也定義了,那么view將在到達這個最小高度的時候開始顯示,并且從這個時候開始慢慢展開,當滾動到頂部的時候展開完。
  • exitUntilCollapsed: 同樣顧名思義,這個flag時定義何時退出,當你定義了一個minHeight,這個view將在滾動到達這個最小高度的時候消失。

只看概覽可能還是不會太清楚,注意一定要多實踐,感興趣的讀者可以一個一個去試一試效果。

3.定位到ImageView,有這兩個屬性是我們平常用沒看到的,說明我寫在注釋上了

app:layout_collapseMode="parallax"http://這個是配置當ImageView消失或者顯示時候有一種視差滾動效果
app:layout_collapseParallaxMultiplier="0.7"http://視差因子,越大視差特效越明顯,最大為1


4.Toolbar中有一個app:layout_collapseMode="pin" 這個確保Toolbar在AppBarLayout折疊的時候仍然被固定在屏幕的頂部
布局文件就到這,我們來看看Activity中的邏輯

/*  Created by _SOLID
  Date:2016/3/30  Time:20:16
 */
public class BookDetailActivity extends BaseActivity {
    private String mUrl;
    private Toolbar mToolbar;
    private CollapsingToolbarLayout mCollapsingToolbarLayout;
    private ImageView mIvBook;
    private BookBean mBookBean;
    private TextView mTvTitle;
    private TextView mTvMsg;
    private TextView mTvRating;
    private ViewPager mViewPager;
    private TabLayout mTabLayout;
    @Override
    protected void initView() {
        //設置Toolbar
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);
        getSupportActionBar().setHomeButtonEnabled(true);//決定左上角的圖標是否可以點擊
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);//決定左上角圖標的右側是否有向左的小箭頭
        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                onBackPressed();
            }
        });
        mCollapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar_layout);
        mIvBook = (ImageView) findViewById(R.id.iv_book_image);
        mTvTitle = (TextView) findViewById(R.id.tv_title);
        mTvMsg = (TextView) findViewById(R.id.tv_msg);
        mTvRating = (TextView) findViewById(R.id.tv_rating);
        mViewPager = (ViewPager) findViewById(R.id.viewpager);
        mTabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
        mTabLayout.addTab(mTabLayout.newTab().setText("作者信息"));
        mTabLayout.addTab(mTabLayout.newTab().setText("章節"));
        mTabLayout.addTab(mTabLayout.newTab().setText("書籍簡介"));
    }
    @Override
    protected int setLayoutResourseID() {
        return R.layout.activity_book_detail;
    }
    @Override
    protected void init() {
        mUrl = getIntent().getStringExtra("url");
    }
    @Override
    protected void initData() {
        SolidHttpUtils.getInstance().loadString(mUrl, new SolidHttpUtils.HttpCallBack() {
            @Override
            public void onLoading() {
            }
            @Override
            public void onSuccess(String result) {
                Gson gson = new Gson();
                mBookBean = gson.fromJson(result, BookBean.class);
                mCollapsingToolbarLayout.setTitle(mBookBean.getTitle());
                mTvTitle.setText(mBookBean.getTitle());
                mTvMsg.setText(mBookBean.getAuthor() + "/" + mBookBean.getPublisher() + "/" + mBookBean.getPubdate());
                mTvRating.setText(mBookBean.getRating().getAverage() + "分");
                SolidHttpUtils.getInstance().loadImage(mBookBean.getImages().getLarge(), mIvBook);
                BookInfoPageAdapter adapter = new BookInfoPageAdapter(BookDetailActivity.this, mBookBean, getSupportFragmentManager());
                mViewPager.setAdapter(adapter);
                mTabLayout.setupWithViewPager(mViewPager);
            }
            @Override
            public void onError(Exception e) {
            }
        });
    }
}

其實并不難,這里只需注意一點,當Toolbar被CollapsingToolbarLayout包裹的時候,設置標題是設置CollapsingToolbarLayout的,而不是Toolbar。

好了終于寫完了,差不多寫了接近4個小時,好累的說,現在整個人都是暈暈的。

源碼獻上:MaterialDesignDemo

參考鏈接:
Android的材料設計兼容庫(Design Support Library)
Material Design控件使用
Floating Action Button(翻譯)
Material Design 中文版
Android Design Support Library概覽

附(一些控件使用使用應注意的地方):

1.在使用CardView的時候,一定要注意,當CardView的寬和高填充父容器的時候,CardView的margin最好要比cardElevation大,不然就看不到立體的效果。

2.我們知道ListView有一個onItemClick的事件,但是RecyclerView卻沒有,那么我們應該怎樣去設置呢?其實很簡單,關于RecyclerView設置item的點擊事件,只需在創建ViewHolder的時候,給填充的View設置單擊事件即可。

3.在使用android.support.design.widget.AppBarLayout的時候內容區最好使用android.support.v4.widget.NestedScrollView,之前我的內容區用的是ScrollView,在往上拉的時候AppBarLayout一直沒有動畫效果,折騰了幾個小時都沒找到原因。最后逼不得用Android Studio創建了一個他自帶的關于AppBarLayout的模板項目,看到他用的是NestedScrollView作為內容區,我果斷就把我的內容區換成了這個,立馬就有動畫效果了。

NestedScrollView官方的描述:

NestedScrollView is just likeScrollView, but it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.

如果感覺還不錯的就給個喜歡支持一下吧,有問題請留言,謝謝

最后附一張MD的主題色解析圖:

Android Material Design 兼容庫的使用詳解

MD主題色解析.jpg


 

文/_SOLID(簡書作者)
來源:http://www.jianshu.com/p/1e6eed09d48b
 

 本文由用戶 Misty8089 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!