使用ScrollView滾動事件打造動畫框架ScrollAnimationSherlock


1

一個用來打造上述引導(dǎo)界面動畫效果的Scroll框架, 集成進(jìn)https://github.com/Jerey-Jobs/KeepGank中义郑,作為首次啟動的歡迎界面糠排。

工程源碼:https://github.com/Jerey-Jobs/ScrollAnimationSherlock

目前支持:

  • 透明度動畫與平移動畫(四種方向),支持混合調(diào)用
  • 背景色漸變設(shè)置
  • SherlockLinearLayout與SherlockRelativeLayout提供動畫界面的線性布局與相對布局支持
  • SherlockAnimationCallBack提供自定義擴(kuò)展

如何使用

如何使用

project's build.gradle (工程下的 build.gradle)

  allprojects {
    repositories {
      ...
      maven { url 'https://jitpack.io' }
    }
  }

module's build.gradle (模塊的build.gradle)

  dependencies {
          compile 'com.github.Jerey-Jobs:ScrollAnimationSherlock:1.0'
  }

項目中:

頂層布局:cn.jerey.animationlib.SherlockScrollView,內(nèi)嵌一個SherlockLinearLayout


    <cn.jerey.animationlib.SherlockScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <cn.jerey.animationlib.SherlockLinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical">

            <include layout="@layout/splash_layout"></include>

        </cn.jerey.animationlib.SherlockLinearLayout>
    </cn.jerey.animationlib.SherlockScrollView>

SherlockLinearLayout的第一個子View會被默認(rèn)設(shè)置為全屏别凹,因此

<include layout="@layout/splash_layout"></include>

splash_layout中完成第一個界面的搭建

這是上圖的splash_layout

-

接下來就是使用SherlockLinearLayoutSherlockRelativeLayout進(jìn)行其他界面搭建赛蔫。demo中使用的是SherlockRelativeLayout其中放置了四個子View,并設(shè)置了相應(yīng)動畫花椭。

<cn.jerey.animationlib.SherlockRelativeLayout
     android:layout_width="match_parent"
     android:layout_height="300dp">


     <ImageView
         android:id="@+id/moon"
         android:layout_width="100dp"
         android:layout_height="100dp"
         android:layout_marginLeft="30dp"
         android:layout_marginTop="30dp"
         android:background="@drawable/moon"
         app:animation_alpha="true"
         app:animation_translation="left"/>

     <ImageView
         android:id="@+id/astronaut"
         android:layout_width="50dp"
         android:layout_height="50dp"
         android:layout_marginLeft="120dp"
         android:layout_marginTop="28dp"
         android:background="@drawable/astronaut"
         app:animation_alpha="true"
         app:animation_translation="right|bottom"/>

     <ImageView
         android:id="@+id/imageView"
         android:layout_width="200dp"
         android:layout_height="180dp"
         android:layout_alignParentRight="true"
         android:layout_marginTop="100dp"
         android:background="@drawable/planet_earth_1"
         app:animation_alpha="true"
         app:animation_translation="right|bottom"/>

     <ImageView
         android:layout_width="100dp"
         android:layout_height="100dp"
         android:layout_alignParentBottom="true"
         android:layout_marginLeft="30dp"
         android:background="@drawable/rocket_1"
         app:animation_alpha="true"/>

 </cn.jerey.animationlib.SherlockRelativeLayout>

原理

總的原理一句話:賦予每個需要進(jìn)行動畫變換的View動畫屬性,并根據(jù)位置改變屬性坪郭。

如何做到

我們需要做的事情有

  1. 如何確定某個View需要進(jìn)行動畫變換
  2. 確定后如何賦予動畫屬性
  3. 如何分發(fā)動畫變換事件个从,讓子View進(jìn)行變換

確定是否需要動畫變換

ViewGroup的addView方法脉幢,該方法是添加子View的時候調(diào)用的歪沃。

 public void addView(View child, int index, ViewGroup.LayoutParams params)

我們需要在這邊進(jìn)行子View的判斷,如何判斷呢嫌松,我們可以參照support包的設(shè)計沪曙,添加app屬性,

