Android:展開/折りたたみアニメーション


449

垂直方向のlinearLayoutがあるとしましょう:

[v1]
[v2]

デフォルトでは、v1にはvisibily = 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の1行です。アンドロイドでこれを行う簡単な方法はありますか?

回答:


734

この質問が人気になったので、実際の解決策を投稿します。主な利点は、アニメーションを適用するために拡張された高さを知る必要がないことです。ビューが拡張されると、コンテンツが変更された場合に高さが調整されます。それは私にとって素晴らしい働きをします。

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の速度に設定されています


13
v.measure(MeasureSpec.makeMeasureSpec(LayoutParams.MATCH_PARENT、MeasureSpec.EXACTLY)、MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT、MeasureSpec.EXACTLY)); 場合によっては(my-ListView)、この不一致によりtargtetHeightの値が正しくなくなります
Johnny Doe

12
@Tom Esterezこれは機能しますが、非常にスムーズではありません。スムーズに進めるための追加作業はありますか?
acntwww 2013

9
あなたは4のように、いくつかの要因によって時間を掛けるスムーズにアニメーションを得ることができます@acntwwwa.setDuration(((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density)) * 4)
ジェファーソンエンリケC.ソアレス

10
@Alioo、インポートandroid.view.animation.Transformation;
ジョミア2014

5
よく働く!私は、私はメジャーを変更して、固定DPの要素を拡大したいと私は、測定した高さとの問題を抱えていたv.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));し、v.getLayoutParams().height = interpolatedTime == 1 ? targetHeight : (int)(targetHeight * interpolatedTime);それは私のために働きました!
vkislicins 2015

140

私は非常に似たアニメーションであると私が信じていることをしようとしていて、エレガントな解決策を見つけました。このコードは、常に0-> hまたはh-> 0(hは最大の高さ)から移動することを前提としています。3つのコンストラクターパラメーターは、view =アニメーション化するビュー(この場合はwebview)、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;
    }
}

5
コードにタイプミスがあります。「初期化」メソッド名は「初期化」でなければなりません。そうしないと呼び出されません。;)将来、@ Overrideを使用することをお勧めします。そうすることで、この種のタイプミスがコンパイラーに捕捉されるようになります。
Lorne Laliberte、2009

4
「DropDownAnim anim = new DropDownAnim(grid_titulos_atual、GRID_HEIGHT、true); anim.setDuration(500); anim.start();」を実行しています。しかし、それは機能していません。applyTransformationにいくつかのブレークポイントを設定しましたが、到達しませんでした
Paulo Cesar

5
Ops、動作しました。view.startAnimation(a)です...パフォーマンスはあまり良くありませんが、動作します:)
Paulo Cesar

3
@IamStalkerそのような状況では、おそらく2つの変数、startingHeightとendingHeightで初期化する必要があります。次に、次のように変更します。if(down){newHeight =(int)(((endingHeight-startingHeight)* interpolatedTime)+ startingHeight); } else {newHeight =(int)(((endingHeight-startingHeight)*(1-interpolatedTime))+ startingHeight); }
セスネルソン

3
@Seth newHeightは単純に(int)(((targetHeight -startingHeight)* interpolatedTime)+ startingHeight)にできると思います。startingHeightがinitialize()で設定されている限り、方向は関係ありません。
Giorgos Kylafas

138

私は今日同じ問題に出くわしました、そして私はこの質問の本当の解決策はこれだと思います

<LinearLayout android:id="@+id/container"
android:animateLayoutChanges="true"
...
 />

このプロパティは、シフトに関係するすべての最上位のレイアウトに設定する必要があります。1つのレイアウトの表示をGONEに設定すると、もう1つのレイアウトがスペースを使用して、消えるレイアウトが解放されます。「フェードアウト」のようなデフォルトのアニメーションがありますが、これは変更できると思いますが、今のところ、私がテストしていない最後のアニメーションです。


2
+1、今私はスピードを探しています:animateLayoutChangesの期間
Tushar Pandey

9
レイアウト変更のアニメーション化:developer.android.com/training/animation/layout.html
ccpizza '18

戻るボタンを押した後は機能しません。助言がありますか?
Hassan Tareq 2017

4
これは、展開アニメーションでは完全に機能しますが、折りたたみでは、親レイアウトが縮小された後にアニメーションが実行されます。
shine_joseph 2018

3
@shine_josephええ、私はこれをリサイクラービューの中で使用しています。折りたたみが本当に奇妙に見えるとき:/
AmirG

65

私は正しく機能しなかった@LenaYanのソリューションを採用し(折りたたんだり展開したりする前にビューを高さ0のビューに変換していたため)、いくつかの変更を加えました。

これで、ビューの以前の 高さを使用して、うまく機能しますしてこのサイズで拡張を開始するます。崩壊は同じです。

