Android: ขยาย / ยุบแอนิเมชัน


449

สมมติว่าฉันมี linearLayout แนวตั้งด้วย:

[v1]
[v2]

โดยค่าเริ่มต้น v1 มีให้เห็น = GONE ฉันต้องการแสดง v1 ด้วยแอนิเมชั่นที่ขยายและกด v2 พร้อมกัน

ฉันลองทำสิ่งนี้:

Animation a = new Animation()
{
    int initialHeight;

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final int newHeight = (int)(initialHeight * interpolatedTime);
        v.getLayoutParams().height = newHeight;
        v.requestLayout();
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        initialHeight = height;
    }

    @Override
    public boolean willChangeBounds() {
        return true;
    }
};

แต่ด้วยวิธีนี้ฉันจะกระพริบตาเมื่อภาพเคลื่อนไหวเริ่มต้นขึ้น ฉันคิดว่ามันเกิดจากการแสดงขนาดเต็ม v1 ก่อนที่ภาพเคลื่อนไหวจะถูกนำไปใช้

ด้วย javascript นี่คือ jQuery หนึ่งบรรทัด! มีวิธีง่ายๆในการทำเช่นนี้กับ Android ไหม?

คำตอบ:


734

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

public static void expand(final View v) {
    int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) v.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
    int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    v.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
    final int targetHeight = v.getMeasuredHeight();

    // Older versions of android (pre API 21) cancel animations for views with a height of 0.
    v.getLayoutParams().height = 1;
    v.setVisibility(View.VISIBLE);
    Animation a = new Animation()
    {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            v.getLayoutParams().height = interpolatedTime == 1
                    ? LayoutParams.WRAP_CONTENT
                    : (int)(targetHeight * interpolatedTime);
            v.requestLayout();
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };

    // Expansion speed of 1dp/ms
    a.setDuration((int)(targetHeight / v.getContext().getResources().getDisplayMetrics().density));
    v.startAnimation(a);
}

public static void collapse(final View v) {
    final int initialHeight = v.getMeasuredHeight();

    Animation a = new Animation()
    {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if(interpolatedTime == 1){
                v.setVisibility(View.GONE);
            }else{
                v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
                v.requestLayout();
            }
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };

    // Collapse speed of 1dp/ms
    a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
    v.startAnimation(a);
}

ตามที่ @Jefferson กล่าวไว้ในความคิดเห็นคุณสามารถรับภาพเคลื่อนไหวที่นุ่มนวลขึ้นโดยการเปลี่ยนระยะเวลา (และความเร็ว) ของภาพเคลื่อนไหว ปัจจุบันได้มีการตั้งค่าความเร็วที่ 1dp / ms


13
v.measure (MeasureSpec.makeMeasureSpec (LayoutParams.MATCH_PARENT, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec (LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY)); ในบางกรณี (ของฉัน - ListView) ความไม่ตรงกันนี้นำไปสู่ค่าเป้าหมายที่ไม่ถูกต้องความสูง
Johnny Doe

12
@Tom Esterez นี้ใช้งานได้ แต่ไม่ราบรื่นมาก มีงานอะไรเพิ่มเติมที่จะทำให้มันราบรื่น?
acntwww

9
@acntwww คุณสามารถสร้างอนิเมชั่นได้อย่างราบรื่นคูณด้วยปัจจัยบางอย่างเช่น 4a.setDuration(((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density)) * 4)
เจฟเฟอร์สันเฮนริคซี Soares

10
@Alioo นำเข้า android.view.animation.Transformation
Jomia

5
ใช้งานได้ดี! ฉันมีปัญหากับความสูงที่วัดได้เนื่องจากฉันต้องการขยายองค์ประกอบ dp คงที่ดังนั้นฉันจึงเปลี่ยนการวัดเป็นv.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));และv.getLayoutParams().height = interpolatedTime == 1 ? targetHeight : (int)(targetHeight * interpolatedTime);นั่นได้ผลสำหรับฉัน!
vkislicins

140

ฉันพยายามทำสิ่งที่ฉันเชื่อว่าเป็นภาพเคลื่อนไหวที่คล้ายกันมากและพบวิธีแก้ปัญหาที่สง่างาม รหัสนี้อนุมานว่าคุณจะไปจาก 0-> h หรือ h-> 0 (h เป็นความสูงสูงสุดเสมอ) พารามิเตอร์ Constructor สามตัวคือ view = มุมมองที่จะเคลื่อนไหว (ในกรณีของฉัน webview), targetHeight = ความสูงสูงสุดของมุมมองและ down = a บูลีนที่ระบุทิศทาง (true = expand, 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;
    }
}

5
มีการพิมพ์ผิดในรหัส: ชื่อวิธี "initalize" ควร "เริ่มต้น" หรือจะไม่ได้รับการเรียก ;) ฉันอยากจะแนะนำให้ใช้ @Override ในอนาคตเพื่อคอมไพเลอร์ประเภทนี้จะถูกดักจับ
Lorne Laliberte

4
ฉันกำลังทำสิ่งต่อไปนี้: "DropDownAnim anim = new DropDownAnim (grid_titulos_atual, GRID_HEIGHT, true); anim.setDuration (500); anim.start ();" แต่มันไม่ทำงาน ฉันวางเบรกพอยต์บางส่วนกับการใช้การเปลี่ยนรูป แต่ไม่เคยไปถึง
Cesar เปาโล

5
Ops ได้ไปทำงานมันเป็นมุมมองเริ่มต้นการเคลื่อนไหว (a) ... ประสิทธิภาพไม่ดีมาก แต่ใช้งานได้ดี :)
เปาโล Cesar

3
@IamStalker ในสถานการณ์นั้นคุณควรเริ่มต้นด้วยตัวแปรสองตัวคือเริ่มต้นความสูงและจุดสิ้นสุดความสูง จากนั้นเปลี่ยนเป็น: ถ้า (ลง) {newHeight = (int) ((สิ้นสุดความสูงตอนเริ่มต้นความสูง) * การแก้ไขเวลา) + การเริ่มต้นความสูง); } else {newHeight = (int) ((สิ้นสุด (ความสูงตอนเริ่มต้นความสูง) * (1 - interpolatedTime)) + กำลังเริ่มต้นความสูง); }
Seth Nelson

3
@Seth ฉันคิดว่า newHeight สามารถเป็นได้ (int) (((targetHeight -startingHeight) * การแก้ไขเวลา) + การเริ่มต้นความสูง) ไม่ว่าจะอยู่ในทิศทางใด
Giorgos Kylafas

138

ฉันสะดุดปัญหาเดียวกันวันนี้และฉันเดาว่าคำตอบที่แท้จริงสำหรับคำถามนี้คืออะไร

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