我們?nèi)ザx幾個屬性

       <attr name="animation_alpha" format="boolean" />//是否支持透明度動畫萎羔;
       <attr name="animation_scaleX" format="boolean" />//是否支持X軸縮放動畫液走;
       <attr name="animation_scaleY" format="boolean" />//是否支持Y軸縮放動畫;
       <attr name="bgColorStart" format="color" />//背景漸變顏色的開始顏色值;
       <attr name="bgColorEnd" format="color" />//背景漸變顏色的結(jié)束顏色值缘眶,與bgColorStart成對出現(xiàn)嘱根;
       <attr name="animation_translation">//移動動畫,是一個枚舉類型巷懈,支持上下左右四種值该抒。
           <flag name="left" value="0x01" />
           <flag name="top" value="0x02" />
           <flag name="right" value="0x04" />
           <flag name="bottom" value="0x08" />
       </attr>

在addView時候,通過layoutParams參數(shù)來判斷顶燕,那么這里的LayoutParams是我們自定義的凑保,繼承于系統(tǒng)的LayoutParams,
不過在其構(gòu)造方法時追加參數(shù)解析涌攻。

/**
 * 不能將此LayoutParams抽象出來, 其繼承的是自己內(nèi)部類的Params
 */
public class RelativeLayoutParams extends LayoutParams {
    //是否支持透明度欧引;
    public boolean mAlphaSupport;
    //是否支持X Y軸縮放;
    public boolean mScaleXSupport;
    public boolean mScaleYSupport;

    //顏色變化的起始值恳谎;
    public int mBgColorStart;
    public int mBgColorEnd;
    //移動值芝此;
    public int mTranslationValue;

    public RelativeLayoutParams(Context c, AttributeSet attrs) {
        super(c, attrs);
        TypedArray typedArray = c.obtainStyledAttributes(attrs, R.styleable.MyFrameLayout);
        mAlphaSupport = typedArray.getBoolean(R.styleable.MyFrameLayout_animation_alpha, false);
        mBgColorStart = typedArray.getColor(R.styleable.MyFrameLayout_bgColorStart, -1);
        mBgColorEnd = typedArray.getColor(R.styleable.MyFrameLayout_bgColorEnd, -1);
        mScaleXSupport = typedArray.getBoolean(R.styleable.MyFrameLayout_animation_scaleX, false);
        mScaleYSupport = typedArray.getBoolean(R.styleable.MyFrameLayout_animation_scaleY, false);
        mTranslationValue = typedArray.getInt(R.styleable.MyFrameLayout_animation_translation, -1);
        typedArray.recycle();
    }

    /**
     * 判斷當(dāng)前params是否包含自定義屬性;
     *
     * @return
     */
    public boolean isHaveMyProperty() {
        if (mAlphaSupport || mScaleXSupport || mScaleYSupport || (mBgColorStart != -1 && mBgColorEnd != -1) || mTranslationValue != -1) {
            return true;
        }
        return false;
    }
}

這樣我們在addView時能夠拿到這個params因痛,并且里面已經(jīng)解析了是否支持動畫了癌蓖。

@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
    RelativeLayoutParams myLayoutParams = (RelativeLayoutParams) params;
    if (myLayoutParams.isHaveMyProperty())

賦予動畫變換屬性

我們的View是不大可能自己動的,而且我們也沒法去改view的代碼婚肆。這些view都是系統(tǒng)的view租副,
這樣我們只能說讓view有一個父類,去操作它了较性∮蒙或者說。給View“偽增加”一個方法赞咙,使其接收到我們的移動事件后责循,能夠進(jìn)行動畫變換。

如何增加呢攀操?

我們在解析view的屬性時院仿,即addView時,在其外面包裹一層父View速和,我稱之為 Frame歹垫。 使用FrameView去包裹它,
當(dāng)然需要注意的是颠放,為了讓view能夠直接完整的進(jìn)行動畫顯示排惨。我們需要設(shè)置各個父類的ClipChildren屬性為false。

