ViewPager внутри ViewPager - PullRequest
       61

ViewPager внутри ViewPager

37 голосов
/ 18 августа 2011

Я хотел бы создать ViewPager (с тремя элементами), где каждый его вид представляет собой другой ViewPager (с двумя элементами).Затем пользователь проведет пальцем по следующим пунктам:

ViewPager1[0] ViewPager2[0]
ViewPager1[0] ViewPager2[1]
ViewPager1[1] ViewPager2[0]
ViewPager1[1] ViewPager2[1]
ViewPager1[2] ViewPager2[0]
ViewPager1[2] ViewPager2[1]

Как это возможно?

Ответы [ 8 ]

56 голосов
/ 02 декабря 2012

переопределить canScroll в родительском ViewPager:

@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
   if(v != this && v instanceof ViewPager) {
      return true;
   }
   return super.canScroll(v, checkV, dx, x, y);
}
28 голосов
/ 24 сентября 2011

Попробуйте это:

public class CustomViewPager extends ViewPager {
    private int childId;

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
     @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (childId > 0) {          
            ViewPager pager = (ViewPager)findViewById(childId);

            if (pager != null) {           
                pager.requestDisallowInterceptTouchEvent(true);
            }

        }

        return super.onInterceptTouchEvent(event);
    }

    public void setChildId(int id) {
        this.childId = id;
    }
}
12 голосов
/ 15 мая 2013

Я долго искал, чтобы заставить ViewPager работать внутри другого ViewPager, и нашел решение «Android Noob» здесь. Большое спасибо за это!

Я тоже хотел поделиться своим решением. Я добавил возможность переключать управление смахиванием на окружающий ViewPager после достижения последнего (самого правого) элемента во внутреннем ViewPager. Чтобы предотвратить сбои, я также сохраняю первое направление пролистывания для последнего элемента: то есть, если вы проведете пальцем влево первым, минимальное пролистывание вправо не сбрасывает состояние прокрутки.

public class GalleryViewPager extends ViewPager {

    /** the last x position */
    private float   lastX;

    /** if the first swipe was from left to right (->), dont listen to swipes from the right */
    private boolean slidingLeft;

    /** if the first swipe was from right to left (<-), dont listen to swipes from the left */
    private boolean slidingRight;

    public GalleryViewPager(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    public GalleryViewPager(final Context context) {
        super(context);
    }

    @Override
    public boolean onTouchEvent(final MotionEvent ev) {
        final int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:

                // Disallow parent ViewPager to intercept touch events.
                this.getParent().requestDisallowInterceptTouchEvent(true);

                // save the current x position
                this.lastX = ev.getX();

                break;

            case MotionEvent.ACTION_UP:
                // Allow parent ViewPager to intercept touch events.
                this.getParent().requestDisallowInterceptTouchEvent(false);

                // save the current x position
                this.lastX = ev.getX();

                // reset swipe actions
                this.slidingLeft = false;
                this.slidingRight = false;

                break;

            case MotionEvent.ACTION_MOVE:
                /*
                 * if this is the first item, scrolling from left to
                 * right should navigate in the surrounding ViewPager
                 */
                if (this.getCurrentItem() == 0) {
                    // swiping from left to right (->)?
                    if (this.lastX <= ev.getX() && !this.slidingRight) {
                        // make the parent touch interception active -> parent pager can swipe
                        this.getParent().requestDisallowInterceptTouchEvent(false);
                    } else {
                        /*
                         * if the first swipe was from right to left, dont listen to swipes
                         * from left to right. this fixes glitches where the user first swipes
                         * right, then left and the scrolling state gets reset
                         */
                        this.slidingRight = true;

                        // save the current x position
                        this.lastX = ev.getX();
                        this.getParent().requestDisallowInterceptTouchEvent(true);
                    }
                } else
                /*
                 * if this is the last item, scrolling from right to
                 * left should navigate in the surrounding ViewPager
                 */
                if (this.getCurrentItem() == this.getAdapter().getCount() - 1) {
                    // swiping from right to left (<-)?
                    if (this.lastX >= ev.getX() && !this.slidingLeft) {
                        // make the parent touch interception active -> parent pager can swipe
                        this.getParent().requestDisallowInterceptTouchEvent(false);
                    } else {
                        /*
                         * if the first swipe was from left to right, dont listen to swipes
                         * from right to left. this fixes glitches where the user first swipes
                         * left, then right and the scrolling state gets reset
                         */
                        this.slidingLeft = true;

                        // save the current x position
                        this.lastX = ev.getX();
                        this.getParent().requestDisallowInterceptTouchEvent(true);
                    }
                }

                break;
        }

        super.onTouchEvent(ev);
        return true;
    }

}

Надеюсь, это поможет кому-то в будущем!

11 голосов
/ 05 сентября 2013

Если дочерний просмотрщик находится в конце, прокрутите родительский

protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
    if(v != this && v instanceof ViewPager) {
        int currentItem = ((ViewPager) v).getCurrentItem();
        int countItem = ((ViewPager) v).getAdapter().getCount();
        if((currentItem==(countItem-1) && dx<0) || (currentItem==0 && dx>0)){
            return false;
        }
        return true;
    }
    return super.canScroll(v, checkV, dx, x, y);
}
5 голосов
/ 02 ноября 2013

Сначала создайте пользовательский класс ViewPager следующим образом:

public class CustomViewPager extends ViewPager {

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        if(v instanceof ViewPager) {
            return true;
        }
        return super.canScroll(v, checkV, dx, x, y);
    }
}

Возврат (логический) метода canScroll скажет вам, должен ли горизонтальный жест для изменения страницы для ViewPager быть правильным илилевая граница фрагмента (true) или если это работает для полного экрана фрагмента (false).Например, если вы хотите, чтобы только ваш первый фрагмент использовал правую границу для перехода к следующему фрагменту, поскольку первый фрагмент имеет другое событие горизонтальной прокрутки, это будет код для переопределения метода canScroll:

@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
    if(v instanceof ViewPager) {
        int currentItem = ((ViewPager) v).getCurrentItem();
        if((currentItem==0)){
            return true;
        }
        return false;
    }
    return super.canScroll(v, checkV, dx, x, y);
}

Последним шагом будет использование класса CustomViewPager в вашем основном классе:

ViewPager myPager= (CustomViewPager)myContext.findViewById(R.id.myCustomViewPager);

и xml:

<my.cool.package.name.CustomViewPager
        android:id="@+id/myCustomViewPager"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
0 голосов
/ 25 декабря 2015

Я только что протестировал этот случай, вы можете сделать это без дополнительной работы, ниже моя демка

public class MainActivity extends AppCompatActivity {

    public static final String TAG = "TAG";
    ViewPager parentPager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        initViews();
        initData();

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

    private void initViews() {
        parentPager = (ViewPager) findViewById(R.id.parent_pager);
    }

    private void initData() {
        List<ViewPager> pagers = new ArrayList<ViewPager>();
        for(int j = 0; j < 3; j++) {
            List<LinearLayout> list = new ArrayList<LinearLayout>();
            for (int i = 0; i < 5; i++) {
                LinearLayout layout = new LinearLayout(this);
                TextView textView = new TextView(this);
                textView.setText("This is the" + i + "th page in PagerItem" + j);
                layout.addView(textView);
                textView.setGravity(Gravity.CENTER);
                LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) textView.getLayoutParams();
                params.gravity = Gravity.CENTER;
                list.add(layout);
            }
            MyViewPagerAdapter adapter = new MyViewPagerAdapter(list);
            final ViewPager childPager = (ViewPager) LayoutInflater.from(this).inflate(R.layout.child_layout, null).findViewById(R.id.child_pager);
            childPager.setAdapter(adapter);
            childPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                    Log.d(TAG, "onPageScrolled: position: " + position + ",   positionOffset: " + positionOffset);
                }

                @Override
                public void onPageSelected(int position) {

                }

                @Override
                public void onPageScrollStateChanged(int state) {

                }
            });
            pagers.add(childPager);
        }
        MyParentViewPagerAdapter parentAdapter = new MyParentViewPagerAdapter(pagers);
        parentPager.setAdapter(parentAdapter);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    class MyViewPagerAdapter extends PagerAdapter {

        private List<LinearLayout> data;

        public MyViewPagerAdapter(List<LinearLayout> data) {
            this.data = data;
        }

        @Override
        public int getCount() {
            return data.size();
        }

        @Override
        public int getItemPosition(Object object) {
            return data.indexOf(object);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            LinearLayout linearLayout = data.get(position);
            container.addView(linearLayout);
            return data.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            LinearLayout layout = data.get(position);
            container.removeView(layout);
            layout = null;
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
    }

    class MyParentViewPagerAdapter extends PagerAdapter {

        private List<ViewPager> data;

        public MyParentViewPagerAdapter(List<ViewPager> data) {
            this.data = data;
        }

        @Override
        public int getCount() {
            return data.size();
        }

        @Override
        public int getItemPosition(Object object) {
            return data.indexOf(object);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ViewPager pager = data.get(position);
            if(pager.getParent() != null) {
                ((ViewGroup) pager.getParent()).removeView(pager);
            }
            container.addView(pager);
            return data.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            ViewPager pager = data.get(position);
            container.removeView(pager);
            pager = null;
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
    }
}

XML простой, внешний ViewPager в моем основном макете и внутренний ViewPager в другом LinearLayout

0 голосов
/ 26 сентября 2013

Я решаю эту задачу, создав два пользовательских наследника ViewPager. В моем случае - OuterViewPager и InnerViewPager.

public class InnerViewPager extends ViewPager
{
    private int mPrevMoveX;

    public InnerViewPager(Context context)
    {
        super(context);
    }

    public InnerViewPager(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event)
    {
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                mPrevMoveX = (int) event.getX();

                return super.onTouchEvent(event);

            case MotionEvent.ACTION_MOVE:
                int distanceX = mPrevMoveX - (int) event.getX();
                mPrevMoveX = (int) event.getX();

                boolean canScrollLeft = true;
                boolean canScrollRight = true;

                if(getCurrentItem() == getAdapter().getCount() - 1)
                {
                    canScrollLeft = false;
                }

                if(getCurrentItem() == 0)
                {
                    canScrollRight = false;
                }

                if(distanceX > 0)
                {
                    return canScrollRight;
                }
                else
                {
                    return canScrollLeft;
                }
        }

        return super.onInterceptTouchEvent(event);
    }

    public boolean onTouchEvent(MotionEvent event)
    {
        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                mPrevMoveX = (int) event.getX();

                return super.onTouchEvent(event);

            case MotionEvent.ACTION_MOVE:
                int distanceX = mPrevMoveX - (int) event.getX();
                mPrevMoveX = (int) event.getX();

                boolean canScrollLeft = true;
                boolean canScrollRight = true;

                if(getCurrentItem() == getAdapter().getCount() - 1)
                {
                    canScrollLeft = false;
                }

                if(getCurrentItem() == 0)
                {
                    canScrollRight = false;
                }

                if(distanceX > 0)
                {
                    super.onTouchEvent(event);
                    return canScrollLeft;
                }
                else
                {
                    super.onTouchEvent(event);
                    return canScrollRight;
                }
        }

        return super.onTouchEvent(event);
    }
}


