首页 文章

像Pinterest或Tumblr一样向后滑动

提问于
浏览
39

有没有人知道Pinterest或Tumblr如何实现“刷卡”方法 .

即在Pinterest上,您可以点击新闻Feed上的帖子 . 比 DetailActivity 已启动并显示所选帖子的详细信息 . 您可以按后退按钮返回新闻Feed活动,或者您可以向左滑动(详细信息活动)以返回新闻Feed活动 .

视频:http://youtu.be/eVcSCWetnTA

通常我会使用 overridePendingTransition() ,但 overridePendingTransition() 需要动画(资源ID如 R.anim.foo ) . 仅当用户进行滑动手势时,Pinterest和Tumblr才会启动动画 . 他们还根据手指移动支持某种"frame by frame animation" . 因此,他们跟踪手指移动的距离,并将过渡动画设置为相应的百分比值 .

我知道如何使用带有 FragmentTransaction 的"real java" Animation / AnimatorSet对象来动画片段替换 . 使用片段我必须覆盖 onCreateAnimator() ,但我不知道如何使用Activities实现类似的东西 . 活动有 onCreateAnimator() (或类似的东西)吗?也不知道如何滑动行为,因为它现在没有启动动画,但更多的是逐步改变Window / Activity / Fragment的属性或者其他......

有什么建议?

编辑:我在youtube上找到了一个pinterest应用程序的视频:http://youtu.be/eVcSCWetnTA这就是我想要实现的内容 .

我猜Pinterest正在使用Fragments和 onCreateAnimator() 来实现"swipe back" . 由于我的应用程序已经在活动中已经有Fragment和ChildFragments,因此如果我可以为活动实现它,那将对我更加容易 .

再一次:我知道如何检测滑动手势,这不是我要求的 . 观看YouTube视频:http://youtu.be/eVcSCWetnTA


更新:我创建了一个小库,它与Pinterest或Tumblrs实现的行为并不完全相同,但对于我的应用程序,这似乎是一个很好的解决方案:https://github.com/sockeqwe/SwipeBack?source=c

9 回答

  • 1

    看来你正在寻找的效果是android开发者网站中ViewPager的样本之一 .

    在Depth页变换器部分中查看http://developer.android.com/training/animation/screen-slide.html#depth-page . 它有一个视频和源代码 .

    使用ViewPager.PageTransformer,您可以决定从一个页面切换到下一个页面时页面的行为方式 .

    样本和你链接的视频之间的唯一区别是左右似乎是倒置的,但应该是我在问题中链接的YouTube video上看到的一个很好的起点 . 必须对两种观点的行动进行讨论 . 如这段代码所示( mPager.setPageTransformer 的第一个参数应为 reverseDrawingOrder = false) . 请注意,中间的2 if 部分被交换,并且 position 变量的处理方式略有不同 . 弹性效应留作练习 . 当你得到它时请分享!

    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);
                        }
                }
        }
    

    如果带有示例的页面变得很糟糕,这里是该部分的原始代码:

    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);
                }
            }
        }
    
  • 8

    Update: 修复了此项目的内存使用问题,并将滑回式样式更改为iOS .

    enter image description here

    我写了一个完全像Pinterest和tumblr的演示,你只需扩展BaseActivity,你就可以获得一个滑动效果,工作顺利!

    检查一下:https://github.com/chenjishi/SlideActivity

    和截图:
    enter image description here

  • 0

    我找到了一个基于SwipeBack的GitHub项目,如Pinterest .

    它确实是一个很好的开源项目,可以解决您的问题 . 它可以根据您的需要进行操作,例如按后退或简单滑动即可转到上一屏幕 . 由于这个项目有选择权

    1. Swipe left to Right

    2. Swipe Right to Left

    3. Swipe Bottom to top

    https://github.com/Issacw0ng/SwipeBackLayout

    and also you install this demo application from Google Play.

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

    附上截图: -

    enter image description here

    希望这会帮助你 .

  • 7

    我能在15分钟内做到这一点,一开始就不错 . 如果你花一些时间,你可能能够优化它 .

    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;
    
        }
    }
    
  • 0

    我只是检查了层次结构查看器 . 看起来他们正在使用ViewPager以及之前活动的屏幕截图 .

  • 2

    我建议做以下事情:

    首先检测用户在设备中执行的手势 . 你可以参考this link

    我不会复制上述链接中的相关代码,因为我认为这是可接受的答案

    其次你可以用这种方法

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

    按照this question的建议执行以下操作

    他们谈到用动画完成一项活动

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

    希望这对你有所帮助

  • 20

    所以我想我自己找到了解决方案:

    首先:Pinterest确实使用了ViewPager像@frozenkoi这样的自定义Page Transformer在他的回答中提到过 . 您可以在pinterest应用中看到视图寻呼机的过冲边缘效应 .

    @Amit Gupta指出了让活动滑动的图书馆 . 它与各种导航抽屉的概念相同,并设置了半透明的主题 . 他们滑动布局 . 但这并不是我想要的,因为它将顶部活动滑动到右边而不是简单地调用finish() . 但是下面的活动不会被动画化 .

    解决方案是(并且我猜这是Tumblr做的)用动画对象编写你自己的动画并逐步动画它 . 这可以通过ActivityOptions完成 . In my opinion this will be the solution.

  • 3

    我写了一个项目 . 它允许您轻松开发由Fragments导航的应用程序,就像Pinterest一样 .

    https://github.com/fengdai/FragmentMaster

    也许这不是你想要的答案 . 但我希望它对其他人有用 .

  • 1

    我正在处理我目前正在处理的项目中的这个,并提出了以下代码 . 也许它现在与你无关,但它可以帮助这篇文章的新人 . :)

    基本上它是你在答案中提到的ViewPager实现,但我认为这是你问题最简单,最快捷的解决方案 . 缺点是它只适用于Fragments(可以很容易地更改为Objects),如果你想将ActionBar添加到swipe中,你可能最终会创建一个自定义的 .

    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();
        }
    }
    

    }

    这是使用另一个ViewPager作为SlidingMenu实现它的活动 . (作为额外的)

    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;
        }
    
    }
    

    }

相关问题