ここでは、toolbar
を含むActivity
にSearchView
があります。そして、そのアクティビティには複数のフラグメントがあります。それらのうちの1つの主要なフラグメントには、さらに10個のフラグメントが含まれています。 10個のフラグメントすべてがリストビューにデータを表示しています。今、私はフラグメントのすべてのリストをSearchView
のMainActivity
でフィルタリングしようとしています。ただし、各フラグメントのリストをフィルタリングすることはありません。次に、すべてをどのように実装したかを示します。
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 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パターンを使用で管理できます。これにより、ネストされた各リストが1つから更新されます Observable parent 。 すべてのトラブル を修正しましたが、正しい動作を実現するために正常に機能するようになりました。
したがって、これを達成するために私がしたことは次のとおりです。
SearchView
で1つの親Activity
を使用するFilter
にAdapter
クラス(Android.widget.Filter)を作成しますObservable
とObserver
のFragment
/Activity
パターンを使用します背景:コードを試したところ、次の3つの問題が発生しました。
onQueryTextChange
がFragment
sで呼び出されることはないようです。検索アイコンをタップすると、SearchView
(編集テキスト、アイコンなど)が検索ウィジェットに添付されていないように見えます(アクティビティのウィジェットに添付されています)。filter2
_:つまり、前のポイントを解決したときに、このメソッドが機能しません。実際、私はFilter
とその2つのメソッドperformFiltering
とpublishResults
によって拡張されるカスタムクラスを試してみる必要があります。それがないと、検索バーで単語をタップすると空白の画面が表示されます。ただし、これは私のコードにすぎない可能性があり、おそらくfilter2()
は完全に機能します...SearchView
が作成されます。ネストされたフラグメントでこの行を繰り返しSearchView sv = new SearchView(...);
と呼んでいるようです。したがって、次のフラグメントに切り替えるたびに、展開された検索ビューは前のテキスト値を削除します。とにかく、いくつかの調査の結果、 この回答 on SO 検索フラグメント の実装について)が見つかりました。ただし、それ以外はほぼ同じコードです。親アクティビティとフラグメントでオプションメニューコードを「複製」します。そうすべきではありません。これが、前のポイントでの最初の問題の原因だと思います。
その上、回答のリンクで使用されているパターン(1つのフラグメントで1回の検索)があなたのパターン(複数のフラグメントでの1回の検索)に適合していない可能性があります。ネストされたすべてのSearchView
に対して、親Activity
で1つの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()
で動作させることができないので、Web上の例として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
オブジェクトであり、ネストされたフラグメントはObserver
sです( オブザーバーパターンを参照 )。基本的に、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
);
}
_
アダプターは オブザーバーを追加 親オブザーバブルに追加し、子フラグメントが破棄されたときにそれを削除します。
親フラグメントの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結論:
これはうまく機能し、ネストされたすべてのフラグメントのリストは、1つの検索ビューで期待どおりに更新されます。ただし、上記のコードには、次の点に注意する必要がある不便があります。
Fragment
汎用オブジェクトを呼び出して、それをオブザーバーとして追加することはできません。実際、特定のフラグメントクラス(ここではNestedFragment
)をキャストして初期化する必要があります。簡単な解決策があるかもしれませんが、私は今のところそれを見つけられませんでした。それにもかかわらず、私は正しい振る舞いをします、そして-私は思う-それは活動の中で1つの検索ウィジェットを一番上に保つことによって良いパターンかもしれません。したがって、このソリューションを使用すると、目的を達成するための手がかり、正しい方向性を得ることができます。楽しんでいただければ幸いです。
#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) { }
}
_
質問をよりよく理解するためだけに。 1.アクションバーに検索ビューがあります2.複数のフラグメントがあります(画像Advisory/TopAdvisorsから...)3。これらのそれぞれの中に、タブごとに複数のフラグメントがあります(例:エクイティ...など)4 。フラグメント内のすべてのリストビューで、検索コンテンツに基づいてデータをフィルタリングする必要があります
正しい??
現在の実装では、ステータスは何ですか?現在表示されているリストビューはフィルタリングされていますか?
それともそれでも機能していませんか?次に、notifydatasetChangedがアダプタに正しく伝播していることを確認する必要があります。つまり、UIThread自体にある必要があります。
すべてのフラグメントを更新するには、つまり、検索テキストを入力したときに画面に表示されなかったものを更新するには、フラグメントのライフサイクルを考慮し、フラグメントのonResumeにコードを含めて、アダプターの初期化に使用する前にリストがフィルター処理されていることを確認する必要があります。 。これは、検索テキストをすでに入力していて、タブ/フラグメント間を移動しているシナリオ用です。したがって、フラグメントは画面のオンとオフを切り替えるため、onResumeが必要になります。
更新:テキストの検索ボックスをチェックし、onResumeにadapter.filter2()を呼び出すものがある場合は、それが基本的にリストをフィルタリングしているためです。 。
ビューページャー/タブは、表示されているフラグメントを切り替えるたびに破棄されるように実装されていると思います。
次の変更を加えます(コメントを読んでください)
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;
}
});
私はあなたが一般的な考えを得ることを望みます
私はこのデザインをお勧めしませんが、私がしたいのは