Gmail 3 단계 애니메이션 시나리오의 완전한 실무 샘플?


128

TL; DR : "Gmail 3 단편 애니메이션"시나리오라고 할 수 있는 완전한 작업 샘플 을 찾고 있습니다. 특히 다음과 같이 두 개의 조각으로 시작하려고합니다.

두 조각

UI 이벤트 (예 : 조각 B에서 무언가를 두드리는 것)가 발생하면 다음을 원합니다.

  • 화면을 왼쪽으로 밀기위한 조각 A
  • 조각 B를 사용하여 화면의 왼쪽 가장자리로 밀고 조각 A로 비워진 지점을 차지하도록 축소
  • 화면의 오른쪽에서 미끄러 져 들어가고 조각 B로 비워진 지점을 차지하는 조각 C

그리고 BACK 버튼을 누르면 작업 세트가 반대로 되길 원합니다.

이제 많은 부분 구현을 보았습니다. 아래에서 그 중 네 가지를 검토하겠습니다. 불완전한 것 외에도 모두 문제가 있습니다.


@Reto Meier는 기본 답변 을 동일한 기본 질문에 공헌 setCustomAnimations()하여 a와 함께 사용할 것임을 나타냅니다 FragmentTransaction. 두 조각 시나리오의 경우 (예를 들어, 처음에는 조각 A 만 표시되고 애니메이션 효과를 사용하여 새 조각 B로 교체하고 싶습니다) 완전히 동의합니다. 하나:

  • 하나의 "in"과 하나의 "out"애니메이션 만 지정할 수 있기 때문에 세 조각 시나리오에 필요한 모든 다른 애니메이션을 처리하는 방법을 볼 수 없습니다.
  • <objectAnimator>자신의 샘플 코드에서 픽셀 단위로 하드 와이어 위치를 사용하고, 그 화면 크기를 변화 주어진 비현실적으로 보일 것입니다, 아직 setCustomAnimations()자바에서이 일을 정의의 가능성을 배제, 애니메이션 자원을 필요로
  • 스케일에 대한 객체 애니메이터가 공간을 백분율로 할당 android:layout_weight하는 것과 같은 것들과 어떻게 묶여 있는지에 대한 손실이 있습니다.LinearLayout
  • Fragment C가 처음에 어떻게 처리되는지에 대한 손실이 있습니다 ( GONE?? android:layout_weight0스케일로 사전 애니메이션? 다른 것?)

@Roman Nurik 은 자신이 정의한 속성 을 포함 하여 모든 속성에 애니메이션을 적용 할 수 있다고 지적합니다 . 이를 통해 사용자 정의 레이아웃 관리자 서브 클래스를 발명하는 대신 유선 위치 문제를 해결할 수 있습니다. 도움이 되겠지만 나머지 Reto 솔루션으로 여전히 당황합니다.


이 pastebin 항목 의 작성자는 기본적으로 세 개의 조각이 모두 컨테이너에 상주하고 조각 C가 hide()트랜잭션 작업을 통해 처음에 숨겨져 있다고 말하는 일부 유쾌한 의사 코드를 보여줍니다 . 우리는 show()C와 hide()A는 UI의 이벤트가 발생했을 때. 그러나 B가 크기를 변경한다는 사실을 어떻게 처리하는지 알 수 없습니다. 또한 분명히 동일한 컨테이너에 여러 조각을 추가 할 수 있다는 사실에 의존하며 장기적으로 신뢰할 수있는 동작인지 여부는 확실하지 않습니다 ( findFragmentById()내가 살 수는 있지만 언급하지 않아야 함).


이 블로그 게시물 의 작성자는 Gmail이 전혀 사용하지 setCustomAnimations()않고 객체 애니메이터를 직접 사용 한다는 것을 나타냅니다 ( "루트 뷰의 왼쪽 여백 변경 + 오른쪽 뷰의 너비 변경"). 그러나 이것은 여전히 ​​2 조각 솔루션 AFAICT이며, 구현은 픽셀 단위로 치수를 다시 고정시킵니다.


