Gmail Üç Parçalı Animasyon Senaryosunun Tam Çalışma Örneği?


128

TL; DR: "Gmail'in üç parçalı animasyonu" senaryosu olarak adlandıracağım şeyin eksiksiz bir çalışan örneğini arıyorum . Spesifik olarak, aşağıdaki gibi iki parça ile başlamak istiyoruz:

iki parça

Bir kullanıcı arayüzü olayında (örneğin, B Parçasındaki bir şeye dokunmak), şunu istiyoruz:

  • Ekrandan sola kaydırmak için Parça A
  • Fragman B ekranın sol kenarına kayacak ve Fragment A tarafından boşaltılan yeri kaplayacak şekilde küçülecektir.
  • C Parçası ekranın sağ tarafından içeri kayması ve B Parçası tarafından boşaltılan yeri kaplaması

Ve GERİ düğmesine basıldığında, bu işlem setinin tersine çevrilmesini istiyoruz.

Şimdi, birçok kısmi uygulama gördüm; Aşağıda dördünü gözden geçireceğim. Eksik olmanın ötesinde, hepsinin sorunları var.


@Reto Meier , aynı temel soruya bu popüler cevaba katkıda bulundu setCustomAnimations()ve bir FragmentTransaction. İki parçalı bir senaryo için (örneğin, başlangıçta yalnızca A Parçasını görüyorsunuz ve onu animasyonlu efektler kullanarak yeni bir B Parçası ile değiştirmek istiyorsunuz), tamamen hemfikirim. Ancak:

  • Yalnızca bir "giriş" ve bir "çıkış" animasyonu belirleyebildiğiniz için, üç parçalı senaryo için gereken tüm farklı animasyonları nasıl işleyeceğinizi anlayamıyorum
  • <objectAnimator>Onun örnek kodda piksel olarak sabit kablolu pozisyonları kullanır ve bu ekran boyutları değişen verilen pratik olacak gibi görünüyor, henüz setCustomAnimations()Java bunları tanımlayan artması önlenir, animasyon kaynak gerektirir
  • Şeylerle ölçek kravat nesne animatörler gibi nasıl şekilde bir kayıp am android:layout_weightbir de LinearLayoutyüzde bazında yer tahsisi için
  • Ben Fragman C başlangıçta nasıl işleneceğini olarak bir kayıp am ( GONE? android:layout_weightAit 0? Başka 0? Şeyin bir ölçeğe ön animasyon?)

@Roman Nurik , kendi tanımladığınız özellikler de dahil olmak üzere herhangi bir özelliği canlandırabileceğinizi belirtiyor. Bu, kendi özel düzen yöneticisi alt sınıfınızı icat etme pahasına, fiziksel bağlantılı pozisyonlar sorununu çözmeye yardımcı olabilir. Bu bazılarına yardımcı olur, ancak Reto'nun çözümünün geri kalanından hala şaşkınım.


Bu pastebin girişinin yazarı , temel olarak üç parçanın da başlangıçta bir hide()işlem işlemi yoluyla Fragment C gizlenerek, başlangıçta konteynerde yer alacağını söyleyerek bazı kışkırtıcı sözde kodlar gösteriyor . Sonra show()C ve hide()A UI olay meydana geldiğinde. Ancak, B'nin boyut değiştirdiği gerçeğini nasıl ele aldığını anlamıyorum. Aynı konteynere birden fazla parça ekleyebileceğiniz gerçeğine de dayanıyor ve bunun uzun vadede güvenilir bir davranış olup olmadığından emin değilim (bununla findFragmentById()yaşayabilsem de kırılması gerektiğini belirtmiyorum).


Bu blog gönderisinin yazarı, Gmail'in hiç kullanmadığını setCustomAnimations(), bunun yerine doğrudan nesne animatörlerini kullandığını belirtir ("kök görünümün sol kenar boşluğunu değiştirirsiniz + sağ görünümün genişliğini değiştirirsiniz"). Bununla birlikte, bu hala iki parçalı bir çözüm AFAICT'dir ve uygulama bir kez daha sert kabloları piksel olarak gösterir.


