Android: Mở rộng / thu gọn hình ảnh động


449

Giả sử tôi có một linearLayout dọc với:

[v1]
[v2]

Theo mặc định v1 có visibily = Gone. Tôi muốn hiển thị v1 với hình ảnh động mở rộng và đẩy xuống v2 cùng một lúc.

Tôi đã thử một cái gì đó như thế này:

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

Nhưng với giải pháp này, tôi có một cái chớp mắt khi hoạt hình bắt đầu. Tôi nghĩ đó là do v1 hiển thị kích thước đầy đủ trước khi hình động được áp dụng.

Với javascript, đây là một dòng của jQuery! Bất kỳ cách đơn giản để làm điều này với Android?

Câu trả lời:


734

Tôi thấy rằng câu hỏi này đã trở nên phổ biến vì vậy tôi đăng giải pháp thực tế của mình. Ưu điểm chính là bạn không cần phải biết chiều cao mở rộng để áp dụng hình động và một khi chế độ xem được mở rộng, nó sẽ điều chỉnh chiều cao nếu nội dung thay đổi. Đó là công việc tốt cho tôi.

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

Như được đề cập bởi @Jefferson trong các bình luận, bạn có thể có được một hình ảnh động mượt mà hơn bằng cách thay đổi thời lượng (và do đó là tốc độ) của hình ảnh động. Hiện tại, nó đã được đặt ở tốc độ 1dp / ms


13
v.measure (Biện pháp Trong một số trường hợp (của tôi - ListView), sự không phù hợp này dẫn đến giá trị targtetHeight sai
Johnny Doe

12
@Tom Esterez Điều này không hoạt động, nhưng không trơn tru lắm. Có bất kỳ công việc bổ sung để làm cho nó trơn tru?
acntwww

9
@acntwww Bạn có thể có được một hình ảnh động mượt mà nhân thời lượng với một số yếu tố, như 4.a.setDuration(((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density)) * 4)
Jefferson Henrique C. Soares

10
@Alioo, nhập android.view.animation.Transifying;
Jomia

5
Hoạt động tuyệt vời! Tôi gặp vấn đề với chiều cao đo được vì tôi muốn mở rộng một yếu tố dp cố định, vì vậy tôi đã thay đổi số đo thành 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);Điều đó hiệu quả với tôi!
vkislicins

140

Tôi đã cố gắng làm những gì tôi tin là một hình ảnh động rất giống nhau và tìm thấy một giải pháp tao nhã. Mã này giả định rằng bạn luôn đi từ 0-> h hoặc h-> 0 (h là chiều cao tối đa). Ba tham số của hàm tạo là view = view để được tạo hình động (trong trường hợp của tôi, một webview), targetHeight = chiều cao tối đa của khung nhìn và down = a boolean chỉ định hướng (true = extend, false = sụp đổ).

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
Có một lỗi đánh máy trong mã: tên phương thức "initalize" phải là "khởi tạo" hoặc nó sẽ không được gọi. ;) Tôi khuyên bạn nên sử dụng @Override trong tương lai để loại lỗi đánh máy này bị trình biên dịch bắt gặp.
Lorne Laliberte

4
Tôi đang làm như sau: "DropDownAnim anim = new DropDownAnim (Grid_titulos_atual, GRID_HEIGHT, true); anim.setDuration (500); anim.start ();" nhưng nó không hoạt động. Tôi đã đặt một số điểm dừng trên ứng dụng Thông tin nhưng chúng không bao giờ đạt được
Paulo Cesar

5
Ops, làm cho nó hoạt động, đó là view.startAnimation (a) ... Hiệu suất không tốt lắm, nhưng nó hoạt động :)
Paulo Cesar

3
@IamStalker Trong tình huống đó, có lẽ bạn nên khởi tạo với hai biến là startHeight và endHeight. Sau đó đổi thành: if (down) {newHeight = (int) (((endHeight-startedHeight) * interpiatedTime) + startedHeight); } other {newHeight = (int) (((endHeight-startedHeight) * (1 - interpiatedTime)) + startedHeight); }
Seth Nelson

3
@Seth Tôi nghĩ newHeight có thể chỉ đơn giản là (int) (((targetHeight -startingHeight) * interpiatedTime) + startedHeight), bất kể hướng nào, miễn là startHeight được đặt trong khởi tạo ().
Giorgos Kylafas

138

Tôi đã vấp phải vấn đề tương tự ngày hôm nay và tôi đoán giải pháp thực sự cho câu hỏi này là

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