以下のコードをコピーして貼り付けるだけです。

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に感謝します。


機能しますが、開発者の設定(アニメーション期間)によって異なります。無効にすると、アニメーションは表示されません。
CoolMind 2016

はい。ただし、問題がある場合とそうでない場合があります。アプリケーションによって異なります。たとえば、簡単な変更で、アニメーションの継続時間を展開/折りたたみサイズに比例させることができます。アニメーション期間を設定できるので、もう少し自由が与えられます。
Geraldo Neto

エキスパンドアニメーションが機能しない。折りたたみアニメーションのように見えます。
Ahamadullah Saikat

39

別の方法として、以下のスケーリング係数を備えたスケールアニメーションを使用して拡張します。

ScaleAnimation anim = new ScaleAnimation(1, 1, 0, 1);

そして崩壊のために:

ScaleAnimation anim = new ScaleAnimation(1, 1, 1, 0);

アニメーションの開始方法.. View.startAnimation(anim); 動作していないようです
Mahendran

それが私がアニメーションを始める方法のエクササイズです。他のアニメーションでうまくいきますか?
ChristophK

1
このアプローチを採用すると、魅力のように機能し、すでに実装されているものを実装する必要はありません。
erbsman 2012年

15
これはアニメーション中にその下のビューを押し下げず、アニメーションビューを0-> hから引き伸ばしているように見えます。

5
ところで、ビューアニメーションはスケーリングに最適です。oView.animate()。scaleY(0)は垂直方向に折りたたみます。oView.animate()。scaleY(1)で開きます(SDK 12以降でのみ使用できます)。
Kirk B.

27

@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);
    }

1
addHeightおよびDURATION_MULTIPLIERとは何ですか?
MidasLefko

それらについて忘れてしまった、addHeightはエキスパンドで追加の高さが必要な場合(おそらく必要ない場合)であり、DURATION_MODIFIERはアニメーションを高速化/低速化したい場合の単なる速度修飾子です。
エリックB

1
よく働く!最後の行に1語のみのTextViewを使用しているときに、1つの小さなラグが発生します。そして、あなたはPathInterpolatorが何をするかについて説明できますか?
イネンサラ2015年

easeInOutQuartは、アニメーションを最初は遅く、次に速く、最後に遅くして、非常に自然な感じにします。彼らはここでそれについて詳しく話しますeasings.net
Erik B

1
私はあなたの方法を試しましたが、アニメーションが終了するたびにビューが表示されなくなります。
Aman Verma 2016

26

わかりました、とても醜い解決策を見つけました:

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;
}

より良いソリューションをご提案ください!


3
+1、これは醜い名前でも、まだサイズがわからないビューで機能します(たとえば、新しく作成されたビュー(サイズがFILL_PARENT)を親に追加し、アニメーション化したい場合親のサイズの増加をアニメーション化することを含むこのプロセス)。
Vit Khudenko、2012

ところで、View.onMeause(widthMeasureSpec, heightMeasureSpec)呼び出しに少しエラーがあるように見えるので、幅と高さの仕様を交換する必要があります。
Vit Khudenko、2012

22
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();
}

1
この問題があります。折りたたみ可能なビュー内のコンテンツが展開時に表示されなくなります。このビューを展開すると消えるリサイクラービューがあります。@LenaYan
Akshay Mahajan

21

完全に展開したり折りたたんだりしたくない場合-単純な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);

13

Tom Esterezが現在受け入れている回答を採用ましたが、うまくいきましたが、アニメーションが途切れ途切れでスムーズではありませんでした。私のソリューションは、基本的に置き換えAnimationValueAnimator装備することができ、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 );


ありがとう。最小限の高さが0でないときにも状況を追加することができます
CoolMind

私はlinearlayoutのために私のために働いています
ロジャー

で使用されたパラメータを修正しただけでv.measure()、現在は完全に機能しています。ありがとう!
Shahood ul Hassan

9

Kotlin拡張機能を使用するこれはテスト済みで、最も短い答えです

任意のビューで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()
}

同じ答えを投稿したかった:)残念ながら、これはここの奥深くに埋められている。
muetzenflo

@muetzenfloもっと多くの人が答えに賛成すると、それが出てくるでしょう。:)
Rajkiran

wrap_contentの高さの複数行のテキストビューがある場合、展開すると、テキストビューに1行しか表示されないことに気づくまで、私はこのソリューションを気に入りました。私は今修正しようとしています
olearyj234 '18年

試しましたが、アニメーションがなめらかに見えません。展開の場合、テキストビュー全体が一度に短時間だけ表示され、その後アニメーションが再生されます。折りたたみの場合、何らかの理由で、折りたたんだ直後にテキストビューがすぐに再び拡大します。私が間違っていることは何か考えていますか?
Anchith Acharya

7

