Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use onQueryTextChange with many fragments

I have many fragments they're shown in 2 tab layouts using FragmentStatePagerAdapter in each fragment there is a Recycler view and I want to use a search bar in the app bar to filter results.

First I did this using the onQueryTextChange listener in the onCreateOptionsMenu and it worked fine with a couple fragments but when I added the implementation to the other fragments (just ctrl+c and ctrl+v) it stopped working, no results even on fragments that used to work i then read here that it was better to stick the onQueryTextChange listener in onPrepareOptionsMenu in an attempt to avoid an issue with invalidateOptionsMenu but i decided to give that a try which also worked and then when i added the methods to all my other fragments yet again it fails but it does work on a handful of fragments, oddly these happen to be all attached to the same parent fragment but the code for the 4 parent fragments, and for calling them is identical.

A different way around this I've read is creating an interface and using that from my main activity by getting a reference to the current fragment but then id have to get the currently showing fragment from my viewpager which i dont know is possible can anyone weigh in on this for me,

Many thanks

And thanks for the great edit lol

EDIT Tried the interface approach to no avail although I'm still a beginner i do need to find the attached fragment and with a fragmentstateviewpager its just not possible without using hacks there must be some reason why it works in some and not in others and sometimes not at all

Edit 2

So im still fiddling with this and ive had almost no replies so lets flesh this out a little so i was adding the menu layout in the main activity like this

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.dashboard, menu);
    SearchView searchView =  
    (SearchView)menu.findItem(R.id.action_search).getActionView();
    SearchManager searchManager = (SearchManager)  
    getSystemService(SEARCH_SERVICE);

    searchView.setSearchableInfo(searchManager
    .getSearchableInfo(getComponentName()));

    return true;
}

and then adding a listener on to it from the fragment in onPrepareOptionsMenu like this

@Override
public void onPrepareOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.       
    //in this instance we wont as it will create a duplicate

    SearchView searchView = (SearchView)
    menu.findItem(R.id.action_search).getActionView();
    searchView.setOnQueryTextListener(null);
    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener(){
        @Override
        public boolean onQueryTextSubmit(String query) {
            return false;
        }

        @Override
        public boolean onQueryTextChange(String newText) {

            showResults(newText);

            return false;
        }

    });

    super.onPrepareOptionsMenu(menu);
}

and i have called setHasOptionsMenu(true) in onCreateView of the fragment, this works perfectly but i have about 20 fragments to cycle through and when I add this to the other fragments it fails it either does nothing or makes the view blank or in rare occasions works, but that for me says my code is okay and maybe its a lifecycle thing, ive tried calling it from the onCreateOptionsMenu of the fragments and achieved the same results, and ive tried not calling anything from the MainActivity in onCreateOptions except inflating the menu and allow the fragments to call the search activity using

SearchView searchView = (SearchView)menu.findItem
(R.id.action_search).getActionView();
SearchManager searchManager = (SearchManager) 
getActivity().getSystemService(getActivity().SEARCH_SERVICE);
searchView.setSearchableInfo(searchManager.getSearchableInfo
(getActivity().getComponentName())); 

which again works up to around 8 fragments but my 20 or so just causes it to fall on its sword, is there something im not doing that could help

Edit 3 Okay so by adding some, checking it, adding some more, checking it, it seems to be a problem with my parent fragments so I have main activity 4 fragments that each hold a fragment state pager that each holds 7 or so fragments if I add the methods for the query listener to the first 7 or so fragments in the first parent fragment they all work beautifully, if I then add the methods to the next set of 7 or so fragments in the second parent only the second parents child fragments work, if I then quit my app and open it again only the first parents fragments work, will continue investigating any help appreciated I'll post the code for my parent fragments here soon.

EDIT 4 Just going to add the code i use for the parent fragments and the fragmentstatepager from my main activity