Bu konuyu kapatmaya devam edeceğim, bu yüzden bir gün bunu kendim yanıtlayabilirim, ama gerçekten birinin bu animasyon senaryosu için üç parçalı çözümü bulduğunu ve kodu (veya oraya bir bağlantı) gönderebileceğini umuyorum. Android'deki animasyonlar saçlarımı koparmak istememe neden oluyor ve beni görenleriniz bunun büyük ölçüde sonuçsuz bir çaba olduğunu biliyor.


2
Romain Guy'ın gönderdiği böyle bir şey değil mi? meraklı
Fernando Gallego

1
@ ferdy182: AFAICT parçalarını kullanmıyor. Görsel olarak bu doğru temel etkidir ve bunu parça tabanlı bir cevap oluşturmaya çalışmak için başka bir veri noktası olarak kullanabilirim. Teşekkürler!
CommonsWare

1
Sadece bir düşünce: standart E-posta uygulamasının Nexus 7'imde benzer (tamamen aynı olmasa da) bir davranışı vardır - bu açık kaynak olmalıdır.
Christopher Orr

4
E-posta uygulaması, sol taraftaki Görünümün konumunu ve her biri ilgili Parçayı içeren diğer iki Görünümün genişliklerini / görünürlüğünü canlandıran özel bir "ThreePaneLayout" kullanır.
Christopher Orr

2
hey @CommonsWare Buradaki cevabımın tamamı ile ppl'yi rahatsız etmeyeceğim, ancak çok tatmin edici bir cevap verdiğim sizinkine benzer bir soru vardı. Bu yüzden, kontrol etmenizi önerin (yorumları da kontrol edin): stackoverflow.com/a/14155204/906362
Budius

Yanıtlar:


67

Benim önerisini yüklendi github (görünüm donanım hızlandırma kuvvetle animasyonlar bu tür önerilir rağmen tüm android sürümleri ile çalışıyor. Dışı donanım aygıtlarını hızlandırılmış için bir bitmap önbelleğe alma uygulaması daha uygun olmalı)

Animasyonlu demo video İşte (Ekran yayınının yavaş kare hızı nedeni. Gerçek performans çok hızlı)


Kullanımı:

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

Açıklama :

Yeni bir özel RelativeLayout (ThreeLayout) ve 2 özel Animasyon (MyScalAnimation, MyTranslateAnimation) oluşturdu

ThreeLayoutdiğer görünür görüntünün sahip olduğunu varsayarak, sol bölmenin ağırlığını param olarak alır weight=1.

Böylece new ThreeLayout(context,3)3 çocuklu ve sol bölmede toplam ekranın 1 / 3'üne sahip yeni bir görünüm oluşturur. Diğer görünüm mevcut tüm alanı kaplar.

Çalışma zamanında genişliği hesaplar, daha güvenli bir uygulama, boyutların draw () 'da ilk kez hesaplanabilmesidir. gönderi yerine ()

Ölçekle ve Çevir animasyonları aslında görünümü yeniden boyutlandırır ve hareket ettirir, sözde [ölçekleyin, taşıyın] değil. fillAfter(true)Hiçbir yerde kullanılmadığına dikkat edin .

View2, View1'in sağında

ve

View3, View2'nin sağında

Bu kuralları belirledikten sonra RelativeLayout diğer her şeyi halleder. Animasyonlar margins(hareket halindeyken) ve [width,height]ölçeği değiştirir

