Android: Perluas / runtuh animasi

449

Katakanlah saya memiliki linearLayout vertikal dengan:

[v1]
[v2]

Secara default v1 memiliki visibily = GONE. Saya ingin menunjukkan v1 dengan animasi yang diperluas dan menekan v2 secara bersamaan.

Saya mencoba sesuatu seperti ini:

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

Tetapi dengan solusi ini, saya memiliki berkedip ketika animasi dimulai. Saya pikir itu disebabkan oleh v1 menampilkan ukuran penuh sebelum animasi diterapkan.

Dengan javascript, ini adalah satu baris jQuery! Adakah cara sederhana untuk melakukan ini dengan android?

Tom Esterez
sumber

Jawaban:

734

Saya melihat bahwa pertanyaan ini menjadi populer sehingga saya memposting solusi saya yang sebenarnya. Keuntungan utama adalah Anda tidak perlu tahu ketinggian yang diperluas untuk menerapkan animasi dan begitu tampilan diperluas, itu menyesuaikan ketinggian jika konten berubah. Ini sangat bagus untuk saya.

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

Seperti yang disebutkan oleh @Jefferson dalam komentar, Anda dapat memperoleh animasi yang lebih halus dengan mengubah durasi (dan karenanya kecepatan) dari animasi tersebut. Saat ini, telah ditetapkan pada kecepatan 1dp / ms

Tom Esterez
sumber
13
v.measure (MeasureSpec.makeMeasureSpec (LayoutParams.MATCH_PARENT, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec (LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY)); Dalam beberapa kasus (my - ListView) ketidakcocokan ini mengarah pada nilai targtetHeight yang salah
Johnny Doe
12
@ Tom Esterez Ini berfungsi, tetapi tidak terlalu lancar. Apakah ada pekerjaan tambahan untuk membuatnya lancar?
acntwww
9
@acntwww Anda bisa mendapatkan animasi yang lancar mengalikan durasi dengan beberapa faktor, seperti 4.a.setDuration(((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density)) * 4)
Jefferson Henrique C. Soares
10
@Alioo, impor android.view.animation.Transformation;
Jomia
5
Bagus sekali! Saya memiliki masalah dengan ketinggian yang diukur karena saya ingin memperluas elemen dp tetap, jadi saya mengubah ukuran menjadi v.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));dan v.getLayoutParams().height = interpolatedTime == 1 ? targetHeight : (int)(targetHeight * interpolatedTime);Itu berhasil untuk saya!
vkislicins
140

Saya mencoba melakukan apa yang saya yakini sebagai animasi yang sangat mirip dan menemukan solusi yang elegan. Kode ini mengasumsikan bahwa Anda selalu bergerak dari 0 -> jam atau jam -> 0 (jam adalah ketinggian maksimum). Tiga parameter konstruktor adalah view = tampilan yang akan dianimasikan (dalam kasus saya, tampilan web), targetHeight = tinggi maksimum tampilan, dan turun = boolean yang menentukan arah (true = expansion, false = collapsing).

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;
    }
}
Seth Nelson
sumber
5
Ada kesalahan ketik dalam kode: nama metode "initalize" harus "menginisialisasi" atau tidak akan dipanggil. ;) Saya akan merekomendasikan menggunakan @Override di masa depan sehingga kesalahan ketik semacam ini ditangkap oleh kompiler.
Lorne Laliberte
4
Saya melakukan yang berikut: "DropDownAnim anim = DropDownAnim baru (grid_titulos_atual, GRID_HEIGHT, true); anim.setDuration (500); anim.start ();" tapi itu tidak berhasil. Saya menempatkan beberapa breakpoint pada applyTransformation tetapi tidak pernah tercapai
Paulo Cesar
5
Ops, mulai bekerja, ini view.startAnimation (a) ... Performa tidak terlalu bagus, tetapi bekerja :)
Paulo Cesar
3
@IamStalker Dalam situasi itu, Anda mungkin harus menginisialisasi dengan dua variabel, startingHeight dan endingHeight. Kemudian ubah ke: if (down) {newHeight = (int) (((endingHeight-startingHeight) * interpolatedTime) + startingHeight); } else {newHeight = (int) (((endingHeight-startingHeight) * (1 - interpolatedTime)) + startingHeight); }
Seth Nelson
3
@Seth Saya pikir newHeight dapat dengan mudah (int) (((targetHeight -startingHeight) * interpolatedTime) + startingHeight), tidak peduli arah, selama startingHeight diatur di inisialisasi ().
Giorgos Kylafas
138