so from my Main activity i set the fragmentstatepager like this

    private void setupViewPager(ViewPager viewPager) {
    ViewPagerAdapter adapter = new 
    ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFrag(new sentenceMakers(), "QUICKS");
    adapter.addFrag(new tabFragOne(), "NOUNS");
    adapter.addFrag(new tabFragTwo(), "VERBS");
    adapter.addFrag(new tabFragThree(), "OBJECTS");
    viewPager.setAdapter(adapter);

}

class ViewPagerAdapter extends FragmentStatePagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public void restoreState(Parcelable arg0, ClassLoader arg1) {
        //do nothing here! no call to super.restoreState(arg0, arg1);
    }
    @Override
    public int getCount() {
        return mFragmentList.size();
    }

    public void addFrag(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }
}

and my parent fragments look like this

public class sentenceMakers extends Fragment{

public ImageView imageView;
private ViewPager viewPager;
public static TabLayout tabLayout;

public sentenceMakers() {
    // Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View rootView = inflater.inflate
    (R.layout.tab_frag_one, container, false);
    // Inflate the layout for this fragment
    //setRetainInstance(true);
    viewPager = (ViewPager) rootView.findViewById(R.id.viewpager);
    setupViewPager(viewPager);
    tabLayout = (TabLayout) rootView.findViewById(R.id.tabs);
    tabLayout.setupWithViewPager(viewPager);
    setupTabIcons();

    return rootView;

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

}

private void setupTabIcons() {
    TextView tabZero2 = (TextView) 
    LayoutInflater.from(getActivity()).inflate(R.layout.custom_tab, null);
    tabZero2.setText("FAVOURITES");
    //tabOne.setGravity(View.TEXT_ALIGNMENT_CENTER);
    tabZero2.setCompoundDrawablesWithIntrinsicBounds(0, 
    R.drawable.ic_star_white_24dp, 0, 0);
    tabLayout.getTabAt(0).setCustomView(tabZero2);

    TextView tabZero = (TextView) 
    LayoutInflater.from(getActivity()).inflate(R.layout.custom_tab, null);
    tabZero.setText("FREQUENTS");
    //tabOne.setGravity(View.TEXT_ALIGNMENT_CENTER);
    tabZero.setCompoundDrawablesWithIntrinsicBounds(0, 
    R.drawable.ic_people_outline_white_24dp, 0, 0);
    tabLayout.getTabAt(1).setCustomView(tabZero);

    TextView tabOne = (TextView) 
    LayoutInflater.from(getActivity()).inflate(R.layout.custom_tab, null);
    tabOne.setText("PRONOUNS");
    //tabOne.setGravity(View.TEXT_ALIGNMENT_CENTER);
    tabOne.setCompoundDrawablesWithIntrinsicBounds(0, 
    R.drawable.ic_accessibility_white_24dp, 0, 0);
    tabLayout.getTabAt(2).setCustomView(tabOne);

    TextView tabTwo = (TextView) 
    LayoutInflater.from(getActivity()).inflate(R.layout.custom_tab, null);
    tabTwo.setText("CONJUCTIONS");
    tabTwo.setCompoundDrawablesWithIntrinsicBounds(0, 
    R.drawable.ic_add_white_24dp, 0, 0);
    tabLayout.getTabAt(3).setCustomView(tabTwo);

    TextView tabThree = (TextView) 
    LayoutInflater.from(getActivity()).inflate(R.layout.custom_tab, null);
    tabThree.setText("ADJECTIVES");
    tabThree.setCompoundDrawablesWithIntrinsicBounds(0, 
    R.drawable.ic_favorite_border_white_24dp, 0, 0);
    tabLayout.getTabAt(4).setCustomView(tabThree);

    tabLayout.getTabAt(0).getCustomView().setSelected(true);
}

private void setupViewPager(ViewPager viewPager) {
    ViewPagerAdapter adapter = new 
    ViewPagerAdapter(getChildFragmentManager());
    adapter.addFrag(new favouriteCards(), "FAVOURITES");
    adapter.addFrag(new predictedCards(), "FREQUENTS");
    adapter.addFrag(new pronouns(), "PRONOUNS");
    adapter.addFrag(new conjuctions(), "CONJUNCTIONS");
    adapter.addFrag(new Others(), "ADJECTIVES");
    viewPager.setAdapter(adapter);
}

class ViewPagerAdapter extends FragmentPagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

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

