16

In my app, I have a ViewPager, and inside that ViewPager there're about 17 Fragments. I've designed the layouts for both Portrait and Landscape. When I Rotate my Screen, the method getView() its returning a null object, don't know why this happens, I've used setRetainInstance(true), but still the same thing, I'm using a PagerStateFragmentAdapter as my adapter implementation.

Why does this happens?

Thank you in advance.

EDIT

MainActivity

public class MainActivity extends FragmentActivity implements ViewPagerController{
    public static final String READ_TYPE = "Read_type";
    private static final String TAG = MainActivity.class.getSimpleName();
    private int mReadType;
    private ViewPager mBookPager;

    @Override
    public void goToNextPage() {
        // TODO Auto-generated method stub
        int currentPage = mBookPager.getCurrentItem();
        if(currentPage+1 < mBookPager.getAdapter().getCount()){
            FragmentPagerAdapter adapter = (FragmentPagerAdapter)mBookPager.getAdapter();
            BookPageFragment bookPage = (BookPageFragment)adapter.getItem(currentPage);
            bookPage.setCurrentPage(false);

            bookPage = (BookPageFragment)adapter.getItem(currentPage+1);
            bookPage.setCurrentPage(true);
            mBookPager.setCurrentItem(currentPage+1, true);
        }
    }

    @Override
    public void goToPreviousPage() {
        // TODO Auto-generated method stub
        int currentPage = mBookPager.getCurrentItem();
        if(currentPage >= 0){
            mBookPager.setCurrentItem(currentPage-1,true);
        }
    }

    @Override
    public void onBackPressed() {
        // TODO Auto-generated method stub
        finish();
        overridePendingTransition(R.anim.anim_move_to_right,R.anim.anim_from_left_to_right);
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        Log.i(TAG,"onDestroy");
        mBookPager.removeAllViews();
        mBookPager = null;
        super.onDestroy();
    }

    @Override
    protected void onCreate(Bundle arg0) {
        // TODO Auto-generated method stub
        super.onCreate(arg0);
        setContentView(R.layout.main_layout);
        overridePendingTransition(R.anim.anim_from_right_to_left, R.anim.anim_move_to_left);
        initReadType();
        mBookPager = (ViewPager)findViewById(R.id.mainLayout_mainViewPager);
        initViewPager();

    }

    private void initReadType(){
        mReadType = getIntent().getIntExtra(READ_TYPE, -1);
    }

    private void initViewPager(){
        BookPagerAdapter mPagerAdapter = new BookPagerAdapter(getSupportFragmentManager(),mReadType);
        ((BookPageFragment)mPagerAdapter.getItem(0)).setCurrentPage(true);
        mBookPager.setAdapter(mPagerAdapter);
        mBookPager.setOnPageChangeListener(mOnPageChangeListener);
    }

    private OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener(){

        @Override
        public void onPageScrollStateChanged(int arg0) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onPageSelected(int selection) {
            // TODO Auto-generated method stub
            BookPagerAdapter bookAdapter = (BookPagerAdapter)mBookPager.getAdapter();
            BookPageFragment fragment = (BookPageFragment)bookAdapter.getItem(selection);
            if(selection > 0){
                bookAdapter.getItem(selection - 1).onPause();
//              bookAdapter.getItem(selection - 1).onDestroy();
            }
            if(selection + 1 < bookAdapter.getCount() && (bookAdapter.getItem(selection + 1) != null)){
                bookAdapter.getItem(selection + 1).onPause();
            }
            fragment.setCurrentPage(true);
            fragment.onResume();
        }

    };

    public static class BookPagerAdapter extends FragmentStatePagerAdapter{
        private List<Fragment> mBookPagesList;
        private int mReadType;

        public BookPagerAdapter(FragmentManager fm,int readType) {
            super(fm);
            // TODO Auto-generated constructor stub
            mReadType = readType;
            initPages();
        }

        @Override
        public Fragment getItem(int position) {
            // TODO Auto-generated method stub
            return mBookPagesList.get(position);
        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return mBookPagesList.size();
        }

