4

Well, Consider I have have two fragments FragmentTab1 & ShowAllContactFragment. FragmentTab1 consists a list-view & and a button. When the button is clicked I replace ShowAllContactFragment in my viewpager. When shows ShowAllContactFragment, user can select several contacts by selecting check-box in a list-view & it also has a ADD contact button.

What I need: I want to update existing listview in FragmentTab1 , when user pressing ADD button in ShowAllContactFragment, after selecting some contacts in this list-view. I also remove ShowAllContactFragment, and show FragmentTab1 when this will occur.

My Solving Status: I follow the the android developers forum to communicate data between fragment via Activity. I'm almost done. I create Interface OnContactSelectedListener in ShowAllContactFragment & Implements in MainActivity. Everything is ok! . After debugging, I check my callback methods that I have data in MainActivity but I can't replace the ShowAllContactFragment & can't show the previous fragment FragmentTab1 & update it's list-view.

To open ShowAllContactFragment from FragmentTab1, I wrote like:

ShowAllContactFragment allContactsFragment = new ShowAllContactFragment();

            FragmentTransaction transaction = getFragmentManager()
                    .beginTransaction();
            transaction.addToBackStack(null);
            transaction.add(R.id.fragmentTabLayout1, allContactsFragment);
            transaction.commit();

My MainActivity & FragmentAdapter Looks :

public class MainActivity extends SherlockFragmentActivity implements
        ShowAllContactFragment.OnContactSelectedListener {

    ActionBar.Tab Tab1, Tab2, Tab3, Tab4;
    private Context context = this;
    // view pager
    // Declare Variables
    ActionBar actionBar;
    ViewPager mPager;
    Tab tab;
    FragmentAdapter mAdapter;
    List<Fragment> fragmentList = new ArrayList<Fragment>();
    ArrayList<Person> blackListPersonList;

    private final static String TAG_FRAGMENT = "TAG_FRAGMENT";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // set application in portrait mode
        ActivityHelper.initialize(this);

        actionBar = getSupportActionBar();
        actionBar.setDisplayShowHomeEnabled(true);
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        addFragmentsInList();
        // Locate ViewPager in activity_main.xml
        mPager = (ViewPager) findViewById(R.id.pager);
        // add an adapter to pager
        mAdapter = new FragmentAdapter(getSupportFragmentManager(), mPager,
                actionBar, fragmentList);
        mPager.setAdapter(mAdapter);
        addActionBarTabs();

    }

    public void addFragmentsInList() {

        FragmentTab1 aFragmentTab1 = new FragmentTab1();

        fragmentList.add(new FragmentTab1());
        fragmentList.add(new FragmentTab2());
        fragmentList.add(new FragmentTab3());

    }

    private void addActionBarTabs() {

        String[] tabs = { "Tab 1", "Tab 2", "Tab 3" };
        for (String tabTitle : tabs) {
            ActionBar.Tab tab = actionBar.newTab().setText(tabTitle)
                    .setTabListener(tabListener);
            actionBar.addTab(tab);

        }

    }

    private ActionBar.TabListener tabListener = new ActionBar.TabListener() {
        @Override
        public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
            mPager.setCurrentItem(tab.getPosition());
        }

        @Override
        public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
        }

        @Override
        public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
        }
    };

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // add action menu here
        MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.activity_itemlist, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle presses on the action bar items
        switch (item.getItemId()) {
        case R.id.add_item:
            // openSearch();
            Toast.makeText(context, " add_item  ", Toast.LENGTH_SHORT).show();
            return true;
        case R.id.about:
            // composeMessage();
            Toast.makeText(context, "   about", Toast.LENGTH_SHORT).show();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    class FragmentAdapter extends FragmentPagerAdapter implements
            ViewPager.OnPageChangeListener {

        private ViewPager mViewPager;
        final int TOTAL_PAGES = 3;
        private List<Fragment> fragments;
        SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

        public FragmentAdapter(FragmentManager fm, ViewPager pager,
                ActionBar actionBar, List<Fragment> fragmentsList) {
            super(fm);
            this.mViewPager = pager;
            this.mViewPager.setOnPageChangeListener(this);
            this.fragments = fragmentsList;
        }

        @Override
        public Fragment getItem(int position) {
            // switch (position) {
            // case 0:
            // return FragmentTab1.newInstance();
            // case 1:
            // return FragmentTab2.newInstance();
            // case 2:
            // return FragmentTab3.newInstance();
            // default:
            // throw new IllegalArgumentException(
            // "The item position should be less or equal to:"
            // + TOTAL_PAGES);
            // }

            return this.fragments.get(position);
        }

        @Override
        public int getCount() {
            // return TOTAL_PAGES;
            return this.fragments.size();

        }

        public ViewPager getViewPager() {

            return mViewPager;
        }

        // added newly
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment fragment = (Fragment) super.instantiateItem(container,
                    position);
            registeredFragments.put(position, fragment);
            return fragment;
        }

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

        public Fragment getRegisteredFragment(int position) {
            return registeredFragments.get(position);
        }

        @Override
        public void onPageScrollStateChanged(int arg0) {

        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {

        }

        @Override
        public void onPageSelected(int position) {
            actionBar.setSelectedNavigationItem(position);
        }
    }

    @Override
    public void onBackPressed() {

        Log.e(TAG_FRAGMENT, "onBackPressed");

        FragmentManager fm = getSupportFragmentManager();
        if (fm.getBackStackEntryCount() > 0) {
            Log.i("MainActivity", "popping backstack");
            fm.popBackStack();
        } else {
            Log.i("MainActivity", "nothing on backstack, calling super");
            super.onBackPressed();
        }

    }


    @Override
    public void onContactSelected(ArrayList<Person> contactNumberlist) {
        // data comes here, no problem.
        this.blackListPersonList = contactNumberlist;
        Log.d("onContactSelected", "onContactSelected");

        // get error here
        FragmentTab1 aFragmentTab1 = (FragmentTab1) mAdapter.getItem(0);

        if (aFragmentTab1 != null) {
            aFragmentTab1.updateFragment1(blackListPersonList);
        }
        FragmentTransaction transaction = getSupportFragmentManager()
                .beginTransaction();

        transaction.add(R.id.fragment_all_contacts_layout, aFragmentTab1);
        transaction.commit();

    }

    public ArrayList<Person> getBlackListContacts() {

        return blackListPersonList;
    }

    // public Fragment getFragment(ViewPager pager){
    // Fragment theFragment = fragments.get(pager.getCurrentItem());
    // return theFragment;
    // }

}

