アニメーションを使ってビューを表示または非表示にする

Compose を試す
Jetpack Compose は、Android に推奨される UI ツールキットです。Compose でアニメーションを使用する方法を学習します。
<ph type="x-smartling-placeholder"></ph> クロスフェード →

アプリの使用中は、新しい情報が画面に表示され、古いバージョンも 削除されます。画面に表示される内容をすぐに変更するには 突然表示される新しいコンテンツを見逃してしまう可能性があります。アニメーションが遅い ユーザーの注意を引き、更新が確実に 明らかです。

ビューの表示と非表示を切り替えるには、次の 3 つの一般的なアニメーションを使用できます。 アニメーション、クロスフェード アニメーション、カードフリップ アニメーションなど、さまざまな用途に活用できます。

クロスフェード アニメーションを作成する

クロスフェード アニメーション(ディゾルブとも呼ばれます)は徐々にフェードアウトします。 1 つの View または 同時に ViewGroup にします。 フェードインしますこのアニメーションは、動画内で アプリ内のコンテンツやビューの切り替え。ここに示されているクロスフェード アニメーションでは、 ViewPropertyAnimator このアプリは Android 3.1(API レベル 12)以降で利用できます。

進行状況インジケーターからテキスト コンテンツへのクロスフェードの例を次に示します。

<ph type="x-smartling-placeholder">
図 1.クロスフェード アニメーション

ビューを作成する

クロスフェードする 2 つのビューを作成します。次の例では、Cloud Storage バケットを 進行状況インジケーターとスクロール可能なテキストビュー:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   
android:layout_width="match_parent"
   
android:layout_height="match_parent">

   
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
       
android:id="@+id/content"
       
android:layout_width="match_parent"
       
android:layout_height="match_parent">

       
<TextView style="?android:textAppearanceMedium"
           
android:lineSpacingMultiplier="1.2"
           
android:layout_width="match_parent"
           
android:layout_height="wrap_content"
           
android:text="@string/lorem_ipsum"
           
android:padding="16dp" />

   
</ScrollView>

   
<ProgressBar android:id="@+id/loading_spinner"
       
style="?android:progressBarStyleLarge"
       
android:layout_width="wrap_content"
       
android:layout_height="wrap_content"
       
android:layout_gravity="center" />

</FrameLayout>

クロスフェード アニメーションをセットアップする

クロスフェード アニメーションを設定する手順は次のとおりです。

  1. クロスフェードするビューのメンバー変数を作成します。必要な これらの参照は、後でアニメーション中にビューを変更するときに使用できます。
  2. フェードインするビューの公開設定を GONE。これにより 使用できなくなり、レイアウト計算から除外されるため、処理速度が向上します。 処理中
  3. config_shortAnimTime をキャッシュに保存する メンバー変数に格納されています。このプロパティでは標準の「short」 設定します。この時間は、繊細なアニメーションや 頻繁に使用するアニメーションが含まれます。 config_longAnimTime および config_mediumAnimTime も使用できます。

この例は、前のコード スニペットのレイアウトを アクティビティ コンテンツ ビュー:

KotlinJava
class CrossfadeActivity : Activity() {

   
private lateinit var contentView: View
   
private lateinit var loadingView: View
   
private var shortAnimationDuration: Int = 0
   
...
   
override fun onCreate(savedInstanceState: Bundle?) {
       
super.onCreate(savedInstanceState)
        setContentView
(R.layout.activity_crossfade)

        contentView
= findViewById(R.id.content)
        loadingView
= findViewById(R.id.loading_spinner)

       
// Initially hide the content view.
        contentView
.visibility = View.GONE

       
// Retrieve and cache the system's default "short" animation time.
        shortAnimationDuration
= resources.getInteger(android.R.integer.config_shortAnimTime)
   
}
   
...
}

public class CrossfadeActivity extends Activity {

   
private View contentView;
   
private View loadingView;
   
private int shortAnimationDuration;
   
...
   
@Override
   
protected void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);
        setContentView