        private void initPages(){
            mBookPagesList = new ArrayList<Fragment>();
            mBookPagesList.add(FirstPageFragment.newInstance(mReadType));
            mBookPagesList.add(SecondPageFragment.newInstance(mReadType));
            mBookPagesList.add(ThirdPageFragment.newInstance(mReadType));
            mBookPagesList.add(FourthPageFragment.newInstance(mReadType));
            mBookPagesList.add(FifthPageFragment.newInstance(mReadType));
            mBookPagesList.add(SixthPageFragment.newInstance(mReadType));
            mBookPagesList.add(SeventhPageFragment.newInstance(mReadType));
            mBookPagesList.add(EigthPageFragment.newInstance(mReadType));
            mBookPagesList.add(NinethPageFragment.newInstance(mReadType));
            mBookPagesList.add(TenthPageFragment.newInstance(mReadType));
            mBookPagesList.add(EleventhPageFragment.newInstance(mReadType));
            mBookPagesList.add(TwelvethPageFragment.newInstance(mReadType));
            mBookPagesList.add(ThirteenthPageFragment.newInstance(mReadType));
            mBookPagesList.add(FourteenthPageFragment.newInstance(mReadType));
            mBookPagesList.add(FifteenthPageFragment.newInstance(mReadType));
            mBookPagesList.add(SixteenthPageFragment.newInstance(mReadType));
            mBookPagesList.add(SeventeenthPageFragment.newInstance(mReadType));
            mBookPagesList.add(CreditsPageFragment.newInstance(mReadType));
        }
    }
}

BookPageFragment

public abstract class BookPageFragment extends Fragment{
public static final String TAG = BookPageFragment.class.getSimpleName();

public static final int AUTO_PLAY = 1;
public static final int READ_WITH_ME = 2;
public static final int READ_TO_MYSELF = 3;

private ViewPagerController mPageController;
protected final static String READ_TYPE_KEY = "readType";

private boolean mIsCurrentPage;

private int mCurrentWordIndex;
private MediaPlayer mMediaPlayer;
private BookPage mBookPage;
private SpeechController mSpeechController;

private TextHighlightController mCurrentHighlightController;
protected List<TextHighlightController> mHighlighters;
private int mCurrentHighlightIndex;

public abstract int getJsonId();
public abstract int getAudioResourceId();
protected abstract int[] getParagraphsTextView();
protected abstract int[] getParagraphsStringRes();
protected abstract int[] getPopupButtonsId();

public BookPageFragment(){
    mIsCurrentPage = false;
    mCurrentHighlightIndex = 0;
}

public void endAudioPlayback(){
    if(getReadTypeFromArgs() == AUTO_PLAY){
        mPageController.goToNextPage();
    }
}

private void loadHighlighters(){
    int[] paragraphsTextViews = getParagraphsTextView();
    int[] stringRes = getParagraphsStringRes();
    for(int i=0;i<paragraphsTextViews.length;i++){
        int currentParagraph = paragraphsTextViews[i];
        int stringId = stringRes[i];
        TextView paragraph = (TextView)getView().findViewById(currentParagraph);
        TextHighlightController controller = TextHighlightController.newInstance(getActivity(), paragraph, stringId);
        addHighlighter(controller);
    }
}

public final void highlightNextWord(){
    if(!mCurrentHighlightController.highlightNextWord()){
        mCurrentHighlightIndex++;
        if(mCurrentHighlightIndex < mHighlighters.size()){
            mCurrentHighlightController = mHighlighters.get(mCurrentHighlightIndex);
            mCurrentHighlightController.highlightNextWord();
        }
    }
}

@Override
public void onAttach(Activity activity) {
    // TODO Auto-generated method stub
    super.onAttach(activity);
    Log.i(TAG,"onAttach");
    try{
        mPageController = (ViewPagerController)activity;
    }catch(ClassCastException e){
        throw new IllegalArgumentException("Activity not implementing ViewPagerController");
    }
}

@Override
public void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    Log.i(TAG,"onCreate");
}

@Override
public void onDetach() {
    // TODO Auto-generated method stub
    super.onDetach();
    resetAll();
    Log.i(TAG,"onDetach");
}

public void goToNextPage(){
    mPageController.goToNextPage();
}

public void goToPreviousPage(){
    mPageController.goToPreviousPage();
}

public void setCurrentPage(boolean isCurrentPage){
    mIsCurrentPage = isCurrentPage;
}

public boolean isCurrentPage(){
    return mIsCurrentPage;
}

@Override
public void onStart() {
    // TODO Auto-generated method stub
    super.onStart();
    Log.i(TAG,"onStart");
}

@Override
public void onResume() {
    // TODO Auto-generated method stub
    Log.i(TAG,"onResume");
    super.onResume();
    resetAll();
    configureBookPage();
}