    public void addFrag(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }
}
}

still struggling with this and i cannot for the life of me work out why it would work in one lot of fragments untill i add it to the next set, its the same thing with the same code it must just be initializing the last fragments that are asking for it can anyone help me here? Thanks for any help

like image 540
Martin Seal Avatar asked Oct 08 '16 22:10

Martin Seal


2 Answers

Basically, there is a thing that you have to keep in mind when you use fragments: do not keep references to your fragments: just create them and add to your layout, or provide to your ViewPager but then use other methods to retrieve them, like FragmentManager.findFragmentBy...

That said, your code shoud be (let me simplify with just two fragments):

Main activity

private void setupViewPager(ViewPager viewPager) {
    ViewPagerAdapter adapter = new MyViewPagerAdapter(getSupportFragmentManager());
    viewPager.setAdapter(adapter);
}

class MyViewPagerAdapter extends FragmentStatePagerAdapter {

    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        switch(position) {
            case 0:
                return new sentenceMakers();
            case 1:
                return new tabFragOne();
            // add here fragments for any other position
        }
    }

    @Override
    public int getCount() {
        return 2;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        switch(position) {
            case 0:
                return "QUICKS";
            case 1:
                return "NOUNS";
            // add here titles for any other position
        }
    }
}

sentenceMakers

The code you will have to use in your sentenceMakers Fragment is very similar to the previous one, but you will have to use the child fragmentManager (as you did in your code) and again, do not keep your fragments in a List, let the system create them when needed.

like image 187
Mimmo Grottoli Avatar answered Nov 02 '22 05:11

Mimmo Grottoli


ok i fixed it so the problem is that the parent fragmentstatepager loads fragmentA + fragmentB into memory, then the child fragmentstatepagers load there own childfragmentA + childfragmentB so we now have 2 parents and 4 children, onPrepareOptionsMenu is called from the child fragments so the the first is loaded childfragmentA(child of fragmentA) and then childFragmentA(child of fragmentB) is loaded and steals the search view, my 'fix' for this is probably a little crude i'm self taught and i'm not very aware of how to manage memory or if its bad to keep hold of references etc, i digress, in the parent fragment i check which fragment is visible using

setMenuVisibility(final boolean visible)

and in there i set a public static boolean and check for it in my childfragments and it works flawlessly for me as mentioned this is probably a terrible thing to do hopefully someone here can give a better solution

check which parent is visible

public static boolean tabFragOneVisible;

@Override
public void setUserVisibleHint(boolean isVisible){
    super.setUserVisibleHint(isVisible);
    if(!isVisible){
        tabFragOneVisible = false;
    }else{
        tabFragOneVisible = true;
    }
}

check if parent is visible hook up search view

@Override
public void onPrepareOptionsMenu(Menu menu) {
    // need to check if fragment is visible
    if (tabFragOne.tabFragOneVisible) {
        SearchView searchView = (SearchView)
        menu.findItem(R.id.action_search).getActionView();
        SearchManager searchManager = (SearchManager) getActivity()
        .getSystemService(getActivity().SEARCH_SERVICE);
        searchView.setSearchableInfo(searchManager
        .getSearchableInfo(getActivity().getComponentName()));
        // searchView.setOnQueryTextListener(null);
        searchView.setOnQueryTextListener(new  
        SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {

                showResults(newText);
                return false;
            }

        });
    }
    else{

    }
    super.onPrepareOptionsMenu(menu);
}
like image 38
Martin Seal Avatar answered Nov 02 '22 05:11

Martin Seal