Bạn sẽ phải đặt thuộc tính này cho tất cả các bố trí trên cùng, có liên quan đến sự thay đổi. Nếu bây giờ bạn đặt mức độ hiển thị của một bố cục thành Gone, thì bố cục kia sẽ chiếm không gian khi bố cục biến mất đang giải phóng nó. Sẽ có một hình ảnh động mặc định là một loại "mờ dần", nhưng tôi nghĩ bạn có thể thay đổi điều này - nhưng cái cuối cùng tôi chưa thử nghiệm, cho đến bây giờ.


2
+1, Bây giờ tôi đang tìm kiếm Tốc độ: thời lượng của animateLayoutChanges
Tushar Pandey

9
Thay đổi bố cục hoạt hình: developer.android.com/training/animation/layout.html
ccpizza

Nó không hoạt động sau khi nhấn nút quay lại. Bất kỳ đề xuất?
Hassan Tareq

4
Điều này hoạt động hoàn hảo để mở rộng hoạt hình, nhưng để thu gọn hoạt hình diễn ra sau khi bố cục cha mẹ bị thu hẹp.
Shine_joseph

3
@light_joseph yeah tôi đang sử dụng cái này trong một recyclerview và khi sụp đổ trông thật kỳ lạ: /
AmirG

65

Tôi đã sử dụng giải pháp của @LenaYan không hoạt động đúng với tôi ( vì nó đã chuyển đổi Chế độ xem thành chế độ xem 0 trước khi thu gọn và / hoặc mở rộng ) và thực hiện một số thay đổi.

Bây giờ nó hoạt động rất tốt , bằng cách lấy chiều cao trước đó của View và bắt đầu mở rộng với kích thước này. Sụp đổ là như nhau.

Bạn chỉ có thể sao chép và dán mã dưới đây:

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

Sử dụng:

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

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

Vừa đủ dễ!

Cảm ơn LenaYan cho mã ban đầu!


Mặc dù nó hoạt động, nó phụ thuộc vào cài đặt của nhà phát triển (thời lượng hoạt hình). Nếu nó bị vô hiệu hóa, không có hình ảnh động sẽ được hiển thị.
CoolMind

Có, nhưng nó có thể hoặc không thể là một vấn đề. Phụ thuộc vào ứng dụng của bạn. Chẳng hạn, bạn có thể dễ dàng làm cho thời lượng hoạt ảnh tỷ lệ thuận với kích thước được mở rộng / thu gọn với những thay đổi đơn giản. Có thời lượng hoạt hình ổn định giúp bạn tự do hơn một chút.
Geraldo Neto

Mở rộng hình ảnh động không hoạt động. nó trông giống như hoạt hình sụp đổ.
Ahamadullah Saikat

39

Một cách khác là sử dụng hình ảnh động tỷ lệ với các yếu tố tỷ lệ sau để mở rộng:

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

và cho sự sụp đổ:

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

làm thế nào để bắt đầu hoạt hình .. View.startAnimation (hoạt hình); dường như không hoạt động
Mahendran

đó là exaclty làm thế nào tôi bắt đầu hoạt hình. Các hoạt hình khác làm việc cho bạn?
ChristophK

1
Đã đi với phương pháp này, làm việc như một cơ duyên và không cần phải thực hiện những gì đã được thực hiện.
erbsman

15
Điều này không đẩy các khung nhìn xuống dưới nó trong hình ảnh động và xuất hiện như thể nó đang kéo dài chế độ xem hoạt hình từ 0 -> h.

5
Btw, xem hình ảnh động hoạt động tuyệt vời để nhân rộng: oView.animate (). ScaleY (0) để thu gọn theo chiều dọc; oView.animate (). scaleY (1) để mở (lưu ý rằng nó chỉ có sẵn sdk 12 trở lên).
Kirk B.

27

Câu trả lời của @Tom Esterez , nhưng được cập nhật để sử dụng view.measure () đúng cho mỗi Android getMeasuredHeight trả về các giá trị sai!

    // 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 và DURATION_MULTIPLIER là gì?
MidasLefko

Quên những thứ đó đi, addHeight là trong trường hợp bạn cần thêm chiều cao trong bản mở rộng của mình (có thể không) và DURATION_MODIFIER chỉ là công cụ sửa đổi tốc độ trong trường hợp bạn muốn tăng tốc / làm chậm hình ảnh động.
Erik B

