3窗格布局片段动画是口吃

时间:2013-02-02 19:12:10

标签: android android-fragments android-animation objectanimator

实施信息: 我已经在他自己的问题上发布了CommonsWare响应之后实现了3个窗格布局:Complete Working Sample of the Gmail Three-Fragment Animation Scenario?

作为一般概念,我的布局包括以下级别(1到3):

  1. MainActivity
  2. 隐藏在左侧的
  3. SlidingMenu(侧抽屉UI模式)片段和作为包含3个窗格布局的片段的ContentFragment
  4. ContentFragmentLeftListFragment(每个包含3个TextView的行),MiddleListFragment(每个包含8个TextView的行),DetailFragment内。
  5. LeftListFragmentMiddleListFragment使用CursorLoaders从每个列表中的ContentProvider加载数据。 DetailFragment还在需要时调用带有数据的游标。所以我甚至没有实现自定义Adapters(这种方式更好的设计)。然后我添加了3个窗格布局+动画。就工作而言,它按预期工作,没有问题。 Animation时间是500毫秒。

    问题: 动画口吃一点。一些丢帧。当左侧和中间都可见时,我单击中间列表项以打开详细信息;当我点击“返回”按钮再次查看“左”和“中间”列表时(实际上什么都没有加载)。

    我尝试了什么:

    1. 删除了在DetailView中加载片段的代码。我只是点击MiddleFragment中的一个项目,动画开始,没有任何细节实际加载。仍然口吃。此外,当击退时,没有任何负载,它仍然口吃,所以我认为装载器/光标不是原因。
    2. 我使用dumpsys gfxinfo来查看计算每个帧的平均时间(以毫秒为单位)。实际上,计算的平均时间是18ms,高于16ms阈值。这是否意味着口吃是因为在制作动画时再次绘制列表所需的时间?如果是这样,为什么?我的意思是......在行视图中我根本没有任何图像。我无法搞砸Adapters代码,因为我没有写过任何代码...
    3. Animation时间从500毫秒减少到200毫秒。如果你仔细观察它仍然会口吃,它会更快。
    4. 编辑:我从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;
        }
      }
      

      ContentFragment的XML:

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

2 个答案:

答案 0 :(得分:10)

问题不是ObjectAnimator,而是你的应用程序在动画的每一帧上都做得太多了。具体来说,您可以设置布局参数的动画并在每个帧上请求布局。布局功能强大且有用......但除了最简单的视图层次结构之外,它可能非常昂贵。在动画期间避免昂贵的每帧操作非常重要,并且布局属于“昂贵”类别。滑动的东西很好(translationX / Y),淡入/淡出的东西是好的(alpha),但实际上在每一帧上放置东西?说不。

答案 1 :(得分: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;
  }
}