Save/Update Fragment's EditText data in a FragmentStatePagerAdapter
Asked Answered
H

1

7

Introduction

I have an activity with a Book that contains an arraylist of type "page" and to handle all the pages I decided to use a FragmentStatePagerAdapter in which one of my fragment contains one page.

I created an interface for the communication between every fragment in the pager and the father activity. The method that I need in this interface is for update a page showed in a fragment, so I made implement to the father activity, the interface for receiving the data from a fragment and store them in a page to save in the arraylist.

Every fragment has an EditText in which the user can write and I set an addTextChangedListener to catch the moment when the user stops to write. When the user stops to write in the EditText, in the onTextChanged(), I call the function implemented in the activity father.

This is my activity code

public class BookEditorActivity implements BookEditorFragment.EditorFrToEditorActInterface {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.book_editor_activity);

        Bundle extras = getIntent().getExtras();
        b = (Book) extras.get("book");

        setBookViewPager(b);

    }

    private void setBookViewPager(Book b) {
        mBookViewPager = (ViewPager) findViewById(R.id.book_editor_pager);
        mBookViewPagerAdapter = new BookViewPagerAdapter(getSupportFragmentManager(), b);
        mBookViewPager.setAdapter(mBookViewPagerAdapter);
    }

    @Override
    public void saveBookPageTextContent(String textContent) {

        int current = mBookViewPager.getCurrentItem();

        if (b.getPages().get(current) instanceof BookPageText) {
            ((BookPageText) b.getPages().get(current)).setContentPageText(textContent);
        }
    }

    @Override
    public void newBookPageText() {
        int current = mBookViewPager.getCurrentItem();

        BookPageText bookPageText = new BookPageText();
        bookPageText.setContentPageText("");

        b.getPages().add(b.getPages().size() - 1, bookPageText);

        setIndicator(current + 1, b.getPages().size());
        mBookViewPagerAdapter.notifyDataSetChanged();

    }
}

This is the fragment code

public class BookEditorFragment extends Fragment {

    private EditorFrToEditorActInterface mCallback;

    public interface EditorFrToEditorActInterface {
        void newBookPageText();
        void saveBookPageTextContent(String s);
     }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Bundle pageContent = getArguments();
        if (pageContent != null) {
            page = pageContent.getParcelable("page");
        }
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (page instanceof BookPageText) {
            BookPageText bookPage = (BookPageText) page;

            mView = inflater.inflate(R.layout.book_page_text_fragment, container, false);
            contentPage = (EditText) mView.findViewById(R.id.content_text);

            String contentPageText = bookPage.getContentPageText();
            contentPage.setText(contentPageText.isEmpty() ? "" : Html.fromHtml(contentPageText));

            contentPage.addTextChangedListener(new TextWatcher() {
                public void onTextChanged(CharSequence c, int start, int before, int count) {
                    mCallback.saveBookPageTextContent(c.toString());
                }

                public void beforeTextChanged(CharSequence c, int start, int count, int after) {
                }

                public void afterTextChanged(Editable c) {
                }
            });

        } 

        return mView;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            mCallback = (EditorFrToEditorActInterface) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString()
                    + " must implement NewPageInterface");
        }
    }

    @Override
    public void onDetach() {
        mCallback = null;
        super.onDetach();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.editor_new_text:
                mCallback.newBookPageText();
            break;
        }
    }

}

This is the FragmentStatePagerAdapter code

public class BookViewPagerAdapter extends FragmentStatePagerAdapter {
    private ArrayList<Object> bookPages = new ArrayList<>();
    private final SparseArray<WeakReference<BookEditorFragment>> instantiatedFragments = new SparseArray<>();

    public BookViewPagerAdapter(FragmentManager fm, Book b) {
        super(fm);
        this.bookPages = b.getPages();
    }