1
Hoạt động tuyệt vời! Một độ trễ nhỏ xảy ra trong khi sử dụng TextView chỉ với một từ trên dòng cuối cùng. Và bạn có thể giải thích những gì PathInterpolator làm ..?
yennsarah

EasInOutQuart làm cho hình ảnh động lúc đầu chậm, sau đó nhanh, sau đó chậm ở cuối cho cảm giác rất tự nhiên. Họ nói về nó một cách sâu sắc ở đây Easings.net
Erik B

1
tôi đã thử phương pháp của bạn nhưng bất cứ khi nào hoạt hình kết thúc, chế độ xem của tôi không còn hiển thị nữa.
Aman Verma

26

Ok, tôi vừa tìm thấy một giải pháp RẤT xấu xí:

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

Hãy đề xuất một giải pháp tốt hơn!


3
+1, thậm chí cái này được đặt tên là xấu, nó hoạt động cho chế độ xem mà chúng ta chưa biết kích thước của nó (ví dụ: trong trường hợp chúng ta thêm chế độ xem mới được tạo (có kích thước là FILL_PARENT) cho phụ huynh và muốn tạo hiệu ứng quá trình này, bao gồm cả hoạt hình tăng trưởng kích thước cha mẹ).
Vit Khudenko

BTW, có vẻ như có một chút lỗi trong việc gọi View.onMeause(widthMeasureSpec, heightMeasureSpec), vì vậy thông số kỹ thuật chiều rộng và chiều cao nên được hoán đổi.
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();
}

1
Tôi có vấn đề này ... nội dung bên trong chế độ xem có thể thu gọn đang biến mất khi mở rộng. Tôi có Recycler View sẽ biến mất khi mở rộng khung nhìn này. @LenaYan
Akshay Mahajan

21

Nếu bạn không muốn mở rộng hoặc thu gọn tất cả các cách - đây là một Chiều cao đơn giản -

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

Sử dụng:

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

13

Tôi đã điều chỉnh câu trả lời hiện đang được chấp nhận bởi Tom Esterez , nó đã hoạt động nhưng có một hình ảnh động mượt mà và không mượt mà. Giải pháp của tôi về cơ bản thay thế Animationbằng một ValueAnimator, có thể được trang bị mộtInterpolator sự lựa chọn của bạn để đạt được các hiệu ứng khác nhau như vượt quá, nảy, tăng tốc, v.v.

Giải pháp này hoạt động tuyệt vời với các chế độ xem có chiều cao động (nghĩa là sử dụng WRAP_CONTENT), vì trước tiên, nó đo chiều cao yêu cầu thực tế và sau đó hoạt ảnh theo chiều cao đó.

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

Sau đó, bạn chỉ cần gọi expand( myView );hoặc collapse( myView );.


Cảm ơn. Bạn cũng có thể thêm một tình huống khi chiều cao tối thiểu không bằng 0.
CoolMind

tôi làm việc cho tôi cho linearlayout
Roger

Chỉ cần sửa các thông số được sử dụng trong v.measure()và bây giờ nó đang hoạt động hoàn hảo. Cảm ơn!
Shahood ul Hassan

9

Sử dụng các hàm mở rộng của Kotlin, đây là bài kiểm tra và câu trả lời ngắn nhất

Chỉ cần gọi animateVisibility (mở rộng / thu gọn) trên bất kỳ Chế độ xem nào.

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

muốn đăng cùng một câu trả lời :) Quá tệ điều này được chôn quá sâu ở đây.
muetzenflo

@muetzenflo Nếu ngày càng có nhiều người đưa ra câu trả lời, nó sẽ xuất hiện. :)
Rajkiran

Tôi thích giải pháp này cho đến khi tôi nhận ra nếu có một textview có nhiều dòng có chiều cao của quấn_content, khi được mở rộng, textview sẽ chỉ hiển thị một dòng. Tôi đang cố gắng khắc phục ngay bây giờ
olearyj234

Tôi đã thử điều này, nhưng hình ảnh động không được mượt mà. Để mở rộng, toàn bộ textview xuất hiện cùng một lúc và sau đó hoạt hình phát. Đối với sự sụp đổ, textview ngay lập tức mở rộng trở lại ngay sau khi sụp đổ, vì một số lý do. Bất cứ ý tưởng những gì tôi đang làm sai?
Neoith Acharya

7

Thêm vào câu trả lời tuyệt vời của Tom Esterez và bản cập nhật tuyệt vời của Erik B cho nó, tôi nghĩ rằng tôi đã đăng bài của riêng mình, thu gọn các phương thức mở rộng và hợp đồng thành một. Bằng cách này, ví dụ bạn có thể có một hành động như thế này ...

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

... sẽ gọi phương thức bên dưới và cho phép nó tìm ra những việc cần làm sau mỗi lần nhấp () ...

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

}

Tôi đã thử mã này nhưng để nó hoạt động trên nhiều chế độ xem, bạn phải cuộn. Bất cứ ý tưởng làm thế nào tôi có thể khắc phục điều này? stackoverflow.com/q/43916369/1009507
sammyukavi

@Ukavi Tôi đang sử dụng tính năng này với nhiều chế độ xem và nó hoạt động tốt trong ScrollView.
mjp66

Điều gì về một recyclerview?
sammyukavi

@Ukavi chưa có nhu cầu sử dụng nó trong tái chế nhưng tôi không thể hiểu tại sao nó không hoạt động. Bạn sẽ phải tự mình thử nghiệm một chút;)
mjp66

6

Tôi muốn thêm một cái gì đó vào câu trả lời rất hữu ích ở trên . Nếu bạn không biết chiều cao bạn sẽ đạt được kể từ khi lượt xem của bạn .getHeight () trả về 0, bạn có thể làm như sau để có được chiều cao:

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

Trong đó DUMMY_HIGH_DIMENSIONS là chiều rộng / chiều cao (tính bằng pixel), chế độ xem của bạn bị hạn chế để ... có một con số khổng lồ này là hợp lý khi chế độ xem được gói gọn trong ScrollView.


6

Đây là đoạn trích mà tôi đã sử dụng để thay đổi kích thước chiều rộng của chế độ xem (linearLayout) bằng hình ảnh động.

Mã được cho là mở rộng hoặc thu nhỏ theo kích thước mục tiêu. Nếu bạn muốn có chiều rộng fill_parent, bạn sẽ phải chuyển cha .getMeasuredWidth làm chiều rộng mục tiêu trong khi đặt cờ thành đúng.

Hy vọng nó sẽ giúp một số bạn.

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

}


Có mẹo nào để làm việc này không? Lớp có được độ rộng gốc và đích chính xác, nhưng các khung nhìn của tôi sẽ không thay đổi kích thước. Tôi đang sử dụng resizeAnim.start(). Cũng đã cố gắng có và không cósetFillAfter(true)
Ben Kane

Hiểu rồi. Phải gọi .startAnimation(resizeAnim)vào xem.
Ben Kane

6

Để có hình động mượt mà, vui lòng sử dụng Trình xử lý với phương thức chạy ..... Và thưởng thức Mở rộng / Thu gọn hình động

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

Và gọi bằng mã này:

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

}

Giải pháp khác là:

               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

giải pháp kết hợp từ @Tom Esterez và @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

Vâng, tôi đồng ý với các ý kiến ​​trên. Và thực tế, có vẻ như điều đúng (hoặc ít nhất là dễ nhất?) Là chỉ định (trong XML) chiều cao bố cục ban đầu là "0px" - và sau đó bạn có thể chuyển vào một đối số khác cho "toHeight" ( tức là "chiều cao cuối cùng") cho hàm tạo của lớp con Hoạt hình tùy chỉnh của bạn, ví dụ như trong ví dụ trên, nó sẽ trông giống như vậy:

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

Dù sao, hy vọng rằng sẽ giúp! :)


4

Đây là giải pháp của tôi. Tôi nghĩ nó đơn giản hơn. Nó chỉ mở rộng tầm nhìn nhưng có thể dễ dàng được mở rộng.

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

Tôi nghĩ giải pháp đơn giản nhất là đặt android:animateLayoutChanges="true"thành của bạn LinearLayoutvà sau đó chỉ hiển thị / ẩn chế độ xem bằng cách đặt mức độ hiển thị của nó. Hoạt động như một bùa mê, nhưng bạn không có kiểm soát về thời lượng hoạt hình


3

Bạn đang đi đúng hướng. Đảm bảo rằng bạn đã đặt v1 để có chiều cao bố trí bằng 0 ngay trước khi hoạt ảnh bắt đầu. Bạn muốn khởi tạo thiết lập của mình để trông giống như khung đầu tiên của hình động trước khi bắt đầu hoạt hình.