คุณจะต้องตั้งค่าคุณสมบัตินี้สำหรับเลย์เอาต์สูงสุดทั้งหมดซึ่งเกี่ยวข้องกับการเปลี่ยนแปลง หากคุณตั้งค่าการมองเห็นของหนึ่งเลย์เอาต์เป็น GONE อีกรูปแบบหนึ่งจะใช้พื้นที่เป็นส่วนที่หายไปกำลังถูกปล่อยออกมา จะมีอนิเมชั่นเริ่มต้นซึ่งเป็น "จางหายไป" บางอย่าง แต่ฉันคิดว่าคุณสามารถเปลี่ยนได้ - แต่ตอนสุดท้ายที่ฉันยังไม่ได้ทดสอบตอนนี้


2
+1, ตอนนี้ฉันกำลังค้นหาความเร็ว: ระยะเวลาของการเคลื่อนไหว LoutoutChanges
Tushar Pandey

9
การเปลี่ยนแปลง
โครง

มันไม่ทำงานหลังจากกดปุ่มย้อนกลับ ข้อเสนอแนะใด ๆ
Hassan Tareq

4
วิธีนี้ใช้งานได้อย่างสมบูรณ์แบบสำหรับการขยายภาพเคลื่อนไหว แต่สำหรับการยุบภาพเคลื่อนไหวจะเกิดขึ้นหลังจากที่เค้าโครงแม่ย่อ
shine_joseph

3
@shine_joseph ใช่ฉันกำลังใช้สิ่งนี้ใน recyclerview และเมื่อการยุบดูแปลก ๆ จริงๆ: /
AmirG

65

ฉันใช้วิธีแก้ปัญหาของ @LenaYan ที่ทำงานไม่ถูกต้องกับฉัน ( เพราะมันเปลี่ยนมุมมองเป็น 0 ความสูงก่อนที่จะยุบและ / หรือขยาย ) และทำการเปลี่ยนแปลงบางอย่าง

ตอนนี้มันใช้งานได้ดีโดยใช้ความสูงก่อนหน้า ของมุมมองและเริ่มขยายด้วยขนาดนี้ การยุบเหมือนกัน

คุณสามารถคัดลอกและวางรหัสด้านล่าง:

public static void expand(final View v, int duration, int targetHeight) {

    int prevHeight  = v.getHeight();

    v.setVisibility(View.VISIBLE);
    ValueAnimator valueAnimator = ValueAnimator.ofInt(prevHeight, targetHeight);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            v.getLayoutParams().height = (int) animation.getAnimatedValue();
            v.requestLayout();
        }
    });
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.setDuration(duration);
    valueAnimator.start();
}

public static void collapse(final View v, int duration, int targetHeight) {
    int prevHeight  = v.getHeight();
    ValueAnimator valueAnimator = ValueAnimator.ofInt(prevHeight, targetHeight);
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            v.getLayoutParams().height = (int) animation.getAnimatedValue();
            v.requestLayout();
        }
    });
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.setDuration(duration);
    valueAnimator.start();
}

การใช้งาน:

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

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

ง่ายพอ!

ขอบคุณ LenaYan สำหรับรหัสเริ่มต้น!


แม้ว่าจะใช้งานได้ แต่ก็ขึ้นอยู่กับการตั้งค่าของนักพัฒนาซอฟต์แวร์ (ระยะเวลาของภาพเคลื่อนไหว) หากปิดใช้งานจะไม่มีการแสดงภาพเคลื่อนไหว
CoolMind

ใช่ แต่มันอาจจะใช่หรือไม่ใช่ปัญหา ขึ้นอยู่กับใบสมัครของคุณ ตัวอย่างเช่นคุณสามารถทำให้ช่วงเวลาของภาพเคลื่อนไหวเป็นสัดส่วนกับขนาดที่ขยาย / ยุบได้อย่างง่ายดายด้วยการเปลี่ยนแปลงอย่างง่าย การมีช่วงเวลาของภาพเคลื่อนไหวที่กำหนดได้ช่วยให้คุณมีอิสระมากขึ้น
Geraldo Neto

ขยายภาพเคลื่อนไหวไม่ทำงาน ดูเหมือนว่าภาพเคลื่อนไหวการล่มสลาย
Ahamadullah Saikat

39

อีกทางเลือกหนึ่งคือการใช้ภาพเคลื่อนไหวในระดับที่มีปัจจัยการปรับขนาดต่อไปนี้สำหรับการขยาย:

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

และสำหรับการยุบ:

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

วิธีการเริ่มต้นการเคลื่อนไหว .. View.startAnimation (anim); ดูเหมือนจะไม่ทำงาน
Mahendran

นั่นเป็นวิธีที่แปลกประหลาดที่ฉันเริ่มต้นการเคลื่อนไหว ภาพเคลื่อนไหวอื่น ๆ ใช้งานได้สำหรับคุณหรือไม่?
ChristophK

1
ไปด้วยวิธีนี้ทำงานเหมือนมีเสน่ห์และไม่จำเป็นต้องใช้สิ่งที่ได้ดำเนินการไปแล้ว
erbsman

15
สิ่งนี้ไม่ได้กดมุมมองลงด้านล่างในระหว่างการเคลื่อนไหวและจะปรากฏราวกับว่ากำลังขยายมุมมองภาพเคลื่อนไหวจาก 0 -> h

5
Btw การดูภาพเคลื่อนไหวทำได้ดีสำหรับการปรับขนาด: oView.animate (). scaleY (0) เพื่อยุบในแนวตั้ง oView.animate (). scaleY (1) เพื่อเปิด (โปรดทราบว่ามันมีเฉพาะ sdk 12 ขึ้นไป)
Kirk B.

27

