앱이 사용되는 동안 새로운 정보가 화면에 표시되고 기존 정보가 표시됩니다. 삭제됩니다. 화면에 즉시 표시되는 항목을 변경할 수도 있습니다. 사용자는 갑자기 나타나는 새 콘텐츠를 놓칠 수 있습니다. 애니메이션이 느림 변경사항을 아래로 내리고 움직임으로 사용자의 시선을 끌어 업데이트가 더 분명해집니다.
뷰를 표시하거나 숨기는 데 사용할 수 있는 일반적인 애니메이션에는 세 가지가 있습니다. 애니메이션, 크로스페이드 애니메이션, 카드플립 애니메이션입니다.
크로스페이드 애니메이션 만들기
디졸브라고도 하는 크로스페이드 애니메이션은 점진적으로 페이드 아웃됩니다.
View
1개 또는
동시에 ViewGroup
또 하나는 페이드인됩니다. 이 애니메이션은
전환할 수 있습니다 여기에 표시된 크로스페이드 애니메이션은
ViewPropertyAnimator
님,
(Android 3.1(API 수준 12) 이상에서 사용 가능)
다음은 진행률 표시기에서 텍스트 콘텐츠로 바뀌는 크로스페이드의 예입니다.
뷰 만들기
크로스페이드할 뷰를 두 개 만듭니다. 다음 예에서는 진행률 표시기와 스크롤 가능한 텍스트 뷰를 제공합니다.
<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>
크로스페이드 애니메이션 설정
크로스페이드 애니메이션을 설정하려면 다음 단계를 따르세요.
- 크로스페이드할 뷰의 멤버 변수를 만들어야 합니다. 요구사항 나중에 애니메이션 중에 뷰를 수정할 때 이러한 참조를 조정할 수 있습니다.
- 페이드인되는 뷰의 표시 여부 설정
GONE
이렇게 하면 레이아웃 계산에서 이를 생략하여 업처리 중 config_shortAnimTime
캐시 시스템 속성을 지정해야 합니다. 이 속성은 표준 'short'를 정의합니다. 시간을 설정할 수 있습니다. 이 시간은 섬세한 애니메이션이나 자주 발생하는 애니메이션입니다.config_longAnimTime
드림 및config_mediumAnimTime
도 사용할 수 있습니다.
다음은 이전 코드 스니펫의 레이아웃을 활동 콘텐츠 뷰:
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);
}
...
}
뷰 크로스페이드
뷰가 적절히 설정되었으면 다음을 실행하여 뷰를 크로스페이드합니다.
- 페이드인되는 뷰의 경우 알파 값을 0으로 설정하고 가시성을 설정합니다.
초기 값에서
VISIBLE
로GONE
의 설정입니다. 이렇게 하면 뷰가 표시되지만 투명해집니다. - 페이드인되는 뷰의 알파 값을 0에서 1로 애니메이션 처리합니다. 대상 페이드아웃되는 뷰를 생성할 때 알파 값을 1에서 0으로 애니메이션 처리합니다.
- 사용
onAnimationEnd()
드림 ,Animator.AnimatorListener
, 페이드 아웃되는 뷰의 가시성을GONE
로 설정합니다. 그러나 알파 값이 0인 경우 뷰의 공개 상태를GONE
로 설정하면 뷰가 차단됩니다. 레이아웃 계산에서 이를 생략하여 많은 시간을 절약할 수 있습니다
다음은 이 작업을 어떻게 수행하는지를 보여주는 예제입니다.
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
다음은 카드플립 애니메이션입니다.
애니메이터 객체 만들기
카드플립 애니메이션을 만들려면 4개의 애니메이터가 필요합니다. 두 애니메이터는 카드 앞면이 애니메이션 및 왼쪽으로 애니메이션될 때와 안팎으로 들어갑니다. 나머지 두 애니메이터는 카드 뒷면의 가 애니메이션/오른쪽에서 애니메이션으로 바뀌었고 오른쪽으로
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>
뷰 만들기
카드의 각 면에 원하는 콘텐츠를 포함할 수 있는 별도의 레이아웃은 (예: 두 개의 텍스트 뷰, 두 개의 이미지 또는 뒤집을 뷰 조합) 있습니다. 나중에 애니메이션을 적용할 프래그먼트에서 두 개의 레이아웃을 사용합니다. 이 다음 레이아웃은 텍스트를 표시하는 카드의 한 면을 만듭니다.
<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()
드림
메서드를 사용하여 축소하도록 요청합니다. 그런 다음 상위 활동에서 이 프래그먼트의 인스턴스를 만들 수 있습니다.
선택합니다.
다음 예는 상위 활동 내에 중첩된 프래그먼트 클래스를 보여줍니다. 다음과 같습니다.
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);
}
}
}
카드플립 애니메이션
상위 활동 내에 프래그먼트를 표시합니다. 이렇게 하려면
정의합니다. 다음 예에서는
추가할 수 있는 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" />
활동 코드에서 콘텐츠 뷰를 직접 만든 레이아웃으로 설정합니다. 활동이 생성될 때 기본 프래그먼트를 표시하는 것이 좋습니다. 이 다음 예시 활동은 카드 앞면을 표시하는 방법을 보여줍니다. 기본값:
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();
}
}
...
}
카드 앞면을 표시하면 카드 뒷면을 볼 수 있으며 플립 애니메이션을 사용하세요. 데이터 레이크의 반대쪽을 표시하는 메서드를 만듭니다. 다음 작업을 수행하는 카드입니다.
- 프래그먼트 전환을 위해 만든 맞춤 애니메이션을 설정합니다.
- 표시된 프래그먼트를 새 프래그먼트로 바꾸고 이 이벤트에 애니메이션을 적용합니다. 만들 수 있습니다.
- 이전에 표시된 프래그먼트를 프래그먼트 백 스택에 추가하므로 사용자가 뒤로 버튼을 탭하면 카드가 다시 뒤집힙니다.
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();
}
}
회전 표시 애니메이션 만들기
그룹을 표시하거나 숨길 때 사용자에게 시각적 연속성을 제공하는 애니메이션 표시
UI 요소가 있습니다. 이
ViewAnimationUtils.createCircularReveal()
드림
메서드를 사용하면 뷰를 표시하거나 숨기도록 클리핑 서클에 애니메이션을 적용할 수 있습니다. 이
애니메이션은
ViewAnimationUtils
클래스
(Android 5.0(API 수준 21) 이상에서 사용 가능)
다음은 이전에 보이지 않았던 뷰를 표시하는 방법을 보여주는 예제입니다.
// 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개의 매개변수가 사용됩니다.
첫 번째 매개변수는 화면에서 숨기거나 표시할 뷰입니다. 이
다음 두 매개변수는 클리핑 중심에 대한 X 및 Y 좌표입니다.
있습니다. 일반적으로 이 위치가 뷰의 중심이지만,
사용자가 탭하여 선택한 지점에서 애니메이션이 시작되도록 합니다. 이
네 번째 매개변수는 클리핑 서클의 시작 반경입니다.
이전 예에서는 초기 반지름이 0으로 설정되어 뷰가 원에 의해 숨겨집니다. 마지막 매개변수는 표시됩니다. 보기를 표시할 때 최종 반경을 애니메이션이 완료되기 전에 뷰가 완전히 표시될 수 있도록 합니다.
이전에 표시된 뷰를 숨기려면 다음 단계를 따르세요.
// 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);
}
이 경우 클리핑 서클의 초기 반지름은
애니메이션이 시작되기 전에 뷰가 표시되도록 합니다. 마지막
애니메이션이 끝나면 뷰가 숨겨지도록 반경이 0으로 설정됩니다.
뷰의 가시성을 다음과 같이 설정할 수 있도록 애니메이션에 리스너를 추가합니다.
애니메이션이 실행되었을 때 INVISIBLE
나타냅니다.