Swipe Back comme Pinterest ou Tumblr

Quelqu’un a-t-il une idée de la façon dont Pinterest ou Tumblr a mis en œuvre la méthode de “retour en arrière”.

c’est-à-dire sur Pinterest, vous pouvez cliquer sur un article sur le fil d’actualité. Que DetailActivity est démarré et affiche les détails de la publication sélectionnée. Vous pouvez ensuite appuyer sur le bouton Précédent pour revenir à l’activité du fil d’actualité ou vous pouvez glisser (l’activité des détails) vers la gauche pour revenir à l’activité du fil d’actualité.

Vidéo: http://youtu.be/eVcSCWetnTA

Normalement, j’utiliserais overridePendingTransition() , mais overridePendingTransition() prend des animations (identificateurs de ressources comme R.anim.foo ). Pinterest et Tumblr ne démarrent l’animation que si l’utilisateur effectue un geste de balayage. Ils supportent également une sorte d’animation image par image en fonction du mouvement des doigts. Ainsi, ils suivent la distance du mouvement du doigt et animent la transition vers la valeur en pourcentage correspondante.

Je sais utiliser un object Animation / AnimatorSet “real java” avec FragmentTransaction pour animer un remplacement de fragment. Avec des fragments, je dois remplacer onCreateAnimator() , mais je ne sais pas comment implémenter quelque chose comme ça avec Activities. Existe-t-il un onCreateAnimator() (ou quelque chose de similaire) pour Activities? Aussi pas sûr de savoir comment glisser le comportement, car il ne commence pas l’animation en ce moment, mais plus un changement de propriété étape par étape de la fenêtre / activité / fragment ou autre chose …

Aucune suggestion?

EDIT: J’ai trouvé une vidéo de l’application pinterest sur youtube: http://youtu.be/eVcSCWetnTA C’est ce que je veux implémenter.

Je suppose que Pinterest travaille avec Fragments et onCreateAnimator() pour obtenir le “retour en arrière”. Étant donné que mon application a déjà des fragments et ChildFragments dans une activité, ce serait tellement plus facile pour moi si je pouvais implémenter cela pour les activités.

Encore une fois: je sais détecter les gestes par glissement et ce n’est pas ce que je demande. Regardez la vidéo sur youtube: http://youtu.be/eVcSCWetnTA


MISE À JOUR: J’ai créé une petite bibliothèque qui n’a pas exactement le même comportement que Pinterest ou l’implémentation de Tumblr, mais pour mes applications, cela me semble une bonne solution: https://github.com/sockeqwe/SwipeBack?source=c

Il semble que l’effet recherché soit l’un des exemples de ViewPager sur le site Web du développeur Android.

Découvrez http://developer.android.com/training/animation/screen-slide.html#depth-page , dans la section Transformateur de page de profondeur . Il a une vidéo et un code source.

En utilisant un ViewPager.PageTransformer, vous pouvez décider du comportement des pages lors du passage de l’une à l’autre.

