Android 動畫深入分析

Android 動畫深入分析.png

7.1 View 動畫


View 動畫對應(yīng)這 Animation 的四個子類:

名稱 標(biāo)簽 子類 效果
平移動畫 <translate> TranslateAnimation 移動 View
縮放動畫 <scale> ScaleAnimation 放大或縮小 View
旋轉(zhuǎn)動畫 <rotate> RotateAnimation 旋轉(zhuǎn) View
透明度動畫 <alpha> AlphaAnimation 改變 View 的透明度

<set> 標(biāo)簽表示動畫集合区转,對應(yīng) AnimationSet 類函似,它可以包含若干個動畫,并且內(nèi)部也可以嵌套動畫集合店乐。

  • android:interpolator
    表示動畫集合采用的插值器艰躺,影響動畫的速度,可以不指定為勻速眨八。

  • android:shareInterpolator
    表示集合中的動畫是否和集合共享同一個插值器腺兴。如果集合不指定插值器,那么自動化就需要單獨指定所需的插值器或者使用默認(rèn)值廉侧。

<translate>標(biāo)簽表示平移動畫含长,對呀 TranslateAnimation 類,它可以使一個 View 在水平和豎直方向完成平移動畫效果伏穆。

  • android:fromXDelta:表示 x 的起始值,比如 0纷纫;

  • android:toXDelta:表示 x 的結(jié)束值枕扫,比如 100;

  • android:fromYDelta:表示 y 的起始值辱魁;

  • android:toYDelta:表示 y 的結(jié)束值;

<scale>標(biāo)簽表示縮放動畫,對應(yīng) ScaleAnimation雌团,它可以使 View 具有放大或者縮小的動畫效果檐蚜。

  • android:fromXScale:水平方向的起始值,比如 0.5漩仙;

  • android:toXScale:水平方向縮放的結(jié)束值,比如 1.2;

  • android:pivotX:縮放軸點的x坐標(biāo)蝌箍,它會影響縮放的效果;

  • android:pivotY:縮放軸點的y坐標(biāo)暴心,它會影響縮放的效果妓盲;

  • android:toXScale:水平方向縮放的結(jié)束值,比如1.2专普;

  • android:toYScale:豎直方向縮放的起始值悯衬;

<rotate>標(biāo)簽表示旋轉(zhuǎn)動畫,對應(yīng)于 RotateAnimation檀夹,它可以使 View 具有旋轉(zhuǎn)效果筋粗。

android:fromDegrees:旋轉(zhuǎn)開始的角度,比如 0炸渡;

android:toDegrees:旋轉(zhuǎn)結(jié)束的角度娜亿,比如180;

android:pivotX:旋轉(zhuǎn)軸點的 x偶摔;

android:pivotY:旋轉(zhuǎn)軸點的 y暇唾;

<alpha>表示透明動畫。對應(yīng)的 AlphaAnimation辰斋。

  • android:fromAlpha:表示透明度的起始值策州,比如0.1

  • android:toAlpha:表示透明度的結(jié)束值,比如1

上面都只是很簡單的介紹了XM格式宫仗,具體的使用方法還是看文檔够挂,我們還有一些常用的屬性如下

  • android:duration:動畫的時間

  • android:fillAfter:動畫結(jié)束之后是否停留在結(jié)束的位置

演示一個一秒鐘平移 100 加旋轉(zhuǎn) 360 度然后復(fù)原的案例:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="false">

    <translate
        android:fromYDelta="0"
        android:fromXDelta="0"
        android:toXDelta="100"
        android:toYDelta="100"
        android:duration ="1000"
        android:interpolator = "@android:anim/linear_interpolator"
        />

    <rotate
        android:duration ="1000"
        android:fromDegrees="0"
        android:toDegrees="360"
        />
</set>

再加一個代碼寫一個 透明動畫:

    Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.my_anim);
    button.startAnimation(animation);
    //代碼動畫
    AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
    alphaAnimation.setDuration(1000);
    animation.setAnimationListener(new Animation.AnimationListener() {
        //開始
        @Override
        public void onAnimationStart(Animation animation) {}
        //結(jié)束
        @Override
        public void onAnimationEnd(Animation animation) {}
        //重復(fù)
        @Override
        public void onAnimationRepeat(Animation animation) { }
    });    
    button2.startAnimation(alphaAnimation);
