MaterialDesign--(9)AppBarLayout+CollapsingToolbarLayout的使用及源碼分析

AppBarLayout

這個(gè)玩意去年就特別火了,主要是因?yàn)楹糜茫凑乙呀?jīng)在 app 里面用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+Toolbar實(shí)現(xiàn)了好多讓 iOS 目瞪狗呆的效果。不過(guò)話說(shuō)回來(lái),實(shí)現(xiàn)歸實(shí)現(xiàn),每次實(shí)現(xiàn)都是去找別人的博客,然后一頓 CV 大法,然后屬性參數(shù)到處亂配置,最終效果達(dá)到,然后提交代碼不管。
至于各個(gè)類(lèi)是干嘛的,有哪些方法,我都不 care。當(dāng)然咯,程序員首先得先滿足產(chǎn)品的需求,CV 大法的前提也是你知道CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+Toolbar這些東西能夠?qū)崿F(xiàn)你的需求。所以,程序員的見(jiàn)識(shí)很重要。這里我給廣大的 Android 猿們推薦一款Chrome瀏覽器插件“掘金”。對(duì),沒(méi)錯(cuò),就是稀土掘金發(fā)布的,我覺(jué)得這個(gè)插件用起來(lái)別稀土掘金的官網(wǎng)簡(jiǎn)潔多了,主要是便捷,節(jié)省信息檢索時(shí)間。扯遠(yuǎn)了,會(huì)用是程序員的最低要求,想要更進(jìn)一步,當(dāng)然是去看源碼,理解如何實(shí)現(xiàn)。

* AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of
* material designs app bar concept, namely scrolling gestures.
* <p>
* Children should provide their desired scrolling behavior through
* {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute:
* {@code app:layout_scrollFlags}.
*
* <p>
* This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}.
* If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will
* not work.
* <p>
* AppBarLayout also requires a separate scrolling sibling in order to know when to scroll.
* The binding is done through the {@link ScrollingViewBehavior} behavior class, meaning that you
* should set your scrolling view's behavior to be an instance of {@link ScrollingViewBehavior}.
* A string resource containing the full class name is available.
……省略了一個(gè) xml 布局 demo

簡(jiǎn)單翻譯一下吧,反正我英語(yǔ)不好,翻譯的也不一定對(duì)~
AppBarLayout是一個(gè)實(shí)現(xiàn)了許多 MaterialDesign app bar 思想(即滾動(dòng)手勢(shì))的垂直布局。子控件需要通過(guò)setScrollFlags()或 app:"layout_scrollFlags"來(lái)提供他們的滑動(dòng)行為。這個(gè) View 作為一個(gè)子 View,對(duì) CoordinatorLayout 依賴(lài)性很強(qiáng),如果CoordinatorLayout不是父View,很多功能會(huì)失效。最后一句話翻譯起來(lái)有點(diǎn)別扭,就是說(shuō) AppBarLayout 需要給他依賴(lài)度 View 設(shè)置 ScrollingViewBehavoir 來(lái)監(jiān)聽(tīng)依賴(lài)的 View 什么時(shí)候滾動(dòng)。

按照國(guó)際慣例,我們先看一下 attrs 和 public 方法把

attributes

<declare-styleable name="AppBarLayout">
    <attr name="elevation"/>
    <attr name="android:background"/>
    <attr format="boolean" name="expanded"/>
</declare-styleable>
<declare-styleable name="AppBarLayoutStates">
    <attr format="boolean" name="state_collapsed"/>
    <attr format="boolean" name="state_collapsible"/>
</declare-styleable>
<declare-styleable name="AppBarLayout_Layout">
    <attr name="layout_scrollFlags">
        <flag name="scroll" value="0x1"/>
        <flag name="exitUntilCollapsed" value="0x2"/>        
        <flag name="enterAlways" value="0x4"/>        
        <flag name="enterAlwaysCollapsed" value="0x8"/>        
        <flag name="snap" value="0x10"/>
    </attr>
    <attr format="reference" name="layout_scrollInterpolator"/>