(R.layout.activity_crossfade);

        contentView
= findViewById(R.id.content);
        loadingView
= findViewById(R.id.loading_spinner);

       
// Initially hide the content view.
        contentView
.setVisibility(View.GONE);

       
// Retrieve and cache the system's default "short" animation time.
        shortAnimationDuration
= getResources().getInteger(
                android
.R.integer.config_shortAnimTime);
   
}
   
...
}

ビューをクロスフェードする

ビューが正しくセットアップされたら、次の手順でクロスフェードします。

  1. フェードインするビューのアルファ値を 0 に設定し、表示 初期値から VISIBLE に変更 GONE の設定。これにより、ビューは表示されますが、透明になります。
  2. フェードインするビューのアルファ値を 0 から 1 にアニメーション化します。対象: アルファ値を 1 から 0 にアニメーション化します。
  3. 使用 onAnimationEnd()Animator.AnimatorListener, フェードアウトするビューの表示を GONE に設定します。しかし、 アルファ値が 0 の場合、ビューの表示設定を GONE に設定するとビューが表示されなくなります 使用できなくなり、レイアウト計算から除外されるため、処理速度が向上します。 ありません

以下に、その方法の例を示します。

KotlinJava
class CrossfadeActivity : Activity() {

   
private lateinit var contentView: View
   
private lateinit var loadingView: View
   
private var shortAnimationDuration: Int = 0
   
...
   
private fun crossfade() {
        contentView
.apply {
           
// Set the content view to 0% opacity but visible, so that it is
           
// visible but fully transparent during the animation.
            alpha
= 0f
            visibility
= View.VISIBLE

           
// Animate the content view to 100% opacity and clear any animation
           
// listener set on the view.
            animate
()
                   
.alpha(1f)
                   
.setDuration(shortAnimationDuration.toLong())
                   
.setListener(null)
       
}
       
// Animate the loading view to 0% opacity. After the animation ends,
       
// set its visibility to GONE as an optimization step so it doesn't
       
// participate in layout passes.
        loadingView
.animate()
               
.alpha(0f)
               
.setDuration(shortAnimationDuration.toLong())
               
.setListener(object : AnimatorListenerAdapter() {
                   
override fun onAnimationEnd(animation: Animator) {
                        loadingView
.visibility = View.GONE
                   
}
               
})
   
}
}

public class CrossfadeActivity extends Activity {

   
private View contentView;
   
private View loadingView;
   
private int shortAnimationDuration;
   
...
   
private void crossfade() {

       
// Set the content view to 0% opacity but visible, so that it is
       
// visible but fully transparent during the animation.
        contentView
.setAlpha(0f);
        contentView
.setVisibility(View.VISIBLE);

       
// Animate the content view to 100% opacity and clear any animation
       
// listener set on the view.
        contentView
.animate()
               
.alpha(1f)
               
.setDuration(shortAnimationDuration)
               
.setListener(null);

       
// Animate the loading view to 0% opacity. After the animation ends,
       
// set its visibility to GONE as an optimization step so it doesn't
       
// participate in layout passes.
        loadingView
.animate()
               
.alpha(0f)
               
.setDuration(shortAnimationDuration)
               
.setListener(new AnimatorListenerAdapter() {
                   
@Override
                   
public void onAnimationEnd(Animator animation) {
                        loadingView
.setVisibility(View.GONE);
                   
}
               
});
   
}
}

カードフリップ アニメーションを作成する

カードフリップでは、エミュレートするアニメーションが表示され、コンテンツの表示が切り替わります 表示されますここに示されているカードフリップ アニメーションでは、 FragmentTransaction

カードフリップは次のようになります。

<ph type="x-smartling-placeholder">
図 2.カード反転のアニメーション。

アニメーター オブジェクトを作成する