GIF.gif

7.1.2 自定義 View 動畫

自定義動畫只需要繼承 Animation,然后重寫它的 initialize 和 applyTransformation 方法

  • initialize:初始化
  • applyTransformation:進(jìn)行相應(yīng)的矩陣變換藕夫,很多時候需要采用 Camera 來簡化矩陣變換的過程孽糖。
    GIF.gif

可以參照 ApiDemos 中的 Rotate3dAnimation。

7.1.3 幀動畫

幀動畫使用很簡單毅贮,但是比較容易引起 OOM办悟,所以要避免使用大尺寸圖片。

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@mipmap/timg" android:duration="500"/>
    <item android:drawable="@mipmap/timgb" android:duration="500"/>
    <item android:drawable="@mipmap/timgc" android:duration="500"/>
</animation-list>
 button.setBackgroundResource(R.drawable.my_list);
 AnimationDrawable background = (AnimationDrawable) button.getBackground();
 background.start();
GIF.gif

7.2 View 動畫的特殊使用場景


7.2.1 LayoutAnimation

LayoutAnimation 作用于 ViewGroup滩褥,為 ViewGroup 指定一個動畫病蛉,這樣當(dāng)它的子元素出場時都會具有這種動畫效果。

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="0.5"
    android:animationOrder="normal"
    android:animation="@anim/anim_item">
</layoutAnimation>
  • android:delay
    表示子元素開始動畫的延遲,0.5 就是一半的時間周期铺然。

  • android:animationOrder
    表示子元素動畫的順序俗孝。

  • normal:順序顯示

  • reverse:逆向顯示

  • random:隨機(jī)顯示

  • android:animation
    為子元素指定具體的入場動畫。

GIF.gif

先新建一個 set 動畫集合魄健,里面是從透明到不透明并且平移的一個效果

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:shareInterpolator="true" >

    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" />

    <translate android:fromXDelta="500" android:toXDelta="0" />

</set>

新建一個 layoutAnimation 赋铝,設(shè)置顯示樣式效果

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
                 android:animation="@anim/anim_item"
                 android:animationOrder="normal"
                 android:delay="0.5">
</layoutAnimation>

在 XML 中指定 LayoutAnimation

<ListView
        android:id="@+id/list_view"
        android:layoutAnimation="@anim/my_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

或者可以通過 LayoutAnimationConreoller 來實現(xiàn)

Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item);
        LayoutAnimationController controller = new LayoutAnimationController(animation);
        controller.setDelay(0.5f);
        controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
        mListView.setLayoutAnimation(controller);

7.2.2 Activity 的切換效果

Activity 的自定義切換效果。主要用到 overridePendingTransition (int enterAnim沽瘦,int enterAnim)這個方法革骨。

  • enterAnim: Activity 被打開時,所需要的動畫資源 id其垄;
  • exitAnim: Activity 被暫停時苛蒲,所需要的動畫資源 id;
Intent intent = new Intent(MainActivity.this, ListViewActivity.class);
                startActivity(intent);
                overridePendingTransition(R.anim.my_anim,R.anim.my_anim);
@Override
    public void finish() {
        super.finish();
        overridePendingTransition(R.anim.my_anim,R.anim.my_anim);
    }
GIF.gif

7.3 屬性動畫


7.3.1 使用屬性動畫

屬性動畫可以對任意對象進(jìn)行動畫而不僅僅是 View绿满,幾乎是無所不能的臂外,只要有對象這個屬性。

改變一個對象的 translationX 值喇颁。

ObjectAnimator.ofFloat(mButton, "translationX",200).start();

