当前位置: 首页 > 面试题库 >

解决:SearchView不会在TabLayout的每个子Tab中进行过滤

施驰
2023-03-14
问题内容

在这里,我有一个toolbarActivity其中包含SearchView。该活动具有多个片段。其中一个主要片段本身内部还有10个片段。所有10个片段都在列表视图中显示数据。现在,我试图通过筛选片段的所有列表SearchViewMainActivity。但是它永远不会过滤每个片段的列表。现在,我向您展示如何实现这一切。

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

Adapter类中的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();
}

问题答案:

您可以 使用Observable / Observer模式
来管理嵌套列表上的筛选器,这将从一个Observable父对象中更新每个嵌套列表。我解决了所有麻烦,现在可以很好地实现正确的行为。

因此,这是我要做的事情:

  1. 使用一个家长SearchViewActivity
  2. (可选)在嵌套列表中创建一个Filter类( android.widget.FilterAdapter
  3. 然后,使用一个Observable/ Observer图案嵌套FragmentActivity

背景: 在尝试您的代码时,我遇到了三个问题:

  • 我无法使用ActionBar进行搜索:onQueryTextChange似乎从未在Fragments中调用过。当我点击搜索图标时,在我看来,SearchView(编辑文本,图标等)未附带搜索小部件(而是附加到活动的小部件)。
  • 我无法运行自定义方法filter2:我的意思是,当我解决上一点时,该方法不起作用。确实,我必须使用通过Filter和扩展其两种方法的自定义类:performFilteringpublishResults。没有它,当我在搜索栏中点击一个单词时,我将得到一个空白屏幕。但是,这可能只是我的代码,可能filter2()对您来说效果很好…
  • 我无法在片段之间进行持久搜索:为每个子片段SearchView创建一个新片段。在我看来,您反复SearchView sv = new SearchView(...);在嵌套片段中调用此行。因此,每次我切换到下一个片段时,展开的searchview都会删除其先前的文本值。

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

解决方案: 这是我的管理方式:

#1使用父母SearchView

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

就像我之前说过的那样,我无法使其与一起使用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使用Observable/ Observer模式:

活动(带有searchview)是Observable对象,嵌套片段是Observers(请参阅Observer模式)。基本上,当onQueryTextChange将调用时,它将update()在现有的观察者中触发该方法。
这是parent中的声明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

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

最后,使用filterManager.setQuery()in调用inactivity时onQueryTextChange(),这将在update()实现的方法中的嵌套片段中接收Observer
这是带有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。结论:

效果很好,所有嵌套片段中的列表仅通过一个searchview即可按预期进行更新。但是,在我上面的代码中有一个不便之处,您应注意:

  • (请参见下面的改进) 我无法调用 Fragment常规对象并将其添加为观察者。实际上,我必须使用特定的片段类(在此处 NestedFragment)进行强制转换和初始化。可能有一个简单的解决方案,但我暂时找不到。

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

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

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


 类似资料:
  • 搜索视图显示在每个列表视图行:检查附加的scrrenshot。 我怎样才能让它只在顶端出现一次呢。 在此输入图像描述

  • 问题内容: 我有一个带有自定义标签的组件。该组件包含(显示标签标题)和(关闭按钮)。当我在停止接收鼠标事件的站点中更改文本时,如果直接单击标签,则无法再选择该选项卡,而如果单击标签周围,则可以选择该选项卡。有任何想法吗? 一段代码: 问题答案: 问题与我进行更多挖掘之后在此处发布的问题有关:setToolTipText消耗鼠标事件的解决方法?

  • 当我运行这段代码时,两个不同片段中的toast显示在一个选项卡上。当我滑动到下一个选项卡时,什么也不显示。 这是我的主要选项卡活动: 这是适配器类: 这是我的第一个片段: 如有任何建议,我们将不胜感激。我被困在这了。

  • 我一直在尝试在工作中实现Autosys作业计划的树表示。由于每个作业(流程)可以有一个或多个依赖作业,因此我决定使用n元树实现,以便映射流程。我使用java集合来实现同样的目标。 Q1(已解决):目前的问题是显示功能被挂在一个无限循环中。我尝试寻找现有的线程,但无法遇到使用迭代器进行遍历的示例。 Q2:(打开)显示功能以后序方式遍历树。我希望以一种级别顺序的方式遍历它,其中节点以级别为基础进行打印

  • 我正在尝试隐藏ActionBar中的子菜单和SearchView。我在用ActionBarsherlock。 我想通过点击按钮隐藏子菜单和SearchView