如何从所有片段中使用工具栏中的 SearchView

Posted

技术标签:

【中文标题】如何从所有片段中使用工具栏中的 SearchView【英文标题】:How to use SearchView in Toolbar from all fragments 【发布时间】:2020-07-26 11:35:40 【问题描述】:

我的应用程序有一个导航抽屉以及一个带有 2 个菜单项的工具栏。其中一项是 SearchView...

public boolean onCreateOptionsMenu(Menu menu)

方法放置在 MainActivity 中,并且从我的应用程序的所有片段中显然都可以看到 Toolbar。 为了让 SearchView 可以从所有片段中使用,我一直在使用这个可爱的方法:

public void onPrepareOptionsMenu(@NonNull Menu menu)

该方法基本上在我的应用程序的每个片段中,所有搜索逻辑在所有片段中重复,看起来像这样:

public class ChampagneFragment extends Fragment 

private View champagneView;
private RecyclerView champagneList;
private ItemsAdapter itemsAdapter;
private List<NewModel> list;

private DatabaseOpenHelper databaseOpenHelper;


public ChampagneFragment() 
    // Required empty public constructor



@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) 
    setHasOptionsMenu(true);
    // Inflate the layout for this fragment
    champagneView = inflater.inflate(R.layout.fragment_champagne, container, false);

    champagneList = champagneView.findViewById(R.id.champagneRVList);
    champagneList.setLayoutManager(new LinearLayoutManager(getContext()));

    return champagneView;



@Override
public void onStart() 
    super.onStart();

    databaseOpenHelper = new DatabaseOpenHelper(getContext());

    //Check exists database
    File database = getContext().getDatabasePath(DatabaseOpenHelper.DBNAME);
    if(false == database.exists()) 
        databaseOpenHelper.getReadableDatabase();
        //Copy db
        if(copyDatabase(getContext())) 
            Toast.makeText(getContext(), "Copy database success", Toast.LENGTH_SHORT).show();
         else 
            Toast.makeText(getContext(), "Copy data error", Toast.LENGTH_SHORT).show();
            return;
        

    
    //Get product list in db when db exists
    list = databaseOpenHelper.getChampagne();
    //Init adapter
    itemsAdapter = new ItemsAdapter(getContext(), list);
    //Set adapter for listview
    champagneList.setAdapter(itemsAdapter);



// copy SQLite data
private boolean copyDatabase(Context context) 
    try 

        InputStream inputStream = context.getAssets().open(DatabaseOpenHelper.DBNAME);
        String outFileName = DatabaseOpenHelper.DBLOCATION + DatabaseOpenHelper.DBNAME;
        OutputStream outputStream = new FileOutputStream(outFileName);
        byte[]buff = new byte[1024];
        int length = 0;
        while ((length = inputStream.read(buff)) > 0) 
            outputStream.write(buff, 0, length);
        
        outputStream.flush();
        outputStream.close();
        Log.w("MainActivity","DB copied");
        return true;
    catch (Exception e) 
        e.printStackTrace();
        return false;
    


 //======================SEARCHING FUNCTIONALITY=========================

//fetching SearchView    
@Override
public void onPrepareOptionsMenu(@NonNull Menu menu) 
    super.onPrepareOptionsMenu(menu);

    MenuItem mSearchMenuItem = menu.findItem(R.id.action_search);
    SearchView searchView = (SearchView) mSearchMenuItem.getActionView();

    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() 
        @Override
        public boolean onQueryTextSubmit(String query) 
            return false;
        

        @Override
        public boolean onQueryTextChange(String newText) 

            // filtering data
            if (newText != null && TextUtils.getTrimmedLength(newText) > 0) 
                newText = newText.toLowerCase();
                List<NewModel> myList = new ArrayList<>();
                for (NewModel newModel : list) 

                    String title = newModel.getTitle().toLowerCase();
                    String description = newModel.getDescription().toLowerCase();
                    if (title.contains(newText) || description.contains(newText)) 

                        myList.add(newModel);
                        onSerach();
                    

                

                itemsAdapter.setFilter(myList);
            
            else 

                list = databaseOpenHelper.getChampagne();
                itemsAdapter = new ItemsAdapter(getContext(), list);
                champagneList.setAdapter(itemsAdapter);
                itemsAdapter.notifyDataSetChanged();
            
            return true;
        
    );


//getting data from search table in SQLite
public void onSerach()

    databaseOpenHelper = new DatabaseOpenHelper(getContext());

    //Check exists database
    File database = getContext().getDatabasePath(DatabaseOpenHelper.DBNAME);
    if(false == database.exists()) 
        databaseOpenHelper.getReadableDatabase();
        //Copy db
        if(copyDatabase(getContext())) 
            Toast.makeText(getContext(), "Copy database success", Toast.LENGTH_SHORT).show();
         else 
            Toast.makeText(getContext(), "Copy data error", Toast.LENGTH_SHORT).show();
            return;
        

    
    //Get product list in db when db exists
    list = databaseOpenHelper.getSearch();
    //Init adapter
    itemsAdapter = new ItemsAdapter(getContext(), list);
    //Set adapter for listview
    champagneList.setAdapter(itemsAdapter);

首先我尝试过滤数据并将其显示在 MainActivty 的新列表中,但后来我无法使用片段中的 SearchView...所以过了一会儿,我决定这样做。但不知何故,对我来说,这似乎是一种不好的做法……你怎么看? 我怎么能以不同的方式做到这一点,这样我就没有很多重复的代码行并提高效率?

【问题讨论】:

几乎每次你在需要使用继承的类中有重复代码时 巴曼,谢谢!让我阅读有关继承的文档...老实说,对此一无所知!一开始我还是个初学者:) 但无论如何有用的信息,所以我知道要寻找什么! @Bahman,我正在阅读文档,我绝对理解使用继承的目的。但是,我仍然无法从基本片段中继承一些方法......在这种情况下,我想继承 onPrepareOptionsMenu() 和 onSearch() 方法......你有什么说明吗? @Bahman,非常感谢!你今天教会了我一些新东西!我已尽我所能尽可能有效地做到这一点。有时间就来看看吧! 【参考方案1】:
/***
 * Base class ChampagneBaseFragment extends Fragment
***/   
public abstract class ChampagneBaseFragment extends Fragment

  // here override any Fragment method that has same code in all childs
  // here put variables used by methods of this class
  // if you want to read or write to this variables in child class 
  // then you have to make them public or protected
  public void onSerach()
   // your onSearch code that is same in childs
  

  @Override
  public void onPrepareOptionsMenu(@NonNull Menu menu) 
   // your onPrepareOptionsMenu code that is same in childs
  
  // other common functions...



/***
 * Child class ChampagneFragment extends ChampagneBaseFragment 
***/   
public class ChampagneFragment extends ChampagneBaseFragment // extends ChampagneBaseFragment 

// here you are using methods of ChampagneBaseFragment 
// you can use them without changing them
// or you can override methods of ChampagneBaseFragment and call super to use them
// then add your code below super call



【讨论】:

【参考方案2】:

实现后,这是我继承BasaFragment类的Fragment类

public class ChampagneFragment extends BaseFragment 

private View champagneView;
private RecyclerView champagneList;
private ItemsAdapter itemsAdapter;
private List<NewModel> list;

private DatabaseOpenHelper databaseOpenHelper;


public ChampagneFragment() 
    // Required empty public constructor



@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) 
    setHasOptionsMenu(true);
    // Inflate the layout for this fragment
    champagneView = inflater.inflate(R.layout.fragment_champagne, container, false);

    champagneList = champagneView.findViewById(R.id.champagneRVList);
    champagneList.setLayoutManager(new LinearLayoutManager(getContext()));
    databaseOpenHelper = new DatabaseOpenHelper(getContext());

    //Get product list in db when db exists
    list = databaseOpenHelper.getChampagne();
    //Init adapter
    itemsAdapter = new ItemsAdapter(getContext(), list);
    //Set adapter for listview
    champagneList.setAdapter(itemsAdapter);


    return champagneView;



@Override
public void onPrepareOptionsMenu(@NonNull Menu menu) 
    super.onPrepareOptionsMenu(menu);

    MenuItem mSearchMenuItem = menu.findItem(R.id.action_search);
    SearchView searchView = (SearchView) mSearchMenuItem.getActionView();

    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() 
        @Override
        public boolean onQueryTextSubmit(String query) 
            return false;
        

        @Override
        public boolean onQueryTextChange(String newText) 

            Toast.makeText(getContext(), "Hello", Toast.LENGTH_SHORT).show();
            if (newText != null && TextUtils.getTrimmedLength(newText) > 0) 
                newText = newText.toLowerCase();
                List<NewModel> myList = new ArrayList<>();
                for (NewModel newModel : list) 

                    String title = newModel.getTitle().toLowerCase();
                    String description = newModel.getDescription().toLowerCase();
                    if (title.contains(newText) || description.contains(newText)) 

                        myList.add(newModel);
                        onSerach();
                    

                

                itemsAdapter.setFilter(myList);
            
            else 

                list = databaseOpenHelper.getChampagne();
                itemsAdapter = new ItemsAdapter(getContext(), list);
                champagneList.setAdapter(itemsAdapter);
                itemsAdapter.notifyDataSetChanged();
            
            return true;
        
    );



@Override
public void onSerach() 
    super.onSerach();

    list = databaseOpenHelper.getSearch();
    itemsAdapter = new ItemsAdapter(getContext(), list);
    champagneList.setAdapter(itemsAdapter);

还有继承自的类,BaseFragment 类

public abstract class BaseFragment extends Fragment 


public DatabaseOpenHelper databaseOpenHelper;
public RecyclerView recyclerView;


@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) 
    View view = inflater.inflate(R.layout.base_fragment, container, false);

    databaseOpenHelper = new DatabaseOpenHelper(getContext());

    //Check exists database
    File database = getContext().getDatabasePath(DatabaseOpenHelper.DBNAME);
    if(false == database.exists()) 
        databaseOpenHelper.getReadableDatabase();
        //Copy db
        if(copyDatabase(getContext())) 
            Toast.makeText(getContext(), "Copy database succes", Toast.LENGTH_SHORT).show();
         else 
            Toast.makeText(getContext(), "Copy data error", Toast.LENGTH_SHORT).show();
            //return;
        

    


    recyclerView = view.findViewById(R.id.baseFragmentRCV);
    recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

    return view;



public boolean copyDatabase(Context context) 
    try 

        InputStream inputStream = context.getAssets().open(DatabaseOpenHelper.DBNAME);
        String outFileName = DatabaseOpenHelper.DBLOCATION + DatabaseOpenHelper.DBNAME;
        OutputStream outputStream = new FileOutputStream(outFileName);
        byte[]buff = new byte[1024];
        int length = 0;
        while ((length = inputStream.read(buff)) > 0) 
            outputStream.write(buff, 0, length);
        
        outputStream.flush();
        outputStream.close();
        Log.w("MainActivity","DB copied");
        return true;
    catch (Exception e) 
        e.printStackTrace();
        return false;
    



public void onSerach()

    databaseOpenHelper = new DatabaseOpenHelper(getContext());

    //Check exists database
    File database = getContext().getDatabasePath(DatabaseOpenHelper.DBNAME);
    if(false == database.exists()) 
        databaseOpenHelper.getReadableDatabase();
        //Copy db
        if(copyDatabase(getContext())) 
            Toast.makeText(getContext(), "Copy database success", Toast.LENGTH_SHORT).show();
         else 
            Toast.makeText(getContext(), "Copy data error", Toast.LENGTH_SHORT).show();
            return;
        

    


所以我删除了 onStart() 方法并将这些代码行移到 onCreateView() 下面...检查数据是否存在的部分在 BaseFragment 类中,所以现在在我的片段中,我只是从数据库中获取产品列表因为每个片段都有自己的列表。对于 search() 方法,我也做了同样的事情。从 SQLite 数据库复制数据的方法已放在 BaseFragment 类中。我唯一无法完成的是 onPrepareOptionsMenu() 方法,我不知道为什么,但在这一行中 for (NewModel newModel : list) list 正在返回 null...

【讨论】:

以上是关于如何从所有片段中使用工具栏中的 SearchView的主要内容,如果未能解决你的问题,请参考以下文章

如何从 MenuItem 导航到片段(Android)?

如何使工具栏与片段中的内容一起滚动?

如何使用java将数据从片段传递到android中的另一个片段?

从 Apollo 缓存中读取特定类型的所有片段

Android如何使用工具栏中的按钮切换以使用片段打开/关闭导航抽屉

如果使用导航控制器,如何删除某些片段中的底部导航视图和工具栏?