Эффект прокрутки с несколькими просмотрщиками

У меня есть концепция для представления. Пожалуйста, расскажите мне, как я могу это достичь. Проверьте каркас. enter image description here

Я уже посмотрел на FadingActionBar, но, похоже, это не помогает. Проблема в том, что у меня на экране несколько зрителей, и я не собираюсь пытаться достичь желаемого результата. Было бы супер-потрясающе, если бы я смог достичь крутого эффекта перехода/параллакса.

Любые входы будут высоко оценены.

Edit1:

Вкладки помещаются на PagerTabStrip и подключаются к нему под ним. Попытка здесь прокрутить представление и прикрепить PagerTabStrip к ActionBar и прокрутить вниз вниз, чтобы показать ImageViewPager.

Ответы

Ответ 1

Итак, это может быть достигнуто довольно легко, но это требует небольшого трюка, больше похоже на иллюзию. Кроме того, я буду использовать ListView вместо ScrollView для моего "прокручиваемого содержимого", главным образом потому, что с этой задачей легче работать, и для моих вкладок я буду использовать эта библиотека с открытым исходным кодом.

Сначала вам понадобится View, который может хранить y-координаты для данного индекса. Этот пользовательский View будет помещен поверх вашего нижнего ViewPager и будет отображаться как "реальный" заголовок для каждого ListView. Вам нужно запомнить координату y заголовка для каждой страницы в ViewPager, чтобы вы могли восстановить их позже, когда пользователь просматривает их. Я расскажу об этом позже, но теперь вот что должно выглядеть 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;
        }
    };

}

Теперь вы можете создать основной макет для Activity или Fragment. Макет содержит нижние ViewPager и CoordinatedHeader; который состоит из нижнего 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, чтобы восстановить y-координаты, когда пользователь просматривает страницы с помощью ViewPager.OnPageChangeListener.

Примечание При прикреплении вашего PagerAdapter к нижней части ViewPager важно вызвать ViewPager.setOffscreenPageLimit и установить эту сумму на общее количество страниц в вашем PagerAdapter. Это значит, что CoordinatedHeader может сразу сохранить y-координату для каждого Fragment, иначе вы столкнетесь с проблемой, когда она не синхронизирована.

Главная 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);
        }
    });
}

Ответ 2

Вы также можете добиться этого эффекта, используя Android-ParallaxHeaderViewPager хороший пример прокрутки заголовка вкладки kmshack Github page

Образец кода дает в этом Здесь Git Ссылка на концентратор

Вот скриншот enter image description here

Решение @adneal также очень полезно для прокрутки заголовка вкладки.

Hop это поможет вам

Новое обновление

Пожалуйста, проверьте этот ответ Профиль Google+, например, эффект прокрутки

Ответ 3

Я использую компоновку контейнера фрагмента вместо viewpager с FadingActionBar. Также я использую MaterialTabs библиотеку.

Макет содержимого

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

активность

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

Наконец, получим этот результат: ссылка (gif)