Android 過渡動畫原來可以這樣寫

DeaCPIQ 8年前發布 | 31K 次閱讀 安卓開發 Android開發 移動開發

關鍵類

  • android.transition.TransitionManager
  • android.transition.Transition 抽象類
    • TransitionSet
      • AutoTransition
      </li>
    • 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) {...}
        1. 方法一:效果如上面的默認效果
        2. 方法二:參數而用于定制動畫效果, 參數為 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 制作 打亂動畫

        使用場景:

        1. 需要動態生成 ViewGroup 的子 View , 并且子 View 內容需要更新時

        2. 需要制作隨機時

        布局

        注意:這里把 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

         

 本文由用戶 DeaCPIQ 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!