カードフリップ アニメーションを作成するには、4 つのアニメーターが必要です。2 人のアニメーターは カードの前面が左側に移動するとき 出入りします他の 2 つのアニメーターは カードの裏面が アニメーションが右から出たり右に動かされたりします

card_flip_left_in.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
   
<!-- Before rotating, immediately set the alpha to 0. -->
   
<objectAnimator
       
android:valueFrom="1.0"
       
android:valueTo="0.0"
       
android:propertyName="alpha"
       
android:duration="0" />

   
<!-- Rotate. -->
   
<objectAnimator
       
android:valueFrom="-180"
       
android:valueTo="0"
       
android:propertyName="rotationY"
       
android:interpolator="@android:interpolator/accelerate_decelerate"
       
android:duration="@integer/card_flip_time_full" />

   
<!-- Halfway through the rotation, set the alpha to 1. See startOffset. -->
   
<objectAnimator
       
android:valueFrom="0.0"
       
android:valueTo="1.0"
       
android:propertyName="alpha"
       
android:startOffset="@integer/card_flip_time_half"
       
android:duration="1" />
</set>

card_flip_left_out.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
   
<!-- Rotate. -->
   
<objectAnimator
       
android:valueFrom="0"
       
android:valueTo="180"
       
android:propertyName="rotationY"
       
android:interpolator="@android:interpolator/accelerate_decelerate"
       
android:duration="@integer/card_flip_time_full" />

   
<!-- Halfway through the rotation, set the alpha to 0. See startOffset. -->
   
<objectAnimator
       
android:valueFrom="1.0"
       
android:valueTo="0.0"
       
android:propertyName="alpha"
       
android:startOffset="@integer/card_flip_time_half"
       
android:duration="1" />
</set>

card_flip_right_in.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
   
<!-- Before rotating, immediately set the alpha to 0. -->
   
<objectAnimator
       
android:valueFrom="1.0"
       
android:valueTo="0.0"
       
android:propertyName="alpha"
       
android:duration="0" />

   
<!-- Rotate. -->
   
<objectAnimator
       
android:valueFrom="180"
       
android:valueTo="0"
       
android:propertyName="rotationY"
       
android:interpolator="@android:interpolator/accelerate_decelerate"
       
android:duration="@integer/card_flip_time_full" />

   
<!-- Halfway through the rotation, set the alpha to 1. See startOffset. -->
   
<objectAnimator
       
android:valueFrom="0.0"
       
android:valueTo="1.0"
       
android:propertyName="alpha"
       
android:startOffset="@integer/card_flip_time_half"
       
android:duration="1" />
</set>

card_flip_right_out.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
   
<!-- Rotate. -->
   
<objectAnimator
       
android:valueFrom="0"
       
android:valueTo="-180"
       
android:propertyName="rotationY"
       
android:interpolator="@android:interpolator/accelerate_decelerate"
       
android:duration="@integer/card_flip_time_full" />

   
<!-- Halfway through the rotation, set the alpha to 0. See startOffset. -->
   
<objectAnimator
       
android:valueFrom="1.0"
       
android:valueTo="0.0"
       
android:propertyName="alpha"
       
android:startOffset="@integer/card_flip_time_half"
       
android:duration="1" />
</set>

ビューを作成する

カードの両面は個別のレイアウトで、 たとえば、2 つのテキストビュー、2 つの画像、またはビューの組み合わせを反転させることができます。 あります。後でアニメーション化するフラグメントで 2 つのレイアウトを使用します。「 次のレイアウトでは、テキストを表示するカードの片側が作成されます。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   
android:layout_width="match_parent"
   
android:layout_height="match_parent"
   
android:orientation="vertical"
   
android:background="#a6c"
   
android:padding="16dp"
   
android:gravity="bottom">

   
<TextView android:id="@android:id/text1"
       
style="?android:textAppearanceLarge"
       
android:textStyle="bold"
       
android:textColor="#fff"
       
android:layout_width="match_parent"
       
android:layout_height="wrap_content"
       