Her çocuğa erişmek için (böylece onu Parçanızla şişirmek için arayabilirsiniz.

public FrameLayout getLeftLayout() {}

public FrameLayout getMiddleLayout() {}

public FrameLayout getRightLayout() {}

Aşağıda 2 animasyon gösterilmektedir


1. Aşama

--- EKRAN İÇİ ----------! ----- OUT ----

[View1] [_____ View2 _____] [_____ View3_____]

2. aşama

--OUT -! -------- EKRAN İÇİ ------

[View1] [View2] [_____ View3_____]


1
Henüz bir ölçek animatörü uygulamamış olsam da, düz renk alanları için iyi çalışacağından ve "gerçek" içerikte daha az işe yarayacağından endişe ediyorum. Fragment A'nın boşluğuna uyacak şekilde yeniden boyutlandırılan Fragment B'nin sonuçları, Fragment B'nin oraya orijinal olarak yerleştirilmiş olmasından farklı olmamalıdır. Örneğin, bir ölçek animatörü olan AFAIK, yazı tipi boyutunu ölçeklendirecektir (her şeyi Viewdaha küçük animasyonlu olarak çizerek ), ancak Gmail / E-posta'da daha küçük yazı tipleri değil, sadece daha az kelime.
CommonsWare

1
@CommonsWare scaleAnimation sadece soyut bir isimdir. Aslında hiçbir ölçeklendirme gerçekleşmez. Aslında görünümün genişliğini yeniden boyutlandırır. Kaynağı kontrol edin. LayoutResizer bunun için daha iyi bir isim olabilir.
zayıfwire

1
Bazı şeyleri yanlış yorumluyor olsam da , bu benim scaleXdeğerin etrafındaki kaynağı yorumlamam değil View.
CommonsWare

1
@CommonsWare bu github.com/weakwire/3PaneLayout/blob/master/src/com/example/… 'e bakın . Animasyonu genişletmemin tek nedeni interpolatedTime'a erişim sağlamaktır. p.width = (int) genişlik; tüm sihri yapar ve bu scaleX değildir. Belirtilen süre boyunca değişen gerçek görünüm boyutlarıdır.
zayıfwire

2
Videonuz iyi bir kare hızı gösteriyor, ancak örneğinizde kullanılan görünümler çok basit. Bir animasyon sırasında requestLayout () yapmak çok maliyetlidir. Özellikle çocuklar karmaşıksa (örneğin bir ListView). Bu muhtemelen dalgalı bir animasyona neden olacaktır. GMail uygulaması requestLayout'u çağırmaz. Bunun yerine, animasyon başlamadan hemen önce orta panele daha küçük bir görünüm yerleştirilir.
Thierry-Dimitri Roy

31

Tamam, işte sorunun yorumlarındaki @ Christopher'ın önerisine göre E-posta AOSP uygulamasından türetilen kendi çözümüm.

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

@ zayıfwire'ın çözümü benimkini anımsatıyor, ancak Animationanimatörler yerine klasik kullanıyor ve RelativeLayoutkonumlandırmayı uygulamak için kuralları kullanıyor . Ödül açısından bakıldığında, daha ince bir çözüme sahip başka biri henüz bir cevap göndermediği sürece, muhtemelen ödülü alacak.


Özetle, ThreePaneLayoutbu proje, LinearLayoutpeyzajda üç çocukla çalışmak üzere tasarlanmış bir alt sınıftır. Bu çocukların genişlikleri, istenen araçlarla XML düzeninde ayarlanabilir - Ağırlıkları kullanarak gösteriyorum, ancak boyut kaynakları veya her neyse, belirli genişlikleriniz olabilir. Üçüncü çocuk - Sorudaki C Parçası - sıfır genişliğe sahip olmalıdır.

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

Bununla birlikte, çalışma zamanında, genişlikleri başlangıçta nasıl ayarladığınız önemli değil, genişlik yönetimi, A ve B Parçası olarak anılan soruyu B ve C Parçası olarak anılan soruyu göstermekten ThreePaneLayoutilk kez kullandığınızda devralınır hideLeft(). ThreePaneLayout- fragmanlara belirli bir ilişki içinde olduğu - üç adet vardır left, middleve right. Aradığınızda zamanda hideLeft(), biz büyüklüğünü kaydetmelidir leftve middlebiz tamamen boyutlarını kontrol edebilirsiniz böylece, üç herhangi üzerinde kullanılan herhangi bir ağırlıkları dışarı sıfır. Şu anda hideLeft(), boyutunu rightorijinal boyutu olarak ayarladık middle.

Animasyonlar iki aşamalıdır:

  • Bir donanım katmanı kullanarak ViewPropertyAnimatorüç parçanın genişliğine göre sola çevirisini gerçekleştirmek için a kullanın.left
  • Genişliği, başladığı şeyden orijinal genişliğine değiştirmek için ObjectAnimatorözel bir sözde özelliğini kullanın .middleWidthmiddleleft

( Şimdilik işe yarasa da, tüm bunlar için AnimatorSetve kullanmak daha iyi bir fikir olabilir ObjectAnimators)

( middleWidth ObjectAnimatoroldukça sürekli geçersiz kılma gerektirdiğinden, donanım katmanının değerini olumsuzlaması da mümkündür )

( Animasyonu anlama konusunda hâlâ eksikliklerim olması ve parantez içindeki ifadelerden hoşlanmam kesinlikle mümkündür)

Net etki, leftekrandan middlekayması, orijinal konumuna ve boyutuna kayması leftve righthemen arkasına çevrilmesidir middle.

showLeft() sadece yönleri tersine çevrilerek aynı animatör karışımıyla süreci tersine çevirir.

Aktivite, bir ThreePaneLayoutçift ListFragmentwidget'ı tutmak için a ve a Button. Sol parçadaki bir şeyin seçilmesi orta parçayı ekler (veya içeriğini günceller). Orta fragmanın bir şey seçilmesi başlığını ayarlar Buttonartı yürütür hideLeft()üzerinde ThreePaneLayout. Eğer sol tarafı sakladıysak GERİ tuşuna basmak çalıştırılır showLeft(); aksi takdirde BACK, aktiviteden çıkar. Bu FragmentTransactions, animasyonları etkilemek için kullanılmadığından , bu "arka yığını" kendimiz yönetmekte sıkışıp kalıyoruz.

Yukarıya bağlanan proje, yerel parçaları ve yerel animatör çerçevesini kullanır. Aynı projenin , animasyon için Android Destek parçaları arka portunu ve NineOldAndroids'i kullanan başka bir sürümüne sahibim :

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

Backport, 1. nesil Kindle Fire'da iyi çalışıyor, ancak daha düşük donanım özellikleri ve donanım hızlandırma desteği eksikliği göz önüne alındığında animasyon biraz sarsıntılı. Her iki uygulama da bir Nexus 7 ve diğer mevcut nesil tabletlerde sorunsuz görünüyor.

Bu çözümü nasıl geliştirebileceğime veya burada yaptıklarıma (veya @weakwire'ın kullandığına) göre açık avantajlar sunan diğer çözümlere kesinlikle açığım.

Katkıda bulunan herkese tekrar teşekkürler!


1
Selam! Çözüm için teşekkürler, ancak v.setLayerType () eklemek bazen işleri biraz daha yavaşlatıyor. Sadece bu satırları kaldırın (ve Dinleyiciyi de) Bu, en azından Galaxy Nexus'ta denediğim Android 3+ cihazlar için geçerli ve bu hatlar olmadan çok daha iyi çalışıyor
Evgeny Nacu

1
"Her iki uygulama da bir Nexus 7 ve diğer mevcut nesil tabletlerde sorunsuz görünüyor." Sana inanıyorum; ancak uygulamanızı bir Nexus 7'de yerel ObjectAnimator ile test ediyorum ve biraz gecikiyor. En olası nedenler neler olabilir? AndroidMAnifest'te zaten hardwareAccelerated: true var. Gerçekten sorunun ne olabileceğini bilmiyorum. DanielGrech'in varyantını da denedim ve aynı kalıyor. Animasyonda farklılıklar görebiliyorum (animasyonu ağırlıklara dayanıyor), ancak her iki durumda da neden geciktiğini anlayamıyorum.
Bogdan Zurac

1
@Andrew: "Biraz gecikiyor" - Burada "gecikme" fiilini nasıl kullandığınızı bilmiyorum. Bana göre "gecikme", karşılaştırma için somut bir hedef anlamına geliyor. Bu nedenle, örneğin, kaydırma hareketine bağlı bir animasyon, parmak hareketinin gerisinde kalabilir. Bu durumda, her şey dokunuşlarla tetiklenir ve bu nedenle animasyonun ne kadar geride kaldığından emin değilim. Belki de "biraz gecikiyor" un sadece "halsiz hissettiriyor" anlamına geldiğini tahmin ettim, bunu görmedim, ancak güçlü bir estetik anlayışa sahip olduğumu iddia etmiyorum ve bu yüzden benim için kabul edilebilir bir animasyon olabilecek şeyler sizin için kabul edilemez olabilir.
CommonsWare

2
@CommonsWare: Kodunuzu 4.2 ile derledim, harika iş. Ortaya dokunduğumda ListViewve geri düğmesine hızlıca bastığımda ve tekrarladığımda, tüm Düzen sağa kaydı. Solda fazladan boşluk sütunu göstermeye başladı. Bu davranış, ilk animasyonun (ortada hafifçe vurarak başlatılan ListView) tamamlanmasına izin vermediğim ve geri düğmesine bastığım için ortaya çıktı. I değiştirerek sabit translationXByile translationXde translateWidgetsyöntem ve translateWidgets(leftWidth, left, middle, right);birlikte translateWidgets(0, left, middle, right);olarak showLeft()bir yöntem.
M-WaJeEh

2
Ayrıca ikame requestLayout();ile middle.requestLayout();de setMiddleWidth(int value);bir yöntem. GPU'nun yine de tam bir düzen geçişi yapacağını tahmin ettiğim için performansı etkilemeyebilir, herhangi bir yorum var mı?
M-WaJeEh

13

Bu sorunu çözen PanesLibrary adlı bir kitaplık oluşturduk. Daha önce sunulandan daha esnektir çünkü:

  • Her bölme dinamik olarak boyutlandırılabilir
  • Herhangi bir sayıda bölmeye izin verir (sadece 2 veya 3 değil)
  • Bölmelerin içindeki parçalar, yön değişikliklerinde doğru şekilde tutulur.

Buradan kontrol edebilirsiniz: https://github.com/Mapsaurus/Android-PanesLibrary

İşte bir demo: http://www.youtube.com/watch?v=UA-lAGVXoLU&feature=youtu.be

Temel olarak, dinamik olarak boyutlandırılmış herhangi bir sayıda bölmeyi kolayca eklemenize ve bu bölmelere parçalar eklemenize olanak tanır. Umarım yararlı bulursunuz! :)


6

Bağlantı verdiğiniz örneklerden birini ( http://android.amberfog.com/?p=758 ) temel alarak layout_weightmülkü canlandırmaya ne dersiniz ? Bu şekilde, 3 parçanın ağırlık değişimini birlikte canlandırabilir ve hepsinin birlikte güzelce kaydığı bonusu alırsınız:

Basit bir düzen ile başlayın. Animasyon yapacağımız için, 3 panel için kök görünüm olarak a'ya layout_weightihtiyacımız var LinearLayout:

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

Ardından demo sınıfı:

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

Ayrıca bu örnek, animasyonları elde etmek için FragmentTransactions kullanmaz (bunun yerine, parçaların eklendiği görünümleri canlandırır), bu nedenle tüm arka yığın / parça işlemlerini kendiniz yapmanız gerekir, ancak animasyonların çalışmasını sağlama çabasıyla karşılaştırıldığında güzel bir şekilde, bu kötü bir takas gibi görünmüyor :)

Eylem halindeyken gösteren korkunç düşük çözünürlüklü videosu: http://youtu.be/Zm517j3bFCo


1
Eh, Gmail kesinlikle 0'a ağırlığını azaltarak (örneğin görsel eserleri tanıtmak olabileceğini endişelenirdi ben Fragment A. I olarak nitelendirdikleri bir çevirisini yapar TextViewile wrap_contentanimasyon ilerledikçe dikey kendini germe). Ancak, ağırlığı canlandırmak, B Parçası olarak adlandırdığım şeyi yeniden boyutlandırmak olabilir. Teşekkürler!
CommonsWare

1
Telaşa gerek yok! Yine de bu iyi bir nokta, bu muhtemelen 'A Parçası'nda çocukların görüşlerinin ne olduğuna bağlı olarak görsel kusurlara neden olacaktır. Birinin bunu yapmanın daha sağlam bir yolunu
bulmasını umalım

5

Bu parçalar kullanmıyor .... 3 çocuklu özel bir düzen. Bir mesaja tıkladığınızda, 3 çocuğu kullanarak dengelersiniz.offsetLeftAndRight() bir animatör .

İçinde JellyBean Seçenekleri" ayarlarında "Düzen sınırlarını göster" seçeneğini etkinleştirebilirsiniz. Slayt animasyonu tamamlandığında, sol menünün hala orada, ancak orta panelin altında olduğunu görebilirsiniz.

Bu benzeyen uygulama menüsünde Fly-Cyril Mottier en ama 3 elemanların yerine 2 ile.

Ek olarak, ViewPagerüçüncü çocuklarınki bu davranışın başka bir göstergesidir: ViewPagergenellikle Parçalar kullanır (mecbur olmadıklarını biliyorum, ancak bunun dışında bir uygulama görmedim Fragment) ve 3 çocuğu Fragmentsbaşka bir Fragmentçocuğun içinde kullanamayacağınız için muhtemelen parça değil ...


Daha önce "Düzen sınırlarını göster" i hiç kullanmadım - bu, bunun gibi kapalı kaynaklı uygulamalar için çok faydalıdır (teşekkürler!). Ekran kapalı çeviri ne ben Fragment A olarak tanımlanan bir gibi geliyor Fragment B olarak nitelendirdiği ve alt kısımda yer arkasını haşhaş önce (sol sağdan sağ kenarı slayt görebilir) görünür TranslateAnimationben de, elbette aynı amaçları gerçekleştirmek için animatörleri kullanmanın yolları vardır. Bununla ilgili bazı deneyler yapmam gerekecek. Teşekkürler!
CommonsWare

2

Şu anda böyle bir şey yapmaya çalışıyorum, ancak mevcut alanı almak için Fragment B ölçeği ve yeterli yer varsa 3 bölmenin aynı anda açılabilmesi dışında. Şimdiye kadarki çözümüm burada, ancak buna bağlı kalıp kalmayacağımdan emin değilim. Umarım birisi Doğru Yolu gösteren bir cevap verir.

Bir LinearLayout kullanmak ve ağırlığa animasyon uygulamak yerine, RelativeLayout kullanıyorum ve kenar boşluklarını canlandırıyorum. Bunun en iyi yol olduğundan emin değilim çünkü her güncellemede requestLayout () için bir çağrı gerektiriyor. Yine de tüm cihazlarımda sorunsuz.

Yani, düzeni canlandırıyorum, fragment işlemini kullanmıyorum. Açıksa C parçasını kapatmak için geri düğmesini manuel olarak kullanıyorum.

FragmentB, parçayı A ve C ile hizalı tutmak için layout_toLeftOf / ToRightOf komutunu kullanın.

Uygulamam C parçasını görüntülemek için bir olay tetiklediğinde, C parçasını içeri kaydırıyorum ve aynı anda A parçasını dışarı kaydırıyorum. (2 ayrı animasyon). Tersine, Parça A açıldığında, C'yi aynı anda kapatırım.

Dikey modda veya daha küçük ekranda, biraz farklı bir düzen kullanıyorum ve Parça C'yi ekranın üzerinde kaydırıyorum.

Parça A ve C'nin genişliği için yüzde kullanmak için, bunu çalışma zamanında hesaplamanız gerektiğini düşünüyorum ... (?)

İşte aktivitenin düzeni:

<?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'yi içeri veya dışarı kaydırmak için animasyon:

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.