web-dev-qa-db-fra.com

Glissez en arrière comme Pinterest ou Tumblr

Quelqu'un a-t-il une idée de la façon dont Pinterest ou Tumblr ont implémenté la méthode de "balayage".

c'est-à-dire que sur Pinterest, vous pouvez cliquer sur un post dans le fil d'actualité. Ensuite, DetailActivity est démarré et affiche les détails de la publication sélectionnée. Ensuite, vous pouvez appuyer sur le bouton Retour pour revenir à l'activité du fil d'actualités, ou vous pouvez faire 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 (identifiants de ressource comme R.anim.foo). Pinterest et Tumblr démarrent l'animation uniquement si l'utilisateur fait un geste de balayage. Ils prennent également en charge une sorte d '"animation image par image" en fonction des mouvements des doigts. Ils suivent donc la distance du mouvement du doigt et animent la transition vers la valeur de pourcentage correspondante.

Je sais comment utiliser un objet "vrai Java" Animation/AnimatorSet 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 des activités. Existe-t-il une onCreateAnimator() (ou quelque chose de similaire) pour les activités? Je ne sais pas non plus comment faire glisser le comportement, car il ne démarre pas l'animation en ce moment, mais plutôt un changement de propriété étape par étape de la fenêtre/activité/fragment ou autre ...

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 réaliser le "swipe back". Étant donné que mon application contient déjà des fragments et des fragments d'enfant dans une activité, il serait tellement plus facile pour moi si je pouvais l'implémenter pour les activités.

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


MISE À JOUR: J'ai créé une petite bibliothèque, qui n'a pas exactement le même comportement que la mise en œuvre de Pinterest ou Tumblrs, mais pour mes applications, cela me semble une bonne solution: https://github.com/sockeqwe/SwipeBack? source = c

39
sockeqwe

Je suppose donc que j'ai trouvé la solution par moi-même:

Tout d'abord: Pinterest utilise en effet un ViewPager avec un Page Transformer personnalisé comme @frozenkoi l'a mentionné dans sa réponse. Vous pouvez voir l'effet Edge de survol du pager de vue dans l'application pinterest.

@Amit Gupta a pointé vers la bibliothèque qui laisse glisser l'activité. C'est à peu près le même concept que les différents tiroirs de navigation et définit également le thème comme translucide. Ils font glisser la mise en page. Mais ce n'est pas exactement ce que je cherchais, car il fait glisser l'activité supérieure vers la droite et appelle simplement finish (). Mais l'activité en dessous ne sera pas animée.

La solution est (et je suppose que c'est le cas de Tumblr) d'écrire votre propre animation avec des objets d'animation et de l'animer étape par étape. Cela peut être fait avec ActivityOptions . À mon avis, ce sera la solution.

1
sockeqwe

Il semble que l'effet que vous recherchez 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 le Transformateur de page de profondeur section. Il a une vidéo et un code source.

En utilisant un ViewPager.PageTransformer vous pouvez décider comment les pages se comportent lors du passage de l'une à l'autre.

La seule différence entre l'échantillon et la vidéo à laquelle vous avez lié est que la gauche-droite semble être inversée, mais devrait être un bon point de départ pour ce que j'ai vu sur le vidéo YouTube lié dans la question. Les actions sur les deux vues devraient être échangées. Comme le montre ce morceau de code (le 1er paramètre à mPager.setPageTransformer doit être reverseDrawingOrder = false). Notez que les 2 sections du milieu if sont échangées et que la variable position est gérée légèrement différemment pour changer de côté. L'effet rebondissant est laissé comme exercice. Veuillez partager lorsque 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'exemple 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);
            }
        }
    }
20
frozenkoi

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

enter image description here

J'ai écrit une démo exactement comme Pinterest et tumblr comme, vous étendez simplement la BaseActivity et vous obtenez un effet de balayage arrière, fonctionne en douceur!

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

et la capture d'écran:enter image description here

8
Jishi Chen

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

C'est vraiment un excellent projet open source qui devrait résoudre votre problème. Il fait ce dont vous aviez besoin, comme aller à l'écran précédent en appuyant en arrière ou en glissant simplement. Comme ce projet ayant l'option

1. Glissez de gauche à droite

2. Balayez de droite à gauche

3. Glissez de bas en haut

https://github.com/Issacw0ng/SwipeBackLayout

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

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

Captures d'écran jointes: -

enter image description here

J'espère que cela vous aidera.

7
Amit Gupta

J'ai pu faire ça en 15 minutes, c'est pas mal pour commencer. 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;

    }
}
3
Sherif elKhatib

Je viens de vérifier avec la visionneuse de hiérarchie. Il semble qu'ils utilisent ViewPager avec une capture d'écran de l'activité précédente.

2
Dmitry Ryadnenko

Je suggère de faire ce qui suit:

Détectez d'abord le geste que l'utilisateur fait sur l'appareil. Vous pouvez vous référer à ce lien

Je ne vais pas copier le code correspondant à partir 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

1
Armand

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

https://github.com/fengdai/FragmentMaster

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

0
Feng Dai

J'avais affaire à celui-ci dans le projet sur lequel je travaille actuellement et j'ai trouvé 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 comme vous le mentionnez 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 (pourrait être facilement modifié pour les objets) et si vous souhaitez ajouter ActionBar dans le balayage, 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<Fragment> activeScreens = new SparseArray<Fragment>(SCREEN_COUNT) ;
private DelegateAdapter adapter;

public DoubleViewPager(Context context) {
    this(context, null);
}

public DoubleViewPager(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public DoubleViewPager(Context context, AttributeSet 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 une 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<String> items = new ArrayList<String>(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<String>(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;
    }

}

}

0
Michal