1

J'ai réussi à obtenir les fragments dans mon application pour se déplacer correctement comme je le veux, cependant, il y a quelque chose de incompatible avec l'animation. Pour être plus précis: j'ai une activité avec 2 images, et 3 fragments. Lorsque le fragment mobile se déplace, il apparaît momentanément à son emplacement final avant de glisser comme il le devrait du début à la fin (en utilisant les ressources d'animation). slide_in_left, slide_in_right, slide_out_left et slide_out_right).L'animation FragmentTransaction fonctionne mais apparaît glitchy

EDIT: mes appareils de test sont un 2 e génération nexus 7 et un Samsung Galaxy S3, donc ce ne devrait pas être un problème matériel. L'émulateur saute trop d'images pour le remarquer.

Si je déplace tous les 3 fragments ensemble cela ne se produit pas.

J'ai fait une application simple pour démontrer ce problème, peut-être que quelqu'un peut faire la lumière sur une solution. Voici les fichiers pertinents:

MainActivity.java

public class MainActivity extends Activity { 

    MainFragment mFragment1; 
    MainFragment mFragment2; 
    MainFragment mFragment3; 
    // variable to hold current frame for mFragment2 
    int mFrag2Frame; 



    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     if (savedInstanceState != null) { 
      mFrag2Frame = savedInstanceState.getInt("frag_2_frame"); 
     } else { 
      mFrag2Frame = R.id.rightFrame; 
     } 
     // First add mFragment1 and mFragment3 where they will stay. 
     // Then add mFragment2 over the top of its current frame 
     FragmentTransaction ft = getFragmentManager().beginTransaction(); 
     ft.add(R.id.leftFrame, mFragment1 = MainFragment.Frag1()) 
       .add(R.id.rightFrame, mFragment3 = MainFragment.Frag3()) 
       .add(mFrag2Frame, mFragment2 = MainFragment.Frag2()) 
       .commit(); 
    } 

    @Override 
    public void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 

     outState.putInt("frag_2_frame", mFrag2Frame); 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 

     getMenuInflater().inflate(R.menu.main, menu); 
     return true; 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 

     int id = item.getItemId(); 
     if (id == R.id.action_slide) { 
      slide(); 
      return true; 
     } 
     return super.onOptionsItemSelected(item); 
    } 

    public void slide() { 

     boolean isLeft = mFrag2Frame == R.id.leftFrame; 
     FragmentTransaction ft = getFragmentManager().beginTransaction(); 

     // set animations according to current position of mFragment2 
     ft.setCustomAnimations(isLeft ? R.anim.slide_in_left : R.anim.slide_in_right, 
       isLeft ? R.anim.slide_out_right : R.anim.slide_out_left); 

     // change mFrag2Frame according to its current value 
     mFrag2Frame = isLeft ? R.id.rightFrame : R.id.leftFrame; 

     ft.remove(mFragment2) 
       .add(mFrag2Frame, mFragment2 = MainFragment.Frag2()) 
       .commit(); 
    } 
} 

main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:id="@+id/LinearLayout1" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin" 
    tools:context="com.example.slidingpanels.MainActivity" > 

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

    </FrameLayout> 

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

    </FrameLayout> 

</LinearLayout> 

MainFragment.java

public class MainFragment extends Fragment { 

    int mResourceId = R.layout.frag; 
    int mColour; 

    public MainFragment() { 
    } 

    private MainFragment(int colour) { 
     mColour = colour; 
    } 

    public static MainFragment Frag1() { 
     return new MainFragment(Color.RED); 
    } 
    public static MainFragment Frag2() { 
     return new MainFragment(Color.GREEN); 
    } 
    public static MainFragment Frag3() { 
     return new MainFragment(Color.BLUE); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
      Bundle savedInstanceState) { 

     View view = inflater.inflate(mResourceId, container, false); 
     view.setBackgroundColor(mColour); 
     return view; 
    } 
} 

frag.xml

<?xml version="1.0" encoding="utf-8"?> 
<com.example.slidingpanels.SlidingImageView 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/SlidingImageView1" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:src="@drawable/ic_launcher" > 

</com.example.slidingpanels.SlidingImageView> 

SlidingImageView.java

public class SlidingImageView extends ImageView { 

    public SlidingImageView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     // TODO Auto-generated constructor stub 
    } 

    public float getXFraction() { 

     int width = this.getWidth(); 
     return (width == 0) ? 0 : getX()/(float) width; 
    } 

    public void setXFraction(float xFraction) { 

     int width = this.getWidth(); 
     setX((width > 0) ? (xFraction * width) : 0); 
    } 
} 

et enfin, je vais ajouter mon slide_in_left.xml

<?xml version="1.0" encoding="utf-8"?> 
<set> 
    <objectAnimator 
     xmlns:android="http://schemas.android.com/apk/res/android" 
     android:propertyName="xFraction" 
     android:valueType="floatType" 
     android:valueFrom="-1" 
     android:valueTo="0" 
     android:duration="500" /> 
</set> 

Répondre

2

J'ai eu exactement le même problème ...

Le scintillement/Glitch est provoquée parce que quand setXFraction est d'abord appelé son avant onMeasure est arrivé et donc getWidth() renvoie 0 signifiant pour le premier cadre dessiné le fragment est dans la position non-scrolled, l'appel suivant arrive après le onMeasure et son dessiné dans le bon endroit comme getWidth renvoie maintenant des données valides.

La solution que j'ai trouvée est d'enregistrer la fraction X dans l'objet de vue comme un flottant, puis de surcharger onMeasure() et refaire le setTranslationX, en utilisant mXfract * measureSpec.

Works pour moi, extrait de code ci-dessous:

public class SlidingFrameLayout extends FrameLayout { 

    private static final String TAG = SlidingFrameLayout.class.getSimpleName(); 

    private float mXfract=0f; 
    private float mYfract=0f; 

    public SlidingFrameLayout(Context context) { 
     super(context); 
    } 

    public SlidingFrameLayout(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public SlidingFrameLayout(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    public void setYFraction(final float fraction) { 
     mYfract=fraction; 
     float translationY = getHeight() * fraction; 
     setTranslationY(translationY); 
    } 

    public float getYFraction() { 
     return mYfract; 
    } 

    public void setXFraction(final float fraction) { 
     mXfract=fraction; 
     float translationX = getWidth() * fraction; 
     setTranslationX(translationX); 
    } 

    public float getXFraction() { 
     return mXfract; 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     int width = MeasureSpec.getSize(widthMeasureSpec); 
     int height = MeasureSpec.getSize(heightMeasureSpec); 
     // Correct any translations set before the measure was set 
     setTranslationX(mXfract*width); 
     setTranslationY(mYfract*height); 
    } 
} 

Modifié: Pour utiliser measurespec pour la largeur/hauteur au lieu d'appel.

+0

Génial !! Cela fonctionne parfaitement, bien que j'ai continué à utiliser setX() par opposition à setTranslationX(). Le javadoc suggère que setTranslationX() soit plus approprié à cette fin ... Quelles sont vos pensées? –

+0

Ils sont assez différents mais peuvent se ressembler en fonction de votre disposition, si votre vue est en haut à gauche 0,0, ils seront identiques, si décalés dans le parent alors très différents: setX/Y - Définit la position dans le parent 0 , 0. setTransitionX/Y - Définit la position relative à la visualisation de la position par défaut – Cakey

Questions connexes