Làm cách nào để tạo hiệu ứng cho View.setVisibility (GONE)


84

Tôi muốn thực hiện Animationkhi một Viewkhả năng hiển thị được đặt thành GONE. Thay vì chỉ tan biến, Viewnên 'sụp đổ'. Tôi đã thử điều này với a ScaleAnimationnhưng sau đó Viewlà thu gọn, nhưng bố cục sẽ chỉ thay đổi kích thước không gian của nó sau (hoặc trước) Animationđiểm dừng (hoặc bắt đầu).

Làm cách nào để tôi có thể làm điều này Animationđể, trong khi tạo hoạt ảnh, các chữ cái thấp hơn Viewsẽ ở ngay bên dưới nội dung, thay vì có một khoảng trống?


Tôi đã sử dụng kỹ thuật tương tự, như Andy đã trình bày ở đây, trên ExpandAnimation của tôi: udinic.wordpress.com/2011/09/03/expanding-listview-items Tôi không sử dụng hoạt ảnh tỷ lệ, tôi chỉ tạo một Hoạt ảnh mới lớp cho điều đó.
Udinic

Điều này rất hữu ích khi tôi đang cố gắng làm điều này. Cảm ơn bạn
atraudes

Udinic xuất sắc .. thực sự đã giải quyết được vấn đề của tôi .. :) cảm ơn
Yousuf Qureshi

Tuyệt vời, tôi cần phải thích nghi với vấn đề của mình, nhưng cuối cùng thì nó vẫn hoạt động. Đối với tôi giải pháp này tốt hơn câu trả lời khác.
Derzu

Câu trả lời:


51

Dường như không có cách nào dễ dàng để thực hiện điều này thông qua API, vì hoạt ảnh chỉ thay đổi ma trận kết xuất của chế độ xem, không phải kích thước thực. Nhưng chúng ta có thể đặt một biên âm để đánh lừa LinearLayout nghĩ rằng chế độ xem ngày càng nhỏ.

Vì vậy, tôi khuyên bạn nên tạo lớp Hoạt hình của riêng bạn, dựa trên ScaleAnimation và ghi đè phương thức "applyTransformation" để đặt lề mới và cập nhật bố cục. Như thế này...

public class Q2634073 extends Activity implements OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.q2634073);
        findViewById(R.id.item1).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        view.startAnimation(new MyScaler(1.0f, 1.0f, 1.0f, 0.0f, 500, view, true));
    }

    public class MyScaler extends ScaleAnimation {

        private View mView;

        private LayoutParams mLayoutParams;

        private int mMarginBottomFromY, mMarginBottomToY;

        private boolean mVanishAfter = false;

        public MyScaler(float fromX, float toX, float fromY, float toY, int duration, View view,
                boolean vanishAfter) {
            super(fromX, toX, fromY, toY);
            setDuration(duration);
            mView = view;
            mVanishAfter = vanishAfter;
            mLayoutParams = (LayoutParams) view.getLayoutParams();
            int height = mView.getHeight();
            mMarginBottomFromY = (int) (height * fromY) + mLayoutParams.bottomMargin - height;
            mMarginBottomToY = (int) (0 - ((height * toY) + mLayoutParams.bottomMargin)) - height;
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            if (interpolatedTime < 1.0f) {
                int newMarginBottom = mMarginBottomFromY
                        + (int) ((mMarginBottomToY - mMarginBottomFromY) * interpolatedTime);
                mLayoutParams.setMargins(mLayoutParams.leftMargin, mLayoutParams.topMargin,
                    mLayoutParams.rightMargin, newMarginBottom);
                mView.getParent().requestLayout();
            } else if (mVanishAfter) {
                mView.setVisibility(View.GONE);
            }
        }

    }

}

Cảnh báo thông thường được áp dụng: vì chúng tôi đang ghi đè một phương thức được bảo vệ (applyTransformation), điều này không được đảm bảo sẽ hoạt động trong các phiên bản Android trong tương lai.


3
Tại sao tôi không nghĩ đến điều này ?! Cảm ơn bạn. Ngoài ra, tôi không nhận được: "Cảnh báo thông thường áp dụng: bởi vì chúng tôi đang ghi đè một phương thức được bảo vệ (applyTransformation), điều này không được đảm bảo sẽ hoạt động trong các phiên bản Android trong tương lai." - Tại sao các chức năng được bảo vệ lại khác nhau giữa các phiên bản API? Chúng không bị ẩn và được thực hiện bảo vệ để bạn có thể ghi đè chúng (nếu không chúng sẽ được phân bổ theo phạm vi gói).
MrSnowflake

Bạn có thể đúng về phương pháp được bảo vệ. Tôi có xu hướng thận trọng quá mức khi truy cập chúng trong API.
Andy

1
Điều này rất hiệu quả đối với tôi, ngoại trừ việc để "sụp đổ" hoạt động (từY = 0,0f, đếnY = 1,0f), tôi phải loại bỏ giá trị 0 - trong marginBottomToYphép tính.
dmon