Tom Esterezの優れた答えとErik Bの優れた更新に加えて、私は自分のテイクを投稿し、展開方法と契約方法を1つにまとめると思いました。このようにして、たとえば次のようなアクションを実行できます...

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);

}

私はこのコードを試しましたが、複数のビューで機能するには、スクロールする必要があります。どうすればこれを修正できますか?stackoverflow.com/q/43916369/1009507
sammyukavi

@Ukavi複数のビューでこれを使用しており、ScrollView内で正常に動作します。
mjp66

recyclerviewではどうですか?
sammyukavi

@Ukaviはまだrecyclerviewで使用する必要がありませんでしたが、なぜ機能しないのかわかりません。少し自分で試してみる必要があります;)
mjp66

6

上記の非常に役立つ回答に何かを追加したいと思います。ビューの.getHeight()が0を返すため、最終的に高さがわからない場合は、次のようにして高さを取得できます。

contentView.measure(DUMMY_HIGH_DIMENSION, DUMMY_HIGH_DIMENSION);
int finalHeight = view.getMeasuredHeight();

DUMMY_HIGH_DIMENSIONSは、ビューの幅/高さ(ピクセル単位)です。ビューがScrollViewでカプセル化されている場合、これに大きな数を設定することは妥当です。


6

これは、アニメーションでビューの幅(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;
}

}


これを機能させるためのトリックはありますか?クラスは正しい元の幅とターゲットの幅を取得しますが、ビューのサイズは変更されません。私は使用していresizeAnim.start()ます。また、ととせずに試してみましたsetFillAfter(true)
ベン・ケイン

とった。.startAnimation(resizeAnim)ビューを呼び出す必要がありました。
ベン・ケイン

6

スムーズアニメーションの場合は、ハンドラを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);
}

5

@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);

4

はい、上記のコメントに同意しました。そして確かに、それは正しい(または少なくとも最も簡単な?)ことのように思えます(XMLで)「0px」の初期レイアウトの高さを指定することです-そして、「toHeight」(つまり、「最終的な高さ」)をカスタムAnimationサブクラスのコンストラクターに追加します。たとえば、上記の例では、次のようになります。

    public DropDownAnim( View v, int toHeight ) { ... }

とにかく、それが役に立てば幸い!:)


4

これが私の解決策です。もっと簡単だと思います。ビューを拡大するだけですが、簡単に拡張できます。

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;
    }
}

4

最も簡単な解決策はandroid:animateLayoutChanges="true"あなたに設定し、LinearLayoutその可視性を設定することでビューを表示/非表示にすることだと思います。チャームのように機能しますが、アニメーションの継続時間を制御できません


3

あなたは正しい軌道に乗っています。アニメーションを開始する直前に、v1のレイアウトの高さがゼロに設定されていることを確認してください。アニメーションを開始する前に、アニメーションの最初のフレームのように設定を初期化する必要があります。


私は同意しますが、これを行う場合、initialHeight(アニメーションで必要)を取得する方法は?
トムエステレス

実際に初期化で初期高さを保存し、そこに表示されるビューを設定してから、v.getLayoutParams()。height = 0;を設定してみましたか?直後、すべて初期化中?
Micah

はい、そうする場合、initializeメソッドはheight = 0で呼び出されます
Tom Esterez

3

これは私が、私の解決策だったImageViewから成長100%200%の内側に2つのアニメーションファイルを使用して、そして彼の元のサイズに戻ります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);
        }
    });

}

3

これは適切に機能する解決策です、私はそれをテストしました:

例:

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;
}

ビューvはアニメーション化するビュー、PARENT_VIEWはビューを含むコンテナビューです。


2

これは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);
                                                                 }
                                                             }));

2

展開/折りたたみビューの最適なソリューション:

    @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();
    }

機能しますが、開発者の設定(アニメーション期間)にも依存します。コードを磨き、lambda-functionを削除して再フォーマットしonCheckedChangedます。
CoolMind 2016

vのLayoutParamsを変更した後、vでのみrequestLayoutを呼び出すだけで十分なのはなぜですか?vの親でrequestLayoutを呼び出す必要があると思いました
vlazzle

2

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())


1

レイアウトの高さを指定する必要がないバージョンを作成しました。そのため、使いやすく、すっきりしています。解決策は、アニメーションの最初のフレームの高さを取得することです(少なくとも私のテスト中は、その時点で利用可能です)。このようにして、ビューに任意の高さと下マージンを提供できます。

コンストラクターには1つの小さなハックもあります。下マージンは-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);
    }
}

1

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();

私の場合、何もしません。また、コードを簡単にして、2行をにまとめることもできますmainView.getLayoutParams().height = height
CoolMind 2016

1
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);

1
/**
 * 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();
                    }

1

@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);
}

まあ、3年後、私はいくつかのソリューションを再度テストしましたが、正しく機能したのは私のものだけでした。
CoolMind
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.