2011-09-22 9 views
8

Ho il seguente schema:Come si animano le proprietà di Layout dei ViewGroup?

<?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> 

ho finire con qualcosa di simile:

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

Quando un certo premuto è attivato, voglio animare sinistra in modo che la sua larghezza si espande per riempire il schermo intero Allo stesso tempo, vorrei animare la larghezza di Right in modo che si riduca a zero. Successivamente, quando il commutatore viene nuovamente attivato, devo ripristinare le cose nello stato precedente.

Ho provato a scrivere la mia animazione che chiama View.getWidth() ma quando ritorna a quel valore (impostando View.getLayoutParams().width) è più largo di quando è iniziato. Sospetto che stia sbagliando. Ho anche letto tutta la documentazione sull'animazione di Honeycomb, ma non voglio tradurre o ridimensionare ... Voglio animare la proprietà della larghezza del layout. Non riesco a trovare un esempio di questo.

Qual è il modo corretto per farlo?

risposta

12

Dal momento che nessuno ti ha aiutato ancora e la mia prima risposta è stata un tale caos Cercherò di dare la risposta giusta questa volta ;-)

In realtà mi piace l'idea e penso che questo sia un grande effetto visivo che potrebbe essere utile per un gruppo di persone. Implementerei un overflow della vista a destra (penso che lo strizzacervelli appaia strano dato che il testo si sta espandendo verso il basso).

Ma in ogni caso, ecco il codice che funziona perfettamente (è possibile anche attivare mentre si sta animando).

spiegazione rapida:
ti chiamano toggle con un valore booleano per la vostra direzione e questo inizierà un ciclo gestore chiamata animazione. Ciò aumenterà o diminuirà il peso di entrambe le viste in base alla direzione e al tempo passato (per un calcolo e un'animazione senza intoppi). Il ciclo di chiamata dell'animazione invocherà se stesso finché non ha raggiunto la posizione iniziale o finale.

Il layout:

<?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> 

L'attività:

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

Basta aggiungere i miei 2 centesimi qui per risposta eccellente di Knickedi - nel caso in cui qualcuno ne ha bisogno:

se animate utilizzando pesi si finirà con problemi con ritaglio/non ritaglio su viste e viewgroup contenuti. Ciò è particolarmente vero se si utilizzano i viewgroup con contenitori di pesi come frammenti. Per superarlo, potrebbe anche essere necessario animare i margini delle viste problematiche dei bambini e dei contenitori di gruppi di vista/frammenti.

E, a fare tutte queste cose insieme, la sua sempre meglio andare per ObjectAnimator e AnimatorSet (se si possono usare), insieme ad alcune classi di utilità come MarginProxy

1

Un modo diverso per la soluzione postato da @ knickedi utilizza ObjectAnimator anziché Runnable. L'idea è di usare ObjectAnimator per regolare il peso di entrambe le viste sinistra e destra. Le viste, tuttavia, devono essere personalizzate in modo che il peso possa essere esposto come una proprietà per l'animazione dell'oggetto ObjectAnimator.

Quindi innanzitutto definire una vista personalizzata (utilizzando un LinearLayout come esempio):

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

Quindi, aggiornare i XML layout per usare questa disposizione lineare personalizzato.

Poi, quando è necessario commutare l'animazione, utilizzare 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(); 

Il codice sopra presuppone entrambe le viste sono la layout lineare e sono metà in peso per iniziare. L'animazione espanderà la vista corretta a pieno peso (quindi quella a sinistra è nascosta). Si noti che ObjectAnimator è animato utilizzando la proprietà "MyWeight" del layout lineare personalizzato. L'AnimatorSet è usato per legare insieme gli ObjectAnimator di sinistra e di destra, quindi l'animazione sembra liscia.

Questo approccio riduce la necessità di scrivere codice eseguibile e il calcolo del peso al suo interno, ma necessita di una classe personalizzata da definire.

Problemi correlati