@Tom Esterez คำตอบแต่ได้รับการปรับปรุงให้ใช้ view.measure () อย่างถูกต้องต่อAndroid getMeasuredHeight ส่งกลับค่าที่ไม่ถูกต้อง!

    // http://easings.net/
    Interpolator easeInOutQuart = PathInterpolatorCompat.create(0.77f, 0f, 0.175f, 1f);

    public static Animation expand(final View view) {
        int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) view.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
        int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        view.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
        final int targetHeight = view.getMeasuredHeight();

        // Older versions of android (pre API 21) cancel animations for views with a height of 0 so use 1 instead.
        view.getLayoutParams().height = 1;
        view.setVisibility(View.VISIBLE);

        Animation animation = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {

               view.getLayoutParams().height = interpolatedTime == 1
                    ? ViewGroup.LayoutParams.WRAP_CONTENT
                    : (int) (targetHeight * interpolatedTime);

            view.requestLayout();
        }

            @Override
            public boolean willChangeBounds() {
                return true;
            }
        };

        animation.setInterpolator(easeInOutQuart);
        animation.setDuration(computeDurationFromHeight(view));
        view.startAnimation(animation);

        return animation;
    }

    public static Animation collapse(final View view) {
        final int initialHeight = view.getMeasuredHeight();

        Animation a = new Animation() {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                if (interpolatedTime == 1) {
                    view.setVisibility(View.GONE);
                } else {
                    view.getLayoutParams().height = initialHeight - (int) (initialHeight * interpolatedTime);
                    view.requestLayout();
                }
            }

            @Override
            public boolean willChangeBounds() {
                return true;
            }
        };

        a.setInterpolator(easeInOutQuart);

        int durationMillis = computeDurationFromHeight(view);
        a.setDuration(durationMillis);

        view.startAnimation(a);

        return a;
    }

    private static int computeDurationFromHeight(View view) {
        // 1dp/ms * multiplier
        return (int) (view.getMeasuredHeight() / view.getContext().getResources().getDisplayMetrics().density);
    }

1
addHeight และ DURATION_MULTIPLIER คืออะไร
MidasLefko

ลืมเกี่ยวกับสิ่งเหล่านั้น addHeight คือในกรณีที่คุณต้องการความสูงเป็นพิเศษในส่วนขยาย (อาจไม่ใช่) และ DURATION_MODIFIER เป็นเพียงตัวปรับความเร็วในกรณีที่คุณต้องการเพิ่มความเร็ว / ชะลอภาพเคลื่อนไหว
Erik B

1
ใช้งานได้ดี! เกิดความล่าช้าเล็กน้อยเกิดขึ้นขณะใช้ TextView โดยมีเพียงหนึ่งคำในบรรทัดสุดท้าย และคุณช่วยอธิบายว่า PathInterpolator ทำอะไรได้บ้าง .. ?
yennsarah

ความง่ายดาย InOutQuart ทำให้แอนิเมชั่นช้าลงในตอนแรกจากนั้นอย่างรวดเร็วจากนั้นก็ช้าในตอนท้ายเพื่อความรู้สึกที่เป็นธรรมชาติมาก พวกเขาพูดถึงมันในเชิงลึกที่นี่easings.net
Erik B

1
ฉันลองใช้วิธีการของคุณแล้ว แต่เมื่อใดก็ตามที่ภาพเคลื่อนไหวสิ้นสุดลงมุมมองของฉันจะไม่ปรากฏให้เห็นอีกต่อไป
Aman Verma

26

ตกลงฉันเพิ่งพบทางออกที่น่าเกลียดมาก:

public static Animation expand(final View v, Runnable onEnd) {
    try {
        Method m = v.getClass().getDeclaredMethod("onMeasure", int.class, int.class);
        m.setAccessible(true);
        m.invoke(
            v,
            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
            MeasureSpec.makeMeasureSpec(((View)v.getParent()).getMeasuredHeight(), MeasureSpec.AT_MOST)
        );
    } catch (Exception e){
        Log.e("test", "", e);
    }
    final int initialHeight = v.getMeasuredHeight();
    Log.d("test", "initialHeight="+initialHeight);

    v.getLayoutParams().height = 0;
    v.setVisibility(View.VISIBLE);
    Animation a = new Animation()
    {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            final int newHeight = (int)(initialHeight * interpolatedTime);
            v.getLayoutParams().height = newHeight;
            v.requestLayout();
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };
    a.setDuration(5000);
    v.startAnimation(a);
    return a;
}

อย่าลังเลที่จะเสนอทางออกที่ดีกว่า!


3
+1 แม้ชื่อนี้จะน่าเกลียด แต่ก็ใช้ได้กับมุมมองที่เรายังไม่รู้ขนาดของมัน (เช่นในกรณีที่เราเพิ่มมุมมองที่สร้างขึ้นใหม่ (ขนาดคือ FILL_PARENT) ลงในพาเรนต์และต้องการทำให้เคลื่อนไหว กระบวนการนี้รวมถึงการสร้างการเติบโตของขนาดพาเรนต์)
Vit Khudenko

BTW ดูเหมือนว่ามีข้อผิดพลาดเล็กน้อยในการView.onMeause(widthMeasureSpec, heightMeasureSpec)เรียกดังนั้นข้อมูลจำเพาะความกว้างและความสูงควรสลับ
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
ฉันมีปัญหานี้ ... เนื้อหาภายในมุมมองที่ยุบได้หายไปจากการขยายตัว ฉันมีมุมมองรีไซเลอร์ซึ่งหายไปจากการขยายมุมมองนี้ @LenaYan
Akshay Mahajan

21

หากคุณไม่ต้องการที่จะขยายหรือยุบทุกอย่าง - นี่คือความสูงความเรียบง่ายภาพเคลื่อนไหว -

import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;

public class HeightAnimation extends Animation {
    protected final int originalHeight;
    protected final View view;
    protected float perValue;

    public HeightAnimation(View view, int fromHeight, int toHeight) {
        this.view = view;
        this.originalHeight = fromHeight;
        this.perValue = (toHeight - fromHeight);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        view.getLayoutParams().height = (int) (originalHeight + perValue * interpolatedTime);
        view.requestLayout();
    }

    @Override
    public boolean willChangeBounds() {
        return true;
    }
}

การใช้งาน:

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

13

ฉันปรับคำตอบที่ Tom Esterez ยอมรับซึ่งใช้งานได้ แต่มีภาพเคลื่อนไหวที่เคลื่อนไหวเร็วและไม่ราบรื่น โซลูชันของฉันมักจะแทนที่Animationด้วย a ValueAnimatorซึ่งสามารถเลือกได้ตามความInterpolatorต้องการของคุณเพื่อให้ได้เอฟเฟกต์ต่าง ๆ เช่นการโอเวอร์โหลดเด้งเร่งความเร็ว ฯลฯ

โซลูชันนี้ใช้งานได้ดีกับมุมมองที่มีความสูงแบบไดนามิก (เช่นการใช้WRAP_CONTENT) เนื่องจากเป็นการวัดความสูงที่ต้องการจริงก่อนแล้วจึงย้ายไปที่ความสูงนั้น

public static void expand(final View v) {
    v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    final int targetHeight = v.getMeasuredHeight();

    // Older versions of android (pre API 21) cancel animations for views with a height of 0.
    v.getLayoutParams().height = 1;
    v.setVisibility(View.VISIBLE);

    ValueAnimator va = ValueAnimator.ofInt(1, targetHeight);
    va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            v.getLayoutParams().height = (Integer) animation.getAnimatedValue();
            v.requestLayout();
        }
    });
    va.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationEnd(Animator animation) {
            v.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
        }

        @Override public void onAnimationStart(Animator animation) {}
        @Override public void onAnimationCancel(Animator animation) {}
        @Override public void onAnimationRepeat(Animator animation) {}
    });
    va.setDuration(300);
    va.setInterpolator(new OvershootInterpolator());
    va.start();
}

