實現信息: 我已經實現以下在這裏上了自己的問題發佈CommonsWare響應3面板佈局:Complete Working Sample of the Gmail Three-Fragment Animation Scenario?3面板佈局片段動畫口吃
作爲一般的想法,我的佈局包括的以下的水平(1〜3):
MainActivity
SlidingMenu
(側抽屜UI模式)片段隱藏在左側和ContentFragment
作爲容納3個窗格佈局的片段。- 在
ContentFragment
內部:LeftListFragment
(各有3個TextView的行),MiddleListFragment
(各有8個TextView的行),DetailFragment
。
LeftListFragment
和MiddleListFragment
使用CursorLoaders
從ContentProvider
每個列表內加載數據。 DetailFragment
也會在需要時調用帶有數據的遊標。所以我甚至沒有實現自定義Adapters
(這種方式更好的設計)。然後我添加了3個窗格佈局+動畫。就工作而言,它按預期工作,沒有問題。 Animation
時間是500ms。
問題: 動畫片斷了一下。幾個丟幀。當左邊和中間都可見時,我點擊一箇中間列表項打開一個細節;還有當我點擊返回按鈕再次看到左側和中間列表(當沒有實際加載時)。
我已經試過:
- 刪除加載的片段
DetailView
代碼。我只需點擊MiddleFragment
中的一個項目,動畫就開始了,沒有任何細節實際加載。仍然口吃。另外,當回擊時,沒有任何負載,它仍然結結巴巴,所以我認爲裝載機/遊標不是這個原因。 - 我使用dumpsys gfxinfo查看以毫秒爲單位計算每幀的平均時間。事實上,計算的平均時間是18ms,高於16ms閾值。那麼這是否意味着口吃是因爲在製作動畫時花費時間再次繪製列表?如果是這樣,爲什麼?我的意思是......我在內部行視圖中沒有任何圖像。而且我不能搞錯適配器代碼,因爲我沒有寫任何代碼...
- 將
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>
我認爲通過「實際上將每個框架放在每個框架上」,您指的是通過ObjectAnimator改變中間窗格的寬度。 AFAICT,電子郵件/ Gmail做了一些事情。我誤解了使用這些應用程序時看到的內容?還是有一種方法可以更加動畫友好的方式進行調整(不僅僅是等到結束)?還是那些應用程序也很笨?這裏的目標是爲實現電子郵件/ Gmail三窗格UI動畫效果創建可重複使用的模式。謝謝! – CommonsWare
那麼,當我在Gmail中執行動畫時,'systrace'沒有顯示'performTraversals()'調用。我假設我的眼睛在欺騙我,而Gmail在其動畫期間沒有調整對話列表窗格的大小。 – CommonsWare
如果他們實際調整了容器的大小並導致文本在每個框架上重排,他們會遭受相同的動畫問題(或更多,由於典型的Gmail郵件UI的複雜性)。我不知道他們在做什麼,但我認爲這只是一個滑動動畫,可能會在動畫之前將容器放到最終尺寸,然後簡單地將其轉換爲位置。 –