Saya tersandung masalah yang sama hari ini dan saya kira solusi nyata untuk pertanyaan ini adalah ini

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

Anda harus mengatur properti ini untuk semua tata letak teratas, yang terlibat dalam shift. Jika sekarang Anda mengatur visibilitas satu tata letak ke GONE, yang lain akan mengambil ruang saat yang hilang melepaskannya. Akan ada animasi default yang merupakan semacam "fading out", tapi saya pikir Anda dapat mengubahnya - tetapi yang terakhir belum saya uji, untuk saat ini.

Mr.Fu
sumber
2
+1, Sekarang saya mencari Kecepatan: durasi animateLayoutChanges
Tushar Pandey
9
Animasi Perubahan Tata Letak: developer.android.com/training/animation/layout.html
ccpizza
Ini tidak berfungsi setelah menekan tombol kembali. Ada saran?
Hassan Tareq
4
Ini berfungsi dengan baik untuk memperluas animasi, tetapi untuk runtuh animasi terjadi setelah tata letak induk menyusut.
shine_joseph
3
@shine_joseph ya saya menggunakan ini di dalam recyclerview dan ketika runtuh terlihat sangat aneh: /
AmirG
65

Saya mengambil solusi @LenaYan yang tidak berfungsi dengan baik bagi saya ( karena mengubah tampilan menjadi tampilan ketinggian 0 sebelum runtuh dan / atau meluas ) dan membuat beberapa perubahan.

Sekarang berfungsi dengan baik , dengan mengambil View ketinggian sebelumnya dan mulai berkembang dengan ukuran ini. Runtuh adalah sama.

Anda cukup menyalin dan menempelkan kode di bawah ini:

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

Pemakaian:

//Expanding the View
   expand(yourView, 2000, 200);

// Collapsing the View     
   collapse(yourView, 2000, 100);

Cukup mudah!

Terima kasih LenaYan untuk kode awal!

Geraldo Neto
sumber
Meskipun berfungsi, itu tergantung pada pengaturan pengembang (durasi animasi). Jika dinonaktifkan, animasi tidak akan ditampilkan.
CoolMind
Ya, tapi itu mungkin atau mungkin tidak menjadi masalah. Tergantung pada aplikasi Anda. Misalnya, Anda dapat dengan mudah membuat durasi animasi sebanding dengan ukuran yang diperluas / diciutkan dengan perubahan sederhana. Memiliki durasi animasi yang dapat diatur memberi Anda sedikit lebih banyak kebebasan.
Geraldo Neto
Perluas animasi tidak berfungsi. sepertinya runtuh animasi.
Ahamadullah Saikat
39

Alternatifnya adalah menggunakan animasi skala dengan faktor penskalaan berikut untuk meluas:

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

dan untuk runtuh:

ScaleAnimation anim = new ScaleAnimation(1, 1, 1, 0);
ChristophK
sumber
cara memulai animasinya .. View.startAnimation (anim); sepertinya tidak berfungsi
Mahendran
itulah tepatnya bagaimana saya memulai animasi. Apakah animasi lain berfungsi untuk Anda?
ChristophK
1
Pergi dengan pendekatan ini, bekerja seperti pesona dan tidak perlu menerapkan apa yang sudah dilaksanakan.
erbsman
15
Ini tidak menekan tampilan di bawahnya selama animasi dan muncul seolah-olah itu memperluas tampilan animasi dari 0 -> h.
5
Btw, melihat animasi berfungsi baik untuk penskalaan: oView.animate (). ScaleY (0) untuk runtuh secara vertikal; oView.animate (). scaleY (1) untuk membuka (perhatikan ini hanya tersedia sdk 12 ke atas).
Kirk B.
27

@ Tom jawaban Esterez , tetapi diperbarui untuk menggunakan view.measure () dengan benar per Android getMeasuredHeight mengembalikan nilai yang salah!

    // 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);
    }
Erik B
sumber
1
Apa itu addHeight dan DURATION_MULTIPLIER?
MidasLefko
Lupa tentang itu, addHeight kalau-kalau Anda perlu ketinggian tambahan dalam ekspansi Anda (mungkin tidak) dan DURATION_MODIFIER hanyalah pengubah kecepatan jika Anda ingin mempercepat / memperlambat animasi.
Erik B
1
Bagus sekali! Satu kelambatan kecil terjadi saat menggunakan TextView dengan hanya satu kata di baris terakhir. Dan bisakah Anda menjelaskan apa yang PathInterpolator lakukan ..?
yennsarah
The easyInOutQuart membuat animasi lambat pada awalnya, lalu cepat, kemudian lambat pada akhirnya untuk perasaan yang sangat alami. Mereka membicarakannya secara mendalam di sini easings.net
Erik B
1
saya mencoba metode Anda tetapi setiap kali animasi berakhir tampilan saya tidak lagi terlihat.
Aman Verma
26