</declare-styleable>
  • expanded 是否展開(kāi)
  • AppBarLayoutStates 我也不知道這玩意是干嘛的,以后知道了再來(lái)修改
  • layout_scrollFlags 這個(gè)屬性是用來(lái)控制子 view 的伴隨滾動(dòng)處理,一共有5個(gè)值,5個(gè)值之間是可以進(jìn)行或運(yùn)算的,也就是說(shuō)可以同時(shí)設(shè)置多種狀態(tài)。

為了便于理解這5個(gè)值得效果,我從源碼里面找到了這5個(gè)值的解釋

/**
  * The view will be scroll in direct relation to scroll events. This flag needs to be
  * set for any of the other flags to take effect. If any sibling views
  * before this one do not have this flag, then this value has no effect.
  * 1.view 會(huì)和滾動(dòng)事件關(guān)聯(lián)。
  * 2.如果要設(shè)置其他任何flag,必須同時(shí)設(shè)置這個(gè) flag
  * 3.如果在這個(gè) view 之前,沒(méi)有任何同層級(jí) view 設(shè)置過(guò)這個(gè) flag,那么這個(gè)值也沒(méi)有任何效果
  */
 public static final int SCROLL_FLAG_SCROLL = 0x1;
 
/**
  * When exiting (scrolling off screen) the view will be scrolled until it is
  * 'collapsed'. The collapsed height is defined by the view's minimum height.
  *當(dāng)上拉的時(shí)候,這個(gè) view 也會(huì)滾動(dòng),直到滾動(dòng)到最小高度,固定在屏幕頂部
  * @see ViewCompat#getMinimumHeight(View)
  * @see View#setMinimumHeight(int)
  */
 public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2;

/**
  * When entering (scrolling on screen) the view will scroll on any downwards
  * scroll event, regardless of whether the scrolling view is also scrolling. This
  * is commonly referred to as the 'quick return' pattern.
  * 當(dāng)下拉的時(shí)候,優(yōu)先顯示被隱藏的 view
  */
 public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4;

/**
  * An additional flag for 'enterAlways' which modifies the returning view to
  * only initially scroll back to it's collapsed height. Once the scrolling view has
  * reached the end of it's scroll range, the remainder of this view will be scrolled
  * into view. The collapsed height is defined by the view's minimum height.
  * 下拉的時(shí)候優(yōu)先顯示被隱藏的 view
  * @see ViewCompat#getMinimumHeight(View)
  * @see View#setMinimumHeight(int)
  */
 public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8;

/**
  * Upon a scroll ending, if the view is only partially visible then it will be snapped
  * and scrolled to it's closest edge. For example, if the view only has it's bottom 25%
  * displayed, it will be scrolled off screen completely. Conversely, if it's bottom 75%
  * is visible then it will be scrolled fully into view.
  * 就是一個(gè)自動(dòng)回滾的效果,比如說(shuō)滑動(dòng)到25%松手,就會(huì)自動(dòng)滾回0
  */
 public static final int SCROLL_FLAG_SNAP = 0x10;

這里的設(shè)計(jì)挺棒的,我簡(jiǎn)單提一下,用位運(yùn)算,一個(gè) int 值記錄了5種狀態(tài)的排列組合。一共五個(gè)狀態(tài),五個(gè)不同的 flag,但是源碼里面,就用了一個(gè) int 型的變量就記錄了五個(gè)不同狀態(tài)的排列與組合。正常如果是我們自己寫(xiě)的話,是不是一不小心就定義了5個(gè)變量去記錄這些值,比如說(shuō):mCanScroll,mSnap,然后代碼里面會(huì)有類(lèi)似的代碼:“if(mSnap)do sth”。好了,不扯遠(yuǎn)了,五個(gè) flag 的值分別是1、2、4、8、16,轉(zhuǎn)換成二進(jìn)制分別占了第0、1、2、3、4個(gè)位數(shù),第 n 個(gè)位數(shù)如果為0,則沒(méi)有這個(gè) flag,為1則表示有。比如scroll|enterAlways 這個(gè)flag,位運(yùn)算|就是1|4,得到的值是5,然后賦值給了 mFlag,這個(gè) mFlag 則表示scroll、enterAlways兩種狀態(tài),然后如果要判斷是否可以 scroll,則只需要 mFlag&scroll==scroll即可。
類(lèi)似的代碼設(shè)計(jì)還有 manifeast里面的 android:windowSoftInputMode="adjustPan|adjustResize|stateVisible"
不知道我說(shuō)明百了沒(méi),沒(méi)看懂的小伙伴可以跳過(guò)。。。。。

