我想创建一个 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]
这怎么可能?
我想创建一个 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]
这怎么可能?
在父 ViewPager 中覆盖 canScroll:
@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);
}
尝试这个:
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;
}
}
我搜索了很长时间以使另一个 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;
}
}
希望这对将来的人有所帮助!
如果子viewpager在最后,滚动父viewpager
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);
}
首先这样创建一个自定义的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" />
对于 a ViewPager2
,当前的解决方案是使用 a NestedScrollableHost
:https ://github.com/android/views-widgets-samples/blob/master/ViewPager2/app/src/main/java/androidx/viewpager2/integration/testapp/NestedScrollableHost .kt
您可以在此处查看错误报告和进度:https ://issuetracker.google.com/issues/123006042
我通过创建两个自定义 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);
}
}
仅当内部寻呼机在最后一页时,外部寻呼机才开始向左滚动。反之亦然。
我不明白您为什么不只创建 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
逻辑,你可以自定义它,而不仅仅是我给你的想法
PS我在这里编码,所以如果有字符大小写错误或拼写错误,请原谅我。
希望这会有所帮助,如果您变得更具体并且需要更多帮助以对我的答案添加评论,我会对其进行修改
我只是测试这个案例,你可以不用额外的工作,下面是我的演示
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