public class OuterViewPager extends ViewPager
{
    private int mPrevMoveX;


    public OuterViewPager(Context context)
    {
        super(context);
        init();
    }

    public OuterViewPager(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        init();
    }

    private void init()
    {
        setOnPageChangeListener(new CustomPageChangeListener());
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev)
    {
        switch (ev.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                mPrevMoveX = (int) ev.getX();
                return super.onInterceptTouchEvent(ev);

            case MotionEvent.ACTION_MOVE:

                /*there you should get currentInnerPager - instance of InnerPager on current page of instance of OuterPager*/

                int distanceX = mPrevMoveX - (int) ev.getX();
                mPrevMoveX = (int) ev.getX();

                boolean canScrollLeft = true;
                boolean canScrollRight = true;

                if(currentInnerPager.getCurrentItem() == currentInnerPager.getAdapter().getCount() - 1)
                {
                    canScrollLeft = false;
                }

                if(currentInnerPager.getCurrentItem() == 0)
                {
                    canScrollRight = false;
                }

                if(distanceX > 0)
                {
                    return !canScrollLeft;
                }
                else
                {
                    return !canScrollRight;
                }
        }

        return super.onInterceptTouchEvent(ev);
    }
}

Внешний пейджер начинает прокручивать влево только тогда, когда внутренний пейджер на последней странице. И наоборот.

0 голосов
/ 23 ноября 2011

Я не понимаю, почему бы вам просто не создать 1 пейджер представления и не создать логику экземпляра элемента для получения данных из разных источников, что позволит вам в равной степени достичь своей цели Я не вижу случая, когда нужно 2 viewpagers

Пример

ViewPager1[0] ViewPager2[0] = page 0  (position/2) = 0
ViewPager1[0] ViewPager2[1] = page 1  ((position-1)/2) = 0
ViewPager1[1] ViewPager2[0] = page 2  (position/2) = 1
ViewPager1[1] ViewPager2[1] = page 3 ((position-1)/2) = 1
ViewPager1[2] ViewPager2[0] = page 4  (position/2) = 2
ViewPager1[2] ViewPager2[1] = page 5 ((position-1)/2) = 2

и в коде:

@Override
public Object instantiateItem(View collection, int position) {
    LayoutInflater inflater = THISCLASSNAME.this.getLayoutInflater();
    View v = null;
    if(position%2 == 0) {
         // viewpager 1 code
         int vp1pos = position/2;
         v = inlater.inflate(R.layout.somelayout, collection, false);
         Button b = (Button)v.findViewById(R.id.somebutton);
         b.setText(array1[vp1pos]);
    } else {
         int vp2pos = (position-1)/2;
         v = inlater.inflate(R.layout.somelayout, collection, false);
         Button b = (Button)v.findViewById(R.id.somebutton);
         b.setText(array2[vp2pos]);
    }

    ((DirectionalViewPager) collection).addView(v, 0);

    return v;
}

таким образом, у вас есть логика 2 viewpagers, вы можете настроить ее больше, чем я просто даю вам идеи

P.S. Я закодировал это здесь, так что, если есть ошибки в регистре символов или орфографические ошибки, прости меня.

надеюсь, что это поможет, если вы получите более конкретную информацию и вам нужна дополнительная помощь, чтобы добавить комментарий к моему ответу, и я исправлю его

...