web-dev-qa-db-fra.com

ViewPager dans ViewPager

Je souhaite créer un ViewPager (avec trois éléments) où chacune de ses vues est un autre ViewPager (avec deux éléments). L'utilisateur balaie ensuite les éléments comme ceci:

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

Comment serait-ce possible?

36
xpepermint

remplace canScroll dans le ViewPager parent:

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

Essaye ça:

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;
    }
}
29
Android Noob

J'ai longtemps cherché à créer un ViewPager dans un autre ViewPager et j'ai trouvé la solution proposée par "Android Noob" ici. Merci beaucoup pour ça!

Je voulais aussi partager ma solution. J'ai ajouté la possibilité de basculer la gestion des balayages vers le ViewPager environnant une fois que le dernier élément (le plus à droite) du ViewPager interne est atteint. Pour éviter les problèmes, j’enregistre également le premier sens de glissement pour le dernier élément: c’est-à-dire que si vous glissez à gauche, un glissement à droite minimal ne réinitialise pas l’état de défilement.

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

}

J'espère que cela aidera quelqu'un dans le futur!

12
gtRfnkN

Si la vue enfant est à la fin, faites défiler le parent

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

Commencez par créer une classe ViewPager personnalisée de la manière suivante:

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

Le retour (booléen) de la méthode canScroll vous dira si le geste horizontal pour changer de page pour ViewPager doit être dans la bordure droite ou gauche du fragment (true) ou s'il fonctionne pour un écran de fragment complet (false) ..__ Si vous voulez, par exemple, que seul votre premier fragment utilise la bordure droite pour passer au fragment suivant car le premier fragment comporte un autre événement de défilement horizontal, ce sera le code permettant de remplacer la méthode 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);
}

La dernière étape consistera à utiliser votre classe CustomViewPager dans votre classe principale:

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

et le xml:

<my.cool.package.name.CustomViewPager
        Android:id="@+id/myCustomViewPager"
        Android:layout_width="fill_parent"
        Android:layout_height="0dp"
        Android:layout_weight="1" />
3

Je résous cette tâche en créant deux héritiers ViewPager personnalisés. Dans mon cas - OuterViewPager et 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);
    }
}

Le pager externe commence à défiler à gauche uniquement lorsque le pager intérieur se trouve à la dernière page. Et vice versa.

0
Kabak Evgeniy

Je ne comprends pas pourquoi vous ne créez pas simplement un pageur de vues et créez la logique d’instanciation des éléments pour obtenir des données de différentes sources, ce qui vous permettrait d’atteindre votre objectif de la même manière Je ne vois pas de cas où vous auriez besoin de 2 viewpagers

Exemple

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

et dans le code:

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

de cette façon, vous avez virtuellement 2 logiques viewpagers, vous pouvez le personnaliser plus que cela. Je vous donne simplement des idées.

P.S. J'ai codé ceci ici, alors s'il y a des erreurs de casse de caractères ou des fautes d'orthographe, pardonnez-moi.

j'espère que cela vous aidera si vous êtes plus précis et avez besoin de plus d'aide pour ajouter un commentaire à ma réponse et je le modifierai

0
Shereef Marzouk

Je viens de tester ce cas, vous pouvez le faire sans travail supplémentaire, ci-dessous est ma démo

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

Le XML est simple, ViewPager extérieur dans ma mise en page principale et le ViewPager intérieur dans un autre LinearLayout

0
wqycsu