改變一個對象的背景顏色屬性漏健。

                ObjectAnimator colorAnim = ObjectAnimator.ofInt(mButton, "backgroundColor", 0xFFFF8080, 0xFF8080FF);
                colorAnim.setDuration(2000);
                colorAnim.setEvaluator(new ArgbEvaluator());
                //INFINITE 表示無限次
                colorAnim.setRepeatCount(ValueAnimator.INFINITE);
                //REVERSE 表示反轉(zhuǎn)效果
                colorAnim.setRepeatMode(ValueAnimator.REVERSE);
                colorAnim.start();

動畫集合,5秒內(nèi)對 View 的旋轉(zhuǎn)橘霎、平移蔫浆、縮放和透明度都進(jìn)行了改變

AnimatorSet set = new AnimatorSet();
                set.playTogether(
                        ObjectAnimator.ofFloat(mButton,"rotationX",0,360),
                        ObjectAnimator.ofFloat(mButton,"rotationY",0,360),
                        ObjectAnimator.ofFloat(mButton,"rotation",0,-180),
                        ObjectAnimator.ofFloat(mButton,"translationX",0,90),
                        ObjectAnimator.ofFloat(mButton,"translationY",0,90),
                        ObjectAnimator.ofFloat(mButton,"scaleX",1,1.5f),
                        ObjectAnimator.ofFloat(mButton,"scaleY",0,2.5f),
                        ObjectAnimator.ofFloat(mButton,"alpha",1,0.25f,1)
                );
                set.start();

屬性動畫除了通過代碼實現(xiàn)以外,還可以通過 XML 來定義姐叁,定義在 res/animator/ 目錄下瓦盛。

<objectAnimator
        android:duration="3000"
        android:propertyName="x"
        android:valueFrom="-50"
        android:valueTo="200"
        android:valueType="floatType" />

    <objectAnimator
        android:propertyName="y"
        android:duration="3000"
        android:valueFrom="1"
        android:valueTo="300"
        android:valueType="floatType" />
  AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(ValueActivity.this, R.animator.my_animator);
                set.setTarget(mButton);
                set.start();
GIF.gif
  • android:propertyName:表示屬性動畫作用對象的屬性的名稱

  • android:duration:表示動畫的時長

  • android:valueFrom:表示屬性的起始值

  • android:valueTo:表示屬性的結(jié)束值

  • android:startOffset:表示動畫的延遲時間,當(dāng)動畫開始后外潜,需要延遲多少毫秒才會真正的播放

  • android:repeatCount:表示動畫的重復(fù)次數(shù)原环,默認(rèn) 0,-1 表示無線循環(huán)

  • android:repeatMode:表示動畫的重復(fù)模式

  • restart:表示連續(xù)重復(fù)

  • reverse:表示連逆向重復(fù)处窥,就是反轉(zhuǎn)

  • android:valueType:表示 propertyName 有兩個屬性有 int 和 float 兩個可選項嘱吗,分別表示屬性的類型,和浮點型滔驾,另外谒麦,如果所制定的是顏色類型,那么就不需要指定 propertyName哆致,系統(tǒng)會自動對顏色類型進(jìn)行處理

實際開發(fā)建議采用代碼來實現(xiàn)绕德,簡便動態(tài)。

7.3.2 理解插值器和估值器

TimeInterpolator:中文翻譯是時間插值器的意思摊阀,他的作用是根據(jù)時間流逝的百分比來計算出當(dāng)前屬性值改變的百分比耻蛇,系統(tǒng)預(yù)置的有:

  • LinearInterpolator:(線性加速器剩瓶,勻速加速器),加速和減速插值器

  • AccelerateDecrateInterpolator:(加速減速插值器:動畫兩頭慢中間快)

  • DecelerateInterpolator:(減速插值器:動畫越來越慢)

TypeEvaluator:的中文翻譯是類型估值算法城丧,也叫估值器,他的作用是根據(jù)當(dāng)前屬性變化的百分比來計算變化后的屬性值豌鹤,系統(tǒng)也預(yù)設(shè)的有:

  • IntEvaluator:整型
  • FloatEvaluator:浮點型
  • ArgbEvaluator:Color屬性

屬性動畫中的插值器和估值器很重要亡哄,他們實現(xiàn)非勻速動畫的重要手段。也可以自定義使用布疙。

public class LinearInterpolator implements Interpolator{