Ok, saya baru saja menemukan solusi yang SANGAT jelek:

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

Jangan ragu untuk mengusulkan solusi yang lebih baik!

Tom Esterez
sumber
3
+1, meskipun ini dinamai jelek, ini berfungsi untuk tampilan di mana kita belum tahu ukurannya (misalnya jika kita menambahkan tampilan yang baru dibuat (yang ukurannya FILL_PARENT) ke induknya dan ingin menghidupkan proses ini, termasuk menggerakkan pertumbuhan ukuran induk).
Vit Khudenko
BTW, sepertinya ada sedikit kesalahan dalam View.onMeause(widthMeasureSpec, heightMeasureSpec)invokasi, jadi spesifikasi lebar dan tinggi harus ditukar.
Vit Khudenko
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();
}
LenaYan
sumber
1
Saya memiliki masalah ini ... konten di dalam tampilan yang dapat dilipat menghilang saat ekspansi. Saya memiliki Tampilan Pendaur Ulang yang menghilang saat memperluas tampilan ini. @LenaYan
Akshay Mahajan
21

Jika Anda tidak ingin memperluas atau menutup semua jalan - di sini adalah HeightAnimation sederhana -

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

Pemakaian:

HeightAnimation heightAnim = new HeightAnimation(view, view.getHeight(), viewPager.getHeight() - otherView.getHeight());
heightAnim.setDuration(1000);
view.startAnimation(heightAnim);
Nir Hartmann
sumber
13

Saya mengadaptasi jawaban yang saat ini diterima oleh Tom Esterez , yang berfungsi tetapi memiliki animasi yang berombak dan tidak terlalu mulus. Solusi saya pada dasarnya menggantikan Animationdengan ValueAnimator, yang dapat dilengkapi dengan Interpolatorpilihan Anda untuk mencapai berbagai efek seperti overshoot, bouncing, akselerasi, dll.

Solusi ini bekerja sangat baik dengan tampilan yang memiliki ketinggian dinamis (yaitu menggunakan WRAP_CONTENT), karena pertama-tama mengukur ketinggian yang sebenarnya diperlukan dan kemudian menjiwai ketinggian itu.

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

Anda kemudian cukup menelepon expand( myView );atau collapse( myView );.

Magnus W
sumber
Terima kasih. Anda juga dapat menambahkan situasi saat ketinggian minimal bukan 0.
CoolMind
saya bekerja untuk saya untuk linearlayout
Roger
Hanya memperbaiki params digunakan v.measure()dan sekarang sudah berfungsi dengan baik. Terima kasih!
Shahood ul Hassan
9

Memanfaatkan Fungsi Ekstensi Kotlin ini diuji dan jawaban terpendek

Panggil saja animateVisibility (rentangkan / runtuh) pada Tampilan apa pun.

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()
}
Rajkiran
sumber
ingin memposting jawaban yang sama :) Sayang sekali ini terkubur begitu dalam di sini.
muetzenflo
@muetzenflo Jika semakin banyak orang yang memberikan jawaban, jawabannya akan muncul. :)
Rajkiran
Saya menyukai solusi ini sampai saya menyadari jika ada tampilan teks dengan beberapa baris dengan tinggi wrap_content, ketika diperluas, tampilan teks hanya akan menampilkan satu baris. Saya mencoba untuk memperbaikinya sekarang
olearyj234
Saya mencoba ini, tetapi animasi tampaknya tidak mulus. Untuk meluaskan, Seluruh tampilan teks muncul secara singkat dan kemudian animasi diputar. Untuk runtuh, tampilan teks segera mengembang lagi tepat setelah runtuh, karena alasan tertentu. Ada yang tahu apa yang saya lakukan salah?
Anchith Acharya
7

Menambahkan ke jawaban Tom Esterez yang luar biasa dan pembaruan Erik B yang sangat baik untuk itu, saya pikir saya akan memposting sendiri, memadatkan metode ekspansi dan kontrak menjadi satu. Dengan cara ini, misalnya Anda dapat melakukan tindakan seperti ini ...

