一個用來打造上述引導(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
接下來就是使用SherlockLinearLayout
與SherlockRelativeLayout
進(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ù)位置改變屬性坪郭。
如何做到
我們需要做的事情有
- 如何確定某個View需要進(jìn)行動畫變換
- 確定后如何賦予動畫屬性
- 如何分發(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