I can only use the ViewPager to page backward and can not page forward.
I'm writint an banner,but I can only use the ViewPager to page backward and can not page forward.
This is the adapter I wrote.
class MyPagerAdapter extends PagerAdapter {
@Override
public Object instantiateItem(ViewGroup container, int position) {
int p = position % mImgRes.length;
container.addView(mListView.get(p));
return mListView.get(p);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public int getCount() {
return 100;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return (view == object);
}
}
This is an error message.
07-29 20:45:02.098 20791-20791/com.example.myfirstpro E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.myfirstpro, PID: 20791 java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. at android.view.ViewGroup.addViewInner(ViewGroup.java:4465) at android.view.ViewGroup.addView(ViewGroup.java:4301) at android.support.v4.view.ViewPager.addView(ViewPager.java:1505) at android.view.ViewGroup.addView(ViewGroup.java:4242) at android.view.ViewGroup.addView(ViewGroup.java:4215) at com.example.myfirstpro.MainFragment$MyPagerAdapter.instantiateItem(MainFragment.java:62) at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:1034) at android.support.v4.view.ViewPager.populate(ViewPager.java:1216) at android.support.v4.view.ViewPager.populate(ViewPager.java:1116) at android.support.v4.view.ViewPager$3.run(ViewPager.java:273) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:892) at android.view.Choreographer.doCallbacks(Choreographer.java:704) at android.view.Choreographer.doFrame(Choreographer.java:637) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:878) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5628) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:853) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:737)
The program will crash when I roll forward.
This is in addition to the code other than the adapter.@Hong Duan
private ViewPager mVpScroll;
private int mImgRes[] = new int[] {
R.drawable.banner01,
R.drawable.banner02,
R.drawable.banner03
};
private List<View> mListView = new ArrayList<>();
private LayoutInflater mInfalte;
public MainFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
for (int i = 0; i < mImgRes.length; i++) {
View inflate = inflater.inflate(R.layout.fragment_main_scroll_item, null);
ImageView ivBanner = (ImageView) inflate.findViewById(R.id.iv_scroll);
ivBanner.setImageResource(mImgRes[i]);
mListView.add(inflate);
}
mInfalte = inflater;
return inflater.inflate(R.layout.fragment_main, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
mVpScroll = (ViewPager) view.findViewById(R.id.vp_scroll);
mVpScroll.setAdapter(new MyPagerAdapter());
}
As the error message shows, the root cause is a child View
is added twice. But why this only happens when you swipe backward? I've made a test project and I think I find the reason :)
When ViewPager
scrolls, it will recycle items using its PagerAdapter
's methods: instantiateItem
and destroyItem
, when ViewPager
wants to show a new item, instantiateItem
is called, when ViewPager
scrolls away, the view which off the screen will be destroyed by method destroyItem
.
By default, the ViewPager
will keep 1
more item on each side for smooth scrolling, which means there are at most 3
items keep by the ViewPager
.
The problem is, you are using:
int p = position % mImgRes.length;
to implement a "loop" ViewPager
, that means if mImgRes.length == 3
, the position 3 will show the same View
at position 0.
Now think about your case:
when ViewPager
inited, the item 0
and 1
(right side item) are added by instantiateItem
;
then scroll right a page, the item 1
is shown, and the item 2
is added, now the item 0
(left side item), 1
, 2
(right side item) are kept;
scroll right a page again, the item 2
is shown, the item 3
is added, the item 0
is destroyed by method destroyItem
, now the item 1
(left side item), 2
, 3
(right side item, the same item to 0
) are kept;
Everything works fine if instantiateItem
and destroyItem
work as expected, and it does work when you scroll to the right("forward"), but it does not work when you scroll to the left("backward") because the order of when instantiateItem
and destroyItem
are executed is different! See the log:
07-30 22:00:04.385 26064-26064/com.example.test D/xxxxx: instantiateItem: 0
07-30 22:00:04.388 26064-26064/com.example.test D/xxxxx: instantiateItem: 1
07-30 22:00:17.693 26064-26064/com.example.test D/xxxxx: instantiateItem: 2
07-30 22:00:18.743 26064-26064/com.example.test D/xxxxx: destroyItem: 0
07-30 22:00:18.744 26064-26064/com.example.test D/xxxxx: instantiateItem: 3
07-30 22:00:26.477 26064-26064/com.example.test D/xxxxx: instantiateItem: 0 < -- crash!!!
07-30 22:00:18.743 26064-26064/com.example.test D/xxxxx: destroyItem: 3
when you scroll to the right, destroyItem
executed first, then instantiateItem
, so it works fine, but when you scroll to the left, instantiateItem
executed first, so when the last line of log printed, the item 0
is added the second time, because the item 3
(which share the same View
with item 0
) has not been destroyed yet, then it crashes :)
The interesting thing is that if mImgRes.length > 3
, it will work fine, because when there are 4 items, it's not too late when destroyItem
execute.
So, if you want to implement ViewPager
with the "loop" feature, you have to think about how to work around this issue.