底部彈框動畫及漸變背景色

Android底部彈框動畫及漸變背景色

概述

手機應用中的底部彈框經常都會用到,應該說也比較容易實現,網上有非常多的例子,不過都是視覺效果比較單一的,比如瞬間彈出、從底部慢慢升起、帶半透明的背景色。如果把這些效果組合起來實現就會有些瑕疵,等下我會具體說一下我認爲的瑕疵。現在來說一下實現以上幾種效果的常見方法:

一、 動畫效果實現方法

對於PopupWindow的動畫效果,一種是用style, 一種是用代碼,這兩種沒什麼區別,只是用style的話要另寫一個xml文件,稍微麻煩一點,特別是如果想封裝成一個組件的話就更不方便,除了java文件還帶個xml文件很不方便,所以封裝成組件的話還是用代碼的方式實現爲好。

1、sytle方式

首先在style.xml中聲明一個style,如:

<?xml version="1.0" encoding="utf-8"?>    
<resources>       
  
<style name="PopupAnimation" parent="android:Animation" mce_bogus="1">         
        <item name="android:windowEnterAnimation">@anim/popup_enter</item>    
        <item name="android:windowExitAnimation">@anim/popup_exit</item>    
    </style>    
</resources>

然後在res/anim文件夾中新增一個動畫文件(如果沒有anim文件夾則新建一個),如:

<?xml version="1.0" encoding="utf-8"?>        
<set xmlns:android="http://schemas.android.com/apk/res/android">    
    <scale android:fromXScale="0.6" android:toXScale="1.0" 
        android:fromYScale="0.6" android:toYScale="1.0" android:pivotX="50%" 
        android:pivotY="50%" android:duration="1000" />    
    <alpha android:interpolator="@android:anim/decelerate_interpolator" 
        android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="1000" />    
</set>

上面的是進入動畫,還可以建個退出動畫,如:

<?xml version="1.0" encoding="utf-8"?>    
<set xmlns:android="http://schemas.android.com/apk/res/android">    
    <scale    
        android:fromXScale="1.0" 
        android:toXScale="0.5" 
        android:fromYScale="1.0" 
        android:toYScale="0.5" 
        android:pivotX="50%" 
        android:pivotY="50%" 
        android:duration="500" />    
    <alpha    
        android:interpolator="@android:anim/accelerate_interpolator" 
        android:fromAlpha="1.0" 
        android:toAlpha="0.0" 
        android:duration="500" />    
</set>

最後使用的時候調一下這個方法

popupWindow.setAnimationStyle(R.style.PopupAnimation);

注,以上代碼片斷來自於 http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2013/0303/956.html

2、用代碼的方式

先初始化一個TranslateAnimation對象,如:

// 平移動畫相對於手機屏幕的底部開始,X軸不變,Y軸從1變0
TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0, Animation
    .RELATIVE_TO_PARENT, 0,
    Animation.RELATIVE_TO_PARENT, 1, Animation.RELATIVE_TO_PARENT, 0);
animation.setInterpolator(new AccelerateInterpolator());
animation.setDuration(300);

然後在調完show方法之後,啓動動畫,如:

showAtLocation(activity.getWindow().getDecorView(), Gravity.BOTTOM, 0, 0);
contentView.startAnimation(animation);

以上的contentView是popupWindow.getContentView();的返回值。

二 、背景色實現方案

1、最簡單的就是contentView的layout設置高寬爲match_parent,然後設置背景色。

2、就是在代碼中設置popupWindow的背景色,如:

// 設置背景色
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#00112233")));

如果是瞬間彈出的話這兩種方式都沒什麼問題,但如果加上動畫效果的話就有些瑕疵了:

第一種加上動畫後效果是背景色也會從屏幕底部升起,這種效果看着很彆扭,感覺像在拉幕布。

第二方式加上動畫效果後,屏幕瞬間變成黑色半透明,然後contentView慢慢從屏幕底部彈出,這種效果也是挺難看的。

看了一下其它應用的彈框動畫,它們的效果是背景色在contentView慢慢升起的過程中從完全透明變成半透明,當contentView完全升起時背景色也達到了最終的半透明。經過一番思考,我用以下方案基本實現了這個效果:

動畫這部分不用動,直接用代碼實現就可以了,背景色的話我沒有采用上述兩種方式的任何一種。我首先想到了屬性動畫中的顏色漸變,用這個特性來實現背景色與彈出框的同步,就是contentView一邊慢慢升起,背景色也一邊從全透明開始慢慢變深,並且這個背景色是作用於整個屏幕的,不會跟着contentView往上走。如果這兩個動畫的時間設置爲一樣那基本上也就達到了我們要的效果了,不過實際效果中,觸發彈框後contentView並不會馬上彈出來,而是要等一會纔會彈出來,這點可以通過把動畫時間設置長一些就能看出來了,比如設置成3秒,你會看到前兩秒都沒反應,在最後一秒才能看到框從底部開始彈出(這裏的一秒兩秒是個大概的估計)。因此背景色的漸變最好還不要是線性的,如果是線性的話前兩秒框還沒彈出來背景確變黑了一點了,最好就是從看到框彈出時背景纔開始慢慢變黑。這時我想到了用平方曲線(即y=x²)來控制顏色的漸變過程,最後實現下來感覺還算滿意。下面就來看看具體怎麼實現的。

首先,這個背景色要設置在哪個view上?contnetView肯定不行,因爲contentView是會移動的,那麼放在PopupWindow中那個接受setBackgroundDrawable方法裏的背景色的view上?可是查了下這個view是私有的,並且也沒看到有其它什麼方法能拿到這個view的實例。後來想到popupwindow大多都是在Activity的基礎上彈出來的,所以就想從activity入手來創建一個view,讓這個view來承載背景色。最後選擇了android.R.id.content這個view,它是一個FrameLayout,添加一個view就可以把activity上的內容蓋住,再加上個半透明的背景色,就可以達到我們的要求了。

以下是關鍵代碼:

 /**
 * 從底部彈出
 */
public void show() {
    if (activity != null) {
        showAtLocation(activity.getWindow().getDecorView(), Gravity.BOTTOM, 0, 0);
        contentView.startAnimation(animation);

        // 背景色漸變
        backgroundView = new FrameLayout(activity);
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams
            .MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        backgroundView.setLayoutParams(lp);
        ViewGroup rootView = activity.findViewById(android.R.id.content);
        rootView.addView(backgroundView);
        // 從完全透明開始到半透明的灰色
        int begin = 0x00000000;
        int end = 0x4d000000;

        ValueAnimator colorAnim = ObjectAnimator.ofInt(backgroundView, "backgroundColor",
            begin, end);
        colorAnim.setDuration(300);
        colorAnim.setEvaluator(new ArgbEvaluator());
        Interpolator interpolator = new MyInterpolator();
        colorAnim.setInterpolator(interpolator);
        colorAnim.start();
    }
}

其中屬性動畫 colorAnim還用到一個插值器,就是控制顏色的變化過程的。

/**
 * 動畫插值器
 *
 * @author Huangming 2019/3/17
 */
private class MyInterpolator implements Interpolator {
    @Override
    public float getInterpolation(float input) {
        // y=1-√1-x² 曲線,本來想直接用y=x²的,但是還是不夠理想
        float y = (float) (1.0 - Math.sqrt(1 - input * input));
        // XbdLog.d("x=%s  y=%s", input, y);
        return y;
    }
}

這個插值器裏面的曲線方程比較關鍵,如果過早地變顏色會就出現框還沒彈出來,背景色就已經變了的情況。

本來想直接用y=x²的,但是還是不夠理想,最後找了個這樣的:y=1-√1-x²,曲線如下:

再來看下整體的效果圖,這個效果是將動畫時間拉長到3秒的效果,就是爲了看清楚背景色的變化與彈出框的彈出時機:

最後,上源碼,有興趣的可以去下下來跑一下:

demo源碼:https://github.com/MingHuang1024/HmPopupWindow




由於水平有限,如果文中存在錯誤之處,請大家批評指正,歡迎大家一起來分享、探討!

博客:http://blog.csdn.net/MingHuang2017

GitHub:https://github.com/MingHuang1024

Email: [email protected]

微信:724360018

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章