多个viewpage的滚动效果

时间:2014-01-29 23:59:15

标签: android animation android-viewpager scrollview

我有一个观点的概念。请指导我如何实现它。请检查线框。 enter image description here

我已经看过FadingActionBar,但它似乎没有帮助。问题是我在屏幕上有多个观看者,并且在试图达到预期结果时没有。如果我能够实现很酷的过渡/视差效果,那将是非常棒的。

非常感谢任何投入。

Edit1:

选项卡放在PagerTabStrip上,并与其下方的Viewpager连接。这里的尝试是滚动视图并将PagerTabStrip停靠到ActionBar,然后向下滚动将其调低以显示ImageViewPager。

3 个答案:

答案 0 :(得分:14)

所以,这可以很容易地实现,但它需要一个小技巧,更像是幻觉。此外,我将使用ListView代替ScrollView作为我的“可滚动内容”,主要是因为在这种情况下我更容易使用,而且我将使用{ {3}}

首先,您需要一个View来存储给定索引的y坐标。此自定义View将置于您的底部ViewPager之上,并显示为每个ListView的“真实”标题。您需要记住ViewPager中每个页面的标题y坐标,以便稍后在用户在它们之间滑动时可以恢复它们。我稍后会对此进行扩展,但现在这就是View应该是这样的:

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

}

现在,您可以为ActivityFragment创建主要布局。布局包含底部ViewPagerCoordinatedHeader;它由底部ViewPager和标签组成。

主要布局

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

您需要的唯一其他布局是“假”标题。此布局将添加到每个ListView,从而产生错觉,主布局中的CoordinatedHeader是真实的。

注意此布局的高度与主布局中的CoordinatedHeader相同非常重要,对于此示例,我使用250dp

假标题

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

现在,您需要准备将显示在Fragment底部的每个ViewPager,以通过将CoordinatedHeader附加到AbsListView.OnScrollListener来控制ListView。此Fragment也应在使用Fragment.setArguments创建时传递唯一索引。该索引应表示其在ViewPager中的位置。

注意我在此示例中使用了ListFragment

可滚动内容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
        }

    });
}

最后,当用户使用Coordinated在页面之间滑动时,您需要设置ViewPager.OnPageChangeListener标头以恢复其y坐标。

注意PagerAdapter附加到底部ViewPager时,请务必致电ViewPager.setOffscreenPageLimit并将该金额设置为PagerAdapter中的总页数{1}}。这样CoordinatedHeader可以立即存储每个Fragment的y坐标,否则会因为它不同步而遇到麻烦。

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

答案 1 :(得分:2)

您也可以使用 Android-ParallaxHeaderViewPager 来实现此效果,这是通过kmshack Github page滚动标签标题的一个很好的示例

示例代码在此Here Git Hub link

中给出

这是屏幕截图 enter image description here

@adneal的解决方案对于实现滚动Tab标题非常有用。

跳这会帮助你

新更新

请检查此答案 Google+ profile like scrolling Effect

答案 2 :(得分:0)

我使用片段容器布局而不是使用FadingActionBar的viewpager。我也使用MaterialTabs库。

内容布局

//And populate both set on tab
private void populateTabStrip() {
    final PagerAdapter adapter = mViewPager.getAdapter();
    final OnClickListener tabClickListener = new TabClickListener();

    for (int i = 0; i < adapter.getCount(); i++) {

        View tabView = null;
        TextView tabTitleView = null;

        if (tabView == null) {
        tabView = createDefaultTabView(getContext());           
        }

        if (tabTitleView == null && TextView.class.isInstance(tabView)) {
            tabTitleView = (TextView) tabView;
        }

        //Also want to set some selector here

        tabTitleView.setText(adapter.getPageTitle(i));
        if (mViewPager.getCurrentItem() == i) {
            tabTitleView.setSelected(true);
        }
        tabTitleView.setTextColor(R.color.white);
        mTabStrip.addView(tabView);
    }
}

<强>活动

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

最后有一个结果:link(gif)