public static void collapse(final View v) {
    final int initialHeight = v.getMeasuredHeight();

    ValueAnimator va = ValueAnimator.ofInt(initialHeight, 0);
    va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            v.getLayoutParams().height = (Integer) animation.getAnimatedValue();
            v.requestLayout();
        }
    });
    va.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationEnd(Animator animation) {
            v.setVisibility(View.GONE);
        }

        @Override public void onAnimationStart(Animator animation) {}
        @Override public void onAnimationCancel(Animator animation) {}
        @Override public void onAnimationRepeat(Animator animation) {}
    });
    va.setDuration(300);
    va.setInterpolator(new DecelerateInterpolator());
    va.start();
}

จากนั้นคุณเพียงโทรหรือexpand( myView );collapse( myView );


ขอบคุณ นอกจากนี้คุณยังสามารถเพิ่มสถานการณ์เมื่อความสูงต่ำสุดไม่เท่ากับ 0
CoolMind

ฉันทำงานให้ฉันเพื่อ linearlayout
Roger

เพิ่งแก้ไข params ที่ใช้v.measure()และตอนนี้ก็ทำงานได้อย่างสมบูรณ์แบบ ขอบคุณ!
Shahood ul Hassan

9

การใช้ฟังก์ชั่นเสริม Kotlin นี่คือการทดสอบและคำตอบที่สั้นที่สุด

เพียงเรียก animateVisibility (ขยาย / ยุบ) ในมุมมองใดก็ได้

fun View.animateVisibility(setVisible: Boolean) {
    if (setVisible) expand(this) else collapse(this)
}

private fun expand(view: View) {
    view.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
    val initialHeight = 0
    val targetHeight = view.measuredHeight

    // Older versions of Android (pre API 21) cancel animations for views with a height of 0.
    //v.getLayoutParams().height = 1;
    view.layoutParams.height = 0
    view.visibility = View.VISIBLE

    animateView(view, initialHeight, targetHeight)
}

private fun collapse(view: View) {
    val initialHeight = view.measuredHeight
    val targetHeight = 0

    animateView(view, initialHeight, targetHeight)
}

private fun animateView(v: View, initialHeight: Int, targetHeight: Int) {
    val valueAnimator = ValueAnimator.ofInt(initialHeight, targetHeight)
    valueAnimator.addUpdateListener { animation ->
        v.layoutParams.height = animation.animatedValue as Int
        v.requestLayout()
    }
    valueAnimator.addListener(object : Animator.AnimatorListener {
        override fun onAnimationEnd(animation: Animator) {
            v.layoutParams.height = targetHeight
        }

        override fun onAnimationStart(animation: Animator) {}
        override fun onAnimationCancel(animation: Animator) {}
        override fun onAnimationRepeat(animation: Animator) {}
    })
    valueAnimator.duration = 300
    valueAnimator.interpolator = DecelerateInterpolator()
    valueAnimator.start()
}

ต้องการโพสต์คำตอบเดียวกัน :) แย่มากที่ฝังไว้ลึกมากที่นี่
muetzenflo

@muetzenflo หากผู้คนมากขึ้นโหวตคำตอบมันจะเกิดขึ้น :)
Rajkiran

ฉันชอบวิธีนี้จนกว่าฉันจะรู้ว่ามีมุมมองข้อความที่มีหลายบรรทัดที่มีความสูงของ wrap_content เมื่อขยายตัวมุมมองข้อความจะแสดงเพียงหนึ่งบรรทัด ฉันกำลังพยายามแก้ไขตอนนี้
olearyj234

ฉันลองสิ่งนี้ แต่อนิเมชั่นนั้นดูไม่ราบรื่น สำหรับการขยายมุมมองข้อความทั้งหมดจะปรากฏขึ้นในเวลาสั้น ๆ จากนั้นภาพเคลื่อนไหวจะเล่น สำหรับการยุบ TextView จะขยายอีกครั้งทันทีหลังจากยุบด้วยเหตุผลบางประการ มีความคิดอะไรที่ฉันทำผิดหรือเปล่า?
Anchith Acharya

7

การเพิ่มคำตอบที่ยอดเยี่ยมของ Tom Esterez และการปรับปรุงที่ยอดเยี่ยมของ Erik B นั้นฉันคิดว่าฉันจะโพสต์เรื่องของตัวเองกระชับวิธีการขยายและสัญญาเป็นหนึ่งเดียว ตัวอย่างเช่นคุณสามารถมีการกระทำเช่นนี้ ...

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

... ซึ่งเรียกใช้วิธีการด้านล่างและแจ้งให้ทราบว่าจะทำอย่างไรหลังจากแต่ละ onClick () ...

public static void expandCollapse(View view) {

    boolean expand = view.getVisibility() == View.GONE;
    Interpolator easeInOutQuart = PathInterpolatorCompat.create(0.77f, 0f, 0.175f, 1f);

    view.measure(
        View.MeasureSpec.makeMeasureSpec(((View) view.getParent()).getWidth(), View.MeasureSpec.EXACTLY),
        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
    );

    int height = view.getMeasuredHeight();
    int duration = (int) (height/view.getContext().getResources().getDisplayMetrics().density);

    Animation animation = new Animation() {
        @Override protected void applyTransformation(float interpolatedTime, Transformation t) {
            if (expand) {
                view.getLayoutParams().height = 1;
                view.setVisibility(View.VISIBLE);
                if (interpolatedTime == 1) {
                    view.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
                } else {
                    view.getLayoutParams().height = (int) (height * interpolatedTime);
                }
                view.requestLayout();
            } else {
                if (interpolatedTime == 1) {
                    view.setVisibility(View.GONE);
                } else {
                    view.getLayoutParams().height = height - (int) (height * interpolatedTime);
                    view.requestLayout();
                }
            }
        }
        @Override public boolean willChangeBounds() {
            return true;
        }
    };

    animation.setInterpolator(easeInOutQuart);
    animation.setDuration(duration);
    view.startAnimation(animation);

}

ฉันลองใช้รหัสนี้ แต่ใช้งานได้หลายมุมมองคุณต้องเลื่อน ความคิดใดที่ฉันสามารถแก้ไขได้ stackoverflow.com/q/43916369/1009507
sammyukavi

@Ukavi ฉันใช้สิ่งนี้กับหลายมุมมองและทำงานได้ดีภายใน ScrollView
mjp66