因此封裝了SherlockFrame碰凶,其繼承于FrameLayout暮芭,并實現(xiàn)我們的位移callback接口的鹿驼,注意只有實現(xiàn)了我們的位移回調(diào)接口的。我們分發(fā)事件時才會分發(fā)辕宏。
同樣畜晰,這個接口提供了自定義擴(kuò)展,可以自己編寫實現(xiàn)這個接口的自定義view瑞筐,同樣會接收到位移分發(fā)舷蟀。

public class SherlockFrame extends FrameLayout implements SherlockAnimationCallBack{
    //從哪個方向開始動畫;
    private static final int TRANSLATION_LEFT = 0x01;
    private static final int TRANSLATION_TOP = 0x02;
    private static final int TRANSLATION_RIGHT = 0x04;
    private static final int TRANSLATION_BOTTOM = 0x08;

    //是否支持透明度面哼;
    private boolean mAlphaSupport;
    //顏色變化的起始值野宜;
    private int mBgColorStart;
    private int mBgColorEnd;

    //是否支持X Y軸縮放;
    private boolean mScaleXSupport;
    private boolean mScaleYSupport;
    //移動值魔策;
    private int mTranslationValue;
    //當(dāng)前View寬高匈子;
    private int mHeight, mWidth;
}

SherlockFrame的這些屬性,會在addview的時候進(jìn)行賦值闯袒。

SherlockAnimationCallBack回調(diào)excuteanimation方法時虎敦,SherlockFrame就根據(jù)自身的屬性情況進(jìn)行動畫變換。

事件分發(fā)

作為scrollView政敢,滾動事件的分發(fā)肯定是在onScrollChanged了其徙。在這里面進(jìn)行滾動事件分發(fā)

parseViewGroup方法有點講究了,這是一個遞歸遍歷子view喷户,看其是否實現(xiàn)了SherlockAnimationCallBack接口唾那,若沒有則去判斷是否是ViewGroup,若是的話則繼續(xù)遞歸遍歷其子view褪尝。

若是實現(xiàn)了SherlockAnimationCallBack接口的view闹获。我們要根據(jù)其距離頂部的高度來計算動畫應(yīng)該執(zhí)行百分之多少。我們可以通過view的getTop方法河哑,這個方法是得到view距離其父view的頂部的距離避诽。
因此我們還要進(jìn)行遞歸傳遞。

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);
    parseViewGroup(mLinearLayout, l, t, oldl, oldt, true, 0);
}

/**
 * @param linearLayout
 * @param l
 * @param t
 * @param oldl
 * @param oldt
 * @param isRootLinearLayout 是否是頂層布局
 * @param getTop             距離頂部高度
 */
private void parseViewGroup(ViewGroup linearLayout,
                            int l, int t, int oldl, int oldt,
                            boolean isRootLinearLayout, int getTop) {
    int scrollViewHeight = getHeight();
    Log.w(TAG, "linearLayout.getChildCount()" + linearLayout.getChildCount());
    for (int i = 0; i < linearLayout.getChildCount(); i++) {
        //如果子控件不是MyFrameLayout則循環(huán)下一個子控件璃谨;
        View child = linearLayout.getChildAt(i);

        // 若不是動畫控件,則進(jìn)入判斷是否是ViewGroup,是的話遞歸其子view.不是的話則判斷下一個
        if (!(child instanceof SherlockAnimationCallBack)) {
            if (child instanceof ViewGroup) {
                Log.d(TAG, "parseViewGroup: 該View不是FrameLayout,是ViewGroup: " + child
                        .getClass().getName());
                parseViewGroup((ViewGroup) child, l, t, oldl, oldt, false,
                        child.getTop() + getTop);
            }
            continue;
        }
        //以下為執(zhí)行動畫邏輯沙庐;
        SherlockAnimationCallBack myCallBack = (SherlockAnimationCallBack) child;
        //獲取子View高度;
        int childHeight = child.getHeight();
        //子控件到父控件的距離佳吞;
        int childTop = child.getTop();
        if (!isRootLinearLayout) {
            childTop += getTop;
        }
        //滾動過程中拱雏,子View距離父控件頂部距離;
        int childAbsluteTop = childTop - t;
        //進(jìn)入了屏幕
        if (childAbsluteTop <= scrollViewHeight) {
            //當(dāng)前子控件顯示出來的高度容达;
            int childShowHeight = scrollViewHeight - childAbsluteTop - 100 ;
            float moveRadio = childShowHeight / (float) childHeight;//這里一定要轉(zhuǎn)化成float類型古涧;
            //執(zhí)行動畫;
            myCallBack.excuteanimation(getMiddleValue(moveRadio, 0, 1));
        } else {
            //沒在屏幕內(nèi),恢復(fù)數(shù)據(jù)花盐;
            myCallBack.resetViewanimation();
        }
    }
}

