4

實現信息: 我已經實現以下在這裏上了自己的問題發佈CommonsWare響應3面板佈局:Complete Working Sample of the Gmail Three-Fragment Animation Scenario?3面板佈局片段動畫口吃

作爲一般的想法,我的佈局包括的以下的水平(1〜3):

  1. MainActivity
  2. SlidingMenu(側抽屜UI模式)片段隱藏在左側和ContentFragment作爲容納3個窗格佈局的片段。
  3. ContentFragment內部:LeftListFragment(各有3個TextView的行),MiddleListFragment(各有8個TextView的行),DetailFragment

LeftListFragmentMiddleListFragment使用CursorLoadersContentProvider每個列表內加載數據。 DetailFragment也會在需要時調用帶有數據的遊標。所以我甚至沒有實現自定義Adapters(這種方式更好的設計)。然後我添加了3個窗格佈局+動畫。就工作而言,它按預期工作,沒有問題。 Animation時間是500ms。

問題: 動畫片斷了一下。幾個丟幀。當左邊和中間都可見時,我點擊一箇中間列表項打開一個細節;還有當我點擊返回按鈕再次看到左側和中間列表(當沒有實際加載時)。

我已經試過:

  1. 刪除加載的片段DetailView代碼。我只需點擊MiddleFragment中的一個項目,動畫就開始了,沒有任何細節實際加載。仍然口吃。另外,當回擊時,沒有任何負載,它仍然結結巴巴,所以我認爲裝載機/遊標不是這個原因。
  2. 我使用dumpsys gfxinfo查看以毫秒爲單位計算每幀的平均時間。事實上,計算的平均時間是18ms,高於16ms閾值。那麼這是否意味着口吃是因爲在製作動畫時花費時間再次繪製列表?如果是這樣,爲什麼?我的意思是......我在內部行視圖中沒有任何圖像。而且我不能搞錯適配器代碼,因爲我沒有寫任何代碼...
  3. Animation時間從500ms減少到200ms。如果你仔細觀察它,它仍然會結結巴巴,它只是更快。

編輯:我從rightPaneWidth切換到下面leftPaneWidth(是的,這消除了重新標註動畫)和口吃現在已經沒有了。該列表仍然向左側滑動,但寬度不會變小。所以如果現在沒有更多的口吃,這是否意味着我的代碼中的ObjectAnimator存在問題?

ObjectAnimator.ofInt(this, "middleWidth", rightPaneWidth, leftPaneWidth) 
        .setDuration(ANIM_DURATION).start(); 

謝謝你的時間!

代碼3面板佈局:

package com.xyz.view.widget; 

import android.animation.ObjectAnimator; 
import android.annotation.SuppressLint; 
import android.annotation.TargetApi; 
import android.content.Context; 
import android.util.AttributeSet; 
import android.view.View; 
import android.view.ViewPropertyAnimator; 
import android.widget.LinearLayout; 


public class ThreePaneLayout extends LinearLayout 
{ 
    private View leftView = null; 
    private View middleView = null; 
    private View rightView = null; 

private static final int ANIM_DURATION = 500; 
private int leftPaneWidth = -1; 
private int rightPaneWidth = -1; 


// ------------------------------------------------------------------------------------------- 
// -------------- Constructor 
// ------------------------------------------------------------------------------------------- 


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

@Override 
public void onFinishInflate() 
{ 
    super.onFinishInflate(); 
    leftView = getChildAt(0); 
    middleView = getChildAt(1); 
    rightView = getChildAt(2); 
} 


// ------------------------------------------------------------------------------------------- 
// -------------- Public methods 
// ------------------------------------------------------------------------------------------- 


public View getLeftView() 
{ 
    return leftView; 
} 

public View getMiddleView() 
{ 
    return middleView; 
} 

public View getRightView() 
{ 
    return rightView; 
} 

@SuppressLint("NewApi") 
public void hideLeft() 
{ 
    if (leftPaneWidth == -1) 
    { 

     leftPaneWidth = leftView.getWidth(); 
     rightPaneWidth = middleView.getWidth(); 
     resetWidget(leftView, leftPaneWidth); 
     resetWidget(middleView, rightPaneWidth); 
     resetWidget(rightView, rightPaneWidth); 
     requestLayout(); 
    } 
    translateWidgets(-1 * leftPaneWidth, leftView, middleView, rightView); 
    ObjectAnimator.ofInt(this, "middleWidth", rightPaneWidth, leftPaneWidth) 
        .setDuration(ANIM_DURATION).start(); 
} 

@SuppressLint("NewApi") 
public void showLeft() 
{ 
    translateWidgets(leftPaneWidth, leftView, middleView, rightView); 
    ObjectAnimator.ofInt(this, "middleWidth", leftPaneWidth, rightPaneWidth) 
        .setDuration(ANIM_DURATION) 
        .start(); 
} 


// ------------------------------------------------------------------------------------------- 
// -------------- Private methods 
// ------------------------------------------------------------------------------------------- 


private void setMiddleWidth(int value) 
{ 
    middleView.getLayoutParams().width = value; 
    requestLayout(); 
} 

@TargetApi(12) 
private void translateWidgets(int deltaX, View... views) 
{ 
    for (final View view : views) 
    { 
     ViewPropertyAnimator viewPropertyAnimator = view.animate(); 
     viewPropertyAnimator.translationXBy(deltaX) 
          .setDuration(ANIM_DURATION); 
    } 
    } 

    private void resetWidget(View view, int width) 
    { 
     LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)view.getLayoutParams(); 
     layoutParams.width = width; 
     layoutParams.weight = 0; 
    } 
} 

XML爲ContentFragment:

<?xml version="1.0" encoding="utf-8"?> 
<com.xyz.view.widget.ThreePaneLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/fragment_content_three_pane_layout" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" > 

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

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