나는 이것에 계속 꽂을 것이므로 언젠가 이것에 대답 할 수는 있지만 누군가 가이 애니메이션 시나리오를 위해 3 조각 솔루션을 해결하고 코드 (또는 그 링크)를 게시 할 수 있기를 정말로 바라고 있습니다. Android의 애니메이션을 사용하면 머리카락을 날리고 싶어하며, 나를 본 여러분은 이것이 과일이 거의 없다는 것을 알고 있습니다.


2
Romain Guy가 게시 한 것과 같은 것이 아닌가요? curious-creature.org/2011/02/22/…
Fernando Gallego

1
@ ferdy182 : 그는 AFAICT 조각을 사용하지 않습니다. 시각적으로 올바른 기본 효과이며 조각 기반 답변을 작성하기위한 다른 데이터 포인트로 사용할 수 있습니다. 감사!
CommonsWare

1
생각 : 표준 이메일 앱은 내 Nexus 7에서 비슷한 동작을하지만 완전히 동일하지는 않지만 오픈 소스 여야합니다.
Christopher Orr

4
이메일 앱은 왼쪽 뷰의 위치와 다른 두 뷰의 너비 / 가시성 (각각 관련 조각이 포함되어 있음)을 애니메이션으로 만드는 사용자 정의 "ThreePaneLayout"을 사용합니다.
Christopher Orr

2
안녕하세요 @CommonsWare 여기에 전체 답변을 ppl하지 않아도되지만 매우 만족스러운 답변을 한 것과 비슷한 질문이 있습니다. 따라서, 당신이 그것을 체크 아웃 (의견도 확인) 제안 : stackoverflow.com/a/14155204/906362
Budius

답변:


67

github 에서 제안을 업로드했습니다 (이 종류의 애니메이션에는 view 하드웨어 가속을 사용하지만 모든 안드로이드 버전으로 작업하고 있습니다. 하드웨어 가속 장치의 경우 비트 맵 캐싱 구현이 더 적합해야 함)

애니메이션이 포함 된 데모 비디오는 여기 (화면 캐스트의 느린 프레임 속도 원인. 실제 성능이 매우 빠름)


용법:

layout = new ThreeLayout(this, 3);
layout.setAnimationDuration(1000);
setContentView(layout);
layout.getLeftView();   //<---inflate FragmentA here
layout.getMiddleView(); //<---inflate FragmentB here
layout.getRightView();  //<---inflate FragmentC here

//Left Animation set
layout.startLeftAnimation();

//Right Animation set
layout.startRightAnimation();

//You can even set interpolators

설명 :

새로운 사용자 정의 RelativeLayout (ThreeLayout) 및 2 개의 사용자 정의 애니메이션 (MyScalAnimation, MyTranslateAnimation)을 만들었습니다.

ThreeLayout다른 보이는 뷰가 있다고 가정하고 왼쪽 창의 가중치를 매개 변수로 가져옵니다 weight=1.

따라서 new ThreeLayout(context,3)자식이 3 명이고 왼쪽 창에 전체 화면의 1/3이있는 새보기가 만들어집니다. 다른보기는 사용 가능한 모든 공간을 차지합니다.

런타임에 너비를 계산합니다. 더 안전한 구현은 drawment ()에서 치수가 처음 계산되는 것입니다. post () 대신

스케일링 및 번역 애니메이션은 실제로 의사-[스케일, 이동]이 아니라 뷰의 크기를 조정하고 이동합니다. 공지 사항 fillAfter(true)어디서나 사용되지 않습니다.

View2가 Right_of입니다. View1

View3이 Right_of View2

이러한 규칙을 설정하면 RelativeLayout은 다른 모든 것을 처리합니다. 애니메이션이 margins(이동할 때) [width,height]스케일로 변경

각 어린이에게 접근하려면 (Fragment로 부 풀릴 수 있도록 전화하십시오.)

public FrameLayout getLeftLayout() {}

public FrameLayout getMiddleLayout() {}

public FrameLayout getRightLayout() {}

아래는 2 애니메이션을 보여줍니다


스테이지 1

--- IN 화면 ----------! ----- OUT ----

[보기 1] [_____보기 2 _____] [_____보기 3_____]

2 단계

--OUT-! -------- IN 화면 ------

[보기 1] [보기 2] [_____보기 3_____]