    @Override
    public Fragment getItem(int position) {

        Object page = bookPages.get(position);

        Bundle pageContent = new Bundle();
        if (page instanceof BookPageText) {
            BookPageText bookPageText = (BookPageText) page;
            pageContent.putParcelable("page", bookPageText);
        } 

        BookEditorFragment fragment = new BookEditorFragment();
        fragment.setArguments(pageContent);
        return fragment;
    }

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

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        final BookEditorFragment fragment = (BookEditorFragment) super.instantiateItem(container, position);
        instantiatedFragments.put(position, new WeakReference<>(fragment));
        return fragment;
    }

    @Override
    public void destroyItem(final ViewGroup container, final int position, final Object object) {
        instantiatedFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    @Nullable
    public Fragment getFragment(final int position) {
        final WeakReference<BookEditorFragment> wr = instantiatedFragments.get(position);
        if (wr != null) {
            return wr.get();
        } else {
            return null;
        }
    }

    @Override
    public int getItemPosition(Object object) {
        int position = bookPages.indexOf(object);
        return position == -1 ? POSITION_NONE : position;
    }
}

Due to the code length I removed some parts like the onClickListener, onPageSelectionListener and other types of pages that I'm creating right now.

The problem

This logic seems to work properly, but every time that I modify the the content of an EditText in a fragment, the close fragment to the left is also updated and I don't know why.

enter image description here
As you can see in the image, if I write something in the EditText2, the same content will appear in the EditText1 and this makes no sense.

Another problem is when I add a new page in the book, the page is created correctly, but with the content of the previous page and this makes no sense. (again!)

My attempts

1) I tried to catch the onFocusChange instead of using the addTextChangedListener, but nothing changed.

2) I tried to implement a different logic using the onChangePageListener(), but in this case I loose the mBookViewPager.getCurrentItem() so the update goes wrong.

3) I tried this all this posts: one, two, three and four.

How can I correct this strange behaviour? Is it possible that the problem is in the way I reference the pages in the ArrayList of the book?

Thanks

Horsecar answered 10/6, 2016 at 8:10 Comment(1)
Can you provide link to your code. Its hard to replicate with the code provided by youAdonic
A
0

Some of possible error I found in your implemantation are.

  1. to manage current page posetion implement

     mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
                @Override
                public void onPageSelected(int p) {
                current = p;
                Log.e("Postion", "" + p);
                }
    
                @Override
                public void onPageScrolled(int arg0, float arg1, int arg2) {}
    
                @Override
                public void onPageScrollStateChanged(int arg0) {}
            });
    
  2. viewpager automatically load privious and next view to privent it set pager OffscreenPageLimit by calling mathod

    mViewPager.setOffscreenPageLimit();
    
  3. always make sure that if you are using if statement in adapter, you should put it's counter part always via else statement even for any default value.

    if (page instanceof BookPageText) {
        BookPageText bookPageText = (BookPageText) page;
        pageContent.putParcelable("page", bookPageText);
    }
    
Ash answered 14/6, 2016 at 19:52 Comment(5)
I did the changes that you proposed but nothing changed... Is it possible that the problem is in the model object? I was thinking about this because for saving my Book I created a class that implements "Serializable" and inside the class I have a reference to another class that implements "Serializable" and "Parcelable". Every time that I retrieve and save a book is in / from file.Horsecar
yes, it could be the case, you can pass data via EventBus instead of Serializable or Parcelable. if you want to store that data, then use Gson to convert your model to string and store that string to preference, you can retrieve that string and convert it to the model object using Gson.Ash
I started yesterday in a new branch the conversion to a json version and I hope this would be the right choice. I don't figure out where I can use EventBus or why use EventBus instead of a simple bundle.Horsecar
a simple bundle will pass only when system event will occur, but with eventBus, you can define your own custom event listener, and pass an object of any class without serializing or parable.Ash
Mmh ok I'll try your solution, maybe it could solve my problem. Do you mean this?Horsecar

© 2022 - 2024 — McMap. All rights reserved.