        @Override
        public float getInterpolation(float input) {
                if (mFactor == 1.0f) {
                    return input * input;
                } else {
                    return (float)Math.pow(input, 2);

        }
    }
    public class MyEvaluator extends IntEvaluator {

        //fraction 百分比蚊惯、startValue 開始值、endValue 結(jié)束值
        @Override
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
            return super.evaluate(fraction, startValue, endValue);
        }
    }

7.3.3 屬性動畫的監(jiān)聽器

屬性動畫提供監(jiān)聽器用于監(jiān)聽動畫的播放灵临,主要有兩個接口:

  • AnimatorListener
public static interface AnimatorListener {
        //開始
        void onAnimationStart(Animator animation);
        //結(jié)束
        void onAnimationEnd(Animator animation);
        //取消
        void onAnimationCancel(Animator animation);
        //重復(fù)
        void onAnimationRepeat(Animator animation);
    }
  • **AnimatorUpdateListener **
public static interface AnimatorUpdateListener {

        void onAnimationUpdate(ValueAnimator animation);

    }

AnimatorUpdateListener 比較特殊截型,它會監(jiān)聽整個動畫過程的,每播放一幀儒溉,就會調(diào)用一次 AnimatorUpdateListener宦焦。

7.3.4 對任意屬性做動畫

給 Button 加一個動畫,增加 Button 的寬度到 500 px顿涣。View 動畫只能通過 縮放 Scale 來讓 Button 在 x 方向上被放大波闹,而且有可能超過屏幕大小,而屬性動畫可以并且不會超越屏幕涛碑。

GIF.gif

方法 1:直接使用(TextView 和其子類)

ObjectAnimator.ofInt(mButton, "width",50000).setDuration(2000).start();

方法 2:用一個類包裝原始對象精堕,間接為其提供 get 和 set 方法。

    private void performAnimate() {
        ViewWrapper viewWrapper = new ViewWrapper(mButton);
        ObjectAnimator.ofInt(viewWrapper, "width",500).setDuration(2000).start();
    }

    public static class ViewWrapper {

        private View mTarget;
        public ViewWrapper(View target) {
             mTarget = target;
        }

        public int getWidth() {
            return mTarget.getLayoutParams().width;
        }

        public void setWidth(int width) {
            mTarget.getLayoutParams().width = width;
            mTarget.requestLayout();
        }
    }
ViewWrapper viewWrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(viewWrapper, "width",500).setDuration(2000).start();

方法 3:采用 ValueAnimator蒲障,監(jiān)聽動畫過程的每一幀歹篓,自己實現(xiàn)屬性的改變

private void performAnimator(final View target, final int start, final int end) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            //持有一個IntEvaluator對象,方便下面估值的時候使用
            private IntEvaluator mEvaluator = new IntEvaluator();

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //獲得當(dāng)前動畫的進(jìn)度值揉阎,整形1-100之間
                int currentValue = (int) animation.getAnimatedValue();
                //獲得當(dāng)前進(jìn)度占整個動畫之間的比例庄撮,浮點0-1之間
                float fraction = animation.getAnimatedFraction();
                //直接使用整形估值器,通過比例計算寬度余黎,然后再設(shè)置給 Button
                target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
                target.requestLayout();
            }
        });
        valueAnimator.setDuration(5000).start();
    }
performAnimate(mButton,mButton.getWidth(),5000);

方法 4:給你的對象加上 get 和 set重窟,如果你有權(quán)限的話(一般沒有,所以可以不用)

7.3.5 屬性動畫的工作原理

屬性動畫要求動畫作用的對象提供該屬性的 set 方法惧财,屬性動畫根據(jù)那你傳遞的該屬性的初始值和最終值巡扇,以動畫的效果多次去調(diào)用 set 方法,每次傳遞給 set 方法的值都不一樣垮衷,確切的來說隨著時間的推移厅翔,所傳遞的值越來越接近最終值,如果動畫的時候沒有傳遞初始值搀突,那么還要提供 get 方法刀闷,因為系統(tǒng)需要獲取屬性的初始值。這就上面的 方法 2 需要設(shè)置 get 和 set 的原因。

