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?
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);
}
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;
}
}
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!
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);
}
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" />
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.
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
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