La seule différence entre l’échantillon et la vidéo que vous avez associée est que la gauche semble être inversée, mais devrait être un bon sharepoint départ pour ce que j’ai vu sur la vidéo YouTube liée à la question. Les actions sur les deux vues devront être remplacées. Comme indiqué dans ce morceau de code (le premier paramètre de mPager.setPageTransformer doit être reverseDrawingOrder = false). Notez le milieu 2 if sections sont remplacées et la variable de position est traitée légèrement différente pour changer de côté. L’effet rebondissant est laissé comme exercice. S’il vous plaît partager quand vous obtenez cela!

  package com.example.android.animationsdemo; import android.support.v4.view.ViewPager; import android.view.View; public class SinkPageTransformer implements ViewPager.PageTransformer { private static float MIN_SCALE = 0.75f; public void transformPage(View view, float position) { int pageWidth = view.getWidth(); if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0); } else if (position <= 0) { // [-1,0] // Fade the page out. view.setAlpha(1 + position); // Counteract the default slide transition view.setTranslationX(pageWidth * -position); // Scale the page down (between MIN_SCALE and 1) float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)); view.setScaleX(scaleFactor); view.setScaleY(scaleFactor); } else if (position <= 1) { // (0,1] // Use the default slide transition when moving to the left page view.setAlpha(1); view.setTranslationX(0); view.setScaleX(1); view.setScaleY(1); } else { // (1,+Infinity] // This page is way off-screen to the right. view.setAlpha(0); } } } 

Et juste au cas où la page avec l'échantillon va poof, voici le code original de cette section:

  public class DepthPageTransformer implements ViewPager.PageTransformer { private static float MIN_SCALE = 0.75f; public void transformPage(View view, float position) { int pageWidth = view.getWidth(); if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0); } else if (position <= 0) { // [-1,0] // Use the default slide transition when moving to the left page view.setAlpha(1); view.setTranslationX(0); view.setScaleX(1); view.setScaleY(1); } else if (position <= 1) { // (0,1] // Fade the page out. view.setAlpha(1 - position); // Counteract the default slide transition view.setTranslationX(pageWidth * -position); // Scale the page down (between MIN_SCALE and 1) float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)); view.setScaleX(scaleFactor); view.setScaleY(scaleFactor); } else { // (1,+Infinity] // This page is way off-screen to the right. view.setAlpha(0); } } } 

Mise à jour: correction du problème d’utilisation de la mémoire pour ce projet et modification du style de retour à la norme iOS.

entrer la description de l'image ici

J’ai écrit une démo exactement comme Pinterest et comme tumblr, vous ne faites que prolonger la BaseActivity et vous obtenez un effet de retour rapide, ça marche bien!

vérifier ceci: https://github.com/chenjishi/SlideActivity

et la capture d’écran: entrer la description de l'image ici

J’ai trouvé un projet GitHub basé sur SwipeBack comme Pinterest.

C’est vraiment un grand projet open source qui devrait résoudre votre problème. Il fait comme vous le souhaitez, comme aller à l’écran précédent en appuyant sur ou en glissant simplement. Comme ce projet ayant l’option

1. Glissez de gauche à droite

2. Glissez de droite à gauche

3. Faites glisser le bas vers le haut

https://github.com/Issacw0ng/SwipeBackLayout

et aussi vous installez cette application de démonstration à partir de Google Play.

https://play.google.com/store/apps/details?id=me.imid.swipebacklayout.demo

Captures d’écran attachées: –

entrer la description de l'image ici

J’espère que ceci vous aidera.

J’ai pu le faire en 15 minutes, ce n’est pas mal pour un début. Si vous passez du temps, vous pourrez peut-être l’optimiser.

 package mobi.sherif.activitydrag; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; import android.view.animation.LinearInterpolator; import android.view.animation.TranslateAnimation; import android.widget.FrameLayout.LayoutParams; public class MainActivity extends Activity { private static final double PERCENT_OF_SCROLL_OF_ACTIVITY_TO_FINISH = 0.3; View mView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mView = LayoutInflater.from(this).inflate(R.layout.activity_main, null); setContentView(mView); } private boolean isDragging = false; int startX; int currentX; @Override public boolean onTouchEvent(MotionEvent event) { Log.v("sherif", isDragging?"YES":"NO" + ": " + event.getX()); if(!isDragging) { if(event.getAction() == MotionEvent.ACTION_DOWN && event.getX()<24) { isDragging = true; startX = (int) event.getX(); currentX = 0; return true; } return super.onTouchEvent(event); } switch(event.getAction()) { case MotionEvent.ACTION_MOVE: currentX = (int) event.getX() - startX; LayoutParams params = (LayoutParams) mView.getLayoutParams(); params.leftMargin = currentX; params.rightMargin = -1 * currentX; mView.requestLayout(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: isDragging = false; double currentPercent1 = (double) currentX / mView.getWidth(); float currentPercent = (float) currentPercent1; if(currentX > PERCENT_OF_SCROLL_OF_ACTIVITY_TO_FINISH * mView.getWidth()) { AnimationSet animation = new AnimationSet(false); Animation anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 1.0f - currentPercent, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f); anim.setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime)); anim.setInterpolator(new LinearInterpolator()); anim.setStartTime(AnimationUtils.currentAnimationTimeMillis()); animation.addAnimation(anim); anim = new AlphaAnimation(1.0f, 0.5f); anim.setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime)); anim.setInterpolator(new LinearInterpolator()); anim.setStartTime(AnimationUtils.currentAnimationTimeMillis()); animation.addAnimation(anim); animation.setFillAfter(true); animation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) {} @Override public void onAnimationRepeat(Animation animation) {} @Override public void onAnimationEnd(Animation animation) { finish(); } }); mView.startAnimation(animation); } else { AnimationSet animation = new AnimationSet(false); Animation anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f -1 * currentPercent, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f); anim.setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime)); anim.setInterpolator(new LinearInterpolator()); anim.setStartTime(AnimationUtils.currentAnimationTimeMillis()); animation.addAnimation(anim); animation.setFillAfter(true); animation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) {} @Override public void onAnimationRepeat(Animation animation) {} @Override public void onAnimationEnd(Animation animation) { LayoutParams params = (LayoutParams) mView.getLayoutParams(); params.leftMargin = 0; params.rightMargin = 0; mView.requestLayout(); mView.clearAnimation(); } }); mView.startAnimation(animation); } break; } return true; } } 