FrgmentTab1 looks :

  public class FragmentTab1 extends SherlockFragment implements OnClickListener {

    Button btnTest;
    ViewPager pager;
    LinearLayout layoutBlockNumbers;
    LinearLayout layoutContact, layoutCallLog, layoutSMSLog, layoutManually;
    public Context mContext;
    CustomizedDialog dialog;
    private static final int CONTACT_PICKER_RESULT = 1001;
    private static final String DEBUG_TAG = "Contact List";
    private static final double RESULT_OK = -1;

    ListView listViewOnlyBlackListNumber;
    ArrayList<Person> personList;
    BlackListAdapter aBlackListAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.fragmenttab1, container,
                false);

        layoutBlockNumbers = (LinearLayout) rootView
                .findViewById(R.id.layoutAddBlockNumbers);
        layoutBlockNumbers.setOnClickListener(this);

        listViewOnlyBlackListNumber = (ListView) rootView
                .findViewById(R.id.listViewOnlyBlackListNumber);

        personList = ((MainActivity) getActivity()).getBlackListContacts();

        if (personList != null) {
            aBlackListAdapter = new BlackListAdapter(getActivity(), personList,
                    m_onSelectedEventCalender);
            listViewOnlyBlackListNumber.setAdapter(aBlackListAdapter);

        } else {
            listViewOnlyBlackListNumber.setEmptyView(container);
        }

        return rootView;

    }

    public void updateFragment1(ArrayList<Person> personList) {

        // trying to update when came back here
        aBlackListAdapter = new BlackListAdapter(getActivity(), personList,
                m_onSelectedEventCalender);

        listViewOnlyBlackListNumber.setAdapter(aBlackListAdapter);
        aBlackListAdapter.notifyDataSetChanged();
    }
}

