ฉันจะทำให้ View.setVisibility (GONE) เคลื่อนไหวได้อย่างไร


84

ฉันต้องการที่จะทำให้การAnimationสำหรับการเมื่อได้รับมันของชุดการแสดงผลView GONEแทนที่จะหายไปเพียงอย่างเดียวViewควร 'ยุบ' ฉันลองสิ่งนี้โดยมีScaleAnimationการViewยุบ แต่การจัดวางจะปรับขนาดพื้นที่หลังจาก (หรือก่อนหน้า) Animationจุดหยุด (หรือเริ่มต้น) เท่านั้น

ฉันจะทำAnimationเช่นนั้นได้อย่างไรในขณะที่เคลื่อนไหวส่วนล่างViewจะอยู่ด้านล่างเนื้อหาโดยตรงแทนที่จะมีช่องว่าง


ฉันใช้เทคนิคเดียวกันกับที่ Andy ได้นำเสนอไว้ที่นี่ใน ExpandAnimation ของฉัน: udinic.wordpress.com/2011/09/03/expanding-listview-itemsฉันไม่ได้ใช้ภาพเคลื่อนไหวขนาดฉันเพิ่งสร้างแอนิเมชั่นใหม่ ชั้นเรียนสำหรับสิ่งนั้น
Udinic

สิ่งนี้มีประโยชน์มากในขณะที่ฉันพยายามทำสิ่งนี้ ขอบคุณ
atraudes

Udinic ที่ยอดเยี่ยม .. แก้ปัญหาของฉันได้จริงๆ .. :) ขอบคุณ
Yousuf Qureshi

เยี่ยมมากฉันต้องปรับตัวให้เข้ากับปัญหาของฉัน แต่สุดท้ายมันก็ใช้ได้ สำหรับฉันคำตอบนี้ดีกว่าคำตอบอื่น ๆ
Derzu

คำตอบ:


51

ดูเหมือนจะไม่มีวิธีง่ายๆในการดำเนินการผ่าน API เนื่องจากภาพเคลื่อนไหวจะเปลี่ยนเมทริกซ์การแสดงผลของมุมมองไม่ใช่ขนาดจริง แต่เราสามารถตั้งค่าขอบลบเพื่อหลอกให้ LinearLayout คิดว่ามุมมองเล็กลง

ดังนั้นฉันขอแนะนำให้สร้างคลาส Animation ของคุณเองโดยใช้ ScaleAnimation และแทนที่เมธอด "applyTransformation" เพื่อกำหนดระยะขอบใหม่และอัปเดตเค้าโครง แบบนี้...

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

    }

}

ข้อแม้ทั่วไปมีผล: เนื่องจากเรากำลังลบล้างวิธีการป้องกัน (ใช้การเปลี่ยนแปลง) จึงไม่รับประกันว่าจะใช้ได้กับ Android เวอร์ชันอนาคต


3
ทำไมฉันถึงไม่คิดเรื่องนี้! ขอขอบคุณ. นอกจากนี้ฉันไม่เข้าใจ: "ข้อแม้ตามปกติใช้: เนื่องจากเรากำลังลบล้างวิธีการป้องกัน (ใช้การแปลง) จึงไม่รับประกันว่าจะใช้งานได้กับ Android เวอร์ชันอนาคต" - เหตุใดฟังก์ชันที่ได้รับการป้องกันจึงแตกต่างกันระหว่างเวอร์ชัน API สิ่งเหล่านี้จะไม่ถูกซ่อนและได้รับการป้องกันเพื่อให้คุณสามารถลบล้างได้ (มิฉะนั้นจะถูกกำหนดขอบเขตแพ็คเกจ)
MrSnowflake

คุณอาจถูกต้องเกี่ยวกับวิธีการป้องกัน ฉันมักจะระมัดระวังมากเกินไปเกี่ยวกับการเข้าถึงพวกเขาใน API
Andy

1
วิธีนี้ใช้ได้ผลดีสำหรับฉันยกเว้นว่าการ "ยุบ" ให้ทำงาน (fromY = 0.0f, toY = 1.0f) ฉันต้องลบ0 - การmarginBottomToYคำนวณออก
dmon

2
ฉันขอแนะนำให้ใช้ประเภททั่วไปMarginLayoutParamsแทนการแคสต์เป็นLayoutParamประเภทเฉพาะ
Paul Lammertsma

3
สมมติว่าฉันต้องสลับแอนิเมชั่นแล้วจะกลับด้านได้อย่างไร โปรดให้คำแนะนำ.
Umesh

99

วางมุมมองในเค้าโครงถ้ายังไม่ได้ตั้งค่าandroid:animateLayoutChanges="true"สำหรับเค้าโครงนั้น


1
Min API ที่ต้องการคือ 11 หรือสูงกว่า! ไม่สามารถใช้วิธีนี้สำหรับเวอร์ชันที่ต่ำกว่า
Nagaraj Alagusudaram

2
นี่คือแอตทริบิวต์เค้าโครงที่มีการประเมินต่ำที่สุด ...
Andres Santiago

7

ฉันใช้เทคนิคเดียวกับที่แอนดี้นำเสนอ ฉันเขียนคลาสแอนิเมชั่นของฉันเองเพื่อให้ค่าของระยะขอบเคลื่อนไหวทำให้เอฟเฟกต์ของรายการหายไป / ปรากฏขึ้น ดูเหมือนว่า:

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

ฉันมีตัวอย่างเต็มรูปแบบที่ใช้ได้กับโพสต์บล็อกของฉัน http://udinic.wordpress.com/2011/09/03/expanding-listview-items/


2

ฉันใช้เทคนิคเดียวกับ Andy ที่นี่และปรับแต่งเพื่อให้สามารถใช้ในการขยายและยุบได้โดยไม่มีข้อบกพร่องโดยใช้เทคนิคที่อธิบายไว้ที่นี่: 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();
    }
}

เป็นไปได้ไหมที่จะใช้สิ่งนี้เพื่อยุบ LinearLayout ที่มีอยู่ก่อนแล้วจึงขยาย LinearLayout เดิมอีกครั้งในภายหลัง เมื่อฉันพยายามทำสิ่งนี้มันก็ยุบและจะไม่ขยายอีก (อาจเป็นเพราะตอนนี้ความสูงของมุมมองเป็น 0 หรืออะไรประมาณนั้น)
AHaahr

ฉันพบว่ามันทำงานได้อย่างน่าเชื่อถือมากขึ้นเมื่อเค้าโครงเชิงเส้นมีมากกว่าหนึ่งมุมมอง หากมีเพียงมุมมองเดียวมุมมองนั้นจะไม่ขยายเสมอไป
เรียนรู้ OpenGL ES
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.