Public methods

  • addOnOffsetChangedListener 添加便宜量監(jiān)聽(tīng),就是監(jiān)聽(tīng) AppBarLayout 的可見(jiàn)高度變化
  • removeOnOffsetChangedListener 移除
  • setExpanded 設(shè)置展開(kāi)或者收縮
  • generateLayoutParams 生成 LayoutParams。一般用不到
  • setOrientation 不用關(guān)心的方法,方向只能是 vertical
  • getTotalScrollRange 獲取最大滾動(dòng)偏移量
  • setTargetElevation 設(shè)置 Z 軸高度

CollapsingToolbarLayout

  • CollapsingToolbarLayout is a wrapper for {@link Toolbar} which implements a collapsing app bar.
  • It is designed to be used as a direct child of a {@link AppBarLayout}.
  • CollapsingToolbarLayout contains the following features:
  • <h4>Collapsing title</h4>
  • A title which is larger when the layout is fully visible but collapses and becomes smaller as
  • the layout is scrolled off screen. You can set the title to display via
  • {@link #setTitle(CharSequence)}. The title appearance can be tweaked via the
  • {@code collapsedTextAppearance} and {@code expandedTextAppearance} attributes.
  • <h4>Content scrim</h4>
  • A full-bleed scrim which is show or hidden when the scroll position has hit a certain threshold.
  • You can change this via {@link #setContentScrim(Drawable)}.
  • <h4>Status bar scrim</h4>
  • A scrim which is show or hidden behind the status bar when the scroll position has hit a certain
  • threshold. You can change this via {@link #setStatusBarScrim(Drawable)}. This only works
  • on {@link android.os.Build.VERSION_CODES#LOLLIPOP LOLLIPOP} devices when we set to fit system
  • windows.
  • <h4>Parallax scrolling children</h4>
  • Child views can opt to be scrolled within this layout in a parallax fashion.
  • See {@link LayoutParams#COLLAPSE_MODE_PARALLAX} and
  • {@link LayoutParams#setParallaxMultiplier(float)}.
  • <h4>Pinned position children</h4>
  • Child views can opt to be pinned in space globally. This is useful when implementing a
  • collapsing as it allows the {@link Toolbar} to be fixed in place even though this layout is
  • moving. See {@link LayoutParams#COLLAPSE_MODE_PIN}.
  • <p><strong>Do not manually add views to the Toolbar at run time</strong>.
  • We will add a 'dummy view' to the Toolbar which allows us to work out the available space
  • for the title. This can interfere with any views which you add.</p>

咦,copy 出來(lái)的類(lèi)注釋竟然支持 MarkDown 排版,哈哈哈哈
好了,不說(shuō)題外話,先看類(lèi)注釋吧~
一共五個(gè)小標(biāo)題

  • Collapsing title 折疊標(biāo)題
  • Content scrim 內(nèi)容布
  • Status bar scrim 狀態(tài)欄布
  • parallax scrolling children 視差滾動(dòng)子 View
  • pinned position children 固定子 view 的位置

總結(jié):如果需要折疊標(biāo)題之類(lèi)的如上功能,則把 AppBarLayout 里面的所有子 view 移到CollapsingToolbarLayout節(jié)點(diǎn)下,然后把CollapsingToolbarLayout作為 AppBarLayout 的唯一子節(jié)點(diǎn)。

attributes

<declare-styleable name="CollapsingToolbarLayout">
    <attr format="dimension" name="expandedTitleMargin"/>
    <attr format="dimension" name="expandedTitleMarginStart"/>
    <attr format="dimension" name="expandedTitleMarginTop"/>
    <attr format="dimension" name="expandedTitleMarginEnd"/>
    <attr format="dimension" name="expandedTitleMarginBottom"/>
    <attr format="reference" name="expandedTitleTextAppearance"/>
    <attr format="reference" name="collapsedTitleTextAppearance"/>
    <attr format="color" name="contentScrim"/>
    <attr format="color" name="statusBarScrim"/>
    <attr format="reference" name="toolbarId"/>
    <attr format="dimension" name="scrimVisibleHeightTrigger"/>
    <attr format="integer" name="scrimAnimationDuration"/>
    <attr name="collapsedTitleGravity">
        <flag name="top" value="0x30"/>        
        <flag name="bottom" value="0x50"/>            
        <flag name="left" value="0x03"/>            
        <flag name="right" value="0x05"/>            
        <flag name="center_vertical" value="0x10"/>            
        <flag name="fill_vertical" value="0x70"/>           
        <flag name="center_horizontal" value="0x01"/>          
        <flag name="center" value="0x11"/>          
        <flag name="start" value="0x00800003"/>            
        <flag name="end" value="0x00800005"/>
    </attr>
    <attr name="expandedTitleGravity">            
        <flag name="top" value="0x30"/>            
        <flag name="bottom" value="0x50"/>            
        <flag name="left" value="0x03"/>            
        <flag name="right" value="0x05"/>            
        <flag name="center_vertical" value="0x10"/>            
        <flag name="fill_vertical" value="0x70"/>            
        <flag name="center_horizontal" value="0x01"/>            
        <flag name="center" value="0x11"/>            
        <flag name="start" value="0x00800003"/>            
        <flag name="end" value="0x00800005"/>
    </attr>
    <attr format="boolean" name="titleEnabled"/>
    <attr name="title"/>
</declare-styleable>
<declare-styleable name="CollapsingToolbarLayout_Layout">
    <attr name="layout_collapseMode">            
        <enum name="none" value="0"/>            
        <enum name="pin" value="1"/>            
        <enum name="parallax" value="2"/>
    </attr>
    <attr format="float" name="layout_collapseParallaxMultiplier"/>
</declare-styleable>
  • expandedTitleMargin 展開(kāi)時(shí) title 的 margin
  • expandedTitleTextAppearance 展開(kāi)時(shí)候title 的文字 style
  • contentScrim 在縮放時(shí),內(nèi)容遮蓋的顏色
  • statusBarScrim 狀態(tài)欄顏色
  • toolbarId 指定了 toolbar 而已,用不用無(wú)所謂,源碼里面有就用,沒(méi)有就遍歷子 View 找到 toolbar。
  • scrimVisibleHeightTrigger 設(shè)置收起多少高度時(shí),顯示內(nèi)容遮蓋顏色
  • scrimAnimationDuration 內(nèi)容遮蓋顏色動(dòng)畫(huà)持續(xù)時(shí)間
  • collapsedTitleGravity 折疊時(shí),title 的位置
  • expandedTitleGravity 展開(kāi)時(shí),title 的位置
  • titleEnabled 是否開(kāi)啟折疊 title
  • layout_collapseMode
  • none 跟隨滾動(dòng)的手勢(shì)進(jìn)行折疊
  • parallax 視差滾動(dòng)
  • pin 不動(dòng)
  • layout_collapseParallaxMultiplier 滾動(dòng)因子,取值0-1,1是完全不動(dòng)

public methods

此處省略 N 個(gè)方法,都是和 attrs對(duì)應(yīng)的屬性修改/獲取方法。

問(wèn)題

可能有些同學(xué)會(huì)遇到statusBarScrim不生效的情況,反正我是碰到過(guò),原因是因?yàn)楸幌到y(tǒng)的 statusBar 覆蓋了,在 style 里面或者 activity 里面把狀態(tài)欄設(shè)為透明的就好。

Demo

說(shuō)了這么久,寫(xiě)個(gè) demo 吧,把上面講到的東西盡量用一個(gè) demo 演示出來(lái),不過(guò)我感覺(jué)效果大家應(yīng)該都看到過(guò)~~~

AppBarLayout.gif

就一個(gè)這樣的效果吧,沒(méi)有什么特別的特色,當(dāng)然如果讓我自己手?jǐn)]我表示很操蛋~~
1.滑動(dòng) ScrollView/RecyclerView 的時(shí)候 優(yōu)先把頂部的圖片頂上去,然后固定TabLayout ,再滾動(dòng) ScrollView/RecyclerView 的內(nèi)容,下拉的時(shí)候可以設(shè)置優(yōu)先拖出圖片或者拉到頂部在拖出圖片。
2.Toolbar 的 title 伴隨滾動(dòng)移動(dòng)位置和改變顏色,圖片滾動(dòng)到一定位置的時(shí)候會(huì)漸變一個(gè)主題色的蒙版遮蓋住。

xml 代碼實(shí)現(xiàn)

<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.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
    ...省略內(nèi)容

</android.support.v4.widget.NestedScrollView>

<android.support.design.widget.AppBarLayout
    android:id="@+id/app_bar_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.design.widget.CollapsingToolbarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:collapsedTitleTextAppearance="@style/ToolbarTextAppearanceTitle"
        app:contentScrim="@color/colorPrimary_pinkDark"
        app:expandedTitleGravity="center_horizontal|bottom"
        app:expandedTitleTextAppearance="@style/expandedToolbarTextAppearance"
        app:layout_scrollFlags="scroll|snap|enterAlways"
        app:scrimAnimationDuration="2000"
        app:scrimVisibleHeightTrigger="40dp"
        app:titleEnabled="true">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@mipmap/material_img"
            app:layout_collapseMode="parallax"
            app:layout_collapseParallaxMultiplier="0.5"/>


        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="scroll"
            app:navigationIcon="@mipmap/abc_ic_ab_back_mtrl_am_alpha"
            app:title="湖南農(nóng)業(yè)大學(xué)校歌"/>

    </android.support.design.widget.CollapsingToolbarLayout>

    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        />

</android.support.design.widget.AppBarLayout>

</android.support.design.widget.CoordinatorLayout>

源碼分析

還是寫(xiě)點(diǎn)源碼分析吧,感覺(jué)不分析一下源碼就相當(dāng)于只學(xué)了一個(gè) api,以后出現(xiàn)類(lèi)似的特效,然后現(xiàn)有的東西不能滿足定制,我們也能模仿這些效果自己手?jǐn)]出來(lái)。
好了,說(shuō)正事~
今天的源碼分析就不一行一行的看代碼了,我們就根據(jù)上面的效果來(lái)分析怎么實(shí)現(xiàn)的把

1.滑動(dòng) ScrollView/RecyclerView 的時(shí)候 優(yōu)先把頂部的圖片頂上去,然后固定TabLayout ,再滾動(dòng) ScrollView/RecyclerView 的內(nèi)容,下拉的時(shí)候可以設(shè)置優(yōu)先拖出圖片或者拉到頂部在拖出圖片。
2.Toolbar 的 title 伴隨滾動(dòng)移動(dòng)位置和改變顏色,圖片滾動(dòng)到一定位置的時(shí)候會(huì)漸變一個(gè)主題色的蒙版遮蓋住。

額,這里不止兩個(gè)點(diǎn),不糾結(jié)了,一個(gè)一個(gè)來(lái)吧

  • 我們給 ScrollView/RecyclerView 設(shè)置了 Behavior,在滑動(dòng)的過(guò)程中,會(huì)調(diào)用 Behavior 里面的onStartNestedScroll、onNestedPreScroll、onNestedScroll、onStopNestedScroll等方法,然后 Behavior 持有對(duì) AppBarLayout 的引用,會(huì)在這些方法里面根據(jù)狀態(tài)做一系列的事情。至于這個(gè) Behavior 是怎么調(diào)用的,我會(huì)在下一篇里面重點(diǎn)講 Behavior。
  • 這里的效果實(shí)現(xiàn)全部由CollapsingToolbarLayout,主要是 title 的位置和顏色, 然后就是mContentScrim和 mStatusBarScrim 這兩個(gè)遮蓋布的繪制,方法很簡(jiǎn)單
    @Override
    public void draw(Canvas canvas) {
    super.draw(canvas);

    // If we don't have a toolbar, the scrim will be not be drawn in drawChild() below.
    // Instead, we draw it here, before our collapsing text.
    ensureToolbar();
    if (mToolbar == null && mContentScrim != null && mScrimAlpha > 0) {
        mContentScrim.mutate().setAlpha(mScrimAlpha);
        mContentScrim.draw(canvas);
    }

    // Let the collapsing text helper draw its text
    if (mCollapsingTitleEnabled && mDrawCollapsingTitle) {
        mCollapsingTextHelper.draw(canvas);
    }

    // Now draw the status bar scrim
    if (mStatusBarScrim != null && mScrimAlpha > 0) {
        final int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
        if (topInset > 0) {
            mStatusBarScrim.setBounds(0, -mCurrentOffset, getWidth(),
                    topInset - mCurrentOffset);
            mStatusBarScrim.mutate().setAlpha(mScrimAlpha);
            mStatusBarScrim.draw(canvas);
        }
    }
    }

ensureToolbar()再次確保了有一次對(duì)子 view toolbar 的引用。然后就是三個(gè) if 控制繪制 contentScrim、CollapsingText、statusBarScrim。
其中CollapsingTextHelper保存了折疊 TextTitle 的各種繪制信息。
可能有人會(huì)問(wèn),如何控制 contentScrim,剛剛我們?cè)?draw()的方法里面看到了判斷條件,如果mScrimAlpha>0 則繪制,那么我們可以大膽的猜測(cè),肯定是在收縮的過(guò)程中根據(jù)高度設(shè)置 mScrimAlpha來(lái)控制顏色布的顯示與隱藏。

    final void updateScrimVisibility() {
    if (mContentScrim != null || mStatusBarScrim != null) {
        setScrimsShown(getHeight() + mCurrentOffset < getScrimVisibleHeightTrigger());
        }
    }

這個(gè)方法控制了 mScrimAlpha,getScrimVisibleHeightTrigger()方法獲取scrimVisibleHeightTrigger這個(gè)屬性大家肯定也不陌生。然后我們通過(guò)搜索發(fā)現(xiàn)updateScrimVisibility的調(diào)用在onLayout里面。
熟悉 view 繪制流程的童鞋肯這時(shí)候應(yīng)該都懂了吧。我們?cè)跐L動(dòng)的時(shí)候高度是不斷發(fā)生變化的,而我們的高度發(fā)生變化則會(huì)重新 onMeasure,onMeasure 之后則會(huì)調(diào)用 onLayout,然后 onLayout里面調(diào)用updateScrimVisibility修改了 mScrimAlpha 的值,最后在 draw 方法里面繪制出來(lái)。

好了,就到這里吧,這里沒(méi)有酷炫的 demo,什么防簡(jiǎn)書(shū)首頁(yè)、仿知乎等等,但是看懂了這些api,我相信都能夠自己動(dòng)手防一個(gè)。
有點(diǎn)懶,很多應(yīng)該錄 gif 圖的都沒(méi)錄,還請(qǐng)諒解~
不諒解也沒(méi)事,反正你也打不到我

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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