1
스케일 애니메이터를 아직 구현하지는 않았지만, 단색의 컬러 필드에서는 잘 작동하고 "실제"컨텐츠에 대해서는 잘 작동하지 않을까 걱정됩니다. 단편 A의 공간에 맞게 단편 B의 크기를 조정 한 결과는 단편 B가 원래 배치 된 것과 다르지 않아야합니다. 예를 들어, 스케일 애니메이터 인 AFAIK는 글꼴 크기를 조정 View하지만 ( 애니메이션의 모든 것을 더 작게 그려서 ) Gmail / 이메일에서는 더 작은 글꼴이 아니라 단어 수가 적습니다.
CommonsWare

1
@CommonsWare scaleAnimation은 추상적 이름입니다. 실제로 스케일링이 발생하지 않습니다. 실제로 뷰의 너비가 조정됩니다. 소스를 확인하십시오. LayoutResizer가 더 나은 이름 일 수 있습니다.
weakwire

1
에 대한 scaleX가치에 대한 근원에 대한 나의 해석은 View아니지만, 나는 잘못 해석 할 수 있습니다.
CommonsWare

1
@CommonsWare는이 github.com/weakwire/3PaneLayout/blob/master/src/com/example/… out을 확인하십시오 . 애니메이션을 확장하는 유일한 이유는 interpolatedTime에 액세스하는 것입니다. 폭 = (int) 폭; 모든 마법을 수행하고 scaleX가 아닙니다. 지정된 시간 동안 변경되는 실제보기 치수입니다.
weakwire

2
비디오는 좋은 프레임 속도를 보여 주지만 예제에 사용 된 뷰는 매우 간단합니다. 애니메이션 중 requestLayout ()을 수행하는 것은 비용이 많이 듭니다. 특히 어린이가 복잡한 경우 (예 : ListView). 이로 인해 애니메이션이 고르지 않을 수 있습니다. Gmail 앱은 requestLayout을 호출하지 않습니다. 대신 애니메이션이 시작되기 직전에 작은 패널이 가운데 패널에 추가됩니다.
Thierry-Dimitri Roy

31

자, 여기는 질문의 의견에 대한 @Christopher의 제안에 따라 Email AOSP 앱에서 파생 된 내 솔루션입니다.

https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane

@weakwire의 솔루션은 Animation애니메이터보다는 클래식을 사용하지만 RelativeLayout규칙을 사용하여 위치를 강제합니다. 현상금 관점에서 볼 때, 그는 더 매끄러운 솔루션을 가진 다른 누군가가 아직 답변을 게시하지 않는 한 현상금을 얻을 것입니다.


간단히 말해 ThreePaneLayout해당 프로젝트의 LinearLayout하위 클래스는 세 명의 자녀가있는 환경에서 작동하도록 설계된 하위 클래스입니다. 이 아이들의 너비는 원하는 방법을 통해 레이아웃 XML에서 설정할 수 있습니다-가중치를 사용하여 표시하지만 차원 리소스 또는 기타로 특정 너비를 설정할 수 있습니다. 세 번째 자식-문제의 조각 C는 너비가 0이어야합니다.

package com.commonsware.android.anim.threepane;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;

public class ThreePaneLayout extends LinearLayout {
  private static final int ANIM_DURATION=500;
  private View left=null;
  private View middle=null;
  private View right=null;
  private int leftWidth=-1;
  private int middleWidthNormal=-1;

