Scrolling effect with multiple viewpagers

前端 未结 3 1375
太阳男子
太阳男子 2021-01-31 11:59

I have a concept for a view. Please guide me as to how I can achieve it. Please check the wireframe. \"enter

相关标签:
3条回答
  • 2021-01-31 12:16

    I use fragment container layout instead viewpager with FadingActionBar. Also i use MaterialTabs library.

    Content layout

    <xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?android:attr/windowBackground"
        android:orientation="vertical">
    
        <it.neokree.materialtabs.MaterialTabHost
            android:id="@+id/materialTabHost"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            app:accentColor="@color/second"
            app:primaryColor="@color/actionbar_background"
            app:textColor="#FFFFFF" />
    
        <FrameLayout
            android:id="@+id/container"
            android:layout_width="wrap_content"
            android:layout_height="match_parent">
    
        </FrameLayout>
    </LinearLayout>
    

    Activity

            @Override
                protected void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
    
                    FadingActionBarHelper helper = new FadingActionBarHelper()
                            .actionBarBackground(R.color.actionbar_background)
                            .headerLayout(R.layout.actionbar_header)
                            .contentLayout(R.layout.content_layout);
                    final View root = helper.createView(this);
                    setContentView(root);
                    helper.initActionBar(this);
     mTabHost = (MaterialTabHost) view.findViewById(R.id.materialTabHost);
            //.......
            }
         @Override
            public void onTabSelected(MaterialTab materialTab) {
                int position = materialTab.getPosition();
                switch (position) {
                    case 0:
                        switchFragment(new ProfileFragment(), position);
                        break;
                    case 1:
                        switchFragment(new NotificationFragment(), position);
                        break;
                }
                mTabHost.setSelectedNavigationItem(position);
            }
    

    Finally a have this result: link(gif)

    0 讨论(0)
  • 2021-01-31 12:27

    So, this can be achieved pretty easily, but it requires a little trick, more like an illusion actually. Also, I'm going to be using a ListView instead of a ScrollView for my "scrollable content", mostly because it's easier to work with in this situation and for my tabs I'll be using this open sourced library.

    First, you need a View that can store y-coordinates for a given index. This custom View will be placed on top of your bottom ViewPager and appear as the "real" header for each ListView. You need to remember the header's y-coordinate for each page in the ViewPager so you can restore them later as the user swipes between them. I'll expand on this later, but for now here's what that View should look like:

    CoordinatedHeader

    public class CoordinatedHeader extends FrameLayout {
    
        /** The float array used to store each y-coordinate */
        private final float[] mCoordinates = new float[5];
    
        /** True if the header is currently animating, false otherwise */
        public boolean mAnimating;
    
        /**
         * Constructor for <code>CoordinatedHeader</code>
         * 
         * @param context The {@link Context} to use
         * @param attrs The attributes of the XML tag that is inflating the view
         */
        public CoordinatedHeader(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        /**
         * Animates the header to the stored y-coordinate at the given index
         * 
         * @param index The index used to retrieve the stored y-coordinate
         * @param duration Sets the duration for the underlying {@link Animator}
         */
        public void restoreCoordinate(int index, int duration) {
            // Find the stored value for the index
            final float y = mCoordinates[index];
            // Animate the header to the y-coordinate
            animate().y(y).setDuration(duration).setListener(mAnimatorListener).start();
        }
    
        /**
         * Saves the given y-coordinate at the specified index, the animates the
         * header to the requested value
         * 
         * @param index The index used to store the given y-coordinate
         * @param y The y-coordinate to save
         */
        public void storeCoordinate(int index, float y) {
            if (mAnimating) {
                // Don't store any coordinates while the header is animating
                return;
            }
            // Save the current y-coordinate
            mCoordinates[index] = y;
            // Animate the header to the y-coordinate
            restoreCoordinate(index, 0);
        }
    
        private final AnimatorListener mAnimatorListener = new AnimatorListener() {
    
            /**
             * {@inheritDoc}
             */
            @Override
            public void onAnimationCancel(Animator animation) {
                mAnimating = false;
            }
    
            /**
             * {@inheritDoc}
             */
            @Override
            public void onAnimationEnd(Animator animation) {
                mAnimating = false;
            }
    
            /**
             * {@inheritDoc}
             */
            @Override
            public void onAnimationRepeat(Animator animation) {
                mAnimating = true;
            }
    
            /**
             * {@inheritDoc}
             */
            @Override
            public void onAnimationStart(Animator animation) {
                mAnimating = true;
            }
        };
    
    }
    

    Now you can create the main layout for your Activity or Fragment. The layout contains the bottom ViewPager and the CoordinatedHeader; which consists of, the bottom ViewPager and tabs.

    Main layout

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <android.support.v4.view.ViewPager
            android:id="@+id/activity_home_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <org.seeingpixels.example.widget.CoordinatedHeader
            android:id="@+id/activity_home_header"
            android:layout_width="match_parent"
            android:layout_height="250dp" >
    
            <android.support.v4.view.ViewPager
                android:id="@+id/activity_home_header_pager"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
    
            <com.astuetz.viewpager.extensions.PagerSlidingTabStrip
                android:id="@+id/activity_home_tabstrip"
                android:layout_width="match_parent"
                android:layout_height="48dp"
                android:layout_gravity="bottom"
                android:background="@android:color/white" />
        </org.seeingpixels.example.widget.CoordinatedHeader>
    
    </FrameLayout>
    

    The only other layout you need is a "fake" header. This layout will be added to each ListView, giving the illusion the CoordinatedHeader in the main layout is the real one.

    Note It's important that the height of this layout is the same as the CoordinatedHeader in the main layout, for this example I'm using 250dp.

    Fake header

    <View xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="250dp" />
    

    Now you need to prepare each Fragment that will be displayed in the bottom ViewPager to control the CoordinatedHeader by attaching a AbsListView.OnScrollListener to your ListView. This Fragment should also pass a unique index upon creation using Fragment.setArguments. This index should represent its location in the ViewPager.

    Note I'm using a ListFragment in this example.

    Scrollable content Fragment

    /**
     * {@inheritDoc}
     */
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        final Activity a = getActivity();
    
        final ListView list = getListView();
        // Add the fake header
        list.addHeaderView(LayoutInflater.from(a).inflate(R.layout.view_fake_header, list, false));
    
        // Retrieve the index used to save the y-coordinate for this Fragment
        final int index = getArguments().getInt("index");
    
        // Find the CoordinatedHeader and tab strip (or anchor point) from the main Activity layout
        final CoordinatedHeader header = (CoordinatedHeader) a.findViewById(R.id.activity_home_header);
        final View anchor = a.findViewById(R.id.activity_home_tabstrip);
    
        // Attach a custom OnScrollListener used to control the CoordinatedHeader 
        list.setOnScrollListener(new OnScrollListener() {
    
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
                    int totalItemCount) {
    
                // Determine the maximum allowed scroll height
                final int maxScrollHeight = header.getHeight() - anchor.getHeight();
    
                // If the first item has scrolled off screen, anchor the header
                if (firstVisibleItem != 0) {
                    header.storeCoordinate(index, -maxScrollHeight);
                    return;
                }
    
                final View firstChild = view.getChildAt(firstVisibleItem);
                if (firstChild == null) {
                    return;
                }
    
                // Determine the offset to scroll the header
                final float offset = Math.min(-firstChild.getY(), maxScrollHeight);
                header.storeCoordinate(index, -offset);
            }
    
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                // Nothing to do
            }
    
        });
    }
    

    Finally, you'll need to setup the Coordinated header to restore its y-coordinates when the user swipes between pages using a ViewPager.OnPageChangeListener.

    Note When attaching your PagerAdapter to your bottom ViewPager, it's important to call ViewPager.setOffscreenPageLimit and set that amount to the total amount of pages in your PagerAdapter. This is so the CoordinatedHeader can store the y-coordinate for each Fragment right away, otherwise you'll run into trouble with it being out of sync.

    Main Activity

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
    
        // Setup the top PagerAdapter
        final PagerAdapter topAdapter = new PagerAdapter(getFragmentManager());
        topAdapter.buildData(DummyColorFragment.newInstance(Color.RED));
        topAdapter.buildData(DummyColorFragment.newInstance(Color.WHITE));
        topAdapter.buildData(DummyColorFragment.newInstance(Color.BLUE));
    
        // Setup the top pager
        final ViewPager topPager = (ViewPager) findViewById(R.id.activity_home_header_pager);
        topPager.setAdapter(topAdapter);
    
        // Setup the bottom PagerAdapter
        final PagerAdapter bottomAdapter = new PagerAdapter(getFragmentManager());
        bottomAdapter.buildData(DummyListFragment.newInstance(0));
        bottomAdapter.buildData(DummyListFragment.newInstance(1));
        bottomAdapter.buildData(DummyListFragment.newInstance(2));
        bottomAdapter.buildData(DummyListFragment.newInstance(3));
        bottomAdapter.buildData(DummyListFragment.newInstance(4));
    
        // Setup the bottom pager
        final ViewPager bottomPager = (ViewPager) findViewById(R.id.activity_home_pager);
        bottomPager.setOffscreenPageLimit(bottomAdapter.getCount());
        bottomPager.setAdapter(bottomAdapter);
    
        // Setup the CoordinatedHeader and tab strip
        final CoordinatedHeader header = (CoordinatedHeader) findViewById(R.id.activity_home_header);
        final PagerSlidingTabStrip psts = (PagerSlidingTabStrip) findViewById(R.id.activity_home_tabstrip);
        psts.setViewPager(bottomPager);
        psts.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageScrollStateChanged(int state) {
                if (state != ViewPager.SCROLL_STATE_IDLE) {
                    // Wait until the pager is idle to animate the header
                    return;
                }
                header.restoreCoordinate(bottomPager.getCurrentItem(), 250);
            }
        });
    }
    
    0 讨论(0)
  • 2021-01-31 12:33

    You can also achieve this effect by using the Android-ParallaxHeaderViewPager a good example of scrolling tab header by kmshack Github page

    The Sample code is give in this Here Git Hub link

    Here is the Screen shot enter image description here

    The solution by @adneal is also very useful one to achieve scrolling Tab header.

    Hop this will help you

    New update

    Please check this answer Google+ profile like scrolling Effect

    0 讨论(0)
提交回复
热议问题