Get Error In onContactSelected, inside MainActivity

       10-30 00:22:29.674: E/AndroidRuntime(26834): FATAL EXCEPTION: main
     java.lang.IllegalStateException: Can't change container ID of fragment FragmentTab1{42d27380 #0 id=0x7f040032 android:switcher:2130968626:0}: was 2130968626 now 2130968638
   E/AndroidRuntime(26834):     at android.support.v4.app.BackStackRecord.doAddOp(BackStackRecord.java:407)
     E/AndroidRuntime(26834):   at android.support.v4.app.BackStackRecord.add(BackStackRecord.java:384)
     E/AndroidRuntime(26834):   at com.mobigic.callblocker.MainActivity.onContactSelected(MainActivity.java:240)

Hope, Somebody help me.

Shihab Uddin
  • 6,699
  • 2
  • 59
  • 74
  • I think you have to try stack for your requirement when you add new fragment push this fragment on Stack and when you back pressed check the size of stack if size > 0 then pop fragment from Stack else finish activity.... – Haresh Chhelana Nov 09 '13 at 07:08
  • Haresh: My problem is simple. FragmentTab1 has a listview & a button. when click on a button i open new frament ShowAllContactFragment above FragmentTab1. Then Select some items from ShowAllContactFragment, & click button finish in this layout & Show prvious FragmentTab1 & update it's ListView according to selected items. I can't update list in FragmentTab1 from ShowAllContactFragment. I have already data object in main activity. I use MainActivity to comm. between fragmetns. – Shihab Uddin Nov 09 '13 at 07:35
  • I understand your problem completely. You want to open another fragment for user to select some data and then when user presses OK button you want to transfer that data back to old fragment. What I don't understand is where on earth `ViewPager` comes into this? Is there a particular reason why you are using `ViewPager`? What you are describing does not require any `ViewPager`. – M-Wajeeh Nov 12 '13 at 05:18

2 Answers2

1

Your question is not very clear especially the part about how you handle those fragments. Like when you're showing the ShowAllContactFragment fragment in which layout do you put it? From the code you posted it seems you're blindly adding the ShowAllContactFragment fragment directly in the layout containing the ViewPager which isn't right.

Related to the error, you get a reference to one of the fragments already managed by the FragmentManager through the adapter of the ViewPager and then you retry to add it in a transaction, action which will fail. If you're trying to show the previous shown FragmentTab1 fragment, after showing and working with the ShowAllContactFragment fragment, you should try simply removing the last recorded action of the FragmentManager through one of its popBackStack...() methods.

Edit: I looked at your code but what you're doing is still ambiguous. I've looked at the layout for the main activity but you don't have a container with the id R.id.fragment_all_contacts_layout so I'm assuming you're somehow trying to insert the new FragmentTab1 in the layout of the old FragmentTab1 which really doesn't make any sense(not to mention you add the ShowAllContactFragment to a container with the id R.id.fragmentTabLayout1 which I also can't see). Anyway, I'm assuming that you want the ShowAllContactFragment to replace the FragmentTab1 from the ViewPager. For this you'll need a wrapper fragment to hold the two nested fragment and also enable the communication between them. For example the wrapper fragment:

public static class WrapperFragment extends Fragment implements
        OnContactSelectedListener {

    private static final String TAG_FIRST = "tag_first";
    private static final String TAG_SECOND = "tag_second";
    private static final int CONTENT_ID = 1000;
    private FragmentTab1 mFrag1;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        FrameLayout content = new FrameLayout(getActivity());
        content.setId(CONTENT_ID);
        if (getChildFragmentManager().findFragmentByTag(TAG_SECOND) == null) {
            mFrag1 = (FragmentTab1) getChildFragmentManager()
                    .findFragmentByTag(TAG_FIRST);
            if (mFrag1 == null) {
                mFrag1 = new FragmentTab1();
                getChildFragmentManager().beginTransaction()
                        .add(CONTENT_ID, mFrag1, TAG_FIRST).commit();
            }
        }
        return content;
    }

    @Override
    public void onContactSelected(List<Person> contactNumberlist) {
        getChildFragmentManager().popBackStackImmediate();
        mFrag1.updateFragment1(contactNumberlist);
    }

    public void showPickerfragment() {
        getChildFragmentManager().beginTransaction()
                .replace(CONTENT_ID, new ShowAllContactFragment())
                .addToBackStack(null).commit();
    }

}

This will be the fragment that you'll use in the ViewPager's adapter instead of the FragmentTab1 fragment. Notice that it implements the OnContactSelectedListener interface. You'll also need to make some changes to other parts of the code:

@Override
public void onClick(View v) {
     if (v == layoutCallLog) {
     dialog.dismiss();
             // make the wrapper fragment to open the ShowAllContactFragment fragment
            ((WrapperFragment) getParentFragment()).showPickerfragment();
            // rest of the code

The ShowAllContactFragment will need to be modified to send the selection event to the wrapper fragment which implements its interface:

@Override
public void onClick(View v) {
    if (v == btnAdd) {
        Toast.makeText(getActivity(), "" + blockListedPersonList.size(),
                Toast.LENGTH_SHORT).show();
            ((OnContactSelectedListener) getParentFragment())
                .onContactSelected(blockListedPersonList);
    }
}

And in the main activity to handle the BACK button when the ShowAllContactFragment is showing in the ViewPager:

@Override
public void onBackPressed() {
    if (mViewPager.getCurrentItem() == 0) { // I assumed 0 is the position in the adapter where the WrapperFragment will be used
        Fragment targetPage = getSupportFragmentManager()
                .findFragmentByTag("android:switcher:" + PAGER_ID + ":" + 0); // PAGER_ID is the id of the ViewPager
        if (targetPage.getChildFragmentManager().getBackStackEntryCount() > 0) {
            targetPage.getChildFragmentManager().popBackStack();
        }
        return;
    }
    super.onBackPressed();
}

Keep in mind that you'll need to save the data of the nested fragments.

user
  • 86,916
  • 18
  • 197
  • 190
  • thanks for your time. I share this all 3 classes. Please Help me to do the rest. @Luksprog My FragmentTab1 class: http://pastie.org/8457405 ShowAllContactFragment: http://pastie.org/8457408 MainActivity: http://pastie.org/8457413 I hope this will clear you, to fix the bug. Consider I am new in Fragment. – Shihab Uddin Nov 05 '13 at 16:17
  • @shihab_returns Can you also post the xml layout that you used in the `MainActivity`, `R.layout.activity_main`? – user Nov 05 '13 at 17:23
  • @shihab_returns I don't fully understand what you're doing but I've edited my answer to include some sample code on what I assumed you want. – user Nov 08 '13 at 19:18
  • My problem is simple. FragmentTab1 has a listview & a button. when click on a button i open new frament ShowAllContactFragment above FragmentTab1. Then Select some items from ShowAllContactFragment, & click button finish in this layout & Show prvious FragmentTab1 & update it's ListView according to selected items. I can't update list in FragmentTab1 from ShowAllContactFragment. I have already data object in main activity. I use MainActivity to comm. between fragmetns. – Shihab Uddin Nov 09 '13 at 07:36
  • *when click on a button i open new frament ShowAllContactFragment above FragmentTab1.* - where exactly above? Your activiy's layout only contains a `ViewPager` so where's the `R.id.fragmentTabLayout1` container that sits above FragmentTab1? if it's in FragmentTab1 you're doing this wrong, you can't just insert fragments with the normal FragmentManager in other fragments. – user Nov 09 '13 at 07:58
  • Dear luksprog: I am very thankful to you.. http://www.mediafire.com/download/hib2dp2981q7gqc/ActionBarTestWithSherlock.apk can download my apk, It would be pleasure, if you can find my problem. English is not my native language. So , i can't explain to you much. – Shihab Uddin Nov 09 '13 at 12:16
  • @shihab_returns Your app's apk is not helpful at all. I've explained in my answer the basic steps to implement what you want. Of course this isn't something easy to do if you're new to the entire fragment framework, so I can only recommend that you study it a bit more before attempting the more complex stuff. – user Nov 09 '13 at 13:42
1

I used FragmentActivity and ViewPager in differents projects and there are two solutions:

1: You can use onHiddenChanged(boolean hidden) method on your FragmentTab1.

override fun onHiddenChanged(hidden: Boolean) {
    // TODO Auto-generated method stub
    if (!hidden) {
        // check if personList size is changed 
        // and then call updateFragment1() methode
    }
    super.onHiddenChanged(hidden)
}

2: Use a static method: you can ake updateFragment1() method static so when user pressing "ADD", call updateFragment1().

Hope it helps.

Alessio
  • 3,404
  • 19
  • 35
  • 48
TheATeam
  • 26
  • 2