Tôi đồng ý nhưng làm thế nào để có được initHeight (được yêu cầu bởi hoạt hình của tôi) nếu tôi làm điều này?
Tom Esterez

Bạn đã thử thực sự chỉ lưu chiều cao ban đầu khi khởi tạo, đặt chế độ xem hiển thị ở đó và sau đó đặt v.getLayoutParams (). Height = 0; trực tiếp sau, tất cả trong khởi tạo?
Micah Hainline

Có, nếu tôi làm như vậy, phương thức khởi tạo được gọi với height = 0
Tom Esterez

3

Đây là giải pháp của tôi, tôi ImageViewlớn lên từ 100%để 200%và trở lại kích thước ban đầu của mình, sử dụng hai tập tin hình ảnh động bên trong res/anim/thư mục

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>

Gửi một ImageViewphương thức của tôisetAnimationGrowShrink()

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

setAnimationGrowShrink() phương pháp:

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

Đây là một giải pháp làm việc thích hợp, tôi đã thử nghiệm nó:

Ngoại lệ:

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

Sự sụp đổ:

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

Nhà hoạt hình giá trị:

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

Chế độ xem v là chế độ xem được làm động, PARENT_VIEW là chế độ xem chứa có chứa chế độ xem.


2

Điều này thực sự đơn giản với droidQuery . Để bắt đầu, hãy xem xét bố cục này:

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

Chúng ta có thể làm động chiều cao của giá trị mong muốn - giả 100dpsử - sử dụng mã sau:

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

Sau đó sử dụng droidQueryđể hoạt hình. Cách đơn giản nhất là với điều này:

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

Để làm cho hình ảnh động hấp dẫn hơn, hãy xem xét thêm việc nới lỏng:

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

Bạn cũng có thể thay đổi thời lượng AnimationOptionssử dụng duration()phương thức hoặc xử lý những gì xảy ra khi hoạt ảnh kết thúc. Đối với một ví dụ phức tạp, hãy thử:

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

Giải pháp tốt nhất để mở rộng / thu gọn chế độ xem:

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

Mặc dù nó hoạt động, nó cũng phụ thuộc vào cài đặt của nhà phát triển (thời lượng hoạt hình). Và đánh bóng mã của bạn, xóa chức năng lambda và định dạng lại onCheckedChanged.
CoolMind

Tại sao chỉ gọi requestLayout trên v sau khi thay đổi LayoutParams của v là đủ? Tôi nghĩ rằng cần phải gọi requestLayout trên cha mẹ của v
vlazzle

2

Bạn có thể sử dụng ViewPropertyAnimator với một chút thay đổi. Để thu gọn, mở rộng chế độ xem thành chiều cao 1 pixel, sau đó ẩn nó. Để mở rộng, hiển thị nó, sau đó mở rộng nó lên chiều cao của nó.

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

Trục cho biết chế độ xem tỷ lệ từ đâu, mặc định ở giữa. Thời lượng là tùy chọn (mặc định = 1000). Bạn cũng có thể đặt bộ nội suy để sử dụng, như.setInterpolator(new AccelerateDecelerateInterpolator())


1

Tôi đã tạo phiên bản mà bạn không cần chỉ định chiều cao bố cục, do đó dễ sử dụng và dễ sử dụng hơn rất nhiều. Giải pháp là lấy chiều cao trong khung đầu tiên của hình ảnh động (nó có sẵn tại thời điểm đó, ít nhất là trong các thử nghiệm của tôi). Bằng cách này, bạn có thể cung cấp Chế độ xem với lề cao và lề dưới tùy ý.

Ngoài ra còn có một chút hack trong hàm tạo - lề dưới được đặt thành -10000 để chế độ xem được ẩn trước khi chuyển đổi (ngăn nhấp nháy).

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

Sử dụng 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();

Trong trường hợp của tôi không làm gì cả. Ngoài ra, bạn có thể dễ dàng mã của bạn, thu gọn 2 dòng vào 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);

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

Lớp học có thể được gọi theo cách sau

   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

Dựa trên các giải pháp của @Tom Esterez và @Seth Nelson (top 2) tôi đã mô phỏng chúng. Cũng như các giải pháp ban đầu, nó không phụ thuộc vào tùy chọn Nhà phát triển (cài đặt hoạt hình).

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

Chà, sau 3 năm tôi đã thử nghiệm lại một vài giải pháp, nhưng chỉ có tôi mới hoạt động tốt.
CoolMind
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.