android:text="@string/card_back_title" />

   
<TextView style="?android:textAppearanceSmall"
       
android:textAllCaps="true"
       
android:textColor="#80ffffff"
       
android:textStyle="bold"
       
android:lineSpacingMultiplier="1.2"
       
android:layout_width="match_parent"
       
android:layout_height="wrap_content"
       
android:text="@string/card_back_description" />

</LinearLayout>

次のレイアウトでは、カードの反対側が作成されます。 ImageView:

<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
   
android:layout_width="match_parent"
   
android:layout_height="match_parent"
   
android:src="@drawable/image1"
   
android:scaleType="centerCrop"
   
android:contentDescription="@string/description_image_1" />

フラグメントを作成する

カードの前面用と背面用のフラグメント クラスをそれぞれ作成します。フラグメント内 レッスンで作成したレイアウトを onCreateView() メソッドを呼び出します。次に、このフラグメントのインスタンスを親アクティビティ内に作成します。 配置したい場所を選択します

親アクティビティ内にネストされたフラグメント クラスの例 次の 3 点です。

KotlinJava
class CardFlipActivity : FragmentActivity() {
   
...
   
/**

                    *   A fragment representing the front of the card.
     */

   
class CardFrontFragment : Fragment() {

   
override fun onCreateView(
                inflater
: LayoutInflater,
                container
: ViewGroup?,
                savedInstanceState
: Bundle?
   
): View = inflater.inflate(R.layout.fragment_card_front, container, false)
   
}

   
/**
    *   A fragment representing the back of the card.
    */

   
class CardBackFragment : Fragment() {

   
override fun onCreateView(
                inflater
: LayoutInflater,
                container
: ViewGroup?,
                savedInstanceState
: Bundle?
   
): View = inflater.inflate(R.layout.fragment_card_back, container, false)
   
}
}

public class CardFlipActivity extends FragmentActivity {
   
...
   
/**
    *   A fragment representing the front of the card.
    */

   
public class CardFrontFragment extends Fragment {
   
@Override
   
public View onCreateView(LayoutInflater inflater, ViewGroup container,
               
Bundle savedInstanceState) {
           
return inflater.inflate(R.layout.fragment_card_front, container, false);
   
}
   
}

   
/**
    *   A fragment representing the back of the card.
    */

   
public class CardBackFragment extends Fragment {
   
@Override
   
public View onCreateView(LayoutInflater inflater, ViewGroup container,
               
Bundle savedInstanceState) {
           
return inflater.inflate(R.layout.fragment_card_back, container, false);
   
}
   
}
}

カードフリップをアニメーション化する

親アクティビティ内にフラグメントを表示します。そのためには、まずレイアウトを作成します。 おすすめします次の例では、Cloud Storage バケットを 追加できる FrameLayout フラグメントを実行時に追加:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   
android:id="@+id/container"
   
android:layout_width="match_parent"
   
android:layout_height="match_parent" />

アクティビティ コードで、作成するレイアウトにコンテンツ ビューを設定します。 アクティビティの作成時にデフォルトのフラグメントを表示することをおすすめします。「 次のアクティビティ例は、カードの前面を default:

KotlinJava
class CardFlipActivity : FragmentActivity() {

   
override fun onCreate(savedInstanceState: Bundle?) {
       
super.onCreate(savedInstanceState)
        setContentView
(R.layout.activity_activity_card_flip)
       
if (savedInstanceState == null) {
            supportFragmentManager
.beginTransaction()
                   
.add(R.id.container, CardFrontFragment())
                   
.commit()
       
}
   
}
   
...
}

public class CardFlipActivity extends FragmentActivity {

   
@Override
   
protected void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);
        setContentView
(R.layout.activity_activity_card_flip);

       
if (savedInstanceState == null) {
            getSupportFragmentManager
()
                   
.beginTransaction()
                   
.add(R.id.container, new CardFrontFragment())
                   
.commit();
       
}
   
}
   