แล้วใน recyclerview ล่ะ?
sammyukavi

@Ukavi ยังไม่จำเป็นต้องใช้มันใน recyclerview แต่ฉันไม่เห็นว่าทำไมมันไม่ทำงาน คุณจะต้องทดสอบด้วยตัวเองเล็กน้อย;)
mjp66

6

ผมอยากจะเพิ่มสิ่งที่เป็นประโยชน์มากคำตอบดังกล่าวข้างต้น หากคุณไม่ทราบความสูงคุณจะสิ้นสุดด้วยเนื่องจากมุมมองของคุณ getHeight () ส่งคืนค่า 0 คุณสามารถทำสิ่งต่อไปนี้เพื่อรับความสูง:

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

โดยที่ DUMMY_HIGH_DIMENSIONS คือความกว้าง / ความสูง (เป็นพิกเซล) มุมมองของคุณถูก จำกัด ... การมีจำนวนมากนี้เหมาะสมเมื่อมุมมองถูกห่อหุ้มด้วย ScrollView


6

นี่เป็นตัวอย่างข้อมูลที่ฉันใช้ในการปรับขนาดความกว้างของมุมมอง (LinearLayout) ด้วยภาพเคลื่อนไหว

รหัสควรจะขยายหรือหดตามขนาดเป้าหมาย หากคุณต้องการความกว้าง fill_parent คุณจะต้องผ่านพาเรนต์. getMeasuredWidth เป็นความกว้างเป้าหมายขณะตั้งค่าสถานะเป็นจริง

หวังว่ามันจะช่วยให้คุณบางคน

public class WidthResizeAnimation extends Animation {
int targetWidth;
int originaltWidth;
View view;
boolean expand;
int newWidth = 0;
boolean fillParent;

public WidthResizeAnimation(View view, int targetWidth, boolean fillParent) {
    this.view = view;
    this.originaltWidth = this.view.getMeasuredWidth();
    this.targetWidth = targetWidth;
    newWidth = originaltWidth;
    if (originaltWidth > targetWidth) {
        expand = false;
    } else {
        expand = true;
    }
    this.fillParent = fillParent;
}

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    if (expand && newWidth < targetWidth) {
        newWidth = (int) (newWidth + (targetWidth - newWidth) * interpolatedTime);
    }

    if (!expand && newWidth > targetWidth) {
        newWidth = (int) (newWidth - (newWidth - targetWidth) * interpolatedTime);
    }
    if (fillParent && interpolatedTime == 1.0) {
        view.getLayoutParams().width = -1;

    } else {
        view.getLayoutParams().width = newWidth;
    }
    view.requestLayout();
}

@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
    super.initialize(width, height, parentWidth, parentHeight);
}

@Override
public boolean willChangeBounds() {
    return true;
}

}


มีเคล็ดลับในการทำให้สิ่งนี้ทำงานได้หรือไม่? คลาสได้รับความกว้างดั้งเดิมและเป้าหมายที่ถูกต้อง แต่มุมมองของฉันจะไม่ปรับขนาด resizeAnim.start()ฉันใช้ นอกจากนี้ยังได้ลองกับและไม่ได้setFillAfter(true)
Ben Kane

เข้าใจแล้ว ต้องโทร.startAnimation(resizeAnim)ในมุมมอง
Ben Kane

6

สำหรับภาพเคลื่อนไหวที่ราบรื่นโปรดใช้ตัวจัดการด้วยวิธีการใช้ ..... และเพลิดเพลินกับภาพเคลื่อนไหวขยาย / ยุบ

    class AnimUtils{

                 public void expand(final View v) {
                  int ANIMATION_DURATION=500;//in milisecond
        v.measure(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        final int targtetHeight = v.getMeasuredHeight();

        v.getLayoutParams().height = 0;
        v.setVisibility(View.VISIBLE);
        Animation a = new Animation()
        {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                v.getLayoutParams().height = interpolatedTime == 1
                        ? LayoutParams.WRAP_CONTENT
                        : (int)(targtetHeight * interpolatedTime);
                v.requestLayout();
            }

            @Override
            public boolean willChangeBounds() {
                return true;
            }
        };

        // 1dp/ms
        a.setDuration(ANIMATION_DURATION);

      // a.setDuration((int)(targtetHeight / v.getContext().getResources().getDisplayMetrics().density));
        v.startAnimation(a);
    }



    public void collapse(final View v) {
        final int initialHeight = v.getMeasuredHeight();

        Animation a = new Animation()
        {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                if(interpolatedTime == 1){
                    v.setVisibility(View.GONE);
                }else{
                    v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
                    v.requestLayout();
                }
            }

            @Override
            public boolean willChangeBounds() {
                return true;
            }
        };

        // 1dp/ms
        a.setDuration(ANIMATION_DURATION);
       // a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
        v.startAnimation(a);
    }
}

และโทรโดยใช้รหัสนี้:

       private void setAnimationOnView(final View inactive ) {
    //I am applying expand and collapse on this TextView ...You can use your view 

    //for expand animation
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {

            new AnimationUtililty().expand(inactive);

        }
    }, 1000);


    //For collapse
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {

            new AnimationUtililty().collapse(inactive);
            //inactive.setVisibility(View.GONE);

        }
    }, 8000);

}

ทางออกอื่น ๆ คือ:

               public void expandOrCollapse(final View v,String exp_or_colpse) {
    TranslateAnimation anim = null;
    if(exp_or_colpse.equals("expand"))
    {
        anim = new TranslateAnimation(0.0f, 0.0f, -v.getHeight(), 0.0f);
        v.setVisibility(View.VISIBLE);  
    }
    else{
        anim = new TranslateAnimation(0.0f, 0.0f, 0.0f, -v.getHeight());
        AnimationListener collapselistener= new AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
            v.setVisibility(View.GONE);
            }
        };

        anim.setAnimationListener(collapselistener);
    }

     // To Collapse
        //

    anim.setDuration(300);
    anim.setInterpolator(new AccelerateInterpolator(0.5f));
    v.startAnimation(anim);
}

5

โซลูชั่นแบบรวมจาก @Tom Esterez และ @Geraldo Neto

public static void expandOrCollapseView(View v,boolean expand){

    if(expand){
        v.measure(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
        final int targetHeight = v.getMeasuredHeight();
        v.getLayoutParams().height = 0;
        v.setVisibility(View.VISIBLE);
        ValueAnimator valueAnimator = ValueAnimator.ofInt(targetHeight);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                v.getLayoutParams().height = (int) animation.getAnimatedValue();
                v.requestLayout();
            }
        });
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.setDuration(500);
        valueAnimator.start();
    }
    else
    {
        final int initialHeight = v.getMeasuredHeight();
        ValueAnimator valueAnimator = ValueAnimator.ofInt(initialHeight,0);
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                v.getLayoutParams().height = (int) animation.getAnimatedValue();
                v.requestLayout();
                if((int)animation.getAnimatedValue() == 0)
                    v.setVisibility(View.GONE);
            }
        });
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.setDuration(500);
        valueAnimator.start();
    }
}

