Android : 확장 / 축소 애니메이션
다음과 함께 세로 linearLayout이 있다고 가정 해 보겠습니다.
[v1]
[v2]
기본적으로 v1은 눈에 띄게 = 사라졌습니다. 확장 애니메이션으로 v1을 표시하고 v2를 동시에 내리고 싶습니다.
나는 이와 같은 것을 시도했다 :
Animation a = new Animation()
{
int initialHeight;
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final int newHeight = (int)(initialHeight * interpolatedTime);
v.getLayoutParams().height = newHeight;
v.requestLayout();
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
initialHeight = height;
}
@Override
public boolean willChangeBounds() {
return true;
}
};
그러나이 솔루션을 사용하면 애니메이션이 시작될 때 깜박입니다. 애니메이션이 적용되기 전에 v1이 전체 크기로 표시되어 발생한다고 생각합니다.
자바 스크립트를 사용하면 jQuery의 한 줄입니다! 안드로이드로 이것을 수행하는 간단한 방법은 무엇입니까?
이 질문이 인기를 얻었으므로 실제 솔루션을 게시했습니다. 주요 장점은 애니메이션을 적용하기 위해 확장 된 높이를 알 필요가 없으며 뷰가 확장되면 컨텐츠가 변경되면 높이를 조정한다는 것입니다. 그것은 나를 위해 잘 작동합니다.
public static void expand(final View v) {
int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) v.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
v.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
final int targetHeight = v.getMeasuredHeight();
// Older versions of android (pre API 21) cancel animations for views with a height of 0.
v.getLayoutParams().height = 1;
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
v.getLayoutParams().height = interpolatedTime == 1
? LayoutParams.WRAP_CONTENT
: (int)(targetHeight * interpolatedTime);
v.requestLayout();
}
@Override
public boolean willChangeBounds() {
return true;
}
};
// Expansion speed of 1dp/ms
a.setDuration((int)(targetHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
public static void collapse(final View v) {
final int initialHeight = v.getMeasuredHeight();
Animation a = new Animation()
{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if(interpolatedTime == 1){
v.setVisibility(View.GONE);
}else{
v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
v.requestLayout();
}
}
@Override
public boolean willChangeBounds() {
return true;
}
};
// Collapse speed of 1dp/ms
a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
주석에서 @Jefferson이 언급했듯이 애니메이션의 지속 시간 (따라서 속도)을 변경하여 부드러운 애니메이션을 얻을 수 있습니다. 현재는 1dp / ms의 속도로 설정되었습니다
나는 매우 유사한 애니메이션이라고 생각하고 우아한 해결책을 찾았습니다. 이 코드는 항상 0-> h 또는 h-> 0 (h는 최대 높이 임)에서 시작한다고 가정합니다. 3 개의 생성자 매개 변수는 view = 애니메이션 될 뷰 (내 경우에는 웹뷰), targetHeight = 뷰의 최대 높이, down = 방향을 지정하는 부울 (true = 확장, false = 축소)입니다.
public class DropDownAnim extends Animation {
private final int targetHeight;
private final View view;
private final boolean down;
public DropDownAnim(View view, int targetHeight, boolean down) {
this.view = view;
this.targetHeight = targetHeight;
this.down = down;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
int newHeight;
if (down) {
newHeight = (int) (targetHeight * interpolatedTime);
} else {
newHeight = (int) (targetHeight * (1 - interpolatedTime));
}
view.getLayoutParams().height = newHeight;
view.requestLayout();
}
@Override
public void initialize(int width, int height, int parentWidth,
int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
@Override
public boolean willChangeBounds() {
return true;
}
}
나는 오늘 같은 문제를 우연히 발견 했으며이 질문에 대한 진정한 해결책은 이것이라고 생각합니다.
<LinearLayout android:id="@+id/container"
android:animateLayoutChanges="true"
...
/>
교대와 관련된 모든 최상위 레이아웃에 대해이 속성을 설정해야합니다. 한 레이아웃의 가시성을 GONE으로 설정하면 사라지는 레이아웃이 해제 될 때 다른 레이아웃이 공간을 차지합니다. 일종의 "페이딩 아웃"인 기본 애니메이션이있을 것입니다. 그러나 이것을 변경할 수 있다고 생각합니다. 그러나 지금까지 테스트하지 않은 마지막 애니메이션입니다.
나 에게 제대로 작동하지 않는 @LenaYan의 솔루션 을 가져 왔습니다 ( 붕괴 및 확장하기 전에 뷰를 0 높이로 변환했기 때문에 ).
이제 View의 이전 높이 를 가져 와서이 크기로 확장하기 시작 하면 훌륭하게 작동 합니다. 무너짐은 동일합니다.
아래 코드를 복사하여 붙여 넣을 수 있습니다.
public static void expand(final View v, int duration, int targetHeight) {
int prevHeight = v.getHeight();
v.setVisibility(View.VISIBLE);
ValueAnimator valueAnimator = ValueAnimator.ofInt(prevHeight, targetHeight);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
v.getLayoutParams().height = (int) animation.getAnimatedValue();
v.requestLayout();
}
});
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.setDuration(duration);
valueAnimator.start();
}
public static void collapse(final View v, int duration, int targetHeight) {
int prevHeight = v.getHeight();
ValueAnimator valueAnimator = ValueAnimator.ofInt(prevHeight, targetHeight);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
v.getLayoutParams().height = (int) animation.getAnimatedValue();
v.requestLayout();
}
});
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.setDuration(duration);
valueAnimator.start();
}
용법:
//Expanding the View
expand(yourView, 2000, 200);
// Collapsing the View
collapse(yourView, 2000, 100);
충분히 쉬워요!
초기 코드에 대해 LenaYan에게 감사드립니다!
다른 방법으로는 확장을 위해 다음과 같은 스케일링 요소가있는 스케일 애니메이션을 사용하는 것입니다.
ScaleAnimation anim = new ScaleAnimation(1, 1, 0, 1);
그리고 붕괴를 위해 :
ScaleAnimation anim = new ScaleAnimation(1, 1, 1, 0);
@Tom Esterez의 답변 이지만 Android에 따라 view.measure ()를 올바르게 사용하도록 업데이트되었습니다. getMeasuredHeight 가 잘못된 값을 반환합니다!
// http://easings.net/
Interpolator easeInOutQuart = PathInterpolatorCompat.create(0.77f, 0f, 0.175f, 1f);
public static Animation expand(final View view) {
int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) view.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
final int targetHeight = view.getMeasuredHeight();
// Older versions of android (pre API 21) cancel animations for views with a height of 0 so use 1 instead.
view.getLayoutParams().height = 1;
view.setVisibility(View.VISIBLE);
Animation animation = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
view.getLayoutParams().height = interpolatedTime == 1
? ViewGroup.LayoutParams.WRAP_CONTENT
: (int) (targetHeight * interpolatedTime);
view.requestLayout();
}
@Override
public boolean willChangeBounds() {
return true;
}
};
animation.setInterpolator(easeInOutQuart);
animation.setDuration(computeDurationFromHeight(view));
view.startAnimation(animation);
return animation;
}
public static Animation collapse(final View view) {
final int initialHeight = view.getMeasuredHeight();
Animation a = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (interpolatedTime == 1) {
view.setVisibility(View.GONE);
} else {
view.getLayoutParams().height = initialHeight - (int) (initialHeight * interpolatedTime);
view.requestLayout();
}
}
@Override
public boolean willChangeBounds() {
return true;
}
};
a.setInterpolator(easeInOutQuart);
int durationMillis = computeDurationFromHeight(view);
a.setDuration(durationMillis);
view.startAnimation(a);
return a;
}
private static int computeDurationFromHeight(View view) {
// 1dp/ms * multiplier
return (int) (view.getMeasuredHeight() / view.getContext().getResources().getDisplayMetrics().density);
}
좋아, 방금 매우 못생긴 해결책을 찾았습니다.
public static Animation expand(final View v, Runnable onEnd) {
try {
Method m = v.getClass().getDeclaredMethod("onMeasure", int.class, int.class);
m.setAccessible(true);
m.invoke(
v,
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(((View)v.getParent()).getMeasuredHeight(), MeasureSpec.AT_MOST)
);
} catch (Exception e){
Log.e("test", "", e);
}
final int initialHeight = v.getMeasuredHeight();
Log.d("test", "initialHeight="+initialHeight);
v.getLayoutParams().height = 0;
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final int newHeight = (int)(initialHeight * interpolatedTime);
v.getLayoutParams().height = newHeight;
v.requestLayout();
}
@Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(5000);
v.startAnimation(a);
return a;
}
더 나은 솔루션을 제안하십시오!
public static void expand(final View v, int duration, int targetHeight) {
v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
v.getLayoutParams().height = 0;
v.setVisibility(View.VISIBLE);
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, targetHeight);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
v.getLayoutParams().height = (int) animation.getAnimatedValue();
v.requestLayout();
}
});
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.setDuration(duration);
valueAnimator.start();
}
public static void collapse(final View v, int duration, int targetHeight) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, targetHeight);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
v.getLayoutParams().height = (int) animation.getAnimatedValue();
v.requestLayout();
}
});
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.setDuration(duration);
valueAnimator.start();
}
끝까지 확장하거나 축소하지 않으려는 경우 간단한 HeightAnimation이 있습니다.
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class HeightAnimation extends Animation {
protected final int originalHeight;
protected final View view;
protected float perValue;
public HeightAnimation(View view, int fromHeight, int toHeight) {
this.view = view;
this.originalHeight = fromHeight;
this.perValue = (toHeight - fromHeight);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
view.getLayoutParams().height = (int) (originalHeight + perValue * interpolatedTime);
view.requestLayout();
}
@Override
public boolean willChangeBounds() {
return true;
}
}
용법:
HeightAnimation heightAnim = new HeightAnimation(view, view.getHeight(), viewPager.getHeight() - otherView.getHeight());
heightAnim.setDuration(1000);
view.startAnimation(heightAnim);
나는 Tom Esterez가 현재 받아 들인 대답을 수정 했습니다. 내 솔루션은 기본적으로 대체 Animation
A를 ValueAnimator
를 장착 할 수있는 Interpolator
등 오버 슈트, 바운스, 가속 등의 다양한 효과를 달성하기 위해 당신의 선택을
이 솔루션은 WRAP_CONTENT
먼저 실제 필요한 높이를 측정 한 다음 해당 높이에 애니메이션을 적용하기 때문에 동적 높이 (예 :)를 사용하는 뷰에 적합합니다 .
public static void expand(final View v) {
v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
final int targetHeight = v.getMeasuredHeight();
// Older versions of android (pre API 21) cancel animations for views with a height of 0.
v.getLayoutParams().height = 1;
v.setVisibility(View.VISIBLE);
ValueAnimator va = ValueAnimator.ofInt(1, targetHeight);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
v.getLayoutParams().height = (Integer) animation.getAnimatedValue();
v.requestLayout();
}
});
va.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
v.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
@Override public void onAnimationStart(Animator animation) {}
@Override public void onAnimationCancel(Animator animation) {}
@Override public void onAnimationRepeat(Animator animation) {}
});
va.setDuration(300);
va.setInterpolator(new OvershootInterpolator());
va.start();
}
public static void collapse(final View v) {
final int initialHeight = v.getMeasuredHeight();
ValueAnimator va = ValueAnimator.ofInt(initialHeight, 0);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
v.getLayoutParams().height = (Integer) animation.getAnimatedValue();
v.requestLayout();
}
});
va.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
v.setVisibility(View.GONE);
}
@Override public void onAnimationStart(Animator animation) {}
@Override public void onAnimationCancel(Animator animation) {}
@Override public void onAnimationRepeat(Animator animation) {}
});
va.setDuration(300);
va.setInterpolator(new DecelerateInterpolator());
va.start();
}
그런 다음 단순히 expand( myView );
또는로 전화하십시오 collapse( myView );
.
위 의 매우 유용한 답변에 무언가를 추가하고 싶습니다 . views .getHeight ()가 0을 반환하기 때문에 높이를 모르는 경우 높이를 얻기 위해 다음을 수행 할 수 있습니다.
contentView.measure(DUMMY_HIGH_DIMENSION, DUMMY_HIGH_DIMENSION);
int finalHeight = view.getMeasuredHeight();
여기서 DUMMY_HIGH_DIMENSIONS는 뷰가 너비 / 높이 (픽셀 단위)로 제한됩니다. ScrollView로 뷰를 캡슐화 할 때이 값을 갖는 것이 합리적입니다.
이것은 애니메이션으로 뷰의 너비 (LinearLayout)의 크기를 조정하는 데 사용 된 스 니펫입니다.
코드는 대상 크기에 따라 확장 또는 축소되어야합니다. fill_parent 너비를 원하면 플래그를 true로 설정하면서 부모 .getMeasuredWidth를 대상 너비로 전달해야합니다.
그것이 당신의 일부를 돕기를 바랍니다.
public class WidthResizeAnimation extends Animation {
int targetWidth;
int originaltWidth;
View view;
boolean expand;
int newWidth = 0;
boolean fillParent;
public WidthResizeAnimation(View view, int targetWidth, boolean fillParent) {
this.view = view;
this.originaltWidth = this.view.getMeasuredWidth();
this.targetWidth = targetWidth;
newWidth = originaltWidth;
if (originaltWidth > targetWidth) {
expand = false;
} else {
expand = true;
}
this.fillParent = fillParent;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (expand && newWidth < targetWidth) {
newWidth = (int) (newWidth + (targetWidth - newWidth) * interpolatedTime);
}
if (!expand && newWidth > targetWidth) {
newWidth = (int) (newWidth - (newWidth - targetWidth) * interpolatedTime);
}
if (fillParent && interpolatedTime == 1.0) {
view.getLayoutParams().width = -1;
} else {
view.getLayoutParams().width = newWidth;
}
view.requestLayout();
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
@Override
public boolean willChangeBounds() {
return true;
}
}
부드러운 애니메이션을 위해서는 Handler를 run 메소드와 함께 사용하십시오.
class AnimUtils{
public void expand(final View v) {
int ANIMATION_DURATION=500;//in milisecond
v.measure(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
final int targtetHeight = v.getMeasuredHeight();
v.getLayoutParams().height = 0;
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
v.getLayoutParams().height = interpolatedTime == 1
? LayoutParams.WRAP_CONTENT
: (int)(targtetHeight * interpolatedTime);
v.requestLayout();
}
@Override
public boolean willChangeBounds() {
return true;
}
};
// 1dp/ms
a.setDuration(ANIMATION_DURATION);
// a.setDuration((int)(targtetHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
public void collapse(final View v) {
final int initialHeight = v.getMeasuredHeight();
Animation a = new Animation()
{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if(interpolatedTime == 1){
v.setVisibility(View.GONE);
}else{
v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
v.requestLayout();
}
}
@Override
public boolean willChangeBounds() {
return true;
}
};
// 1dp/ms
a.setDuration(ANIMATION_DURATION);
// a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
}
그리고이 코드를 사용하여 전화 :
private void setAnimationOnView(final View inactive ) {
//I am applying expand and collapse on this TextView ...You can use your view
//for expand animation
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
new AnimationUtililty().expand(inactive);
}
}, 1000);
//For collapse
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
new AnimationUtililty().collapse(inactive);
//inactive.setVisibility(View.GONE);
}
}, 8000);
}
다른 해결책은 다음과 같습니다.
public void expandOrCollapse(final View v,String exp_or_colpse) {
TranslateAnimation anim = null;
if(exp_or_colpse.equals("expand"))
{
anim = new TranslateAnimation(0.0f, 0.0f, -v.getHeight(), 0.0f);
v.setVisibility(View.VISIBLE);
}
else{
anim = new TranslateAnimation(0.0f, 0.0f, 0.0f, -v.getHeight());
AnimationListener collapselistener= new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
v.setVisibility(View.GONE);
}
};
anim.setAnimationListener(collapselistener);
}
// To Collapse
//
anim.setDuration(300);
anim.setInterpolator(new AccelerateInterpolator(0.5f));
v.startAnimation(anim);
}
Tom Esterez의 훌륭한 답변 과 Erik B의 훌륭한 업데이트 에 덧붙여 확장 및 계약 방법을 하나로 압축하여 내 의견을 게시 할 것이라고 생각했습니다. 이렇게하면 예를 들어 다음과 같은 작업을 수행 할 수 있습니다 ...
button.setOnClickListener(v -> expandCollapse(view));
... 아래의 메소드를 호출하고 각 onClick () 후에 수행 할 작업을 파악하도록합니다 ...
public static void expandCollapse(View view) {
boolean expand = view.getVisibility() == View.GONE;
Interpolator easeInOutQuart = PathInterpolatorCompat.create(0.77f, 0f, 0.175f, 1f);
view.measure(
View.MeasureSpec.makeMeasureSpec(((View) view.getParent()).getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
);
int height = view.getMeasuredHeight();
int duration = (int) (height/view.getContext().getResources().getDisplayMetrics().density);
Animation animation = new Animation() {
@Override protected void applyTransformation(float interpolatedTime, Transformation t) {
if (expand) {
view.getLayoutParams().height = 1;
view.setVisibility(View.VISIBLE);
if (interpolatedTime == 1) {
view.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
} else {
view.getLayoutParams().height = (int) (height * interpolatedTime);
}
view.requestLayout();
} else {
if (interpolatedTime == 1) {
view.setVisibility(View.GONE);
} else {
view.getLayoutParams().height = height - (int) (height * interpolatedTime);
view.requestLayout();
}
}
}
@Override public boolean willChangeBounds() {
return true;
}
};
animation.setInterpolator(easeInOutQuart);
animation.setDuration(duration);
view.startAnimation(animation);
}
예, 위의 의견에 동의했습니다. 실제로, 그것은 (또는 적어도 가장 쉬운)해야 할 것처럼 보입니다 (XML에서 초기 레이아웃 높이를 "0px"로 지정하는 것입니다. 그런 다음 "toHeight"에 대한 다른 인수를 전달할 수 있습니다 ( 즉, "최종 높이")를 사용자 정의 애니메이션 하위 클래스의 생성자에 적용합니다 (예 : 위의 예와 같이).
public DropDownAnim( View v, int toHeight ) { ... }
어쨌든 도움이 되길 바랍니다! :)
여기 내 해결책이 있습니다. 나는 그것이 더 간단하다고 생각합니다. 보기 만 확장하지만 쉽게 확장 할 수 있습니다.
public class WidthExpandAnimation extends Animation
{
int _targetWidth;
View _view;
public WidthExpandAnimation(View view)
{
_view = view;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
if (interpolatedTime < 1.f)
{
int newWidth = (int) (_targetWidth * interpolatedTime);
_view.layout(_view.getLeft(), _view.getTop(),
_view.getLeft() + newWidth, _view.getBottom());
}
else
_view.requestLayout();
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight)
{
super.initialize(width, height, parentWidth, parentHeight);
_targetWidth = width;
}
@Override
public boolean willChangeBounds() {
return true;
}
}
가장 쉬운 해결책은 android:animateLayoutChanges="true"
귀하 에게 설정 LinearLayout
한 다음 가시성을 설정하여보기를 표시하거나 숨기는 것입니다. 매력처럼 작동하지만 애니메이션 지속 시간을 제어 할 수는 없습니다
@Tom Esterez와 @Geraldo Neto의 통합 솔루션
public static void expandOrCollapseView(View v,boolean expand){
if(expand){
v.measure(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
final int targetHeight = v.getMeasuredHeight();
v.getLayoutParams().height = 0;
v.setVisibility(View.VISIBLE);
ValueAnimator valueAnimator = ValueAnimator.ofInt(targetHeight);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
v.getLayoutParams().height = (int) animation.getAnimatedValue();
v.requestLayout();
}
});
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.setDuration(500);
valueAnimator.start();
}
else
{
final int initialHeight = v.getMeasuredHeight();
ValueAnimator valueAnimator = ValueAnimator.ofInt(initialHeight,0);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
v.getLayoutParams().height = (int) animation.getAnimatedValue();
v.requestLayout();
if((int)animation.getAnimatedValue() == 0)
v.setVisibility(View.GONE);
}
});
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.setDuration(500);
valueAnimator.start();
}
}
//sample usage
expandOrCollapseView((Your ViewGroup),(Your ViewGroup).getVisibility()!=View.VISIBLE);
당신은 올바른 길을 가고 있습니다. 애니메이션이 시작되기 직전에 v1의 레이아웃 높이가 0으로 설정되어 있는지 확인하십시오. 애니메이션을 시작하기 전에 애니메이션의 첫 번째 프레임처럼 보이도록 설정을 초기화하려고합니다.
이것은 내 솔루션을했다, 내가 ImageView
에서 성장 100%
에 200%
두 개의 애니메이션 파일 내부에 사용하여, 자신의 원래 크기로 반환 및 res/anim/
폴더를
anim_grow.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<scale
android:fromXScale="1.0"
android:toXScale="2.0"
android:fromYScale="1.0"
android:toYScale="2.0"
android:duration="3000"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="2000" />
</set>
anim_shrink.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<scale
android:fromXScale="2.0"
android:toXScale="1.0"
android:fromYScale="2.0"
android:toYScale="1.0"
android:duration="3000"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="2000" />
</set>
ImageView
내 방법으로 보내기setAnimationGrowShrink()
ImageView img1 = (ImageView)findViewById(R.id.image1);
setAnimationGrowShrink(img1);
setAnimationGrowShrink()
방법:
private void setAnimationGrowShrink(final ImageView imgV){
final Animation animationEnlarge = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim_grow);
final Animation animationShrink = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim_shrink);
imgV.startAnimation(animationEnlarge);
animationEnlarge.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
imgV.startAnimation(animationShrink);
}
});
animationShrink.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
imgV.startAnimation(animationEnlarge);
}
});
}
이것은 올바른 작동 솔루션이며 테스트했습니다.
Exapnd :
private void expand(View v) {
v.setVisibility(View.VISIBLE);
v.measure(View.MeasureSpec.makeMeasureSpec(PARENT_VIEW.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
final int targetHeight = v.getMeasuredHeight();
mAnimator = slideAnimator(0, targetHeight);
mAnimator.setDuration(800);
mAnimator.start();
}
무너짐:
private void collapse(View v) {
int finalHeight = v.getHeight();
mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
//Height=0, but it set visibility to GONE
llDescp.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimator.start();
}
가치 애니메이터 :
private ValueAnimator slideAnimator(int start, int end) {
ValueAnimator mAnimator = ValueAnimator.ofInt(start, end);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//Update Height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = llDescp.getLayoutParams();
layoutParams.height = value;
v.setLayoutParams(layoutParams);
}
});
return mAnimator;
}
View v는 애니메이션을 적용 할 뷰이고 PARENT_VIEW는 뷰를 포함하는 컨테이너 뷰입니다.
이것은 droidQuery 로 정말 간단 합니다. 시작하려면 다음 레이아웃을 고려하십시오.
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/v1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View 1" />
</LinearLayout>
<LinearLayout
android:id="@+id/v2"
android:layout_width="wrap_content"
android:layout_height="0dp" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View 2" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View 3" />
</LinearLayout>
</LinearLayout>
100dp
다음 코드를 사용하여 높이를 원하는 값으로 애니메이션 할 수 있습니다 .
//convert 100dp to pixel value
int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());
그런 다음 droidQuery
애니메이션에 사용하십시오. 가장 간단한 방법은 다음과 같습니다.
$.animate("{ height: " + height + "}", new AnimationOptions());
애니메이션을 더 매력적으로 만들려면 여유를 추가하는 것이 좋습니다.
$.animate("{ height: " + height + "}", new AnimationOptions().easing($.Easing.BOUNCE));
방법 을 AnimationOptions
사용하여 지속 시간을 변경 duration()
하거나 애니메이션이 끝날 때 발생하는 작업을 처리 할 수도 있습니다 . 복잡한 예를 보려면 다음을 시도하십시오.
$.animate("{ height: " + height + "}", new AnimationOptions().easing($.Easing.BOUNCE)
.duration(1000)
.complete(new Function() {
@Override
public void invoke($ d, Object... args) {
$.toast(context, "finished", Toast.LENGTH_SHORT);
}
}));
확장 / 축소보기를위한 최상의 솔루션 :
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
View view = buttonView.getId() == R.id.tb_search ? fSearch : layoutSettings;
transform(view, 200, isChecked
? ViewGroup.LayoutParams.WRAP_CONTENT
: 0);
}
public static void transform(final View v, int duration, int targetHeight) {
int prevHeight = v.getHeight();
v.setVisibility(View.VISIBLE);
ValueAnimator animator;
if (targetHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
animator = ValueAnimator.ofInt(prevHeight, v.getMeasuredHeight());
} else {
animator = ValueAnimator.ofInt(prevHeight, targetHeight);
}
animator.addUpdateListener(animation -> {
v.getLayoutParams().height = (animation.getAnimatedFraction() == 1.0f)
? targetHeight
: (int) animation.getAnimatedValue();
v.requestLayout();
});
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(duration);
animator.start();
}
약간의 왜곡으로 ViewPropertyAnimator를 사용할 수 있습니다. 축소하려면보기를 1 픽셀 높이로 조정 한 다음 숨 깁니다. 확장하려면 표시 한 다음 높이로 확장하십시오.
private void collapse(final View view) {
view.setPivotY(0);
view.animate().scaleY(1/view.getHeight()).setDuration(1000).withEndAction(new Runnable() {
@Override public void run() {
view.setVisibility(GONE);
}
});
}
private void expand(View view, int height) {
float scaleFactor = height / view.getHeight();
view.setVisibility(VISIBLE);
view.setPivotY(0);
view.animate().scaleY(scaleFactor).setDuration(1000);
}
피벗은 뷰의 크기를 조정할 위치를 알려줍니다. 기본값은 중간입니다. 기간은 선택 사항입니다 (기본값 = 1000). 보간기를 다음과 같이 사용하도록 설정할 수도 있습니다..setInterpolator(new AccelerateDecelerateInterpolator())
레이아웃 높이를 지정할 필요가없는 버전을 만들었으므로 사용하기가 훨씬 쉽고 깨끗합니다. 해결책은 애니메이션의 첫 번째 프레임에서 높이를 얻는 것입니다 (적어도 테스트 중에는 가능합니다). 이런 식으로 View에 임의의 높이와 하단 여백을 제공 할 수 있습니다.
생성자에는 하나의 작은 해킹이 있습니다. 아래쪽 여백은 -10000으로 설정되어 변환 전에 뷰가 숨겨져 있습니다 (깜박임 방지).
public class ExpandAnimation extends Animation {
private View mAnimatedView;
private ViewGroup.MarginLayoutParams mViewLayoutParams;
private int mMarginStart, mMarginEnd;
public ExpandAnimation(View view) {
mAnimatedView = view;
mViewLayoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
mMarginEnd = mViewLayoutParams.bottomMargin;
mMarginStart = -10000; //hide before viewing by settings very high negative bottom margin (hack, but works nicely)
mViewLayoutParams.bottomMargin = mMarginStart;
mAnimatedView.setLayoutParams(mViewLayoutParams);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
//view height is already known when the animation starts
if(interpolatedTime==0){
mMarginStart = -mAnimatedView.getHeight();
}
mViewLayoutParams.bottomMargin = (int)((mMarginEnd-mMarginStart) * interpolatedTime)+mMarginStart;
mAnimatedView.setLayoutParams(mViewLayoutParams);
}
}
ValueAnimator를 사용하십시오.
ValueAnimator expandAnimation = ValueAnimator.ofInt(mainView.getHeight(), 400);
expandAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(final ValueAnimator animation) {
int height = (Integer) animation.getAnimatedValue();
RelativeLayout.LayoutParams lp = (LayoutParams) mainView.getLayoutParams();
lp.height = height;
}
});
expandAnimation.setDuration(500);
expandAnimation.start();
public static void slide(View v, int speed, int pos) {
v.animate().setDuration(speed);
v.animate().translationY(pos);
v.animate().start();
}
// slide down
slide(yourView, 250, yourViewHeight);
// slide up
slide(yourView, 250, 0);
/**
* Animation that either expands or collapses a view by sliding it down to make
* it visible. Or by sliding it up so it will hide. It will look like it slides
* behind the view above.
*
*/
public class FinalExpandCollapseAnimation extends Animation
{
private View mAnimatedView;
private int mEndHeight;
private int mType;
public final static int COLLAPSE = 1;
public final static int EXPAND = 0;
private LinearLayout.LayoutParams mLayoutParams;
private RelativeLayout.LayoutParams mLayoutParamsRel;
private String layout;
private Context context;
/**
* Initializes expand collapse animation, has two types, collapse (1) and
* expand (0).
*
* @param view
* The view to animate
* @param type
* The type of animation: 0 will expand from gone and 0 size to
* visible and layout size defined in xml. 1 will collapse view
* and set to gone
*/
public FinalExpandCollapseAnimation(View view, int type, int height, String layout, Context context)
{
this.layout = layout;
this.context = context;
mAnimatedView = view;
mEndHeight = mAnimatedView.getMeasuredHeight();
if (layout.equalsIgnoreCase("linear"))
mLayoutParams = ((LinearLayout.LayoutParams) view.getLayoutParams());
else
mLayoutParamsRel = ((RelativeLayout.LayoutParams) view.getLayoutParams());
mType = type;
if (mType == EXPAND)
{
AppConstant.ANIMATED_VIEW_HEIGHT = height;
}
else
{
if (layout.equalsIgnoreCase("linear"))
mLayoutParams.topMargin = 0;
else
mLayoutParamsRel.topMargin = convertPixelsIntoDensityPixels(36);
}
setDuration(600);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f)
{
if (mType == EXPAND)
{
if (layout.equalsIgnoreCase("linear"))
{
mLayoutParams.height = AppConstant.ANIMATED_VIEW_HEIGHT
+ (-AppConstant.ANIMATED_VIEW_HEIGHT + (int) (AppConstant.ANIMATED_VIEW_HEIGHT * interpolatedTime));
}
else
{
mLayoutParamsRel.height = AppConstant.ANIMATED_VIEW_HEIGHT
+ (-AppConstant.ANIMATED_VIEW_HEIGHT + (int) (AppConstant.ANIMATED_VIEW_HEIGHT * interpolatedTime));
}
mAnimatedView.setVisibility(View.VISIBLE);
}
else
{
if (layout.equalsIgnoreCase("linear"))
mLayoutParams.height = mEndHeight - (int) (mEndHeight * interpolatedTime);
else
mLayoutParamsRel.height = mEndHeight - (int) (mEndHeight * interpolatedTime);
}
mAnimatedView.requestLayout();
}
else
{
if (mType == EXPAND)
{
if (layout.equalsIgnoreCase("linear"))
{
mLayoutParams.height = AppConstant.ANIMATED_VIEW_HEIGHT;
mLayoutParams.topMargin = 0;
}
else
{
mLayoutParamsRel.height = AppConstant.ANIMATED_VIEW_HEIGHT;
mLayoutParamsRel.topMargin = convertPixelsIntoDensityPixels(36);
}
mAnimatedView.setVisibility(View.VISIBLE);
mAnimatedView.requestLayout();
}
else
{
if (layout.equalsIgnoreCase("linear"))
mLayoutParams.height = 0;
else
mLayoutParamsRel.height = 0;
mAnimatedView.setVisibility(View.GONE);
mAnimatedView.requestLayout();
}
}
}
private int convertPixelsIntoDensityPixels(int pixels)
{
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return (int) metrics.density * pixels;
}
}
클래스는 다음과 같은 방법으로 호출 할 수 있습니다
if (findViewById(R.id.ll_specailoffer_show_hide).getVisibility() == View.VISIBLE) {
((ImageView) findViewById(R.id.iv_specialhour_seemore)).setImageResource(R.drawable.white_dropdown_up);
FinalExpandCollapseAnimation finalExpandCollapseAnimation = new FinalExpandCollapseAnimation(
findViewById(R.id.ll_specailoffer_show_hide),
FinalExpandCollapseAnimation.COLLAPSE,
SpecialOfferHeight, "linear", this);
findViewById(R.id.ll_specailoffer_show_hide)
.startAnimation(finalExpandCollapseAnimation);
((View) findViewById(R.id.ll_specailoffer_show_hide).getParent()).invalidate();
} else {
((ImageView) findViewById(R.id.iv_specialhour_seemore)).setImageResource(R.drawable.white_dropdown);
FinalExpandCollapseAnimation finalExpandCollapseAnimation = new FinalExpandCollapseAnimation(
findViewById(R.id.ll_specailoffer_show_hide),
FinalExpandCollapseAnimation.EXPAND,
SpecialOfferHeight, "linear", this);
findViewById(R.id.ll_specailoffer_show_hide)
.startAnimation(finalExpandCollapseAnimation);
((View) findViewById(R.id.ll_specailoffer_show_hide).getParent()).invalidate();
}
Kotlin 확장 기능을 사용하여 테스트되었으며 가장 짧은 답변
모든 View에서 animateVisibility (expand / collapse)를 호출하십시오.
fun View.animateVisibility(setVisible: Boolean) {
if (setVisible) expand(this) else collapse(this)
}
private fun expand(view: View) {
view.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val initialHeight = 0
val targetHeight = view.measuredHeight
// Older versions of Android (pre API 21) cancel animations for views with a height of 0.
//v.getLayoutParams().height = 1;
view.layoutParams.height = 0
view.visibility = View.VISIBLE
animateView(view, initialHeight, targetHeight)
}
private fun collapse(view: View) {
val initialHeight = view.measuredHeight
val targetHeight = 0
animateView(view, initialHeight, targetHeight)
}
private fun animateView(v: View, initialHeight: Int, targetHeight: Int) {
val valueAnimator = ValueAnimator.ofInt(initialHeight, targetHeight)
valueAnimator.addUpdateListener { animation ->
v.layoutParams.height = animation.animatedValue as Int
v.requestLayout()
}
valueAnimator.addListener(object : Animator.AnimatorListener {
override fun onAnimationEnd(animation: Animator) {
v.layoutParams.height = targetHeight
}
override fun onAnimationStart(animation: Animator) {}
override fun onAnimationCancel(animation: Animator) {}
override fun onAnimationRepeat(animation: Animator) {}
})
valueAnimator.duration = 300
valueAnimator.interpolator = DecelerateInterpolator()
valueAnimator.start()
}
@Tom Esterez와 @Seth Nelson (상위 2)의 솔루션을 기반으로 솔루션을 단순화했습니다. 독창적 인 솔루션뿐만 아니라 개발자 옵션 (애니메이션 설정)에 의존하지 않습니다.
private void resizeWithAnimation(final View view, int duration, final int targetHeight) {
final int initialHeight = view.getMeasuredHeight();
final int distance = targetHeight - initialHeight;
view.setVisibility(View.VISIBLE);
Animation a = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (interpolatedTime == 1 && targetHeight == 0) {
view.setVisibility(View.GONE);
} else {
view.getLayoutParams().height = (int) (initialHeight + distance * interpolatedTime);
view.requestLayout();
}
}
@Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(duration);
view.startAnimation(a);
}
참고 URL : https://stackoverflow.com/questions/4946295/android-expand-collapse-animation
도와주세요.
'development' 카테고리의 다른 글
여러 공간을 단일 공간으로 대체하는 정규식 (0) | 2020.02.18 |
---|---|
필터링 된 ng-repeat 데이터의 길이를 표시하는 방법 (0) | 2020.02.18 |
DOM parentNode와 parentElement의 차이점 (0) | 2020.02.18 |
브라우저에서 최대 병렬 http 연결? (0) | 2020.02.18 |
확인 된 예외에 대한 사례 (0) | 2020.02.18 |