...
}

カードの裏面が見えるようにして 適切なタイミングでアニメーションを切り替えることができます。反対側を表示するメソッドを作成して、 このカードで次の処理を行います。

  • フラグメントの遷移用に作成したカスタム アニメーションを設定します。
  • 表示されたフラグメントを新しいフラグメントに置き換え、このイベントをアニメーション化します 試しに作成したカスタムアニメーションと結合します
  • 以前表示されていたフラグメントをフラグメントのバックスタックに追加します。これにより、 ユーザーが [戻る] ボタンをタップすると、カードが反転します。
KotlinJava
class CardFlipActivity : FragmentActivity() {
   
...
   
private fun flipCard() {
       
if (showingBack) {
            supportFragmentManager
.popBackStack()
           
return
       
}

       
// Flip to the back.

        showingBack
= true

       
// Create and commit a new fragment transaction that adds the fragment
       
// for the back of the card, uses custom animations, and is part of the
       
// fragment manager's back stack.

        supportFragmentManager
.beginTransaction()

               
// Replace the default fragment animations with animator
               
// resources representing rotations when switching to the back
               
// of the card, as well as animator resources representing
               
// rotations when flipping back to the front, such as when the
               
// system Back button is tapped.
               
.setCustomAnimations(
                        R
.animator.card_flip_right_in,
                        R
.animator.card_flip_right_out,
                        R
.animator.card_flip_left_in,
                        R
.animator.card_flip_left_out
               
)

               
// Replace any fragments in the container view with a fragment
               
// representing the next page, indicated by the just-incremented
               
// currentPage variable.
               
.replace(R.id.container, CardBackFragment())

               
// Add this transaction to the back stack, letting users press
               
// the Back button to get to the front of the card.
               
.addToBackStack(null)

               
// Commit the transaction.
               
.commit()
   
}
}

public class CardFlipActivity extends FragmentActivity {
   
...
   
private void flipCard() {
       
if (showingBack) {
            getSupportFragmentManager
().popBackStack();
           
return;
       
}

       
// Flip to the back.

        showingBack
= true;

       
// Create and commit a new fragment transaction that adds the fragment
       
// for the back of the card, uses custom animations, and is part of the
       
// fragment manager's back stack.

        getSupportFragmentManager
()
               
.beginTransaction()

               
// Replace the default fragment animations with animator
               
// resources representing rotations when switching to the back
               
// of the card, as well as animator resources representing
               
// rotations when flipping back to the front, such as when the
               
// system Back button is pressed.
               
.setCustomAnimations(
                        R
.animator.card_flip_right_in,
                        R
.animator.card_flip_right_out,
                        R
.animator.card_flip_left_in,
                        R
.animator.card_flip_left_out)

               
// Replace any fragments in the container view with a fragment
               
// representing the next page, indicated by the just-incremented
               
// currentPage variable.
               
.replace(R.id.container, new CardBackFragment())

               
// Add this transaction to the back stack, letting users press
               
// Back to get to the front of the card.
               
.addToBackStack(null)

               
// Commit the transaction.
               
.commit();
   
}
}

円形出現エフェクト アニメーションを作成する

出現アニメーションは、グループを表示または非表示にする際に、ユーザーに視覚的な連続性を提供します 説明しました「 ViewAnimationUtils.createCircularReveal() メソッドを使用すると、クリップ円をアニメーション化して、ビューの表示と非表示を切り替えることができます。この アニメーションは ViewAnimationUtils クラス、 このアプリは Android 5.0(API レベル 21)以降で利用できます。

アニメーション前には見えていなかったビューを、アニメーション後に表示する方法の例を以下に示します。

KotlinJava
// A previously invisible view.
val myView: View = findViewById(R.id.my_view)

// Check whether the runtime version is at least Android 5.0.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
   
// Get the center for the clipping circle.
   
val cx = myView.width / 2
   
val cy = myView.height / 2

   
// Get the final radius for the clipping circle.
   
val finalRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat()

   
// Create the animator for this view. The start radius is 0.
   
val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius)
   
// Make the view visible and start the animation.
    myView
.visibility = View.VISIBLE
    anim
.start()
} else {
   
// Set the view to invisible without a circular reveal animation below
   
// Android 5.0.
    myView
.visibility = View.INVISIBLE
}

// A previously invisible view.
View myView = findViewById(R.id.my_view);

// Check whether the runtime version is at least Android 5.0.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
   
// Get the center for the clipping circle.
   
int cx = myView.getWidth() / 2;
   
int cy = myView.getHeight() / 2;

   
// Get the final radius for the clipping circle.
   
float finalRadius = (float) Math.hypot(cx, cy);

   
// Create the animator for this view. The start radius is 0.
   
Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius);

   
// Make the view visible and start the animation.
    myView
.setVisibility(View.VISIBLE);
    anim
.start();
} else {
   
// Set the view to invisible without a circular reveal animation below
   
// Android 5.0.
    myView
.setVisibility(View.INVISIBLE);
}

ViewAnimationUtils.createCircularReveal() アニメーションは 5 つのパラメータを取ります。 最初のパラメータは、画面上で非表示にする、または表示するビューです。「 次の 2 つのパラメータは、クリップの中心の X 座標と Y 座標です。 クリックします。通常はビューの中心ですが、 ユーザーがタップした位置を指定し、選択した位置からアニメーションを開始します。「 4 つ目のパラメータは、切り取り円の始点半径です。

上記の例では、最初の半径が 0 に設定されているため、 円で囲まれて非表示になります。最後のパラメータは最後の半径です。 指定します。ビューを表示するときは、最終的な半径が アニメーション終了前にビューを完全に表示できます。

以前に表示されていたビューを非表示にするには、次の手順を行います。

KotlinJava
// A previously visible view.
val myView: View = findViewById(R.id.my_view)

// Check whether the runtime version is at least Android 5.0.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
   
// Get the center for the clipping circle.
   
val cx = myView.width / 2
   
val cy = myView.height / 2

   
// Get the initial radius for the clipping circle.
   
val initialRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat()

   
// Create the animation. The final radius is 0.
   
val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f)

   
// Make the view invisible when the animation is done.
    anim
.addListener(object : AnimatorListenerAdapter() {

       
override fun onAnimationEnd(animation: Animator) {
           
super.onAnimationEnd(animation)
            myView
.visibility = View.INVISIBLE
       
}
   
})

   
// Start the animation.
    anim
.start()
} else {
   
// Set the view to visible without a circular reveal animation below
   
// Android 5.0.
    myView
.visibility = View.VISIBLE
}

// A previously visible view.
final View myView = findViewById(R.id.my_view);

// Check whether the runtime version is at least Android 5.0.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
   
// Get the center for the clipping circle.
   
int cx = myView.getWidth() / 2;
   
int cy = myView.getHeight() / 2;

   
// Get the initial radius for the clipping circle.
   
float initialRadius = (float) Math.hypot(cx, cy);

   
// Create the animation. The final radius is 0.
   
Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f);

   
// Make the view invisible when the animation is done.
    anim
.addListener(new AnimatorListenerAdapter() {
       
@Override
       
public void onAnimationEnd(Animator animation) {
           
super.onAnimationEnd(animation);
            myView
.setVisibility(View.INVISIBLE);
       
}
   
});

   
// Start the animation.
    anim
.start();
} else {
   
// Set the view to visible without a circular reveal animation below Android
   
// 5.0.
    myView
.setVisibility(View.VISIBLE);
}

この場合、切り取り円の初期半径は、 アニメーションの開始前にビューが表示されるようにします。ファイナル radius がゼロに設定されているため、アニメーションが終了するとビューは非表示になります。 アニメーションにリスナーを追加して、ビューの表示設定を INVISIBLE: アニメーションが 表示されます。

参考情報