java.lang.IllegalStateException in the ViewPager a

2019-09-09 23:48发布

I get an exception when dinamically add data to the viewPager

02-07 19:21:24.488 5577-5577/com.jamesb.encoderyapp E/AndroidRuntime: FATAL EXCEPTION: main Process: com.jamesb.encoderyapp, PID: 5577 java.lang.IllegalStateException: The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: 4, found: 8 Pager id: com.jamesb.encoderyapp:id/pager Pager class: class android.support.v4.view.ViewPager Problematic adapter: class com.jamesb.encoderyapp.adapters.ViewPagerAdapter at android.support.v4.view.ViewPager.populate(ViewPager.java:1000) at android.support.v4.view.ViewPager.populate(ViewPager.java:952) at android.support.v4.view.ViewPager$3.run(ViewPager.java:251) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767) at android.view.Choreographer.doCallbacks(Choreographer.java:580) at android.view.Choreographer.doFrame(Choreographer.java:549) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5254) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

I get data from API - 3 pictures,when this pictures swiped, I call getContent() method again and in this moment i get IllegalStateException .

 try {
        RestClient.getInstance(getActivity()).getContent(date, new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Response response) throws IOException {
                String json = response.body().string();

                GsonBuilder gsonBuilder = new GsonBuilder();
                mGson = gsonBuilder.create();
                jsonParsed = mGson.fromJson(json, JsonParsed.class);
                if (jsonParsed != null) mApodData.add(jsonParsed);
                if (count < iteration) {
                    count++;
                    getContent();
                } else {
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {

                                mAdapter = new ViewPagerAdapter(getActivity(), mApodData);
                                mViewPager.setAdapter(mAdapter);
                                mAdapter.notifyDataSetChanged();
                        }
                    });
                }
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }

View Pager Adapter

public class ViewPagerAdapter extends PagerAdapter {

private Activity mActivity;
private ArrayList<JsonParsed> mApodData = new ArrayList<>();
private Utils mUtils;

public ViewPagerAdapter(Activity activity, ArrayList<JsonParsed> mApodData) {
    this.mActivity = activity;
    this.mApodData = mApodData;
    mUtils = new Utils();
}

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

@Override
public int getItemPosition(Object object) {
    return POSITION_UNCHANGED;
}

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

@Override
public View instantiateItem(final ViewGroup container, final int position) {

    LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    final View itemView = inflater.inflate(R.layout.item, container, false);
    final ViewHolder viewHolder = new ViewHolder();
    viewHolder.mPic = (ImageView) itemView.findViewById(R.id.picture);
    viewHolder.mPicTitle = (TextView) itemView.findViewById(R.id.picTitle);
    viewHolder.mDescription = (TextView) itemView.findViewById(R.id.description);
    itemView.setTag(viewHolder);

    JsonParsed item = mApodData.get(position);
    String descr = item.explanation;
    String title = item.title;
    String picUrl = item.hdurl;
    viewHolder.mPicTitle.setText(title);
    viewHolder.mDescription.setText(descr);

    Picasso.with(mActivity)
            .load(String.valueOf(picUrl))
            .into(viewHolder.mPic);

    viewHolder.mPic.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mUtils.setImageAsWallpaper(mActivity, viewHolder.mPic);
        }
    });
    container.addView(itemView);
    return itemView;
}

@Override
public void destroyItem(ViewGroup collection, int position, Object view) {
    ((ViewPager) collection).removeView((View) view);

}

static class ViewHolder {
    ImageView mPic;
    TextView mPicTitle;
    TextView mDescription;
}

}

I've seen similar questions in google ,but I don't understand where I did not use notifyDataSetChanged() and this throws exception.

2条回答
在下西门庆
2楼-- · 2019-09-10 00:29

It looks like your adapter is sharing the list reference with your activity/fragment. What this means is: when this line executes

                if (jsonParsed != null) mApodData.add(jsonParsed);

the list in the adapter is updated immediately since it is essentially the same list.

While the ViewPager is doing its thing, it references the adapter again and again. So it checks that getCount() and the adapter haven't changed in order to stay internally consistent.

The best way to fix this? if you know how many pages you will end up with, have your getCount() method return that number. Another thing you can do is have the adapter create its own list and do a mList.addAll() on the list from the activity. You could also set the ViewPager adapter to null while retrieving the data from the server.

查看更多
迷人小祖宗
3楼-- · 2019-09-10 00:44

Someone will jump into the following problem so I post it as one possible answer. To get the ViewPager to work perfectly is really an enormous timethief. Both the (missing) tutorials on developer.android.com and the code in FragmentPagerAdapter, they are both a disgrace. All those programmers around the world trying to find the narrow path to a fully working ViewPager!

I found one reason why you would get the illegal state in your logcat, stemming from the override of .destroyItem(), as follows: If you have a number of fixed fragments and e.g. start out to display fragment at position 0, having e.g. set the

viewPager.setOffscreenPageLimit (2);

Then if the user selects a new tab at position e.g. 4, the fragment at position 0 will be destroyed.

In this case, it is mandatory to call notifyDataSetChanged(), because the call to super (see below) will not synchronize the base FragmentPagerAdapter to your extension of it. Therefore, the logcat mysteriously says (with 7 fragments in my case):

"Expected adapter item count: 7, found: 6 Pager id: com.hdsl.a.zapp:id/viewpager Pager class: class android.support.v4.view.ViewPager Problematic adapter:"

The interpretation is: My extension of the base class has the correct count (6), while the base class does not (7). One should expect that a call to super would fix this, and it should, but it does not. Here is the correct code to counteract this bug:

@Override
public void destroyItem(ViewGroup container, int position, Object object) {

    super.destroyItem(container, position, object);//Note: does not help to move this to the end of the method
    registeredFragmentsList.remove(position);
    notifyDataSetChanged();
}

And to android core team: Call to super should be more intelligent! While this is not the case, please enhance the logcat with something much better, for example something like this: "The adapter found inconsistency in the number of fragments between FragmentPagerAdapter and the extended class. Make sure .notifyDataSetChanged() is always called whenever changes are made to your fragment list. Candidates are: (destroyItem, …..)"

"problematic adapter" is nothing but a push on the wild-guess button. Not helpful. In particular in this case, because the exception displayed in logcat does not contain a clue on where in your own code the error was provoked.

查看更多
登录 后发表回答