//sample usage
expandOrCollapseView((Your ViewGroup),(Your ViewGroup).getVisibility()!=View.VISIBLE);

4

ใช่ฉันเห็นด้วยกับความคิดเห็นข้างต้น และจริง ๆ แล้วมันดูเหมือนสิ่งที่ถูกต้อง (หรืออย่างน้อยที่สุดง่ายที่สุด?) สิ่งที่ต้องทำคือการระบุ (ใน XML) ความสูงของเลย์เอาต์เริ่มต้นของ "0px" - จากนั้นคุณสามารถผ่านอาร์กิวเมนต์อีกตัวสำหรับ "toHeight" ( เช่น "ความสูงสุดท้าย") ถึงผู้สร้างคลาสย่อยแอนิเมชันที่คุณกำหนดเองเช่นในตัวอย่างด้านบนมันจะมีหน้าตาเป็นดังนี้:

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

อย่างไรก็ตามหวังว่าจะช่วยได้! :)


4

นี่คือทางออกของฉัน ฉันคิดว่ามันง่ายกว่า มันขยายมุมมอง แต่สามารถขยายได้ง่าย

public class WidthExpandAnimation extends Animation
{
    int _targetWidth;
    View _view;

    public WidthExpandAnimation(View view)
    {
        _view = view;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t)
    {
        if (interpolatedTime < 1.f)
        {
            int newWidth = (int) (_targetWidth * interpolatedTime);

            _view.layout(_view.getLeft(), _view.getTop(),
                    _view.getLeft() + newWidth, _view.getBottom());
        }
        else
            _view.requestLayout();
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight)
    {
        super.initialize(width, height, parentWidth, parentHeight);

        _targetWidth = width;
    }

    @Override
    public boolean willChangeBounds() {
        return true;
    }
}

4

ฉันคิดว่าวิธีที่ง่ายที่สุดคือตั้งค่าandroid:animateLayoutChanges="true"ให้คุณLinearLayoutแล้วเพียงแค่แสดง / ซ่อนมุมมองโดยตั้งค่าการมองเห็น ใช้งานได้อย่างมีเสน่ห์ แต่คุณไม่สามารถควบคุมระยะเวลาของภาพเคลื่อนไหวได้


3

คุณอยู่ในเส้นทางที่ถูกต้อง ตรวจสอบให้แน่ใจว่าคุณได้ตั้งค่า v1 ให้มีความสูงของรูปแบบเป็นศูนย์ก่อนที่ภาพเคลื่อนไหวจะเริ่มต้น คุณต้องการเริ่มต้นการตั้งค่าของคุณให้ดูเหมือนเฟรมแรกของภาพเคลื่อนไหวก่อนที่จะเริ่มภาพเคลื่อนไหว


ฉันเห็นด้วย แต่จะรับ initialHeight ได้อย่างไร (ต้องเป็นภาพเคลื่อนไหวของฉัน) ถ้าฉันทำเช่นนี้?
Tom Esterez

คุณได้ลองจริง ๆ แล้วเพิ่งบันทึกความสูงเริ่มต้นในการเตรียมใช้งานการตั้งค่ามุมมองที่มองเห็นได้จากนั้นตั้งค่า v.getLayoutParams (). height = 0; โดยตรงหลังจากทั้งหมดในการเริ่มต้น?
คาห์ฮินไลน์

ใช่ถ้าฉันทำเช่นนั้นวิธีการเริ่มต้นถูกเรียกว่ามี height = 0
Tom Esterez

3

นี่เป็นวิธีการแก้ปัญหาของฉันของฉันImageViewเติบโตขึ้นจาก100%การ200%และผลตอบแทนขนาดเดิมของเขาโดยใช้สองไฟล์ภาพเคลื่อนไหวภายในres/anim/โฟลเดอร์

anim_grow.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
 android:interpolator="@android:anim/accelerate_interpolator">
 <scale
  android:fromXScale="1.0"
  android:toXScale="2.0"
  android:fromYScale="1.0"
  android:toYScale="2.0"
  android:duration="3000"
  android:pivotX="50%"
  android:pivotY="50%"
  android:startOffset="2000" />
</set>

anim_shrink.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
 android:interpolator="@android:anim/accelerate_interpolator">
 <scale
  android:fromXScale="2.0"
  android:toXScale="1.0"
  android:fromYScale="2.0"
  android:toYScale="1.0"
  android:duration="3000"
  android:pivotX="50%"
  android:pivotY="50%"
  android:startOffset="2000" />
</set>

ส่งImageViewวิธีการของฉันsetAnimationGrowShrink()

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

setAnimationGrowShrink() วิธี:

private void setAnimationGrowShrink(final ImageView imgV){
    final Animation animationEnlarge = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim_grow);
    final Animation animationShrink = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim_shrink);

    imgV.startAnimation(animationEnlarge);

    animationEnlarge.setAnimationListener(new AnimationListener() {         
        @Override
        public void onAnimationStart(Animation animation) {}

        @Override
        public void onAnimationRepeat(Animation animation) {}

        @Override
        public void onAnimationEnd(Animation animation) {
            imgV.startAnimation(animationShrink);
        }
    });

    animationShrink.setAnimationListener(new AnimationListener() {          
        @Override
        public void onAnimationStart(Animation animation) {}

        @Override
        public void onAnimationRepeat(Animation animation) {}

        @Override
        public void onAnimationEnd(Animation animation) {
            imgV.startAnimation(animationEnlarge);
        }
    });

}

3

นี่เป็นวิธีการทำงานที่เหมาะสมฉันได้ทำการทดสอบ:

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

ยุบ:

private void collapse(View v) {
    int finalHeight = v.getHeight();

    mAnimator = slideAnimator(finalHeight, 0);

    mAnimator.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {

        }

        @Override
        public void onAnimationEnd(Animator animator) {
            //Height=0, but it set visibility to GONE
            llDescp.setVisibility(View.GONE);
        }

        @Override
        public void onAnimationCancel(Animator animator) {

        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    });
    mAnimator.start();
}

แอนิเมชั่นค่า:

private ValueAnimator slideAnimator(int start, int end) {
    ValueAnimator mAnimator = ValueAnimator.ofInt(start, end);

    mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            //Update Height
            int value = (Integer) valueAnimator.getAnimatedValue();
            ViewGroup.LayoutParams layoutParams = llDescp.getLayoutParams();
            layoutParams.height = value;
            v.setLayoutParams(layoutParams);
        }
    });
    return mAnimator;
}

มุมมอง v เป็นมุมมองที่จะเคลื่อนไหว PARENT_VIEW เป็นมุมมองคอนเทนเนอร์ที่มีมุมมอง


2

นี้เป็นเรื่องง่ายจริงๆกับdroidQuery สำหรับการเริ่มต้นให้พิจารณาเค้าโครงนี้:

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
    <LinearLayout
        android:id="@+id/v1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:text="View 1" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/v2"
        android:layout_width="wrap_content"
        android:layout_height="0dp" >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:text="View 2" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:text="View 3" />
    </LinearLayout>
</LinearLayout>

เราสามารถทำให้ความสูงเป็นค่าที่ต้องการ - พูด100dp- โดยใช้รหัสต่อไปนี้:

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

จากนั้นใช้droidQueryในการเคลื่อนไหว วิธีที่ง่ายที่สุดคือ:

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

หากต้องการทำให้อนิเมชั่นน่าสนใจยิ่งขึ้นให้ลองเพิ่มมาตรการผ่อนคลาย:

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

นอกจากนี้คุณยังสามารถเปลี่ยนระยะเวลาในการAnimationOptionsใช้duration()วิธีการหรือจัดการสิ่งที่เกิดขึ้นเมื่อภาพเคลื่อนไหวสิ้นสุดลง สำหรับตัวอย่างที่ซับซ้อนลอง:

$.animate("{ height: " + height + "}", new AnimationOptions().easing($.Easing.BOUNCE)
                                                             .duration(1000)
                                                             .complete(new Function() {
                                                                 @Override
                                                                 public void invoke($ d, Object... args) {
                                                                     $.toast(context, "finished", Toast.LENGTH_SHORT);
                                                                 }
                                                             }));

2

ทางออกที่ดีที่สุดสำหรับมุมมองขยาย / ยุบ:

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        View view = buttonView.getId() == R.id.tb_search ? fSearch : layoutSettings;
        transform(view, 200, isChecked
            ? ViewGroup.LayoutParams.WRAP_CONTENT
            : 0);
    }

    public static void transform(final View v, int duration, int targetHeight) {
        int prevHeight  = v.getHeight();
        v.setVisibility(View.VISIBLE);
        ValueAnimator animator;
        if (targetHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
            v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            animator = ValueAnimator.ofInt(prevHeight, v.getMeasuredHeight());
        } else {
            animator = ValueAnimator.ofInt(prevHeight, targetHeight);
        }
        animator.addUpdateListener(animation -> {
            v.getLayoutParams().height = (animation.getAnimatedFraction() == 1.0f)
                    ? targetHeight
                    : (int) animation.getAnimatedValue();
            v.requestLayout();
        });
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(duration);
        animator.start();
    }

แม้ว่าจะใช้งานได้ แต่ก็ขึ้นอยู่กับการตั้งค่าของนักพัฒนาซอฟต์แวร์ (ระยะเวลาของภาพเคลื่อนไหว) onCheckedChangedและขัดรหัสลบแลมบ์ดาฟังก์ชั่และฟอร์แมตของคุณ
CoolMind

ทำไมการโทรขอเลย์เอาต์เฉพาะใน v หลังจากเปลี่ยน LayoutParams ของ v แล้วเพียงพอหรือไม่ ฉันคิดว่ามันจำเป็นที่จะต้องเรียกการ
ร้องขอเลย์เอา

2

คุณสามารถใช้ ViewPropertyAnimator ด้วยการบิดเล็กน้อย หากต้องการยุบให้ปรับขนาดมุมมองเป็นความสูง 1 พิกเซลแล้วซ่อน หากต้องการขยายให้แสดงจากนั้นขยายเป็นความสูง

private void collapse(final View view) {
    view.setPivotY(0);
    view.animate().scaleY(1/view.getHeight()).setDuration(1000).withEndAction(new Runnable() {
        @Override public void run() {
            view.setVisibility(GONE);
        }
    });
}

private void expand(View view, int height) {
    float scaleFactor = height / view.getHeight();

    view.setVisibility(VISIBLE);
    view.setPivotY(0);
    view.animate().scaleY(scaleFactor).setDuration(1000);
}

เดือยบอกมุมมองที่จะไต่จากเริ่มต้นอยู่ตรงกลาง ระยะเวลาเป็นตัวเลือก (ค่าเริ่มต้น = 1,000) นอกจากนี้คุณยังสามารถตั้งค่า interpolator ให้ใช้เช่น.setInterpolator(new AccelerateDecelerateInterpolator())


1

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

นอกจากนี้ยังมีการแฮ็กหนึ่งครั้งเล็ก ๆ ในตัวสร้าง - ระยะขอบด้านล่างถูกตั้งค่าเป็น -10000 เพื่อให้มุมมองยังคงซ่อนอยู่ก่อนการเปลี่ยนแปลง (ป้องกันการสั่นไหว)

public class ExpandAnimation extends Animation {


    private View mAnimatedView;
    private ViewGroup.MarginLayoutParams mViewLayoutParams;
    private int mMarginStart, mMarginEnd;

    public ExpandAnimation(View view) {
        mAnimatedView = view;
        mViewLayoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
        mMarginEnd = mViewLayoutParams.bottomMargin;
        mMarginStart = -10000; //hide before viewing by settings very high negative bottom margin (hack, but works nicely)
        mViewLayoutParams.bottomMargin = mMarginStart;
        mAnimatedView.setLayoutParams(mViewLayoutParams);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
            //view height is already known when the animation starts
            if(interpolatedTime==0){
                mMarginStart = -mAnimatedView.getHeight();
            }
            mViewLayoutParams.bottomMargin = (int)((mMarginEnd-mMarginStart) * interpolatedTime)+mMarginStart;
            mAnimatedView.setLayoutParams(mViewLayoutParams);
    }
}

1

ใช้ ValueAnimator:

ValueAnimator expandAnimation = ValueAnimator.ofInt(mainView.getHeight(), 400);
expandAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(final ValueAnimator animation) {
        int height = (Integer) animation.getAnimatedValue();
        RelativeLayout.LayoutParams lp = (LayoutParams) mainView.getLayoutParams();
        lp.height = height;
    }
});


expandAnimation.setDuration(500);
expandAnimation.start();

ในกรณีของฉันไม่ได้ทำอะไรเลย นอกจากนี้คุณอาจบรรเทารหัสของคุณยุบ 2 mainView.getLayoutParams().height = heightสายเข้า
CoolMind