<FrameLayout 
    android:id="@+id/fragment_content_framelayout_right" 
    android:layout_width="0dp" 
    android:layout_height="match_parent" /> 
</com.xyz.view.widget.ThreePaneLayout> 

回答

10

問題不在於ObjectAnimator,而是您的應用程序只是在動畫的每一幀上做的太多。具體而言,您正在爲每個框架設置佈局參數並請求佈局。佈局功能強大而且有用......但在任何視圖層次結構中都可能非常昂貴。在動畫過程中避免每幀昂貴的操作非常重要,並且佈局會落入該「昂貴」類別中。滑動東西很好(translationX/Y),淡入淡出的東西很好(alpha),但是實際上在每一幀都放置了東西?拒絕吧。

+1

我認爲通過「實際上將每個框架放在每個框架上」,您指的是通過ObjectAnimator改變中間窗格的寬度。 AFAICT,電子郵件/ Gmail做了一些事情。我誤解了使用這些應用程序時看到的內容?還是有一種方法可以更加動畫友好的方式進行調整(不僅僅是等到結束)?還是那些應用程序也很笨?這裏的目標是爲實現電子郵件/ Gmail三窗格UI動畫效果創建可重複使用的模式。謝謝! – CommonsWare

+1

那麼,當我在Gmail中執行動畫時,'systrace'沒有顯示'performTraversals()'調用。我假設我的眼睛在欺騙我,而Gmail在其動畫期間沒有調整對話列表窗格的大小。 – CommonsWare

+1

如果他們實際調整了容器的大小並導致文本在每個框架上重排,他們會遭受相同的動畫問題(或更多,由於典型的Gmail郵件UI的複雜性)。我不知道他們在做什麼,但我認爲這只是一個滑動動畫,可能會在動畫之前將容器放到最終尺寸,然後簡單地將其轉換爲位置。 –

0

我結束了去除ObjectAnimator完全......它仍然具有幻燈片動畫,當然,但不是在同一時間順利重新設計尺寸。不是說它實際上很流暢......

無論如何,如果有人提出了一個實際的解決方案來解決這個問題,隨時分享。謝謝。

package com.anfuddle.view.widget; 

import android.annotation.TargetApi; 
import android.content.Context; 
import android.os.Build; 
import android.util.AttributeSet; 
import android.view.View; 
import android.view.ViewPropertyAnimator; 
import android.widget.LinearLayout; 


public class ThreePaneLayout extends LinearLayout 
{ 
    private View leftView = null; 
    private View middleView = null; 
    private View rightView = null; 

private static final int ANIM_DURATION = 500; 
private int rootWidth = -1; 
private int leftPaneWidth = -1; 
private int rightPaneWidth = -1; 


// ------------------------------------------------------------------------------------------- 
// -------------- Constructor 
// ------------------------------------------------------------------------------------------- 


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

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

    leftView = getChildAt(0); 
    middleView = getChildAt(1); 
    rightView = getChildAt(2); 
} 


// ------------------------------------------------------------------------------------------- 
// -------------- Public methods 
// ------------------------------------------------------------------------------------------- 


public View getLeftView() 
{ 
    return leftView; 
} 

public View getMiddleView() 
{ 
    return middleView; 
} 

public View getRightView() 
{ 
    return rightView; 
} 

@TargetApi(Build.VERSION_CODES.HONEYCOMB) 
public void hideLeftAndMiddle() 
{ 
    if (leftPaneWidth == -1) 
    { 
     rootWidth = getWidth(); 
     leftPaneWidth = leftView.getWidth(); 
     rightPaneWidth = middleView.getWidth(); 
    } 
    resetWidget(leftView, leftPaneWidth); 
    resetWidget(middleView, rightPaneWidth); 
    resetWidget(rightView, rootWidth); 
    requestLayout(); 

    translateWidgets(-1 * rootWidth, leftView, middleView, rightView); 
} 

@TargetApi(Build.VERSION_CODES.HONEYCOMB) 
public void hideLeft() 
{ 
    if (leftPaneWidth == -1) 
    { 
     leftPaneWidth = leftView.getWidth(); 
     rightPaneWidth = middleView.getWidth(); 
    } 
    resetWidget(leftView, leftPaneWidth); 
    resetWidget(middleView, leftPaneWidth); 
    resetWidget(rightView, rightPaneWidth); 
    requestLayout(); 
    translateWidgets(-1 * leftPaneWidth, leftView, middleView, rightView); 
} 

@TargetApi(Build.VERSION_CODES.HONEYCOMB) 
public void showLeftAndMiddle() 
{ 
    translateWidgets(rootWidth, leftView, middleView, rightView); 
} 

@TargetApi(Build.VERSION_CODES.HONEYCOMB) 
public void showLeft() 
{ 
    resetWidget(leftView, leftPaneWidth); 
    resetWidget(middleView, rightPaneWidth); 
    resetWidget(rightView, rightPaneWidth); 
    requestLayout(); 
    translateWidgets(leftPaneWidth, leftView, middleView, rightView); 
} 


// ------------------------------------------------------------------------------------------- 
// -------------- Private methods 
// ------------------------------------------------------------------------------------------- 


@TargetApi(12) 
private void translateWidgets(int deltaX, View... views) 
{ 
    for (final View view : views) 
    { 
     ViewPropertyAnimator viewPropertyAnimator = view.animate(); 
     viewPropertyAnimator.translationXBy(deltaX) 
          .setDuration(ANIM_DURATION); 
    } 
    } 

    private void resetWidget(View view, int width) 
    { 
     LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)view.getLayoutParams(); 
     layoutParams.width = width; 
     layoutParams.weight = 0; 
    } 
}