  public ThreePaneLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    initSelf();
  }

  void initSelf() {
    setOrientation(HORIZONTAL);
  }

  @Override
  public void onFinishInflate() {
    super.onFinishInflate();

    left=getChildAt(0);
    middle=getChildAt(1);
    right=getChildAt(2);
  }

  public View getLeftView() {
    return(left);
  }

  public View getMiddleView() {
    return(middle);
  }

  public View getRightView() {
    return(right);
  }

  public void hideLeft() {
    if (leftWidth == -1) {
      leftWidth=left.getWidth();
      middleWidthNormal=middle.getWidth();
      resetWidget(left, leftWidth);
      resetWidget(middle, middleWidthNormal);
      resetWidget(right, middleWidthNormal);
      requestLayout();
    }

    translateWidgets(-1 * leftWidth, left, middle, right);

    ObjectAnimator.ofInt(this, "middleWidth", middleWidthNormal,
                         leftWidth).setDuration(ANIM_DURATION).start();
  }

  public void showLeft() {
    translateWidgets(leftWidth, left, middle, right);

    ObjectAnimator.ofInt(this, "middleWidth", leftWidth,
                         middleWidthNormal).setDuration(ANIM_DURATION)
                  .start();
  }

  public void setMiddleWidth(int value) {
    middle.getLayoutParams().width=value;
    requestLayout();
  }

  private void translateWidgets(int deltaX, View... views) {
    for (final View v : views) {
      v.setLayerType(View.LAYER_TYPE_HARDWARE, null);

      v.animate().translationXBy(deltaX).setDuration(ANIM_DURATION)
       .setListener(new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
           v.setLayerType(View.LAYER_TYPE_NONE, null);
         }
       });
    }
  }

  private void resetWidget(View v, int width) {
    LinearLayout.LayoutParams p=
        (LinearLayout.LayoutParams)v.getLayoutParams();

    p.width=width;
    p.weight=0;
  }
}

그러나 런타임시 너비를 처음 설정 한 방법에 관계없이 ThreePaneLayout처음 hideLeft()으로 조각 A 및 B라는 질문을 조각 A 및 B로 표시하는 것에서 조각 B 및 C로 전환 할 때 너비 관리가 사용 됩니다. ThreePaneLayout- 단편에 특정한 관계를 갖지 않는 - 세 조각은 left, middle, 및 right. 전화 할 당시 hideLeft(), 우리는의 크기를 기록 left하고 middle우리가 완전히 크기를 제어 할 수 있도록 세 가지 중 하나에서 사용 된 모든 무게를 제로. 시점에서 hideLeft()의 크기를 right원래 크기로 설정했습니다 middle.

애니메이션은 두 가지입니다.

  • a ViewPropertyAnimatorleft사용하여 하드웨어 계층을 사용하여 너비로 3 개의 위젯을 왼쪽으로 변환합니다.
  • 를 사용하여 ObjectAnimator사용자 지정 의사 재산 middleWidth의 변화에 middle가의 원래 너비로 시작 어떤에서 폭left

(현재로서는 효과가 있지만 AnimatorSetObjectAnimators모두 를 사용하는 것이 더 좋습니다. )

(이는 middleWidth ObjectAnimator상당히 지속적인 무효화를 요구하기 때문에 하드웨어 계층의 가치를 무효화 할 수도 있습니다 )

(이다 확실히 나는 아직도 내 애니메이션 이해의 간격을 가지고, 내가 괄호 문을 좋아하는 것이 가능)

순 효과는 즉 left화면 오프 슬라이드, middle원래의 위치와 크기에 슬라이드 left, 그리고 right바로 뒤에 번역 middle.

showLeft() 동일한 방향의 애니메이터를 사용하여 방향을 반대로하여 프로세스를 간단히 되돌립니다.

활동은 a ThreePaneLayout를 사용하여 한 쌍의 ListFragment위젯과 a 를 보유 합니다 Button. 왼쪽 조각에서 무언가를 선택하면 중간 조각이 추가되거나 내용이 업데이트됩니다. 중간 조각에서 무언가를 선택하면의 캡션이 설정되고에서 Button실행 hideLeft()됩니다 ThreePaneLayout. 왼쪽을 숨기면 BACK을 누르면 실행됩니다 showLeft(). 그렇지 않으면 BACK은 활동을 종료합니다. 이것은 FragmentTransactions애니메이션에 영향을주는 데 사용되지 않기 때문에 "백 스택"을 직접 관리해야합니다.

위에 링크 된 프로젝트는 네이티브 프래그먼트와 네이티브 애니메이터 프레임 워크를 사용합니다. 애니메이션에 Android 지원 조각 백 포트와 NineOldAndroids 를 사용하는 동일한 프로젝트의 다른 버전이 있습니다 .

https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC

백 포트는 1 세대 Kindle Fire에서 잘 작동하지만 하드웨어 사양이 낮고 하드웨어 가속 지원이 부족하기 때문에 애니메이션이 약간 흔들립니다. Nexus 7 및 기타 최신 세대 태블릿에서는 두 가지 구현이 모두 매끄럽게 보입니다.