2
Tôi sẽ đề nghị sử dụng kiểu chung MarginLayoutParamsthay vì truyền nó sang một LayoutParamkiểu cụ thể .
Paul Lammertsma

3
Giả sử tôi phải làm chuyển đổi hoạt ảnh, thì làm sao có thể làm ngược lại được. Làm ơn cho lời khuyên.
Umesh

99

Đặt chế độ xem trong một bố cục nếu không và đặt android:animateLayoutChanges="true"cho bố cục đó.


1
Yêu cầu API tối thiểu là 11 trở lên! Không thể sử dụng phương pháp này cho phiên bản thấp hơn.
Nagaraj Alagusudaram

2
đây là thuộc tính bố cục được đánh giá thấp nhất ... cảm ơn bạn!
Andres Santiago,

7

Tôi đã sử dụng kỹ thuật tương tự như Andy đã trình bày ở đây. Tôi đã viết lớp Hoạt hình của riêng mình cho lớp đó, lớp hoạt ảnh này sẽ tạo hiệu ứng cho giá trị của lề, khiến hiệu ứng của mục biến mất / xuất hiện. Nó trông như thế này:

public class ExpandAnimation extends Animation {

// Initializations...

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    super.applyTransformation(interpolatedTime, t);

    if (interpolatedTime < 1.0f) {

        // Calculating the new bottom margin, and setting it
        mViewLayoutParams.bottomMargin = mMarginStart
                + (int) ((mMarginEnd - mMarginStart) * interpolatedTime);

        // Invalidating the layout, making us seeing the changes we made
        mAnimatedView.requestLayout();
    }
}
}

Tôi có một ví dụ đầy đủ hoạt động trên bài đăng trên blog của tôi http://udinic.wordpress.com/2011/09/03/expanding-listview-items/


2

Tôi đã sử dụng kỹ thuật tương tự như Andy ở đây và tinh chỉnh nó để nó có thể được sử dụng để mở rộng và thu gọn mà không gặp trục trặc, cũng sử dụng kỹ thuật được mô tả tại đây: https://stackoverflow.com/a/11426510/1317564

import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
import android.widget.LinearLayout;

class LinearLayoutVerticalScaleAnimation extends ScaleAnimation {
    private final LinearLayout view;
    private final LinearLayout.LayoutParams layoutParams;

    private final float beginY;
    private final float endY;
    private final int originalBottomMargin;

    private int expandedHeight;
    private boolean marginsInitialized = false;
    private int marginBottomBegin;
    private int marginBottomEnd;

    private ViewTreeObserver.OnPreDrawListener preDrawListener;

    LinearLayoutVerticalScaleAnimation(float beginY, float endY,
            LinearLayout linearLayout) {
        super(1f, 1f, beginY, endY);

        this.view = linearLayout;
        this.layoutParams = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();

        this.beginY = beginY;
        this.endY = endY;
        this.originalBottomMargin = layoutParams.bottomMargin;

        if (view.getHeight() != 0) {
            expandedHeight = view.getHeight();
            initializeMargins();
        }
    }

    private void initializeMargins() {
        final int beginHeight = (int) (expandedHeight * beginY);
        final int endHeight = (int) (expandedHeight * endY);

        marginBottomBegin = beginHeight + originalBottomMargin - expandedHeight;
        marginBottomEnd = endHeight + originalBottomMargin - expandedHeight;
        marginsInitialized = true;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);     

        if (!marginsInitialized && preDrawListener == null) {                       
            // To avoid glitches, don't draw until we've initialized everything.
            preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {                    
                    if (view.getHeight() != 0) {
                        expandedHeight = view.getHeight();
                        initializeMargins();
                        adjustViewBounds(0f);
                        view.getViewTreeObserver().removeOnPreDrawListener(this);                               
                    }

                    return false;
                }
            };

            view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);                   
        }

        if (interpolatedTime < 1.0f && view.getVisibility() != View.VISIBLE) {          
            view.setVisibility(View.VISIBLE);           
        }

        if (marginsInitialized) {           
            if (interpolatedTime < 1.0f) {
                adjustViewBounds(interpolatedTime);
            } else if (endY <= 0f && view.getVisibility() != View.GONE) {               
                view.setVisibility(View.GONE);
            }
        }
    }

    private void adjustViewBounds(float interpolatedTime) {
        layoutParams.bottomMargin = 
                marginBottomBegin + (int) ((marginBottomEnd - marginBottomBegin) * interpolatedTime);       

        view.getParent().requestLayout();
    }
}

Có thể sử dụng điều này trước tiên để thu gọn một LinearLayout hiện có và sau đó mở rộng lại LinearLayout đó không? Khi tôi cố gắng làm điều này, nó chỉ sụp đổ và sẽ không mở rộng nữa (có thể do chiều cao của chế độ xem bây giờ là 0 hoặc tương tự như vậy).
AHaahr

Tôi thấy rằng nó hoạt động đáng tin cậy hơn khi bố cục tuyến tính chứa nhiều hơn một chế độ xem. Nếu nó chỉ chứa một chế độ xem, thì nó sẽ không phải lúc nào cũng mở rộng.
Tìm hiểu OpenGL ES
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.