2011-09-22 9 views
8

He la siguiente distribución:¿Cómo funciona una característica de diseño de animación de ViewGroups?

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

    <FrameLayout android:id="@+id/viewgroup_left" 
       android:layout_height="match_parent" 
       android:layout_weight="2" 
       android:layout_width="0dp"> 

     ... children ... 

    </FrameLayout> 

    <LinearLayout android:id="@+id/viewgroup_right" 
        android:layout_height="match_parent" 
        android:layout_weight="1" 
        android:layout_width="0dp" 
        android:orientation="vertical"> 

     ... children ... 

    </LinearLayout> 

</LinearLayout> 

que terminar con algo como esto:

+------------------------+------------+ 
    |      |   | 
    |      |   | 
    |   Left   | Right | 
    |      |   | 
    |      |   | 
    +------------------------+------------+ 

Cuando se activa una determinada palanca, que desea animar izquierda de modo que su anchura se expande para llenar el toda la pantalla. Al mismo tiempo, me gustaría animar el ancho de la derecha para que se reduzca a cero. Más tarde, cuando se vuelva a alternar, necesito restaurar las cosas al estado anterior.

He intentado escribir mi propia animación que llame al View.getWidth() pero cuando vuelvo a animar a ese valor (estableciendo View.getLayoutParams().width) es más ancho que cuando comenzó. Sospecho que lo estoy haciendo mal. También he leído toda la documentación sobre las animaciones de Honeycomb, pero no quiero traducir ni escalar ... Quiero animar la propiedad de ancho de diseño. No puedo encontrar un ejemplo de esto.

¿Cuál es la forma correcta de hacerlo?

Respuesta

12

Dado que nadie te ayudó con todo y mi primera respuesta fue un desastre Voy a tratar de darle la respuesta correcta esta vez ;-)

En realidad me gusta la idea y creo que esto es un gran efecto visual que podría ser útil para un grupo de personas. Implementaría un desbordamiento de la vista correcta (creo que el reflejo se ve extraño ya que el texto se está expandiendo hacia abajo).

Pero de todos modos, aquí está el código que funciona perfectamente bien (incluso puedes alternar mientras se está animando).

explicación rápida:
se llama a toggle con un valor lógico para su dirección y esto va a comenzar un bucle gestor de llamadas animación. Esto aumentará o disminuirá el peso de ambas vistas en función de la dirección y el tiempo pasado (para un cálculo y animación sin problemas). El bucle de llamada de animación se invocará a sí mismo siempre que no haya alcanzado la posición inicial o final.

La disposición:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="horizontal" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:weightSum="10" 
    android:id="@+id/slide_layout"> 
    <TextView 
     android:layout_weight="7" 
     android:padding="10dip" 
     android:id="@+id/left" 
     android:layout_width="0dip" 
     android:layout_height="fill_parent"></TextView> 
    <TextView 
     android:layout_weight="3" 
     android:padding="10dip" 
     android:id="@+id/right" 
     android:layout_width="0dip" 
     android:layout_height="fill_parent"></TextView> 
</LinearLayout> 

La actividad:

public class TestActivity extends Activity { 

    private static final int ANIMATION_DURATION = 1000; 

    private View mSlidingLayout; 
    private View mLeftView; 
    private View mRightView; 

    private boolean mAnimating = false; 
    private boolean mLeftExpand = true; 
    private float mLeftStartWeight; 
    private float mLayoutWeightSum; 
    private Handler mAnimationHandler = new Handler(); 
    private long mAnimationTime; 