Je viens de vérifier avec le visualiseur hiérarchique. Il semble qu’ils utilisent ViewPager avec une capture d’écran de l’activité précédente.

Je suggère de faire ce qui suit:

Tout d’abord, détectez le geste que l’utilisateur fait dans l’appareil. Vous pouvez vous référer à ce lien

Je ne vais pas copier le code correspondant du lien ci-dessus, car je pense que c’est la réponse acceptée

Deuxièmement, vous pouvez dans cette méthode

 public void onSwipeLeft() { Toast.makeText(MyActivity.this, "left", Toast.LENGTH_SHORT).show(); } 

Faites ce qui suit comme suggéré par cette question

Ils parlent de terminer une activité avec une animation

 Look into doing it through a theme. You can define enter exit animations for activities or the entire application 

J’espère que cela vous aide

Donc je suppose que j’ai trouvé la solution par moi-même:

Tout d’abord: Pinterest utilise en effet un ViewPager avec un Transformer Page personnalisé comme @frozenkoi l’a mentionné dans sa réponse. Vous pouvez voir l’effet de bord de la liste déroulante de la vue dans l’application pinterest.

@ Admettre Gupta a désigné la bibliothèque qui laisse glisser l’activité. Son concept est similaire à celui des tiroirs de navigation et le thème est également translucide. Ils font glisser la mise en page. Mais ce n’est pas exactement ce que je cherchais, car il glisse l’activité du haut vers la droite et appelle simplement finish (). Mais l’activité sous-jacente ne sera pas animée.

La solution est (et je suppose que c’est bien le cas pour Tumblr) pour écrire votre propre animation avec des objects d’animation et l’animer pas à pas. Cela peut être fait avec ActivityOptions . À mon avis, ce sera la solution.

J’ai écrit un projet. Il vous permet de développer facilement une application naviguée par Fragments, tout comme Pinterest.

https://github.com/fengdai/FragmentMaster

Peut-être que ce n’est pas la réponse que vous voulez. Mais j’espère que c’est utile à quelqu’un d’autre.

Je travaillais sur celui-ci dans le projet sur lequel je travaille actuellement et j’ai proposé le code suivant. Peut-être que ce n’est pas pertinent pour vous maintenant, mais cela pourrait aider quelqu’un de nouveau dans ce post. 🙂