1
public static void slide(View v, int speed, int pos) {
    v.animate().setDuration(speed);
    v.animate().translationY(pos);
    v.animate().start();
}

// slide down
slide(yourView, 250, yourViewHeight);
// slide up
slide(yourView, 250, 0);

1
/**
 * Animation that either expands or collapses a view by sliding it down to make
 * it visible. Or by sliding it up so it will hide. It will look like it slides
 * behind the view above.
 * 
 */
public class FinalExpandCollapseAnimation extends Animation
{
    private View mAnimatedView;
    private int mEndHeight;
    private int mType;
    public final static int COLLAPSE = 1;
    public final static int EXPAND = 0;
    private LinearLayout.LayoutParams mLayoutParams;
    private RelativeLayout.LayoutParams mLayoutParamsRel;
    private String layout;
    private Context context;

    /**
     * Initializes expand collapse animation, has two types, collapse (1) and
     * expand (0).
     * 
     * @param view
     *            The view to animate
     * @param type
     *            The type of animation: 0 will expand from gone and 0 size to
     *            visible and layout size defined in xml. 1 will collapse view
     *            and set to gone
     */
    public FinalExpandCollapseAnimation(View view, int type, int height, String layout, Context context)
    {
        this.layout = layout;
        this.context = context;
        mAnimatedView = view;
        mEndHeight = mAnimatedView.getMeasuredHeight();
        if (layout.equalsIgnoreCase("linear"))
            mLayoutParams = ((LinearLayout.LayoutParams) view.getLayoutParams());
        else
            mLayoutParamsRel = ((RelativeLayout.LayoutParams) view.getLayoutParams());
        mType = type;
        if (mType == EXPAND)
        {
            AppConstant.ANIMATED_VIEW_HEIGHT = height;
        }
        else
        {
            if (layout.equalsIgnoreCase("linear"))
                mLayoutParams.topMargin = 0;
            else
                mLayoutParamsRel.topMargin = convertPixelsIntoDensityPixels(36);
        }
        setDuration(600);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t)
    {
        super.applyTransformation(interpolatedTime, t);
        if (interpolatedTime < 1.0f)
        {
            if (mType == EXPAND)
            {
                if (layout.equalsIgnoreCase("linear"))
                {
                    mLayoutParams.height = AppConstant.ANIMATED_VIEW_HEIGHT
                            + (-AppConstant.ANIMATED_VIEW_HEIGHT + (int) (AppConstant.ANIMATED_VIEW_HEIGHT * interpolatedTime));
                }
                else
                {
                    mLayoutParamsRel.height = AppConstant.ANIMATED_VIEW_HEIGHT
                            + (-AppConstant.ANIMATED_VIEW_HEIGHT + (int) (AppConstant.ANIMATED_VIEW_HEIGHT * interpolatedTime));
                }
                mAnimatedView.setVisibility(View.VISIBLE);
            }
            else
            {
                if (layout.equalsIgnoreCase("linear"))
                    mLayoutParams.height = mEndHeight - (int) (mEndHeight * interpolatedTime);
                else
                    mLayoutParamsRel.height = mEndHeight - (int) (mEndHeight * interpolatedTime);
            }
            mAnimatedView.requestLayout();
        }
        else
        {
            if (mType == EXPAND)
            {
                if (layout.equalsIgnoreCase("linear"))
                {
                    mLayoutParams.height = AppConstant.ANIMATED_VIEW_HEIGHT;
                    mLayoutParams.topMargin = 0;
                }
                else
                {
                    mLayoutParamsRel.height = AppConstant.ANIMATED_VIEW_HEIGHT;
                    mLayoutParamsRel.topMargin = convertPixelsIntoDensityPixels(36);
                }
                mAnimatedView.setVisibility(View.VISIBLE);
                mAnimatedView.requestLayout();
            }
            else
            {
                if (layout.equalsIgnoreCase("linear"))
                    mLayoutParams.height = 0;
                else
                    mLayoutParamsRel.height = 0;
                mAnimatedView.setVisibility(View.GONE);
                mAnimatedView.requestLayout();
            }
        }
    }

    private int convertPixelsIntoDensityPixels(int pixels)
    {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        return (int) metrics.density * pixels;
    }
}

ชั้นสามารถเรียกได้ในวิธีต่อไปนี้

   if (findViewById(R.id.ll_specailoffer_show_hide).getVisibility() == View.VISIBLE) {
                        ((ImageView) findViewById(R.id.iv_specialhour_seemore)).setImageResource(R.drawable.white_dropdown_up);

                        FinalExpandCollapseAnimation finalExpandCollapseAnimation = new FinalExpandCollapseAnimation(
                                findViewById(R.id.ll_specailoffer_show_hide),
                                FinalExpandCollapseAnimation.COLLAPSE,
                                SpecialOfferHeight, "linear", this);
                        findViewById(R.id.ll_specailoffer_show_hide)
                                .startAnimation(finalExpandCollapseAnimation);
                        ((View) findViewById(R.id.ll_specailoffer_show_hide).getParent()).invalidate();
                    } else {
                        ((ImageView) findViewById(R.id.iv_specialhour_seemore)).setImageResource(R.drawable.white_dropdown);

                        FinalExpandCollapseAnimation finalExpandCollapseAnimation = new FinalExpandCollapseAnimation(
                                findViewById(R.id.ll_specailoffer_show_hide),
                                FinalExpandCollapseAnimation.EXPAND,
                                SpecialOfferHeight, "linear", this);
                        findViewById(R.id.ll_specailoffer_show_hide)
                                .startAnimation(finalExpandCollapseAnimation);
                        ((View) findViewById(R.id.ll_specailoffer_show_hide).getParent()).invalidate();
                    }

1

ขึ้นอยู่กับวิธีแก้ปัญหาโดย @Tom Esterez และ @Seth Nelson (บนสุด 2) ฉันสร้างความแตกต่างให้พวกเขา เช่นเดียวกับโซลูชันดั้งเดิมไม่ได้ขึ้นอยู่กับตัวเลือกของนักพัฒนา (การตั้งค่าภาพเคลื่อนไหว)

private void resizeWithAnimation(final View view, int duration, final int targetHeight) {
    final int initialHeight = view.getMeasuredHeight();
    final int distance = targetHeight - initialHeight;

    view.setVisibility(View.VISIBLE);

    Animation a = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if (interpolatedTime == 1 && targetHeight == 0) {
                view.setVisibility(View.GONE);
            }
            view.getLayoutParams().height = (int) (initialHeight + distance * interpolatedTime);
            view.requestLayout();
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };

    a.setDuration(duration);
    view.startAnimation(a);
}

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