SearchView不会筛选TabLayout的每个子Tab

在这里,我有一个包含SearchViewActivitytoolbar 。 那个活动有多个片段。 其中一个主要的片段里面还有10个片段。 所有10个片段都在列表视图中显示数据。 现在我试图通过MainActivitySearchView过滤所有的片段列表。 但它从不过滤每个片段的列表。 现在我告诉你我是如何实现这一切的。

MainActivity.java

 public class MainActivity extends AppCompatActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); final SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search)); SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); changeSearchViewTextColor(searchView); return true; } } 

Fragment.java

 public class CurrencyFragment2 extends android.support.v4.app.Fragment implements SearchView.OnQueryTextListener { @Override public void setMenuVisibility(boolean menuVisible) { super.setMenuVisibility(menuVisible); if (menuVisible && getActivity() != null) { SharedPreferences pref = getActivity().getPreferences(0); int id = pref.getInt("viewpager_id", 0); if (id == 2) setHasOptionsMenu(true); } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.main, menu); // removed to not double the menu items MenuItem item = menu.findItem(R.id.action_search); SearchView sv = new SearchView(((MainActivity) getActivity()).getSupportActionBar().getThemedContext()); changeSearchViewTextColor(sv); MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); MenuItemCompat.setActionView(item, sv); sv.setOnQueryTextListener(this); sv.setIconifiedByDefault(false); super.onCreateOptionsMenu(menu, inflater); } private void changeSearchViewTextColor(View view) { if (view != null) { if (view instanceof TextView) { ((TextView) view).setTextColor(Color.WHITE); ((TextView) view).setHintTextColor(Color.WHITE); ((TextView) view).setCursorVisible(true); return; } else if (view instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) view; for (int i = 0; i < viewGroup.getChildCount(); i++) { changeSearchViewTextColor(viewGroup.getChildAt(i)); } } } } @Override public boolean onQueryTextSubmit(String query) { return true; } @Override public boolean onQueryTextChange(String newText) { if (adapter != null) { adapter.filter2(newText); } return true; } 

Filter类中的Filter方法。

 // Filter Class public void filter2(String charText) { charText = charText.toLowerCase(Locale.getDefault()); items.clear(); if (charText.length() == 0) { items.addAll(arraylist); } else { for (EquityDetails wp : arraylist) { if (wp.getExpert_title().toLowerCase(Locale.getDefault()).contains(charText)) { items.add(wp); } } } notifyDataSetChanged(); } 

Solutions Collecting From Web of "SearchView不会筛选TabLayout的每个子Tab"

您可以使用Observable / Observer模式pipe理嵌套列表上的filter,这将更新来自一个Observable父级的每个嵌套列表。 我修好了所有的麻烦 ,现在运作良好,以达到正确的行为。

因此,我做了什么来实现它:

  1. Activity使用一个父级SearchView
  2. (可选)在嵌套列表Adapter创build一个Filter类( android.widget.Filter
  3. 然后,使用ActivityObservable / Observer模式嵌套Fragment

背景:当我尝试你的代码时,我有三个问题:

  • 我无法使用ActionBar进行search: onQueryTextChange似乎永远不会在Fragment调用。 当我点击search图标时,在我看来, SearchView (edittext,icon等)没有附加到search小部件(但附加到活动的小部件)。
  • 我无法运行自定义方法filter2 :我的意思是,当我解决了以前的观点,此方法不起作用。 实际上,我必须使用由Filter扩展的自定义类以及它的两个方法: performFilteringpublishResults 。 没有它,当我在search栏中点击一个字时,我得到了一个空白的屏幕。 不过,这可能只是我的代码,也许filter2()完美地为你工作…
  • 我不能在片段之间进行持久search:对于每个子片段,都会创build一个新的SearchView 。 在我看来,你反复调用这一行SearchView sv = new SearchView(...); 在嵌套的片段。 所以每次我切换到下一个片段时,扩展的search视图将删除其先前的文本值。

无论如何,在一些研究之后,我发现了关于执行search片段的 这个答案 。 与您的代码几乎相同,除了在父活动和片段中“复制”选项菜单代码。 你不应该这样做 – 我认为这是我以前的第一个问题的原因。
此外,答案链接中使用的模式(一个片段中的一个search)可能不适合您(一个search多个片段)。 您应该在父Activity为所有嵌套的Fragment调用一个SearchView


解决scheme:这是我pipe理它的方式:

#1使用父级SearchView

它将避免重复的function,并让父母的活动监督所有的孩子。 此外,这将避免您的菜单中的复制图标。
这是主要的父Activity类:

 public class ActivityName extends AppCompatActivity implements SearchView.OnQueryTextListener { @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); MenuItem item = menu.findItem(R.id.action_search); SearchView searchview = new SearchView(this); SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE); searchview.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); ... MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); MenuItemCompat.setActionView(item, searchview); searchview.setOnQueryTextListener(this); searchview.setIconifiedByDefault(false); return super.onCreateOptionsMenu(menu); } private void changeSearchViewTextColor(View view) { ... } @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { // update the observer here (aka nested fragments) return true; } } 

#2(可选)创build一个Filter部件:

就像我以前说过的,我无法使用filter2() ,所以我创build了一个Filter类作为Web上的任何示例。
它在嵌套片段的适配器中看起来很快,如下所示:

 private ArrayList<String> originalList; // I used String objects in my tests private ArrayList<String> filteredList; private ListFilter filter = new ListFilter(); @Override public int getCount() { return filteredList.size(); } public Filter getFilter() { return filter; } private class ListFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); if (constraint != null && constraint.length() > 0) { constraint = constraint.toString().toLowerCase(); final List<String> list = originalList; int count = list.size(); final ArrayList<String> nlist = new ArrayList<>(count); String filterableString; for (int i = 0; i < count; i++) { filterableString = list.get(i); if (filterableString.toLowerCase().contains(constraint)) { nlist.add(filterableString); } } results.values = nlist; results.count = nlist.size(); } else { synchronized(this) { results.values = originalList; results.count = originalList.size(); } } return results; } @SuppressWarnings("unchecked") @Override protected void publishResults(CharSequence constraint, FilterResults results) { if (results.count == 0) { notifyDataSetInvalidated(); return; } filteredList = (ArrayList<String>) results.values; notifyDataSetChanged(); } } 

#3使用Observable / Observer模式:

活动 – 用searchview – 是Observable对象,嵌套的片段是Observer ( 观察者模式 )。 基本上,当onQueryTextChange被调用的时候,它会触发onQueryTextChange观察者的update()方法。
以下是父Activity的声明:

 private static ActivityName instance; private FilterManager filterManager; @Override protected void onCreate(Bundle savedInstanceState) { ... instance = this; filterManager = new FilterManager(); } public static FilterManager getFilterManager() { return instance.filterManager; // return the observable class } @Override public boolean onQueryTextChange(String newText) { filterManager.setQuery(newText); // update the observable value return true; } 

这是可以监听和“传递”更新数据的Observable类:

 public class FilterManager extends Observable { private String query; public void setQuery(String query) { this.query = query; setChanged(); notifyObservers(); } public String getQuery() { return query; } } 

为了添加观察器片段来侦听searchview值,我们在FragmentStatePagerAdapter中初始化它们的时候这样做。
所以在父片段中,我通过传递FilterManager创build内容标签:

 private ViewPager pager; private ViewPagerAdapter pagerAdapter; @Override public View onCreateView(...) { ... pagerAdapter = new ViewPagerAdapter( getActivity(), // pass the context, getChildFragmentManager(), // the fragment manager MainActivity.getFilterManager() // and the filter manager ); } 

适配器会将观察者添加到父级observable,并在子碎片被销毁时将其删除。
这是父代片段的ViewPagerAdapter

 public class ViewPagerAdapter extends FragmentStatePagerAdapter { private Context context; private FilterManager filterManager; public ViewPagerAdapter(FragmentManager fm) { super(fm); } public ViewPagerAdapter(Context context, FragmentManager fm, FilterManager filterManager) { super(fm); this.context = context; this.filterManager = filterManager; } @Override public Fragment getItem(int i) { NestedFragment fragment = new NestedFragment(); // see (*) filterManager.addObserver(fragment); // add the observer return fragment; } @Override public int getCount() { return 10; } @Override public void destroyItem(ViewGroup container, int position, Object object) { NestedFragment fragment = (NestedFragment) object; // see (*) filterManager.deleteObserver(fragment); // remove the observer super.destroyItem(container, position, object); } } 

最后,当使用filterManager.setQuery()调用onQueryTextChange() ,将会在实现Observer update()方法中的嵌套片段中接收到。
这是用ListView过滤的嵌套片段:

 public class NestedFragment extends Fragment implements Observer { private boolean listUpdated = false; // init the update checking value ... // setup the listview and the list adapter ... // use onResume to filter the list if it's not already done @Override public void onResume() { super.onResume(); // get the filter value final String query = MainActivity.getFilterManager().getQuery(); if (listview != null && adapter != null && query != null && !listUpdated) { // update the list with filter value listview.post(new Runnable() { @Override public void run() { listUpdated = true; // set the update checking value adapter.getFilter().filter(query); } }); } } ... // automatically triggered when setChanged() and notifyObservers() are called public void update(Observable obs, Object obj) { if (obs instanceof FilterManager) { String result = ((FilterManager) obs).getQuery(); // retrieve the search value if (listAdapter != null) { listUpdated = true; // set the update checking value listAdapter.getFilter().filter(result); // filter the list (with #2) } } } } 

#4。结论:

这效果很好,所有嵌套的片段中的列表按预期更新一个search视图。 不过,在我上面的代码中有一个不方便的地方,你应该知道:

  • (见下面的改进) 我不能调用Fragment通用对象,并将其添加为观察者。 的确,我必须使用特定的片段类(这里是NestedFragment )进行投射和初始化。 可能有一个简单的解决scheme,但我现在没有find它。

尽pipe如此,我得到了正确的行为,而且我认为,通过在活动中保留一个search小部件,这可能是一个很好的模式。 所以用这个解决scheme,你可以得到一个线索,一个正确的方向,实现你想要的。 我希望你会喜欢。


#5改进(编辑):

  • (请参阅*)您可以通过在所有嵌套片段上保留全局Fragment类扩展来添加观察器。 这我如何实例化我的片段到ViewPager

     @Override public Fragment getItem(int index) { Fragment frag = null; switch (index) { case 0: frag = new FirstNestedFragment(); break; case 1: frag = new SecondFragment(); break; ... } return frag; } @Override public Object instantiateItem(ViewGroup container, int position) { ObserverFragment fragment = (ObserverFragment) super.instantiateItem(container, position); filterManager.addObserver(fragment); // add the observer return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { filterManager.deleteObserver((ObserverFragment) object); // delete the observer super.destroyItem(container, position, object); } 

    通过创buildObserverFragment类如下所示:

     public class ObserverFragment extends Fragment implements Observer { public void update(Observable obs, Object obj) { /* do nothing here */ } } 

    然后,通过扩展和重写嵌套片段中的update()

     public class FirstNestedFragment extends ObserverFragment { @Override public void update(Observable obs, Object obj) { } } 

只是为了更好地理解这个问题。 1.您在操作栏中有一个search视图2.您有多个碎片(来自您的图像Advisory / TopAdvisors …)3.在每个Tab中有多个碎片(例如Equity …等)4 。您想要片段中的所有列表视图根据search内容过滤其数据

对??

在你目前的实施状态是什么? 当前显示的列表视图是否被过滤?

甚至不是工作? 然后你需要检查notifydatasetChanged是否正确传播到适配器。 这意味着它应该在UIThread本身。

对于所有片段的更新(意思是在inputsearch文本时不在屏幕上),您需要考虑片段生命周期,并在片段的onResume中包含代码,以确保列表在被用于初始化适配器之前被过滤。 这是你已经键入search文本,现在正在标签/片段之间移动的场景。 所以碎片会在屏幕上出现,因此需要onResume。

更新:包括检查文本search框,如果它有一些调用onResume中的adapter.filter2(),因为这基本上是什么过滤你的列表。 。

  • 问题是 – 当你启动一个片段时,它会“接pipe”活动的searchView,不pipe你input什么内容,这个片段启动后,调用这个片段的onQueryTextChange监听器。因此,search不会发生在其他片段中。
  • 您希望您的片段在启动时检查上次search查询(由其他片段发布),并针对该查询执行search。 此外,search查询中的任何更改也必须发布到活动的search视图,以便其他片段在启动时可以读取它。

我假设你的viewpager / tabs是以一种方式实现的,只要你切换可见的碎片,它们就会被销毁。

进行以下更改(阅读评论)

 public class MainActivity extends AppCompatActivity { final SearchView searchView; //make searchView a class variable/field @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search)); SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); changeSearchViewTextColor(searchView); return true; } } 

现在,在你所有的片段中这样做 –

  @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.main, menu); // removed to not double the menu items MenuItem item = menu.findItem(R.id.action_search); SearchView sv = new SearchView(((MainActivity) getActivity()).getSupportActionBar().getThemedContext()); changeSearchViewTextColor(sv); MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); MenuItemCompat.setActionView(item, sv); sv.setOnQueryTextListener(this); sv.setIconifiedByDefault(false); sv.setQuery(((MainActivity)getActivity()).searchView.getQuery()); // set the main activity's search view query in the fragment's search view super.onCreateOptionsMenu(menu, inflater); } 

此外,在您的SearchView.OnQueryTextListener的片段,做到这一点

 SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { search(query); // this is your usual adapter filtering stuff, which you are already doing ((MainActivity)getActivity()).searchView.setQuery(query);//sync activity's searchview query with fragment's searchview query, so other fragments can read from the activity's search query when they launch. return false; } @Override public boolean onQueryTextChange(String newText) { search(newText);// this is your usual adapter filtering stuff, which you are already doing ((MainActivity)getActivity()).searchView.setQuery(query);//sync activity's searchview query with fragment's searchview query return false; } }); 

我希望你能得到大致的想法

  • 您在每个片段和活动中都有不同的search视图实例
  • 您需要在每个search查询之间同步search查询,使每个人都知道其他实例有什么。

我不推荐这个devise,我宁愿做的是

  • 只需使用活动的search视图和活动search视图的onQueryTextChange监听器即可。
  • 我会通知活动中的所有从onQueryTextChange活动的片段(片段将注册和注销onQueryTextChange监听器与他们的各自的片段onResume&onPause活动)。 [注意 – 这种devise模式被称为观察者模式]