Fondamentalement, c’est l’implémentation de ViewPager que vous avez mentionnée dans votre réponse, mais je pense que c’est la solution la plus simple et la plus rapide à votre question. Les inconvénients sont que ce n’est que pour les fragments (peut être facilement modifié pour les objects) et si vous voulez append ActionBar dans swipe, vous finirez probablement par en créer un personnalisé.

 public class DoubleViewPager extends FrameLayout implements ViewPager.OnPageChangeListener { /** * Represents number of objects in DelegateViewPager. In others words it stands for one main content * window and one content detail window */ private static final int SCREEN_COUNT = 2; private static final int CONTENT_SCREEN = 0; private static final int DETAIL_SCREEN = 1; private DelegateViewPager delegateViewPager; private SparseArray activeScreens = new SparseArray(SCREEN_COUNT) ; private DelegateAdapter adapter; public DoubleViewPager(Context context) { this(context, null); } public DoubleViewPager(Context context, AtsortingbuteSet attrs) { this(context, attrs, 0); } public DoubleViewPager(Context context, AtsortingbuteSet attrs, int defStyle) { super(context, attrs, defStyle); delegateViewPager = new DelegateViewPager(context); delegateViewPager.setId(R.id.main_page_id); delegateViewPager.setOverScrollMode(ViewPager.OVER_SCROLL_NEVER); final FrameLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER); addView(delegateViewPager, params); } /** * Create a new PagerAdapter and set content fragment as a first object in ViewPager; * @param fragment Fragment you want to use as a main content * @param fm FragmentManager required for ViewPager transactions */ public void initialize(final Fragment fragment, final FragmentManager fm) { adapter = new DelegateAdapter(fm); delegateViewPager.setAdapter(adapter); activeScreens.put(CONTENT_SCREEN, fragment); adapter.notifyDataSetChanged(); } /** * Adds fragment to stack and set it as current selected item. Basically it the same thing as calling * startActivity() with some transitions effects * @param fragment Fragment you want go into */ public void openDetailScreen(Fragment fragment) { activeScreens.put(DETAIL_SCREEN, fragment); adapter.notifyDataSetChanged(); delegateViewPager.setCurrentItem(1, true); } public void hideDetailScreen() { delegateViewPager.setCurrentItem(CONTENT_SCREEN); if (activeScreens.get(DETAIL_SCREEN) != null) { activeScreens.remove(DETAIL_SCREEN); adapter.notifyDataSetChanged(); } } @Override public void onPageScrolled(int i, float v, int i2) { // unused } @Override public void onPageSelected(int i) { if (i == CONTENT_SCREEN) hideDetailScreen(); } @Override public void onPageScrollStateChanged(int i) { // unused } private class DelegateViewPager extends ViewPager { public DelegateViewPager(Context context) { super(context); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { return getCurrentItem() != CONTENT_SCREEN && super.onInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { return getCurrentItem() != CONTENT_SCREEN && super.onTouchEvent(event); } } private final class DelegateAdapter extends FragmentPagerAdapter { public DelegateAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int i) { return activeScreens.get(i); } @Override public int getCount() { return activeScreens.size(); } } 

}

Voici l’activité qui l’implémente avec un autre ViewPager en tant que SlidingMenu. (en supplément)

 public class DoubleViewPagerActivity extends FragmentActivity { DoubleViewPager doubleViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_double_view_pager); doubleViewPager = (DoubleViewPager) findViewById(R.id.doublePager); doubleViewPager.initialize(new MainContentFragment(), getSupportFragmentManager()); } public static final class MainContentFragment extends Fragment { public MainContentFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_doublepager_content_window, parent, false); final ViewPager pager = (ViewPager) view.findViewById(R.id.contentPager); pager.setAdapter(new SimpleMenuAdapter(getChildFragmentManager())); pager.setOffscreenPageLimit(2); pager.setCurrentItem(1); return view; } } public static final class SimpleMenuAdapter extends FragmentPagerAdapter { public SimpleMenuAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int i) { return DoubleViewPagerActivity.PagerFragment.instance(i); } @Override public int getCount() { return 3; } @Override public float getPageWidth(int position) { switch (position) { case 0: case 2: return 0.7f; } return super.getPageWidth(position); } } public static final class PagerFragment extends Fragment { public static PagerFragment instance(int position) { final PagerFragment fr = new PagerFragment(); Bundle args = new Bundle(); args.putInt("position", position); fr.setArguments(args); return fr; } public PagerFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { final FrameLayout fl = new FrameLayout(getActivity()); fl.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); int position = getArguments().getInt("position"); switch (position) { case 0: fl.setBackgroundColor(Color.RED); break; case 1: fl.setBackgroundColor(Color.GREEN); initListView(fl); break; case 2: fl.setBackgroundColor(Color.BLUE); break; } return fl; } private void initListView(FrameLayout fl) { int max = 50; final ArrayList items = new ArrayList(max); for (int i = 1; i <= max; i++) { items.add("Items " + i); } ListView listView = new ListView(getActivity()); fl.addView(listView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER)); listView.setAdapter(new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, items)); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { ((DoubleViewPagerActivity) getActivity()).doubleViewPager.openDetailScreen(new DetailFragment()); } }); } } public final static class DetailFragment extends Fragment { public DetailFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { FrameLayout l = new FrameLayout(getActivity()); l.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); l.setBackgroundColor(getResources().getColor(android.R.color.holo_purple)); return l; } } 

}