button.setOnClickListener(v -> expandCollapse(view));

... yang memanggil metode di bawah ini dan membiarkannya mengetahui apa yang harus dilakukan setelah setiap 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);

}
mjp66
sumber
Saya mencoba kode ini, tetapi untuk membuatnya bekerja pada banyak tampilan, Anda harus menggulir. Adakah yang bisa saya perbaiki? stackoverflow.com/q/43916369/1009507
sammyukavi
@ Ukavi Saya menggunakan ini dengan banyak tampilan dan berfungsi baik di dalam ScrollView.
mjp66
Bagaimana dengan dalam recyclerview?
sammyukavi
@ Ukavi belum memiliki kebutuhan untuk menggunakannya dalam recyclerview, tetapi saya tidak bisa melihat mengapa itu tidak berhasil. Anda harus bereksperimen sendiri sedikit;)
mjp66
6

Saya ingin menambahkan sesuatu ke jawaban yang sangat membantu di atas . Jika Anda tidak tahu ketinggian yang akan Anda dapatkan sejak dilihat .getHeight () menghasilkan 0, Anda dapat melakukan hal berikut untuk mendapatkan ketinggian:

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

Di mana DUMMY_HIGH_DIMENSIONS adalah lebar / tinggi (dalam piksel) tampilan Anda dibatasi ... memiliki jumlah yang besar ini wajar ketika tampilan dienkapsulasi dengan ScrollView.

gardarh
sumber
6

Ini adalah cuplikan yang saya gunakan untuk mengubah ukuran lebar tampilan (LinearLayout) dengan animasi.

Kode tersebut seharusnya melakukan ekspansi atau menyusut sesuai dengan ukuran target. Jika Anda ingin lebar fill_parent, Anda harus meneruskan induk .getMeasuredWidth sebagai lebar target sambil mengatur bendera ke true.

Semoga ini bisa membantu sebagian dari Anda.

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

}

Codewarrior
sumber
Apakah ada trik untuk membuatnya bekerja? Kelas mendapatkan lebar asli dan target yang benar, tetapi pandangan saya tidak akan mengubah ukuran. Saya menggunakan resizeAnim.start(). Juga telah mencoba dengan dan tanpasetFillAfter(true)
Ben Kane
Oke. Harus memanggil .startAnimation(resizeAnim)tampilan.
Ben Kane
6

Untuk animasi Lancar gunakan Handler dengan menjalankan metode ..... Dan Nikmati Expand / Ciutkan animasi

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

Dan Panggil menggunakan kode ini:

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

}

Solusi lain adalah:

               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);
}
Ashish Saini
sumber
5

solusi gabungan dari @Tom Esterez dan @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);
Aliran
sumber
4

Ya, saya setuju dengan komentar di atas. Dan memang, sepertinya hal yang benar (atau setidaknya yang termudah?) Lakukan adalah menentukan (dalam XML) ketinggian tata letak awal "0px" - dan kemudian Anda dapat menyampaikan argumen lain untuk "toHeight" ( yaitu "ketinggian akhir") ke konstruktor sub-kelas Animasi khusus Anda, mis. dalam contoh di atas, akan terlihat seperti:

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

Bagaimanapun, harapan itu membantu! :)

Daniel Kopyc
sumber
4

Ini solusinya. Saya pikir ini lebih sederhana. Ini hanya memperluas tampilan tetapi dapat dengan mudah diperluas.

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;
    }
}
Kaloyan Donev
sumber
4

Saya pikir solusi termudah adalah mengatur android:animateLayoutChanges="true"ke Anda LinearLayoutdan kemudian hanya menampilkan / menyembunyikan tampilan dengan mengatur visibilitasnya. Bekerja seperti pesona, tetapi Anda tidak memiliki kendali atas durasi animasi

Jacek Kwiecień
sumber
3

Anda berada di jalur yang benar. Pastikan Anda telah mengatur v1 untuk memiliki ketinggian tata letak nol tepat sebelum animasi dimulai. Anda ingin menginisialisasi pengaturan Anda agar terlihat seperti bingkai pertama animasi sebelum memulai animasi.

Micah Hainline
sumber
Saya setuju tetapi bagaimana cara mendapatkan initialHeight (diperlukan oleh animasi saya) jika saya melakukan ini?
Tom Esterez
Sudahkah Anda mencoba sebenarnya hanya menyimpan ketinggian awal di inisialisasi, mengatur tampilan terlihat di sana, dan kemudian mengatur v.getLayoutParams (). Height = 0; langsung setelahnya, semua in inisialisasi?
Micah Hainline
Ya, jika saya melakukannya metode inisialisasi disebut dengan height = 0
Tom Esterez
3