나는이 솔루션을 개선하는 방법에 대한 아이디어 또는 여기서 내가 한 일 (또는 @weakwire가 사용한 것)보다 명확한 이점을 제공하는 다른 솔루션에 대해 분명히 열려 있습니다.

기여한 모든 분들께 다시 한번 감사드립니다!


1
안녕하세요! 솔루션에 감사드립니다. 그러나 v.setLayerType ()을 추가하면 때로는 약간 느려집니다. 이 라인을 제거하면됩니다 (리스너도 마찬가지입니다). Galaxy Nexus에서 방금 시도한 Android 3+ 기기의 경우에 해당합니다.이 라인이 없으면 훨씬 더 잘 작동합니다.
Evgeny Nacu

1
"Nexus 7 및 기타 현재 세대 태블릿에서 구현이 순조로워 보입니다." 나는 당신을 믿습니다. 그러나 Nexus 7에서 기본 ObjectAnimator를 사용하여 구현을 테스트하고 있으며 약간 느립니다. 가장 가능한 원인은 무엇입니까? 이미 AndroidMAnifest에 hardwareAccelerated : true가 있습니다. 나는 무엇이 문제가 될 수 있는지 정말로 모른다. 나는 또한 DanielGrech의 변형을 시도했지만 동일합니다. 애니메이션의 차이를 볼 수 있지만 (애니메이션이 가중치를 기반으로 함) 두 경우 모두 왜 지연되는지 알 수 없습니다.
Bogdan Zurac

1
@Andrew : "약간 지연"-여기서 동사를 어떻게 사용하는지 잘 모르겠습니다. 나에게 "지연"이란 비교를위한 구체적인 목표를 의미한다. 예를 들어, 스 와이프 동작과 관련된 애니메이션이 손가락 움직임보다 뒤 떨어질 수 있습니다. 이 경우 모든 것이 탭에 의해 트리거되므로 애니메이션이 뒤쳐 질 수있는 것이 확실하지 않습니다. 아마도 "약간 지연된다"고 생각하는 것은 단순히 "느린 느낌"을 의미하지만, 나는 그것을 보지 못했지만 강한 미적 감각을 가지고 있다고 주장하지 않기 때문에 나에게 허용되는 애니메이션이 허용되지 않을 수도 있습니다.
CommonsWare 2013

2
@ CommonsWare : 4.2로 훌륭한 코드를 작성했습니다. 가운데를 탭 ListView하고 뒤로 버튼을 빠르게 누르고 반복하면 전체 레이아웃이 오른쪽으로 표류했습니다. 왼쪽에 여분의 공간 열이 표시되기 시작했습니다. 이 동작은 첫 번째 애니메이션 (중간 탭으로 시작 ListView)을 완료하고 뒤로 버튼을 누르지 않기 때문에 도입되었습니다 . I는 교체하여 고정 translationXBy으로 translationXtranslateWidgets방법 translateWidgets(leftWidth, left, middle, right);으로 translateWidgets(0, left, middle, right);showLeft()방법.
M-WaJeEh

2
또한 교체 requestLayout();middle.requestLayout();setMiddleWidth(int value);방법. GPU가 여전히 전체 레이아웃 패스를 수행한다고 생각하므로 성능에 영향을 미치지 않을 수 있습니다.
M-WaJeEh

13

이 문제를 해결하는 PanesLibrary라는 라이브러리를 만들었습니다. 다음과 같은 이유로 이전에 제공된 것보다 훨씬 유연합니다.

  • 각 창은 동적으로 크기를 조정할 수 있습니다
  • 2 개 또는 3 개가 아닌 여러 창을 허용합니다.
  • 창 내부의 조각은 방향 변경시 올바르게 유지됩니다.

https://github.com/Mapsaurus/Android-PanesLibrary 에서 확인할 수 있습니다.

데모는 다음과 같습니다. http://www.youtube.com/watch?v=UA-lAGVXoLU&feature=youtu.be

기본적으로 동적으로 크기가 지정된 여러 창을 쉽게 추가하고 해당 창에 조각을 연결할 수 있습니다. 유용하다고 생각하십시오! :)