動畫執(zhí)行

有了事件的分發(fā)了羡滑。我們只需要在excuteanimation的回調(diào)中實現(xiàn)我們的動畫即可了。默認(rèn)的SherlockFrame已經(jīng)實現(xiàn)了一些了算芯。若要強(qiáng)大的自定義動畫效果柒昏,實現(xiàn)這個接口即可。

public interface SherlockAnimationCallBack {
    /**
     * 執(zhí)行自定義動畫方法熙揍;
     */
    void excuteanimation(float moveRadio);

    /**
     * 恢復(fù)初始狀態(tài)职祷;
     */
    void resetViewanimation();
}

優(yōu)化

有了上面的幾步,我們的動畫已經(jīng)能正常跑起來了届囚,不過由于從最下面一開始就執(zhí)行動畫有梆,有點感覺過快了。因此在分發(fā)位置移動事件的時候意系,在計算當(dāng)前子控件顯示出來的高度時減少了100泥耀, 這樣動畫就能延遲,看起來更加自然

      int childShowHeight = scrollViewHeight - childAbsluteTop - 100 ;

總結(jié)

還有很多不完善的地方蛔添,大家多多指教痰催。

歡迎star https://github.com/Jerey-Jobs/ScrollAnimationSherlock


本文作者:Anderson/Jerey_Jobs

博客地址 : http://jerey.cn/

簡書地址 : Anderson大碼渣

github地址 : https://github.com/Jerey-Jobs

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市迎瞧,隨后出現(xiàn)的幾起案子夸溶,更是在濱河造成了極大的恐慌,老刑警劉巖凶硅,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缝裁,死亡現(xiàn)場離奇詭異,居然都是意外死亡足绅,警方通過查閱死者的電腦和手機(jī)压语,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來编检,“玉大人胎食,你說我怎么就攤上這事≡识” “怎么了厕怜?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蕾总。 經(jīng)常有香客問我粥航,道長,這世上最難降的妖魔是什么生百? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任递雀,我火速辦了婚禮,結(jié)果婚禮上蚀浆,老公的妹妹穿的比我還像新娘缀程。我一直安慰自己搜吧,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布杨凑。 她就那樣靜靜地躺著滤奈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撩满。 梳的紋絲不亂的頭發(fā)上蜒程,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音伺帘,去河邊找鬼昭躺。 笑死,一個胖子當(dāng)著我的面吹牛伪嫁,可吹牛的內(nèi)容都是我干的领炫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼礼殊,長吁一口氣:“原來是場噩夢啊……” “哼驹吮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晶伦,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤碟狞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后婚陪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體族沃,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年泌参,在試婚紗的時候發(fā)現(xiàn)自己被綠了脆淹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡沽一,死狀恐怖盖溺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铣缠,我是刑警寧澤烘嘱,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蝗蛙,受9級特大地震影響蝇庭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜捡硅,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一哮内、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧壮韭,春花似錦北发、人聲如沸纹因。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辐怕。三九已至逼蒙,卻和暖如春从绘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背是牢。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工僵井, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人驳棱。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓批什,卻偏偏與公主長得像,于是被迫代替她去往敵國和親社搅。 傳聞我的和親對象是個殘疾皇子驻债,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

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