SearchView 不会在 TabLayout 的每个子选项卡中进行筛选

在这里,我有一个包含.该活动有多个片段。其中一个主要片段内部还有10个片段。所有 10 个片段都在列表视图中显示数据。现在我正在尝试按 过滤所有片段列表。但它从不过滤每个片段的列表。现在我向您展示我是如何实现这一切的。toolbarActivitySearchViewSearchViewMainActivity

主要活动.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;
}
}

片段.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 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();
}

答案 1

您可以使用可观察/观察者模式管理嵌套列表上的筛选器,这将更新来自一个可观察父级的每个嵌套列表。我解决了所有问题,现在工作得很好,以实现正确的行为。

因此,以下是我为实现它所做的:

  1. 在 中使用一个父项SearchViewActivity
  2. (可选)在嵌套列表中创建一个类(android.widget.FilterFilterAdapter
  3. 然后,使用 / 模式嵌套ObservableObserverFragmentActivity

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

  • 我无法使用ActionBar进行搜索:似乎从未在s中调用过。当我点击搜索图标时,在我看来(编辑文本,图标等)没有与搜索小部件一起附加(而是附加到活动的小部件)。onQueryTextChangeFragmentSearchView
  • 我无法运行自定义方法:我的意思是,当我解决上一点时,此方法不起作用。实际上,我必须使用自定义类扩展及其两种方法:和。没有它,当我在搜索栏中点击一个单词时,我得到了一个空白屏幕。但是,这可能只是我的代码,也许非常适合您...filter2FilterperformFilteringpublishResultsfilter2()
  • 我无法在片段之间进行持久搜索:为每个子片段创建一个新片段。在我看来,您反复在嵌套片段中调用此行。因此,每次我切换到下一个片段时,扩展的搜索视图都会删除其以前的文本值。SearchViewSearchView sv = new SearchView(...);

无论如何,经过一些研究,我在SO上找到了关于实现搜索片段的答案。几乎与您的代码相同,只是您在父活动和片段中“复制”了选项菜单代码。你不应该这样做 - 我认为这是我在前几点中的第一个问题的原因。
此外,答案链接中使用的模式(一个片段中的一个搜索)可能不适合您的模式(一个搜索多个片段)。您应该在父级中为所有嵌套的 调用一个。SearchViewActivityFragment


溶液:这就是我管理它的方式:

#1 使用父搜索视图

它将避免重复的功能,并让父活动监督其所有子活动。此外,这将避免菜单中的重复图标。
这是主要的父类: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(可选)创建过滤器微件:

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

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 使用可观察/观察者模式:

活动 ( 使用搜索视图 - 是对象,嵌套片段是 s(请参见观察者模式)。基本上,当调用时,它将在现有的观察者中触发该方法。
下面是父级中的声明:ObservableObserveronQueryTextChangeupdate()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;
    }
}

为了添加观察者片段来侦听搜索视图值,我在 .
因此,在父片段中,我通过传递以下内容来创建内容选项卡:FragmentStatePagerAdapterFilterManager

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
    );
}

适配器会将观察者添加到父可观察对象,并在子片段被销毁时将其删除。
下面是父片段: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);
    }
}

最后,当在活动中调用时,这将在正在实现的方法中的嵌套片段中接收。
这是具有 to 筛选器的嵌套片段:filterManager.setQuery()onQueryTextChange()update()ObserverListView

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 结论:

这很有效,所有嵌套片段中的列表都按预期仅由一个搜索视图更新。但是,在我的上述代码中有一个不方便的地方,您应该注意:

  • (请参阅下面的改进)我无法将 Fragment 称为一般对象并将其添加到观察者中。实际上,我必须使用特定的片段类(此处为NestedFragment)进行投射和初始化;可能有一个简单的解决方案,但我现在还没有找到它。

尽管如此,我还是得到了正确的行为,并且 - 我认为 - 通过在活动顶部保留一个搜索小部件,这可能是一个很好的模式。因此,通过这个解决方案,您可以获得线索,正确的方向,以实现您想要的目标。我希望你会喜欢。


#5 改进(编辑):

  • (见 *)您可以通过在所有嵌套片段上保留全局类扩展来添加观察器。这就是我如何将我的片段实例化为:FragmentViewPager

    @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);
    }
    

    按如下方式创建类:ObserverFragment

    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) { }
    }    
    

答案 2

只是为了更好地理解这个问题。1. 您在操作栏 2 中有一个搜索视图。您有多个片段(来自您的图像咨询/顶级顾问...)3.在其中每个选项卡中,每个选项卡都有多个片段(例如净值...等) 4.您希望片段中的所有列表视图根据搜索内容筛选其数据

右??

在您当前的实施中,状态如何?当前显示的列表视图是否正在被筛选?

或者甚至这不起作用?然后,您需要检查通知数据集更改是否正确传播到适配器。这意味着它应该在UIThread本身上。

对于所有片段的更新,这意味着一旦在键入搜索文本时不在屏幕上,您需要考虑片段生命周期并在片段的 onResume 中包含代码,以确保在使用列表初始化适配器之前对其进行筛选。这适用于您已经键入搜索文本并且现在正在选项卡/片段之间移动的情况。因此,片段会进入和离开屏幕,因此需要onResume。

更新:包括选中文本的搜索框,以及它是否在 onResume 中有调用 adapter.filter2() 的内容,因为这基本上是筛选列表的原因。.


推荐