6

링크 한 예제 중 하나 ( http://android.amberfog.com/?p=758 )를 작성하면 layout_weight속성 에 애니메이션을 적용하는 방법은 무엇입니까? 이런 식으로, 당신은 3 개의 조각들의 무게 변화를 함께 애니메이션 할 수 있고, 그것들이 모두 잘 어울린다는 보너스를 얻습니다 :

간단한 레이아웃으로 시작하십시오. 우리 애니메이션이 될 겁니다 때문에 layout_weight, 우리는 필요 LinearLayout3 개 패널의 루트보기로 :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
    android:id="@+id/panel1"
    android:layout_width="0dip"
    android:layout_weight="1"
    android:layout_height="match_parent"/>

<LinearLayout
    android:id="@+id/panel2"
    android:layout_width="0dip"
    android:layout_weight="2"
    android:layout_height="match_parent"/>

<LinearLayout
    android:id="@+id/panel3"
    android:layout_width="0dip"
    android:layout_weight="0"
    android:layout_height="match_parent"/>
</LinearLayout>

그런 다음 데모 클래스 :

public class DemoActivity extends Activity implements View.OnClickListener {
    public static final int ANIM_DURATION = 500;
    private static final Interpolator interpolator = new DecelerateInterpolator();

    boolean isCollapsed = false;

    private Fragment frag1, frag2, frag3;
    private ViewGroup panel1, panel2, panel3;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        panel1 = (ViewGroup) findViewById(R.id.panel1);
        panel2 = (ViewGroup) findViewById(R.id.panel2);
        panel3 = (ViewGroup) findViewById(R.id.panel3);

        frag1 = new ColorFrag(Color.BLUE);
        frag2 = new InfoFrag();
        frag3 = new ColorFrag(Color.RED);

        final FragmentManager fm = getFragmentManager();
        final FragmentTransaction trans = fm.beginTransaction();

        trans.replace(R.id.panel1, frag1);
        trans.replace(R.id.panel2, frag2);
        trans.replace(R.id.panel3, frag3);

        trans.commit();
    }

    @Override
    public void onClick(View view) {
        toggleCollapseState();
    }

    private void toggleCollapseState() {
        //Most of the magic here can be attributed to: http://android.amberfog.com/?p=758

        if (isCollapsed) {
            PropertyValuesHolder[] arrayOfPropertyValuesHolder = new PropertyValuesHolder[3];
            arrayOfPropertyValuesHolder[0] = PropertyValuesHolder.ofFloat("Panel1Weight", 0.0f, 1.0f);
            arrayOfPropertyValuesHolder[1] = PropertyValuesHolder.ofFloat("Panel2Weight", 1.0f, 2.0f);
            arrayOfPropertyValuesHolder[2] = PropertyValuesHolder.ofFloat("Panel3Weight", 2.0f, 0.0f);
            ObjectAnimator localObjectAnimator = ObjectAnimator.ofPropertyValuesHolder(this, arrayOfPropertyValuesHolder).setDuration(ANIM_DURATION);
            localObjectAnimator.setInterpolator(interpolator);
            localObjectAnimator.start();
        } else {
            PropertyValuesHolder[] arrayOfPropertyValuesHolder = new PropertyValuesHolder[3];
            arrayOfPropertyValuesHolder[0] = PropertyValuesHolder.ofFloat("Panel1Weight", 1.0f, 0.0f);
            arrayOfPropertyValuesHolder[1] = PropertyValuesHolder.ofFloat("Panel2Weight", 2.0f, 1.0f);
            arrayOfPropertyValuesHolder[2] = PropertyValuesHolder.ofFloat("Panel3Weight", 0.0f, 2.0f);
            ObjectAnimator localObjectAnimator = ObjectAnimator.ofPropertyValuesHolder(this, arrayOfPropertyValuesHolder).setDuration(ANIM_DURATION);
            localObjectAnimator.setInterpolator(interpolator);
            localObjectAnimator.start();
        }
        isCollapsed = !isCollapsed;
    }

    @Override
    public void onBackPressed() {
        //TODO: Very basic stack handling. Would probably want to do something relating to fragments here..
        if(isCollapsed) {
            toggleCollapseState();
        } else {
            super.onBackPressed();
        }
    }

    /*
     * Our magic getters/setters below!
     */

    public float getPanel1Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)     panel1.getLayoutParams();
        return params.weight;
    }

    public void setPanel1Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel1.getLayoutParams();
        params.weight = newWeight;
        panel1.setLayoutParams(params);
    }

    public float getPanel2Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel2.getLayoutParams();
        return params.weight;
    }

    public void setPanel2Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel2.getLayoutParams();
        params.weight = newWeight;
        panel2.setLayoutParams(params);
    }

    public float getPanel3Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel3.getLayoutParams();
        return params.weight;
    }

    public void setPanel3Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel3.getLayoutParams();
        params.weight = newWeight;
        panel3.setLayoutParams(params);
    }


    /**
     * Crappy fragment which displays a toggle button
     */
    public static class InfoFrag extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle     savedInstanceState) {
            LinearLayout layout = new LinearLayout(getActivity());
            layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
            layout.setBackgroundColor(Color.DKGRAY);

            Button b = new Button(getActivity());
            b.setOnClickListener((DemoActivity) getActivity());
            b.setText("Toggle Me!");

            layout.addView(b);

            return layout;
        }
    }

    /**
     * Crappy fragment which just fills the screen with a color
     */
    public static class ColorFrag extends Fragment {
        private int mColor;

        public ColorFrag() {
            mColor = Color.BLUE; //Default
        }

        public ColorFrag(int color) {
            mColor = color;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            FrameLayout layout = new FrameLayout(getActivity());
            layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

            layout.setBackgroundColor(mColor);
            return layout;
        }
    }
}