    private Runnable mAnimationStep = new Runnable() { 
     @Override 
     public void run() { 
      long currentTime = System.currentTimeMillis(); 
      float animationStep = (currentTime - mAnimationTime) * 1f/ANIMATION_DURATION; 
      float weightOffset = animationStep * (mLayoutWeightSum - mLeftStartWeight); 

      LinearLayout.LayoutParams leftParams = (LinearLayout.LayoutParams) 
        mLeftView.getLayoutParams(); 
      LinearLayout.LayoutParams rightParams = (LinearLayout.LayoutParams) 
        mRightView.getLayoutParams(); 

      leftParams.weight += mLeftExpand ? weightOffset : -weightOffset; 
      rightParams.weight += mLeftExpand ? -weightOffset : weightOffset; 

      if (leftParams.weight >= mLayoutWeightSum) { 
       mAnimating = false; 
       leftParams.weight = mLayoutWeightSum; 
       rightParams.weight = 0; 
      } else if (leftParams.weight <= mLeftStartWeight) { 
       mAnimating = false; 
       leftParams.weight = mLeftStartWeight; 
       rightParams.weight = mLayoutWeightSum - mLeftStartWeight; 
      } 

      mSlidingLayout.requestLayout(); 

      mAnimationTime = currentTime; 

      if (mAnimating) { 
       mAnimationHandler.postDelayed(mAnimationStep, 30); 
      } 
     } 
    }; 

    private void toggleExpand(boolean expand) { 
     mLeftExpand = expand; 

     if (!mAnimating) { 
      mAnimating = true; 
      mAnimationTime = System.currentTimeMillis(); 
      mAnimationHandler.postDelayed(mAnimationStep, 30); 
     } 
    } 

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

     mLeftView = findViewById(R.id.left); 
     mRightView = findViewById(R.id.right); 
     mSlidingLayout = findViewById(R.id.slide_layout); 

     mLeftStartWeight = ((LinearLayout.LayoutParams) 
       mLeftView.getLayoutParams()).weight; 
     mLayoutWeightSum = ((LinearLayout) mSlidingLayout).getWeightSum(); 
    } 
} 
2

Simplemente añadiendo mis 2 centavos aquí para una excelente respuesta de Knickedi - por si acaso alguien lo necesita:

Si anima el uso de Pesos, terminará con problemas de recorte/no recorte en vistas y grupos de vistas contenidos. Esto es especialmente cierto si usa viewgroups con peso como contenedores de fragmento. Para superarlo, es posible que también necesite animar los márgenes de las vistas secundarias problemáticas y los contenedores de view/groups/fragment.

Y, para hacer todas estas cosas juntas, siempre es mejor ir a ObjectAnimator y AnimatorSet (si se puede usarlos), junto con algunas clases de utilidad como MarginProxy

1

Una manera diferente de la solución Publicado por @ knickedi es usar ObjectAnimator en lugar de Runnable. La idea es usar ObjectAnimator para ajustar el peso de las vistas izquierda y derecha. Sin embargo, las vistas deben personalizarse para que el peso pueda exponerse como una propiedad para que ObjectAnimator pueda animar.

Así que, primero, definir una vista personalizada (utilizando un LinearLayout como ejemplo):

public class CustomLinearLayout extends LinearLayout { 
    public CustomLinearLayout(Context context) { 
     super(context); 
    } 

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

    public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
    } 

    public void setMyWeight(float value) { 
     LinearLayout.LayoutParams p = (LinearLayout.LayoutParams)getLayoutParams(); 
     p.weight = value; 
     requestLayout(); 
    } 
} 

A continuación, actualice el código XML diseño para utilizar este diseño personalizado lineal.

Entonces, cuando se necesita para cambiar la animación, utilice ObjectAnimator:

ObjectAnimator rightView = ObjectAnimator.ofFloat(viewgroup_right, "MyWeight", 0.5f, 1.0f); 
ObjectAnimator leftView = ObjectAnimator.ofFloat(viewgroup_left, "MyWeight", 0.5f, 0.0f); 
AnimatorSet animatorSet = new AnimatorSet(); 
animatorSet.setDuration(1000); // 1 second of animation 
animatorSet.playTogether(rightView, leftView); 
animatorSet.start(); 

El código anterior supone dos puntos de vista son la disposición lineal y son la mitad de peso para empezar. La animación expandirá la vista derecha a todo el peso (por lo que la izquierda está oculta). Tenga en cuenta que ObjectAnimator está animado utilizando la propiedad "MyWeight" del diseño lineal personalizado. AnimatorSet se usa para unir ObjectAnimators izquierdo y derecho, por lo que la animación se ve sin problemas.

Este enfoque reduce la necesidad de escribir código ejecutable y el cálculo del peso dentro de él, pero necesita una clase personalizada para definir.

Cuestiones relacionadas