Ini adalah solusi saya, saya ImageViewtumbuh dari 100%ke 200%dan kembali ke ukuran aslinya, menggunakan dua file animasi di dalam res/anim/folder

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>

Kirim ImageViewke metode sayasetAnimationGrowShrink()

ImageView img1 = (ImageView)findViewById(R.id.image1);
setAnimationGrowShrink(img1);

setAnimationGrowShrink() metode:

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

}
Jorgesys
sumber
3

Ini adalah solusi yang tepat, saya telah mengujinya:

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

Jatuh:

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

Nilai Animator:

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

Tampilan v adalah tampilan yang akan dianimasikan, PARENT_VIEW adalah tampilan wadah yang berisi tampilan.

Anubhav
sumber
2

Ini sangat sederhana dengan droidQuery . Untuk memulai, pertimbangkan tata letak ini:

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

Kami dapat menganimasikan ketinggian ke nilai yang diinginkan - katakan 100dp- menggunakan kode berikut:

//convert 100dp to pixel value
int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());

Kemudian gunakan droidQueryuntuk menghidupkan. Cara paling sederhana adalah dengan ini:

$.animate("{ height: " + height + "}", new AnimationOptions());

Untuk membuat animasi lebih menarik, pertimbangkan untuk menambahkan pelonggaran:

$.animate("{ height: " + height + "}", new AnimationOptions().easing($.Easing.BOUNCE));

Anda juga dapat mengubah durasi AnimationOptionspenggunaan duration()metode, atau menangani apa yang terjadi ketika animasi berakhir. Untuk contoh yang kompleks, cobalah:

$.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);
                                                                 }
                                                             }));
Phil
sumber
2

Solusi terbaik untuk memperluas / menutup tampilan:

    @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();
    }
Владислав Стариков
sumber
Meskipun berfungsi, itu juga tergantung pada pengaturan pengembang (durasi animasi). Dan memoles kode Anda, menghapus fungsi lambda dan memformat ulang onCheckedChanged.
CoolMind
Mengapa cukup untuk memanggil requestLayout hanya pada v setelah mengubah LayoutParams v? Saya pikir itu akan perlu untuk memanggil requestLayout pada orang tua v
vlazzle
2

Anda dapat menggunakan ViewPropertyAnimator dengan sedikit sentuhan. Untuk menutup, skala tampilan hingga ketinggian 1 piksel, lalu sembunyikan. Untuk mengembang, perlihatkan, lalu kembangkan hingga ketinggiannya.

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

Pivot memberi tahu tampilan dari mana skala, default ada di tengah. Durasi adalah opsional (standar = 1000). Anda juga dapat mengatur interpolator untuk digunakan, seperti.setInterpolator(new AccelerateDecelerateInterpolator())

Lagu
sumber
1

Saya membuat versi di mana Anda tidak perlu menentukan ketinggian tata letak, karena itu jauh lebih mudah dan bersih untuk digunakan. Solusinya adalah mendapatkan ketinggian di frame pertama animasi (tersedia pada saat itu, setidaknya selama tes saya). Dengan cara ini Anda bisa memberikan Tampilan dengan ketinggian dan margin bawah sewenang-wenang.

Ada juga satu retasan kecil di konstruktor - margin bawah diatur ke -10000 sehingga tampilan tetap tersembunyi sebelum transformasi (mencegah flicker).

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);
    }
}
Michał K
sumber
1

Gunakan 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();
Jon
sumber
Dalam kasus saya tidak melakukan apa-apa. Anda juga dapat memudahkan kode Anda, menciutkan 2 baris menjadi mainView.getLayoutParams().height = height.
CoolMind
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);
minyak tanah
sumber
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;
    }
}

Kelas dapat dipanggil dengan cara berikut

   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();
                    }
Amardeep
sumber
1

Didasarkan pada solusi oleh @ Tom Esterez dan @Seth Nelson (2 teratas) saya menyimulasikannya. Seperti halnya solusi orisinal, itu tidak bergantung pada opsi Pengembang (pengaturan animasi).

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);
}
CoolMind
sumber
Nah, setelah 3 tahun saya menguji lagi beberapa solusi, tetapi hanya saya yang berhasil.
CoolMind