또한이 예제에서는 FragmentTransactions를 사용하여 애니메이션을 생성하지 않고 (파편이 연결된 뷰에 애니메이션을 적용 함) 모든 백 스택 / 조각 트랜잭션을 직접 수행해야하지만 애니메이션을 작동시키는 노력과 비교할 때 멋지게, 이것은 나쁜 트레이드 오프처럼 보이지 않습니다 :)

실제 끔찍한 저해상도 비디오 : http://youtu.be/Zm517j3bFCo


1
음, Gmail은 확실히 0으로 무게를 줄이는 것이 (예를 들면 시각적 결함을 소개 할 수 있다는 걱정 것 내가 조각 A. I로 무엇을 설명의 번역을 수행 TextView하여 wrap_content애니메이션 진행 수직으로 자신을 스트레칭). 그러나 가중치를 애니메이션하는 것은 내가 Fragment B라고하는 것을 크기 조정하는 방법 일 수 있습니다. 감사합니다!
CommonsWare

1
걱정 마! 그래도 좋은 점은 'Fragment A'에있는 자식 뷰에 따라 시각적 인 인공물을 유발할 수 있습니다. 수 있습니다 희망 누군가가 그것을 할 수있는 더 단단한 방법을 찾아
DanielGrech

5

이것은 프래그먼트를 사용하지 않습니다 ... 3 명의 자식이있는 사용자 정의 레이아웃입니다. 메시지를 클릭하면 3 명의 어린이 offsetLeftAndRight()와 애니메이터 를 오프셋합니다 .

" JellyBean개발자 옵션"설정에서 "레이아웃 범위 표시"를 활성화 할 수 있습니다. 슬라이드 애니메이션이 완료된 후에도 왼쪽 메뉴가 여전히 있지만 가운데 패널 아래에 있음을 알 수 있습니다.

그것은 비슷 시릴 Mottier의 응용 프로그램 플라이의 메뉴 만 3 개 요소 대신 2.

또한 ViewPager세 번째 자녀 ViewPager중이 행동의 또 다른 표시입니다. 일반적으로 Fragments를 사용합니다 (필요하지는 않지만 다른 구현은 보지 못했습니다 Fragment). 그리고 Fragments다른 Fragment세 자녀 내에서 사용할 수 없기 때문에 아마도 조각이 아닙니다 ....