public final void startWithAutoPlay(){
    disablePopupButtons();
    initAudioPlayback();
}

public final void startWithRead(){
    initAudioPlayback();
}

private long prevWordStartTime = 0;
public final long getNextWordStart(){
    long wordStart=-1;
    if(mCurrentWordIndex < mBookPage.getWordList().size()){
        Word currentWord = mBookPage.getWordList().get(mCurrentWordIndex);
        wordStart = currentWord.getStartTime() - prevWordStartTime;
        wordStart = wordStart + currentWord.getWordLength() - 25;
        prevWordStartTime = currentWord.getEndTime();
        mCurrentWordIndex++;
    }
    return wordStart;
}

public final long getNextWordLength(){
    long wordLength;
    if(mCurrentWordIndex < mBookPage.getWordList().size()){
        Word currentWord = mBookPage.getWordList().get(mCurrentWordIndex);
        if(mCurrentWordIndex == 0){
            wordLength= currentWord.getStartTime();
        }else{
            wordLength = currentWord.getWordLength();
        }
        mCurrentWordIndex++;
    }else{
        wordLength = -1;
    }
    return wordLength;
}

public final void startPlayback(){
    mMediaPlayer = MediaPlayer.create(getActivity(), getAudioResourceId());
    mMediaPlayer.start();
}

@Override
public void onPause() {
    // TODO Auto-generated method stub
    Log.i(TAG,"onPause");
    mIsCurrentPage = false;
    if(mSpeechController != null){
        mSpeechController.stopHighlighting();
    }
    if(mMediaPlayer != null){
        try{
            mMediaPlayer.stop();
            mMediaPlayer.release();
        }catch(Exception e){

        }
    }
    resetHighlighters();
    resetAll();
    super.onPause();
}

@Override
public void onStop() {
    // TODO Auto-generated method stub
    super.onStop();
    Log.i(TAG,"onStop");
}

protected void addHighlighter(TextHighlightController controller){
    if(mHighlighters == null){
        mHighlighters = new ArrayList<TextHighlightController>();
    }
    mHighlighters.add(controller);
}

private void resetHighlighters(){
    if(mHighlighters != null){
        for(TextHighlightController controller : mHighlighters){
            controller.reset();
        }
    }
    mCurrentHighlightIndex = 0;
    mCurrentWordIndex = 0;
}

private void resetAll(){
    mCurrentWordIndex = 0;
    mCurrentHighlightIndex = 0;
    if(mMediaPlayer != null){
        try{
            mMediaPlayer.reset();
        }catch(Exception e){

        }
    }
    if(mCurrentHighlightController != null){
        mCurrentHighlightController.reset();
    }
    if(mSpeechController != null){
        Message msg = mSpeechController.obtainMessage(SpeechController.END_AUDIO);
        mSpeechController.sendMessage(msg);
    }
    if(mHighlighters != null){
        for(TextHighlightController controller : mHighlighters){
            controller.reset();
        }
    }
}

private void initAudioPlayback(){
    if(getJsonId() != 0){
        initHighlightersList();
        loadJson();
        mSpeechController = new SpeechController(this);
        Message msg = mSpeechController.obtainMessage(SpeechController.START_AUTO_PLAY);
        mSpeechController.sendMessageDelayed(msg, 1000);
    }
}

private void disablePopupButtons(){
    if(getView() != null){
        int[] buttonsId = getPopupButtonsId();
        for(int currentButtonId : buttonsId){
            getView().findViewById(currentButtonId).setVisibility(View.INVISIBLE);
        }
    }
}

private void initHighlightersList(){
    loadHighlighters();
    if(mHighlighters != null)
        mCurrentHighlightController = mHighlighters.get(mCurrentHighlightIndex);
}

private void loadJson(){
    Gson gson = new Gson();
    InputStream jsonStream = getResources().openRawResource(getJsonId());
    InputStreamReader jsonReader = new InputStreamReader(jsonStream);
    mBookPage = gson.fromJson(jsonReader, BookPage.class);
}

private void configureBookPage(){
    int currentReadType = getReadTypeFromArgs();
    Log.i(TAG,"GotReadType");
    switch(currentReadType){
    case AUTO_PLAY:
        if(mIsCurrentPage){
            startWithAutoPlay();
        }
        break;
    case READ_WITH_ME:
        if(mIsCurrentPage){
            startWithRead();
        }
        break;
    case READ_TO_MYSELF:
        //Should do Nothing
        break;
    default:
        throw new IllegalArgumentException("You must select one of the reading types of BookPageFragment");
    }
}

