Android:애니메이션 확장/축소
예를 들어 다음과 같은 수직 선형 레이아웃이 있다고 가정합니다.
[v1]
[v2]
기본적으로 v1은 가시적으로 = GONE입니다.저는 확장 애니메이션으로 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에 풀사이즈가 표시되어 발생한 것 같습니다.
Javascript를 사용하면 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의 속도로 설정되어 있습니다.
저는 오늘 같은 문제에 걸려 넘어졌고 이 질문에 대한 진정한 해결책은 이것이라고 생각합니다.
<LinearLayout android:id="@+id/container"
android:animateLayoutChanges="true"
...
/>
이동과 관련된 모든 맨 위 레이아웃에 대해 이 속성을 설정해야 합니다.이제 한 레이아웃의 가시성을 GONE으로 설정하면 사라지는 레이아웃이 해제될 때 다른 레이아웃이 공간을 차지합니다.일종의 "페이드 아웃"인 기본 애니메이션이 있을 것이지만, 저는 당신이 이것을 바꿀 수 있다고 생각합니다 - 하지만 제가 지금까지 테스트하지 않은 마지막 애니메이션입니다.
RecyclerView 항목에서 이 항목을 사용하는 경우 BindView에서 확장/축소할 뷰의 가시성을 설정합니다.보유자 및 통화 통지ItemChanged(위치)가 변환을 트리거하도록 변경되었습니다.
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
...
holder.list.visibility = data[position].listVisibility
holder.expandCollapse.setOnClickListener {
data[position].listVisibility = if (data[position].listVisibility == View.GONE) View.VISIBLE else View.GONE
notifyItemChanged(position)
}
}
OnBindView에서 값비싼 작업을 수행하는 경우알림을 사용하여 부분 변경에 대해 최적화할 수 있는 홀더변경된 항목(위치, 페이로드)
private const val UPDATE_LIST_VISIBILITY = 1
override fun onBindViewHolder(holder: ItemViewHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.contains(UPDATE_LIST_VISIBILITY)) {
holder.list.visibility = data[position].listVisibility
} else {
onBindViewHolder(holder, position)
}
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
...
holder.list.visibility = data[position].listVisibility
holder.expandCollapse.setOnClickListener {
data[position].listVisibility = if (data[position].listVisibility == View.GONE) View.VISIBLE else View.GONE
notifyItemChanged(position, UPDATE_LIST_VISIBILITY)
}
}
저는 제가 생각하는 매우 유사한 애니메이션을 하려고 노력하다가 우아한 해결책을 찾았습니다.이 코드는 사용자가 항상 0->h 또는 h->0(h가 최대 높이임)에서 출발한다고 가정합니다.생성자 매개변수 3개는 view = 애니메이션화할 뷰(나의 경우 웹뷰), target입니다.높이 = 뷰의 최대 높이, 아래쪽 = 방향(참 = 확장, 거짓 = 축소)을 지정하는 부울입니다.
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;
}
}
저는 @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);
충분히 쉽습니다!
초기 코드에 대해 Lena Yan에게 감사드립니다!
또는 확장을 위해 다음과 같은 배율 요소가 있는 배율 애니메이션을 사용하는 것이 좋습니다.
ScaleAnimation anim = new ScaleAnimation(1, 1, 0, 1);
접히는 경우:
ScaleAnimation anim = new ScaleAnimation(1, 1, 1, 0);
@Tom Esterez의 답변이지만, 안드로이드별로 view.measure()를 올바르게 사용하도록 업데이트되었습니다.높이가 잘못된 값을 반환합니다!
// 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();
}
계속 확장하거나 접기를 원하지 않는 경우 - 여기 간단한 높이 애니메이션이 있습니다.
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);
저는 톰 에스테레즈가 현재 수락한 답변을 각색했는데, 효과는 있었지만 애니메이션이 거칠고 매끄럽지는 않았습니다.기본적으로 내 솔루션이Animation
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 );
.
Kotlin Extension Functions를 사용하여 테스트되고 가장 짧은 답변입니다.
임의의 보기에서 animateVisibility(확장/축소)를 호출하기만 하면 됩니다.
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()
}
톰 에스테레즈의 훌륭한 답변과 에릭 B의 훌륭한 업데이트에 더해, 나는 확장과 계약 방법을 하나로 압축하여 나만의 테이크를 게시하려고 생각했습니다.이런 식으로 예를 들어 이런 행동을 할 수 있습니다.
button.setOnClickListener(v -> expandCollapse(view));
아래 메소드를 호출하여 클릭()...을 각각 클릭한 후 수행할 작업을 결정합니다.
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);
}
원활한 애니메이션을 위해 실행 방법이 있는 핸들러를 사용하십시오.확장/축소 애니메이션을 즐기십시오.
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);
}
위의 매우 도움이 되는 답변에 추가하고 싶습니다.보기 .getHeight()가 0을 반환하기 때문에 표시되는 높이를 모를 경우 다음 작업을 수행하여 높이를 얻을 수 있습니다.
contentView.measure(DUMMY_HIGH_DIMENSION, DUMMY_HIGH_DIMENSION);
int finalHeight = view.getMeasuredHeight();
DUMINEL_HIGH_DIMENS는 너비/높이(픽셀)입니다. 여기서 뷰는 ...로 제한됩니다.이 숫자는 보기가 ScrollView로 캡슐화된 경우에 적합합니다.
애니메이션을 사용하여 보기 너비(선형 레이아웃)의 크기를 조정하는 데 사용한 조각입니다.
코드는 목표 크기에 따라 확장하거나 축소해야 합니다.fill_parent width를 사용하려면 farent.getMeasuredWidth를 true로 설정하면서 parent.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;
}
}
네, 저는 위의 의견에 동의했습니다.그리고 실제로 올바른(적어도 가장 쉬운) 방법은 "0px"의 초기 레이아웃 높이를 XML로 지정하는 것 같습니다. 그런 다음 "to Height"(즉, "최종 높이")에 대한 다른 인수를 사용자 정의 애니메이션 하위 클래스의 생성자에게 전달할 수 있습니다. 예를 들어, 위의 예제에서는 다음과 같이 보입니다.
public DropDownAnim( View v, int toHeight ) { ... }
어쨌든, 그게 도움이 되길 바래요! :)
@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);
여기 제 해결책이 있습니다.저는 그게 더 간단하다고 생각합니다.보기만 확장할 뿐 쉽게 확장할 수 있습니다.
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
가시성을 설정하여 보기를 표시/숨기기만 하면 됩니다.하지만, 할 수 .
사용할 수 있습니다.Transition
또는Animator
확장/축소할 섹션의 가시성을 변경하는 경우ConstraintSet
다양한 레이아웃으로.
가장 쉬운 방법은 버튼 클릭 시 두 개의 서로 다른 레이아웃 및 제약 조건 세트와 함께 motionLayout을 사용하는 것입니다.다음을 사용하여 레이아웃을 변경할 수 있습니다.
val constraintSet = ConstraintSet()
constraintSet.clone(this, R.layout.layout_collapsed)
val transition = ChangeBounds()
transition.interpolator = AccelerateInterpolator(1.0f)
transition.setDuration(300)
TransitionManager.beginDelayedTransition(YOUR_VIEW, transition)
constraintSet.applyTo(YOUR_VIEW)
전환 api 포함
X.kt 회전
회전 X를 바꾸는 Transitions api를 사용하여 inif를 만들었습니다.
class RotateX : Transition {
@Keep
constructor() : super()
@Keep
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun getTransitionProperties(): Array<String> {
return TRANSITION_PROPERTIES
}
override fun captureStartValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
override fun captureEndValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
override fun createAnimator(
sceneRoot: ViewGroup,
startValues: TransitionValues?,
endValues: TransitionValues?
): Animator? {
if (startValues == null || endValues == null) return null
val startRotation = startValues.values[PROP_ROTATION] as Float
val endRotation = endValues.values[PROP_ROTATION] as Float
if (startRotation == endRotation) return null
val view = endValues.view
// ensure the pivot is set
view.pivotX = view.width / 2f
view.pivotY = view.height / 2f
return ObjectAnimator.ofFloat(view, View.ROTATION_X, startRotation, endRotation)
}
private fun captureValues(transitionValues: TransitionValues) {
val view = transitionValues.view
if (view == null || view.width <= 0 || view.height <= 0) return
transitionValues.values[PROP_ROTATION] = view.rotationX
}
companion object {
private const val PROP_ROTATION = "iosched:rotate:rotation"
private val TRANSITION_PROPERTIES = arrayOf(PROP_ROTATION)
}
}
expand 버튼을 대상으로 하는 xml 파일
<?xml version="1.0" encoding="utf-8"?>
<transitionSet
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/fast_out_slow_in">
<transition class="com.smarttoolfactory.tutorial3_1transitions.transition.RotateX">
<targets>
<target android:targetId="@id/ivExpand" />
</targets>
</transition>
<autoTransition android:duration="200" />
</transitionSet>
My layout to be expanded or collapsed
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<com.google.android.material.card.MaterialCardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_marginVertical="2dp"
android:clickable="true"
android:focusable="true"
android:transitionName="@string/transition_card_view"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:cardPreventCornerOverlap="false">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivAvatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/avatar_1_raster" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivExpand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:padding="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_baseline_expand_more_24" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="6dp"
android:text="Some Title"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/ivAvatar"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="@+id/tvTitle"
app:layout_constraintTop_toBottomOf="@id/tvTitle"
tools:text="Tuesday 7pm" />
<TextView
android:id="@+id/tvBody"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:lines="1"
android:text="@string/bacon_ipsum_short"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/ivAvatar"
app:layout_constraintTop_toBottomOf="@id/tvDate" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:orientation="horizontal"
android:overScrollMode="never"
android:visibility="gone"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvBody"
tools:listitem="@layout/item_image_destination" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
축소 또는 확장할 항목의 가시성 설정
private fun setUpExpandedStatus() {
if (isExpanded) {
binding.recyclerView.visibility = View.VISIBLE
binding.ivExpand.rotationX = 180f
} else {
binding.recyclerView.visibility = View.GONE
binding.ivExpand.rotationX = 0f
}
}
그리고 전환을 시작합니다.
val transition = TransitionInflater.from(itemView.context)
.inflateTransition(R.transition.icon_expand_toggle)
TransitionManager.beginDelayedTransition(parent, transition)
isExpanded = !isExpanded
setUpExpandedStatus()
gif에 있는 것을 포함한 애니메이션과 트랜지션 샘플을 만들었는데, 거기서 확인할 수 있습니다.
당신은 올바른 방향으로 가고 있습니다.애니메이션이 시작되기 직전에 레이아웃 높이가 0이 되도록 v1을 설정했는지 확인합니다.애니메이션을 시작하기 전에 애니메이션의 첫 번째 프레임처럼 보이도록 설정을 초기화하려고 합니다.
이게 내 해결책이었어, 나의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_http.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>
전송 anImageView
내 방식대로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);
}
});
}
이것은 적절한 작동 솔루션입니다. 제가 테스트해 본 결과는 다음과 같습니다.
예:
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는 뷰를 포함하는 컨테이너 뷰입니다.
@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);
}
view.getLayoutParams().height = (int) (initialHeight + distance * interpolatedTime);
view.requestLayout();
}
@Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(duration);
view.startAnimation(a);
}
이것은 제 드로이드 쿼리 라이브러리로 정말 간단합니다.시작의 경우 다음 레이아웃을 고려합니다.
<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();
}
View Property Animator(보기 속성 애니메이터)를 약간 비틀어 사용할 수 있습니다.축소하려면 보기를 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())
레이아웃 높이를 지정할 필요가 없는 버전을 만들어 사용하기가 훨씬 간편하고 깨끗합니다.해결책은 애니메이션의 첫 번째 프레임에서 높이를 얻는 것입니다(적어도 테스트 중에는 그 순간에 사용할 수 있습니다).이렇게 하면 임의의 높이와 아래쪽 여백이 있는 보기를 제공할 수 있습니다.
생성자에도 작은 해킹이 하나 있습니다. - 하단 여백이 -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);
}
}
Value Animator 사용:
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);
언급URL : https://stackoverflow.com/questions/4946295/android-expand-collapse-animation
'programing' 카테고리의 다른 글
전송 보안이 일반 텍스트 HTTP를 차단했습니다. (0) | 2023.06.15 |
---|---|
C 연결 오류: 'main'에 대한 정의되지 않은 참조 (0) | 2023.06.15 |
UI 테이블 보기:일부 행만 선택하고 다른 행은 선택하지 않는 방법 (0) | 2023.06.15 |
C 프로그램에는 main()이 필요합니까? (0) | 2023.06.15 |
리눅스에서 프로그램 성능을 측정하는 고해상도 타이머를 만드는 방법은 무엇입니까? (0) | 2023.06.15 |