전에는 "Show layout bounds"를 사용한 적이 없었습니다.이 방식은 폐쇄 소스 앱에 매우 유용합니다 (감사합니다). 이 화면을 번역되어 제가 조각 A와 설명 나는 같이 느끼는 조각 B. 설명 무엇을 아래에서 다시 터지는 전에, (당신이 왼쪽 오른쪽 오른쪽 가장자리 슬라이드를 볼 수 있습니다) 것으로 보인다 TranslateAnimation난 불구하고, 애니메이터를 사용하여 동일한 목적을 달성 할 수있는 방법이 있는지 확인하십시오. 이것에 대해 몇 가지 실험을해야합니다. 감사!
CommonsWare

2

현재 사용 가능한 공간을 확보하기 위해 Fragment B 스케일을 사용하고 충분한 공간이 있으면 3 개의 창을 동시에 열 수 있다는 점을 제외하고는 이와 같은 일을하려고합니다. 여기 내 솔루션이 지금까지 있지만, 내가 그것을 고수할지 확실하지 않습니다. 누군가가 올바른 길을 보여주는 답변을 제공하기를 바랍니다.

LinearLayout을 사용하고 가중치를 애니메이션하는 대신 RelativeLayout을 사용하여 여백에 애니메이션을 적용합니다. 업데이트 할 때마다 requestLayout ()을 호출해야하기 때문에 이것이 가장 좋은 방법인지 잘 모르겠습니다. 그래도 모든 장치에서 원활합니다.

따라서 레이아웃을 애니메이션으로 처리하고 조각 트랜잭션을 사용하지 않습니다. 조각 C가 열려 있으면 뒤로 버튼을 수동으로 처리하여 조각 C를 닫습니다.

FragmentB는 layout_toLeftOf / ToRightOf를 사용하여 조각 A와 C에 정렬되도록 유지하십시오.

내 앱이 조각 C를 표시하는 이벤트를 트리거하면 조각 C를 슬라이드 아웃하고 조각 A를 동시에 슬라이드 아웃합니다. (2 개의 별도 애니메이션). 반대로 조각 A가 열리면 동시에 C를 닫습니다.

세로 모드 또는 작은 화면에서는 약간 다른 레이아웃을 사용하고 화면에서 조각 C를 슬라이드합니다.

조각 A와 C의 너비에 백분율을 사용하려면 런타임에 계산해야한다고 생각합니다 ... (?)

활동의 레이아웃은 다음과 같습니다.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rootpane"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <!-- FRAGMENT A -->
    <fragment
        android:id="@+id/fragment_A"
        android:layout_width="300dp"
        android:layout_height="fill_parent"
        class="com.xyz.fragA" />

    <!-- FRAGMENT C -->
    <fragment
        android:id="@+id/fragment_C"
        android:layout_width="600dp"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        class="com.xyz.fragC"/>

    <!-- FRAGMENT B -->
    <fragment
        android:id="@+id/fragment_B"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_marginLeft="0dip"
        android:layout_marginRight="0dip"
        android:layout_toLeftOf="@id/fragment_C"
        android:layout_toRightOf="@id/fragment_A"
        class="com.xyz.fragB" />

</RelativeLayout>

FragmentC를 밀어 넣거나 뺄 애니메이션 :

private ValueAnimator createFragmentCAnimation(final View fragmentCRootView, boolean slideIn) {

    ValueAnimator anim = null;

    final RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) fragmentCRootView.getLayoutParams();
    if (slideIn) {
        // set the rightMargin so the view is just outside the right edge of the screen.
        lp.rightMargin = -(lp.width);
        // the view's visibility was GONE, make it VISIBLE
        fragmentCRootView.setVisibility(View.VISIBLE);
        anim = ValueAnimator.ofInt(lp.rightMargin, 0);
    } else
        // slide out: animate rightMargin until the view is outside the screen
        anim = ValueAnimator.ofInt(0, -(lp.width));

    anim.setInterpolator(new DecelerateInterpolator(5));
    anim.setDuration(300);
    anim.addUpdateListener(new AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Integer rightMargin = (Integer) animation.getAnimatedValue();
            lp.rightMargin = rightMargin;
            fragmentCRootView.requestLayout();
        }
    });

    if (!slideIn) {
        // if the view was sliding out, set visibility to GONE once the animation is done
        anim.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                fragmentCRootView.setVisibility(View.GONE);
            }
        });
    }
    return anim;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.