Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>OK, here is my own solution, derived from the Email AOSP app, per @Christopher's suggestion in the question's comments.</p> <p><a href="https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane" rel="noreferrer">https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane</a></p> <p>@weakwire's solution is reminiscent of mine, though he uses classic <code>Animation</code> rather than animators, and he uses <code>RelativeLayout</code> rules to enforce positioning. From the bounty standpoint, he will probably get the bounty, unless somebody else with a slicker solution yet posts an answer.</p> <hr> <p>In a nutshell, the <code>ThreePaneLayout</code> in that project is a <code>LinearLayout</code> subclass, designed to work in landscape with three children. Those childrens' widths can be set in the layout XML, via whatever desired means -- I show using weights, but you could have specific widths set by dimension resources or whatever. The third child -- Fragment C in the question -- should have a width of zero.</p> <pre><code>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; } } </code></pre> <p>However, at runtime, no matter how you originally set up the widths, width management is taken over by <code>ThreePaneLayout</code> the first time you use <code>hideLeft()</code> to switch from showing what the question referred to as Fragments A and B to Fragments B and C. In the terminology of <code>ThreePaneLayout</code> -- which has no specific ties to fragments -- the three pieces are <code>left</code>, <code>middle</code>, and <code>right</code>. At the time you call <code>hideLeft()</code>, we record the sizes of <code>left</code> and <code>middle</code> and zero out any weights that were used on any of the three, so we can completely control the sizes. At the point in time of <code>hideLeft()</code>, we set the size of <code>right</code> to be the original size of <code>middle</code>.</p> <p>The animations are two-fold:</p> <ul> <li>Use a <code>ViewPropertyAnimator</code> to perform a translation of the three widgets to the left by the width of <code>left</code>, using a hardware layer</li> <li>Use an <code>ObjectAnimator</code> on a custom pseudo-property of <code>middleWidth</code> to change the <code>middle</code> width from whatever it started with to the original width of <code>left</code></li> </ul> <p>(it is possible that it is a better idea to use an <code>AnimatorSet</code> and <code>ObjectAnimators</code> for all of these, though this works for now)</p> <p>(it is also possible that the <code>middleWidth</code> <code>ObjectAnimator</code> negates the value of the hardware layer, since that requires fairly continuous invalidation)</p> <p>(it is <em>definitely</em> possible that I still have gaps in my animation comprehension, and that I like parenthetical statements)</p> <p>The net effect is that <code>left</code> slides off the screen, <code>middle</code> slides to the original position and size of <code>left</code>, and <code>right</code> translates in right behind <code>middle</code>.</p> <p><code>showLeft()</code> simply reverses the process, with the same mix of animators, just with the directions reversed.</p> <p>The activity uses a <code>ThreePaneLayout</code> to hold a pair of <code>ListFragment</code> widgets and a <code>Button</code>. Selecting something in the left fragment adds (or updates the contents of) the middle fragment. Selecting something in the middle fragment sets the caption of the <code>Button</code>, plus executes <code>hideLeft()</code> on the <code>ThreePaneLayout</code>. Pressing BACK, if we hid the left side, will execute <code>showLeft()</code>; otherwise, BACK exits the activity. Since this does not use <code>FragmentTransactions</code> for affecting the animations, we are stuck managing that "back stack" ourselves.</p> <p>The project linked-to above uses native fragments and the native animator framework. I have another version of the same project that uses the Android Support fragments backport and <a href="http://nineoldandroids.com/" rel="noreferrer">NineOldAndroids</a> for the animation:</p> <p><a href="https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC" rel="noreferrer">https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC</a></p> <p>The backport works fine on a 1st generation Kindle Fire, though the animation is a bit jerky given the lower hardware specs and lack of hardware acceleration support. Both implementations seem smooth on a Nexus 7 and other current-generation tablets.</p> <p>I am certainly open for ideas of how to improve this solution, or other solutions that offer clear advantages over what I did here (or what @weakwire used).</p> <p>Thanks again to everyone who has contributed!</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. COHi! Thanks for the solution, but I think, adding v.setLayerType() sometimes makes things a little bit slower. Just remove these lines (and Listener too) This is at least true for Android 3+ devices I've just tried on Galaxy Nexus and without these lines it's working much better
      singulars
    2. CO"Both implementations seem smooth on a Nexus 7 and other current-generation tablets." I believe you; but I am testing your implementation with native ObjectAnimator on a Nexus 7 and it lags a bit. What would be the most probable causes ? I already have hardwareAccelerated:true in AndroidMAnifest. I really don't know what could be the problem. I've also tried DanielGrech's variant and it lags the same. I can see differences in the animation (his animation is based on weights), but I can't see why it would lag in both cases.
      singulars
    3. CO@Andrew: "it lags a bit" -- I am uncertain how you are using the verb "lag" here. To me, "lag" implies a concrete target for comparison. So, for example, an animation tied to a swiping motion might lag behind the finger movement. In this case, everything is triggered by taps, and so I am unclear what the animation might be lagging behind. Guessing that perhaps "lags a bit" simply means "feels sluggish", I have not seen that, but I do not claim to have strong aesthetic sense, and so what might be an acceptable animation to me might be unacceptable to you.
      singulars
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload