草庐IT

java - 解决了: SearchView doesn't filter in each child Tab of TabLayout

coder 2023-05-18 原文

在这里,我在toolbar中包含一个Activity,其中包含SearchView。该 Activity 具有多个片段。其中一个主要片段本身内部又有10个片段。所有10个片段都在列表 View 中显示数据。现在,我尝试按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 parent更新每个嵌套列表。我修复了all troubles,现在可以很好地实现正确的行为。

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

  • SearchView中使用一个父Activity
  • (可选)在嵌套列表中创建一个Filter类(android.widget.Filter)Adapter
  • 然后,将Observable/Observer模式用于带有Fragment的嵌套Activity


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

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

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

    #1使用父SearchView:

    它将避免重复的功能,并让父级 Activity 监督其所有子级。此外,这将避免菜单中出现重复图标。
    这是主要的父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的 Activity 是Observable对象,嵌套的片段是Observer s(see Observer pattern)。基本上,当调用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来创建内容选项卡:
    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
        );
    }
    

    适配器将add the observer映射到可观察的父对象,并在销毁子片段时将其删除。
    这是父片段的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()调用 Activity 中的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)进行强制转换和初始化。可能有一个简单的解决方案,但我暂时没有找到它。

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

    #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) { }
    }    
    
  • 关于java - 解决了: SearchView doesn't filter in each child Tab of TabLayout,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33516621/

    有关java - 解决了: SearchView doesn't filter in each child Tab of TabLayout的更多相关文章

    1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

      我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

    2. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    4. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

      在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

    5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

      我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

    6. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

      我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

    7. ruby-on-rails - 新 Rails 项目 : 'bundle install' can't install rails in gemfile - 2

      我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="

    8. java - 等价于 Java 中的 Ruby Hash - 2

      我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

    9. ruby-on-rails - rspec should have_select ('cars' , :options => ['volvo' , 'saab' ] 不工作 - 2

      关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request

    10. ruby-on-rails - Rails 中的 NoMethodError::MailersController#preview undefined method `activation_token=' for nil:NilClass - 2

      似乎无法为此找到有效的答案。我正在阅读Rails教程的第10章第10.1.2节,但似乎无法使邮件程序预览正常工作。我发现处理错误的所有答案都与教程的不同部分相关,我假设我犯的错误正盯着我的脸。我已经完成并将教程中的代码复制/粘贴到相关文件中,但到目前为止,我还看不出我输入的内容与教程中的内容有什么区别。到目前为止,建议是在函数定义中添加或删除参数user,但这并没有解决问题。触发错误的url是http://localhost:3000/rails/mailers/user_mailer/account_activation.http://localhost:3000/rails/mai

    随机推荐