一句話:最后是通過反射來調(diào)用甸昏。(源碼不清不楚顽分,先放著)

7.4 使用動畫的注意事項

  1. OOM 問題
    這個問題主要出現(xiàn)在幀動畫,當(dāng)圖片較多且大就容易出現(xiàn)施蜜,這個時候盡量避免使用幀動畫卒蘸。

  2. 內(nèi)存泄漏
    在屬性動畫中有設(shè)置無線循環(huán)的動畫,這類動畫要在退出時要及時停止翻默,View 動畫并不在此問題缸沃。

  3. ** 兼容性問題**
    動畫在 3.0 以下的系統(tǒng)上有兼容問題,要做好適配工作修械。

  4. View 動畫的問題
    View 動畫是對 View 的影像做動畫趾牧,并不是正真的改變 View 的狀態(tài),調(diào)用方法不能正常使用時肯污,調(diào)用 View.clearAnimation 清除可以解決翘单。

  5. 不要使用 px
    在進(jìn)行動畫的過程中,要使用 dp仇箱,用 px 會有適配問題县恕。

  6. 動畫元素的交互
    3.0 之前的問題忽略,3.0 以后屬性動畫的點擊事件觸發(fā)位置為移動后的位置剂桥,但是 View 動畫任然在原位置忠烛。

  7. 硬件加速
    使用動畫的過程中,建議開啟硬件加速权逗,這樣會提供動畫的流暢性美尸。

源碼Demo 鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市斟薇,隨后出現(xiàn)的幾起案子师坎,更是在濱河造成了極大的恐慌,老刑警劉巖堪滨,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胯陋,死亡現(xiàn)場離奇詭異,居然都是意外死亡袱箱,警方通過查閱死者的電腦和手機(jī)遏乔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來发笔,“玉大人盟萨,你說我怎么就攤上這事×颂郑” “怎么了捻激?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵制轰,是天一觀的道長。 經(jīng)常有香客問我胞谭,道長垃杖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任丈屹,我火速辦了婚禮缩滨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泉瞻。我一直安慰自己,他們只是感情好苞冯,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布袖牙。 她就那樣靜靜地躺著,像睡著了一般舅锄。 火紅的嫁衣襯著肌膚如雪鞭达。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天皇忿,我揣著相機(jī)與錄音畴蹭,去河邊找鬼。 笑死鳍烁,一個胖子當(dāng)著我的面吹牛叨襟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播幔荒,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼糊闽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了爹梁?” 一聲冷哼從身側(cè)響起右犹,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎姚垃,沒想到半個月后念链,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡积糯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年掂墓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片絮宁。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡梆暮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绍昂,到底是詐尸還是另有隱情啦粹,我是刑警寧澤偿荷,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站唠椭,受9級特大地震影響跳纳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贪嫂,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一寺庄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧力崇,春花似錦斗塘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至茧吊,卻和暖如春贞岭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搓侄。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工瞄桨, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讶踪。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓芯侥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乳讥。 傳聞我的和親對象是個殘疾皇子筹麸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android動畫可以分為三種:view動畫、幀動畫雏婶、屬性動畫 View動畫:通過對場景里的對象不斷做圖像變換(平...
    小柏不是大白閱讀 623評論 0 0
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,139評論 25 707
  • 本篇文章主要介紹以下幾個知識點:View 動畫View 動畫的特殊使用場景屬性動畫使用動畫的注意事項 Androi...
    開心wonderful閱讀 430評論 0 4
  • 病房里發(fā)現(xiàn)一個很有趣的現(xiàn)象留晚,經(jīng)辰妥希看到老奶奶無微不至地照顧著患病的老爺爺,卻很少看到老爺爺照顧患病的老奶奶错维。 這普遍...
    原野花開閱讀 1,311評論 8 4
  • 當(dāng)最后一個喜鵲飛走的時候奖地,七夕這個中國式的情人節(jié)的煙花就要結(jié)束了,一切都又回到了常態(tài)赋焕,回想幾天來的情景参歹,真...
    白露丹楓閱讀 201評論 0 0