protected int getReadTypeFromArgs(){
    return getArguments().getInt(READ_TYPE_KEY);
}

}

LOGCAT

    09-30 22:56:37.602: E/AndroidRuntime(7640): FATAL EXCEPTION: main
09-30 22:56:37.602: E/AndroidRuntime(7640): java.lang.NullPointerException
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.mobimanage.kagadventures.fragment.BookPageFragment.loadHighlighters(BookPageFragment.java:68)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.mobimanage.kagadventures.fragment.BookPageFragment.initHighlightersList(BookPageFragment.java:275)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.mobimanage.kagadventures.fragment.BookPageFragment.initAudioPlayback(BookPageFragment.java:257)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.mobimanage.kagadventures.fragment.BookPageFragment.startWithRead(BookPageFragment.java:149)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.mobimanage.kagadventures.fragment.BookPageFragment.configureBookPage(BookPageFragment.java:298)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.mobimanage.kagadventures.fragment.BookPageFragment.onResume(BookPageFragment.java:140)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.mobimanage.kagadventures.MainActivity$1.onPageSelected(MainActivity.java:136)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.support.v4.view.ViewPager.scrollToItem(ViewPager.java:567)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:551)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.support.v4.view.ViewPager.onTouchEvent(ViewPager.java:2008)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.View.dispatchTouchEvent(View.java:7253)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2168)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1903)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2174)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2174)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2174)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2174)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2215)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1458)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.app.Activity.dispatchTouchEvent(Activity.java:2410)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2163)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.View.dispatchPointerEvent(View.java:7433)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3220)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3165)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4292)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4271)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4363)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:179)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.os.MessageQueue.nativePollOnce(Native Method)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.os.MessageQueue.next(MessageQueue.java:125)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.os.Looper.loop(Looper.java:124)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at android.app.ActivityThread.main(ActivityThread.java:5227)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at java.lang.reflect.Method.invokeNative(Native Method)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at java.lang.reflect.Method.invoke(Method.java:511)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:795)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:562)
09-30 22:56:37.602: E/AndroidRuntime(7640):     at dalvik.system.NativeStart.main(Native Method)
NemesisDoom
  • 596
  • 6
  • 21

3 Answers3

34

Instead of using getItem to get the current page use

(BookPageFragment) mBookPagerAdapter.instantiateItem(mBookPager, mBookPager.getCurrentItem());

This returns a reference to the existing fragment which actually does have a view.

Mehdiway
  • 10,337
  • 8
  • 36
  • 68
Barry Irvine
  • 13,858
  • 3
  • 25
  • 36
  • 2
    Small advice: the first `mBookPager` is the adapter, the second one is the viewPager – Ron Mar 07 '14 at 10:56
  • The problem with this is twofold, though: first, it relies on the overridden instantiateItem() that, depending on the implementation, might not simply return the previous fragment but to create a new one. Second, the call referring to a View as first argument is deprecated. -- I found a working solution, I'll post it as an answer. – Gábor Apr 04 '14 at 10:24
  • 2
    you just save my life :-) – Tabish Hussain Oct 12 '16 at 21:18
2

If you keep track of your fragments in your page adapter, as is usual, add the following function to it:

private SparseArray<PageType> fragments;
...
public PageType getFragment(int position) {
  return fragments.get(position);
}

Then you can use this to query a fragment. And getView() will already work all right there:

PageType page = (PageType) pagesAdapter.getFragment(pager.getCurrentItem());
View view = page.getView().findViewById(R.id.whatever);
view.invalidate();
Gábor
  • 9,466
  • 3
  • 65
  • 79
  • Does this really work? Androd might decide to destroy a fragment, it is still in your fragments array then, and recreate it later, then you have an outdated version in the array. I'm not yet sure this can happen though. – Moritz Both Mar 24 '17 at 22:23
2

setRetainInstance is ignored for android.support.v4.app.FragmentManager.

The solution that worked for me was removing the saved fragments in the onCreate of my activity BEFORE super.OnCreate is called.

if(savedInstanceState != null) {
    savedInstanceState.remove("android:support:fragments");
}

This worked for me in the way I expected setRetainInstance(true); to work in the fragments themselves.

I found this solution here

*Note that if you are calling to a DialogFragment from any of the fragments, they will dismiss on orientation change if you use this solution.

Mira_Cole
  • 763
  • 6
  • 13