Android 過渡動畫原來可以這樣寫
關鍵類
- android.transition.TransitionManager
- android.transition.Transition 抽象類
- TransitionSet
- AutoTransition
- ChangeBounds
- Visibility 抽象類
- Fade
- Explode ( design 包中無適配)
- Slide ( design 包中無適配) </ul> </li>
- TextScale
- ChangeClipBounds ( design 包中無適配)
- ChangeImageTransform ( design 包中無適配)
- ChangeScroll ( design 包中無適配)
- ChangeTransform ( design 包中無適配) </ul> </li> </ul>
介紹
Visibility 抽象類的子類:Fade, Explode, Slide 動畫作用于 View 的 Visibility 屬性改變的時候。
適配
對應的適配包在design com.android.support:design:x.x.x 包中
compile 'com.android.support:design:25.0.0'
更好的適配方案:(使用的時候注意導包 com.transitionseverywhere.xxx )
compile "com.andkulikov:transitionseverywhere:1.6.9"
<!--more-->
上代碼
下面的例子 使用適配包 compile "com.andkulikov:transitionseverywhere:1.6.9" 測試系統 Android4.1
默認效果
布局關鍵代碼:
<LinearLayout android:id="@+id/ll_container_one" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical">
<Button android:id="@+id/btn_one" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Fad"/> <TextView android:id="@+id/tv_one" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="Transitions are awesome!" android:visibility="gone"/>
</LinearLayout></code></pre>
Activity 中代碼:
final LinearLayout transitionsContainer = (LinearLayout) findViewById(R.id.ll_container_one); final TextView text = (TextView) findViewById(R.id.tv_one); final Button button = (Button) findViewById(R.id.btn_one);
button.setOnClickListener(v -> { TransitionManager.beginDelayedTransition(transitionsContainer);
if (text.getVisibility() == View.VISIBLE) { text.setVisibility(View.GONE); } else { text.setVisibility(View.VISIBLE); }
});</code></pre>
最終效果:
默認效果
代碼解釋
TransitionManager 中的 beginDelayedTransition 方法:
public static void beginDelayedTransition(final ViewGroup sceneRoot){...} public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) {...}
- 方法一:效果如上面的默認效果
- 方法二:參數而用于定制動畫效果, 參數為 Transition 的子類,常用的有:Fade, ChangeBounds, Slide,
對用畫具體效果做定制:設置動畫時間,設置動畫加速度,設置動畫延時
transition.setDuration(300); transition.setInterpolator(new FastOutSlowInInterpolator()); transition.setStartDelay(200);
Fade 淡出淡入
布局如上 更改 Activity 中代碼:
final LinearLayout transitionsContainer = (LinearLayout) findViewById(R.id.ll_container_one); final TextView text = (TextView) findViewById(R.id.tv_one); final Button button = (Button) findViewById(R.id.btn_one);
button.setOnClickListener(v -> { Fade fade = new Fade(); TransitionManager.beginDelayedTransition(transitionsContainer, fade);
if (text.getVisibility() == View.VISIBLE) { text.setVisibility(View.GONE); } else { text.setVisibility(View.VISIBLE); }
});</code></pre>
最終效果
Fade效果
Slide 移動
布局如上 更改 Activity 中代碼:
final LinearLayout transitionsContainer = (LinearLayout) findViewById(R.id.ll_container_one); final TextView text = (TextView) findViewById(R.id.tv_one); final Button button = (Button) findViewById(R.id.btn_one);
button.setOnClickListener(v -> { // Slide slide = new Slide(); Slide slide = new Slide(Gravity.RIGHT); TransitionManager.beginDelayedTransition(transitionsContainer, slide);
if (text.getVisibility() == View.VISIBLE) { text.setVisibility(View.GONE); } else { text.setVisibility(View.VISIBLE); }
});</code></pre>
最終效果
Slide
Slide 構造方法
/**
- Constructor using the default {@link Gravity#BOTTOM}
- slide edge direction.
*/
public Slide() {
setSlideEdge(Gravity.BOTTOM);
}</code></pre>
設置滑出方向:
/**
- Constructor using the provided slide edge direction.
*/
public Slide(@GravityFlag int slideEdge) {
setSlideEdge(slideEdge);
}</code></pre>
Scale 縮放
布局如上 更改 Activity 中代碼:
final LinearLayout transitionsContainer = (LinearLayout) findViewById(R.id.ll_container_one); final TextView text = (TextView) findViewById(R.id.tv_one); final Button button = (Button) findViewById(R.id.btn_one);
button.setOnClickListener(v -> { // Scale scale = new Scale(); Scale scale = new Scale(0.7f); TransitionManager.beginDelayedTransition(transitionsContainer, scale);
if (text.getVisibility() == View.VISIBLE) { text.setVisibility(View.GONE); } else { text.setVisibility(View.VISIBLE); }
});</code></pre>
最終效果
Scale
Scale 構造方法
public Scale() { }
/**
- @param disappearedScale Value of scale on start of appearing or in finish of disappearing.
- Default value is 0. Can be useful for mixing some Visibility
- transitions, for example Scale and Fade
*/
public Scale(float disappearedScale) {
setDisappearedScale(disappearedScale);
}</code></pre>
參數設置了縮放起始值或者最終值。
TransitionSet 動畫集合
final LinearLayout transitionsContainer = (LinearLayout) findViewById(R.id.ll_container_one); final TextView text = (TextView) findViewById(R.id.tv_one); final Button button = (Button) findViewById(R.id.btn_one);
button.setOnClickListener(v -> {
TransitionSet set = new TransitionSet() .addTransition(new Scale(0.7f)) .addTransition(new Fade()) .setInterpolator(visible ? new LinearOutSlowInInterpolator() : new FastOutLinearInInterpolator()); TransitionManager.beginDelayedTransition(transitionsContainer, set); if (text.getVisibility() == View.VISIBLE) { text.setVisibility(View.GONE); } else { text.setVisibility(View.VISIBLE); }
});</code></pre>
最終效果
TransitionSet
Recolor 顏色漸變
空間的背景色或者文字顏色改變的動畫
布局
<LinearLayout android:id="@+id/ll_container_recolor" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" android:visibility="visible">
<Button android:id="@+id/btn_recolor" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Recolor"/> <Button android:id="@+id/btn_normal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="Normal" android:visibility="visible"/>
</LinearLayout></code></pre>
Activity 代碼
final ViewGroup transitionsContainerRecolor = (ViewGroup) findViewById(R.id.ll_container_recolor); final Button btnRecolor = (Button) findViewById(R.id.btn_recolor); final Button btnNormal = (Button) findViewById(R.id.btn_normal);
int green = getResources().getColor(R.color.green); int white = getResources().getColor(R.color.white); int grey = getResources().getColor(R.color.grey);
btnRecolor.setOnClickListener(v -> { TransitionManager.beginDelayedTransition(transitionsContainerRecolor, new Recolor()); // btnRecolor.setBackgroundColor(visible ? green : white); // 無動畫效果 btnRecolor.setTextColor(visible ? white : grey); // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { // btnRecolor.setBackground(new ColorDrawable(visible ? green : white)); // } else { btnRecolor.setBackgroundDrawable(new ColorDrawable(visible ? green : white)); // } visible = !visible; });
btnNormal.setOnClickListener(v -> { btnNormal.setBackgroundColor(visible ? green : white); btnNormal.setTextColor(visible ? white : green); visible = !visible; });</code></pre>
注意: btnRecolor.setBackgroundColor(visible ? green : white); // 無動畫效果 通過 setBackgroundColor 背景色時沒有動畫效果,可以使用 setBackground , setBackgroundDrawable
最終效果
ReColor
Rotate 旋轉
布局
<LinearLayout android:id="@+id/ll_container_rotate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" android:visibility="visible">
<Button android:id="@+id/btn_rotate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Rotate"/> <ImageView android:id="@+id/iv_rotate" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginTop="10dp" android:src="@drawable/ic_clear_black_24dp" android:visibility="visible"/>
</LinearLayout></code></pre>
Activity 代碼
final ViewGroup transitionsContainerRotate = (ViewGroup) findViewById(R.id.ll_container_rotate); final Button btnRotate = (Button) findViewById(R.id.btn_rotate); ImageView ivRotate = (ImageView) findViewById(R.id.iv_rotate);
btnRotate.setOnClickListener(v -> { TransitionManager.beginDelayedTransition(transitionsContainerRotate, new Rotate()); ivRotate.setRotation(isRotated ? 0 : 135); isRotated = !isRotated; });</code></pre>
最終效果
Rotate
ChangeText 文字改變時動畫
布局
<LinearLayout android:id="@+id/ll_container_change_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" android:visibility="visible">
<Button android:id="@+id/btn_change_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ChangeText"/> <TextView android:id="@+id/tv_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="Transitions are awesome!" android:visibility="visible"/>
</LinearLayout></code></pre>
Activity 代碼
final LinearLayout transitionsContainerChangeText = (LinearLayout) findViewById(R.id.ll_container_change_text); final TextView tvText = (TextView) findViewById(R.id.tv_text); final Button btnChangeText = (Button) findViewById(R.id.btn_change_text); String secText = " Second Text"; String firstText = "First Text";
btnChangeText.setOnClickListener(v -> { TransitionManager.beginDelayedTransition(transitionsContainerChangeText, new ChangeText().setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN));
tvText.setText(isFirstText ? secText : firstText); isFirstText = !isFirstText;
});</code></pre>
最終效果
ChangeText
TransitionName 制作 打亂動畫
使用場景:
-
需要動態生成 ViewGroup 的子 View , 并且子 View 內容需要更新時
-
需要制作隨機時
布局
注意:這里把 Button 移到了 LinearLayout 的外面,原因是一會創建子 View 的時候會先刪除所有子 View(Button 也會被刪除)。
<Button android:id="@+id/btn_transition_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="打亂"/>
<LinearLayout android:id="@+id/ll_container_transition_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" android:visibility="visible"></code></pre>
Activity 代碼
// TransitionName 做打亂動畫 final LinearLayout transitionsContainerTransitionName = (LinearLayout) findViewById(R.id.ll_container_transition_name); final Button btnTransitionName = (Button) findViewById(R.id.btn_transition_name);
LayoutInflater inflater = LayoutInflater.from(this); ArrayList<String> titles = new ArrayList<>(); for (int i = 0; i < 4; i++) { titles.add(String.format(Locale.CHINA, "Item %d", i)); } createViews(inflater, transitionsContainerTransitionName, titles);
btnTransitionName.setOnClickListener(v -> { TransitionManager.beginDelayedTransition(transitionsContainerTransitionName, new ChangeBounds()); Collections.shuffle(titles); createViews(inflater, transitionsContainerTransitionName, titles); });
// 獨立的方法 // In createViews we should provide transition name for every view. private static void createViews(LayoutInflater inflater, ViewGroup layout, List<String> titles) { layout.removeAllViews(); for (String title : titles) { TextView textView = (TextView) inflater.inflate(R.layout.text_view, layout, false); textView.setText(title); TransitionManager.setTransitionName(textView, title); layout.addView(textView); } }</code></pre>
最終效果
TransitionName
Explode and Propagation 爆炸和傳播
爆炸效果,和移動過渡動畫比較相似,不過子 View 的移動方向是由其所在的位置決定的。子 View 的移動方向需要通過計算得到(通過 setEpicenterCallback 方法)
關鍵代碼
這個例子使用 RecyclerView 和 GridLayoutManager 做基本布局,點擊里面的 item 讓其消失。
public void onClick(View clickedView) { // save rect of view in screen coordinates final Rect viewRect = new Rect(); clickedView.getGlobalVisibleRect(viewRect);
// create Explode transition with epicenter Transition explode = new Explode() .setEpicenterCallback(new Transition.EpicenterCallback() { @Override public Rect onGetEpicenter(Transition transition) { return viewRect; } }); explode.setDuration(1000); TransitionManager.beginDelayedTransition(recyclerView, explode); // remove all views from Recycler View recyclerView.setAdapter(null);
}</code></pre>
最終效果
Exlode
ChangeImageTransform
Path (Curved) motion 路徑過渡動畫
所有的過渡動畫都需要兩個值:起始值和結束值
比如:通過 ChangeBounds 來改變 view 的位置,通過 setPathMotion 來提供路徑
布局代碼
<FrameLayout android:id="@+id/fl_container_path_motion" android:layout_width="match_parent" android:layout_height="match_parent">
<Button android:id="@+id/btn_path_motion" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Path Motion"/>
</FrameLayout></code></pre>
Activity 代碼
// path motion 路徑過渡動畫 final FrameLayout transitionsContainerPathMotion = (FrameLayout) findViewById(R.id.fl_container_path_motion); Button btnPathMotion = (Button) findViewById(R.id.btn_path_motion);
btnPathMotion.setOnClickListener(v -> {
TransitionManager.beginDelayedTransition(transitionsContainerPathMotion, new ChangeBounds().setPathMotion(new ArcMotion()).setDuration(500)); FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) btnPathMotion.getLayoutParams(); params.gravity = isReturnAnimation ? (Gravity.LEFT | Gravity.TOP) : (Gravity.BOTTOM | Gravity.RIGHT); btnPathMotion.setLayoutParams(params); isReturnAnimation = !isReturnAnimation;
});</code></pre>
這里通過 LayoutParams 來控制 Button 在其父控件中的位置。
最終效果
Path Motion
Targets 設置動畫的目標對象
小總結
定義動畫:
TransitionManager.beginDelayedTransition( viewGroup, transition);
默認情況下,這里的 transition 動畫會作用于 viewGroup 中的所有子 View
當我們需要在一個 ViewGroup 中定義多個動畫,作用于不同的子 View 該如何做?
比如 讓一個 TextView 移動, 另一個 TextView 淡出
布局
<LinearLayout android:id="@+id/ll_container_target" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" android:visibility="visible">
<Button android:id="@+id/btn_target" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Target"/> <TextView android:id="@+id/tv_target_fade" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="Transitions are awesome fade!" android:visibility="visible"/> <TextView android:id="@+id/tv_target_slide" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="Transitions are awesome slide!" android:visibility="visible"/> </LinearLayout></code></pre>
Activity 代碼
// Targets 設置動畫的目標對象 final LinearLayout transitionsContainerTarget = (LinearLayout) findViewById(R.id.ll_container_target); final Button btnTarget = (Button) findViewById(R.id.btn_target); final TextView tvFade = (TextView) findViewById(R.id.tv_target_fade); final TextView tvSlide = (TextView) findViewById(R.id.tv_target_slide);
btnTarget.setOnClickListener(v -> { Slide slide = new Slide(Gravity.RIGHT); slide.excludeTarget(tvFade, true);
Fade fade = new Fade(); fade.excludeTarget(tvSlide, true); TransitionSet transitionSet = new TransitionSet() .addTransition(slide) .addTransition(fade); TransitionManager.beginDelayedTransition(transitionsContainerTarget, transitionSet); if (tvFade.getVisibility() == View.VISIBLE) { tvFade.setVisibility(View.GONE); tvSlide.setVisibility(View.GONE); } else { tvFade.setVisibility(View.VISIBLE); tvSlide.setVisibility(View.VISIBLE); }
});</code></pre>
最終效果
Target處理
transition 其他有關 target 方法
Methods to add target:
- addTarget(View target) — view itself
- addTarget(int targetViewId) — id of view
- addTarget(String targetName) — do you remember about method TransitionManager.setTransitionName?
- addTarget(Class targetType) — for example android.widget.TextView.class
To remove target:
- removeTarget(View target)
- removeTarget(int targetId)
- removeTarget(String targetName)
- removeTarget(Class target)
To exclude some views:
- excludeTarget(View target, boolean exclude)
- excludeTarget(int targetId, boolean exclude)
- excludeTarget(Class type, boolean exclude)
- excludeTarget(Class type, boolean exclude)
And for excluding all children of some ViewGroup:
- excludeChildren(View target, boolean exclude)
- excludeChildren(int targetId, boolean exclude)
- excludeChildren(Class type, boolean exclude)
使用 xml 創建 Transition
<?xml version="1.0" encoding="utf-8"?> <transitionSet xmlns:app="http://schemas.android.com/apk/res-auto" app:transitionOrdering="together" app:duration="400"> <changeBounds/> <changeImageTransform/> <fade app:fadingMode="fade_in" app:startDelay="200"> <targets> <target app:targetId="@id/transition_title"/> </targets> </fade> </transitionSet>
使用
TransitionInflater.from(getContext()).inflateTransition(R.anim.my_the_best_transition);
Activity and Fragment transitions
Custom Transitions
參考:
來自:http://www.jianshu.com/p/89a9cdde42ae
- TransitionSet