Android поиск с фрагментами
кто-нибудь знает учебник или пример как реализовать стандарт интерфейс поиска Android С Fragment
s? Другими словами, можно ли поставить стандартный поиск с SearchManager
в фрагменте?
12 ответов:
короче, ты не можешь. Есть несколько причин, почему создание поискового интерфейса в
Fragment
не возможно.
при создании интерфейса с возможностью поиска необходимо указать в манифесте Android "активность с возможностью поиска" по умолчанию. Как я уверен, вы знаете, a
Fragment
не может существовать без родителяActivity
и таким образом, это разделение не возможно.если вы уже выяснили #1 уже, я предполагаю, что вы спросили Это вопрос в надежде, что там есть какой-то волшебный "хак", который может выполнить эту работу. Однако, в документации говорится, что
когда пользователь выполняет поиск в диалоговом окне поиска или виджет, система запускает вашу поисковую активность и доставляет ее поиск запрос в намерении с действием ACTION_SEARCH. Ваш поиск activity извлекает запрос из запроса намерения extra, затем поиск данных и представляет результаты.
основная, внутренняя система, которая отвечает за предоставление результатов поиска ожидает
Activity
, а неFragment
; таким образом, реализация интерфейса поиска, который полностью независим отActivity
невозможно, так как это потребует изменений в сама базовая система. Проверьте исходный кодSearchableInfo
класс, если вы мне не верите :).что быть сказал, Не кажется, что было бы слишком трудно достичь чего-то подобного тому, что вы описываете. Например, вы можете рассмотреть возможность реализации вашего поискового действия, чтобы он принял
android.intent.action.SEARCH
намерение и (вместо немедленного отображения результатов вListView
, например) передаст поисковый запрос на вашFragment
s. Например, рассмотрим следующее действие с возможностью поиска:public class SearchableActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) { String query = intent.getStringExtra(SearchManager.QUERY); doMySearch(query); } } /** * Performs a search and passes the results to the container * Activity that holds your Fragments. */ public void doMySearch(String query) { // TODO: implement this } }
когда поисковый запрос сделан, система запустит ваш поисковая активность, выполните запрос и передайте результаты в некоторую контейнерную активность (на основе вашей реализации
doMySearch
). Затем действие контейнера передаст эти результаты в содержащийся объект поискаFragment
, в котором будут отображаться результаты. Реализация требует немного больше работы, чем вы, вероятно, надеялись, но я уверен, что есть способы сделать ее более модульной, и кажется, что это может быть лучшим, что вы можете сделать.p.s. Если вы используете этот подход, вам, возможно, придется обратить особое внимание на то, какие Activitys добавляются/удаляются в backstack. Смотрите это post для получения дополнительной информации о том, как это может быть сделано.
p.p. s. вы также можете полностью забыть о стандартном интерфейсе поиска и просто реализовать простой поиск в пределах
Fragment
как описано в сообщение Рагхава ниже.
вот пример для поиска чего-то с помощью фрагментов. Надеюсь, это поможет, и это то, что вы ищете:
public class LoaderCursor extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FragmentManager fm = getFragmentManager(); // Create the list fragment and add it as our sole content. if (fm.findFragmentById(android.R.id.content) == null) { CursorLoaderListFragment list = new CursorLoaderListFragment(); fm.beginTransaction().add(android.R.id.content, list).commit(); } } public static class CursorLoaderListFragment extends ListFragment implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> { // This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; // If non-null, this is the current filter the user has provided. String mCurFilter; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Give some text to display if there is no data. In a real // application this would come from a resource. setEmptyText("No phone numbers"); // We have a menu item to show in action bar. setHasOptionsMenu(true); // Create an empty adapter we will use to display the loaded data. mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_2, null, new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, new int[] { android.R.id.text1, android.R.id.text2 }, 0); setListAdapter(mAdapter); // Start out with a progress indicator. setListShown(false); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // Place an action bar item for searching. MenuItem item = menu.add("Search"); item.setIcon(android.R.drawable.ic_menu_search); item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); SearchView sv = new SearchView(getActivity()); sv.setOnQueryTextListener(this); item.setActionView(sv); } public boolean onQueryTextChange(String newText) { // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; getLoaderManager().restartLoader(0, null, this); return true; } @Override public boolean onQueryTextSubmit(String query) { // Don't care about this. return true; } @Override public void onListItemClick(ListView l, View v, int position, long id) { // Insert desired behavior here. Log.i("FragmentComplexList", "Item clicked: " + id); } // These are the Contacts rows that we will retrieve. static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY, }; public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. Uri baseUri; if (mCurFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); } public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data); // The list should now be shown. if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); } } public void onLoaderReset(Loader<Cursor> loader) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null); } } }
вполне возможно, чтобы найти в фрагмент с помощью стандартной панели быстрого доступа SearchView ActionView API-интерфейс. Это будет работать обратно в Android 2.1 (уровень API 7) тоже с помощью классов поддержки AppCompat v7.
в свой фрагмент:
@Override public void onCreateOptionsMenu (Menu menu, MenuInflater inflater){ inflater.inflate(R.menu.search, menu); MenuItem item = menu.findItem(R.id.action_search); SearchView sv = new SearchView(((YourActivity) getActivity()).getSupportActionBar().getThemedContext()); MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); MenuItemCompat.setActionView(item, sv); sv.setOnQueryTextListener(new OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { System.out.println("search query submit"); return false; } @Override public boolean onQueryTextChange(String newText) { System.out.println("tap"); return false; } }); }
в вашем меню XML
<item android:id="@+id/action_search" android:icon="@drawable/ic_action_search" android:title="Search Waste Items" android:showAsAction="ifRoom|collapseActionView" nz.govt.app:actionViewClass="android.support.v7.widget.SearchView" nz.govt.app:showAsAction="ifRoom|collapseActionView" />
использование классов поддержки AppCompat v7. Просто добавляю что-то в @David С @Rookie решение, чтобы заставить его работать правильно простым способом, вот мой фрагмент кода:
MyFragment:
public class MyFragment extends Fragment implements SearchView.OnQueryTextListener { @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // What i have added is this 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()); 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.setOnSearchClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Utils.LogDebug("Clicked: "); } }); MenuItemCompat.setOnActionExpandListener(item, new MenuItemCompat.OnActionExpandListener() { @Override public boolean onMenuItemActionCollapse(MenuItem item) { // Do something when collapsed Utils.LogDebug("Closed: "); return true; // Return true to collapse action view } @Override public boolean onMenuItemActionExpand(MenuItem item) { // Do something when expanded Utils.LogDebug("Openeed: "); return true; // Return true to expand action view } }); super.onCreateOptionsMenu(menu,inflater); } @Override public boolean onQueryTextSubmit(String query) { Utils.LogDebug("Submitted: "+query); return true; } @Override public boolean onQueryTextChange(String newText) { Utils.LogDebug("Changed: "+newText); return false; } }
добавил
onActivityCreated
, потому что без вызоваsetHasOptionsMenu(true);
система не будет знать, что этот фрагмент должен взаимодействовать с меню.затем я удалил строку
inflater.inflate(R.menu.main, menu);
потому что он удвоил пункты меню так как активность раздула меню, то фрагмент раздул другое менюспасибо @David и @Rookie
при работе с
Fragments
вам все равно нужно использоватьActivity
для управления и назначитьFragments
. ЭтоActivity
может иметь функцию поиска, как и раньше.я недавно переключился с "нормального"
Activity
приложение, кFragment
на основе приложения и функции поиска работали точно так же для меня.вы пробовали работать над этим, и не удалось? Если да, то дайте более подробную информацию в вашем вопросе.
EDIT:
если вы хотите есть фрагмент конкретного поиска, есть все ваши
Fragments
расширить интерфейсMyFragment
СstartSearch
метод, и есть вашActivity
' sstartSearch
вызов метода текущего фрагментаstartSearch
метод.
Я думаю, что я достиг этого : вы можете использовать фрагменты и добавить значок поиска в панель действий, чтобы поиск был возможен внутри фрагментов. Фокус в том, чтобы использовать панель действий, представление действий, прослушиватель для него, загрузчик и адаптер, конечно.
это работает довольно хорошо, хотя он полностью обходит механизм поиска платформы android (но он может быть завершен некоторой работой, чтобы найти то, что описывает @Alex Lockwood и передать поиск фрагментам). Это было бы не так реагируйте на намерение, как ожидалось в случае действия, но оно работает : пользователи могут искать внутри фрагментов.
вот код :
SearchInFragmentActivity
package com.sof.test.searchfragment; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.view.View; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.ActionBar.Tab; import com.actionbarsherlock.app.ActionBar.TabListener; import com.actionbarsherlock.app.SherlockFragmentActivity; import com.sof.test.searchfragment.SearchFragment; import com.sof.test.R; public class SearchInFragmentActivity extends SherlockFragmentActivity implements TabListener { private SearchFragment tab1 = new SearchFragment(); private SearchFragment tab2 = new SearchFragment(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView( R.layout.search_in_fragments ); getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); createTab( R.string.tab1, R.drawable.ic_menu_search ); createTab( R.string.tab2, R.drawable.ic_menu_search ); getSupportActionBar().setSelectedNavigationItem( 0 ); invalidateOptionsMenu(); } private void createTab(int tabNameResId, int tabIconResId) { ActionBar.Tab tab = getSupportActionBar().newTab(); tab.setText( tabNameResId ); tab.setTabListener(this); getSupportActionBar().addTab(tab); }// met @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { if( ft == null ) { return; }//if View fragmentSlot = findViewById( R.id.fragment ); Fragment newFragment = null; if( fragmentSlot != null ) { newFragment = (tab.getPosition() == 0) ? tab1 : tab2; ft.replace(R.id.fragment, newFragment ); ft.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_FADE); }//if } @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { } @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } }//class
фрагмент класса SearchFragment (я использую 2 экземпляра внутри действия выше).
package com.sof.test.searchfragment; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.os.Bundle; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.SearchView; import android.widget.TextView; import com.sof.test.R; import com.actionbarsherlock.app.SherlockListFragment; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; public class SearchFragment extends SherlockListFragment { private StringLoader loader = null; private StringAdapter adapter = null; private List<String> listData = new ArrayList<String>(); private String query; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); createListData(); loader = new StringLoader( getActivity(), this ); adapter = new StringAdapter(listData); setListAdapter(adapter); getLoaderManager().initLoader(0, null, new LoaderCallBacks() ); loader.forceLoad(); setHasOptionsMenu( true ); return view; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater ) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate( R.menu.menu_search, menu); System.out.println( "inflating menu"); final SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); final SearchView.OnQueryTextListener queryTextListener = new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextChange(String newText) { showFilteredItems( newText ); return true; } @Override public boolean onQueryTextSubmit(String query) { return true; } }; searchView.setOnQueryTextListener(queryTextListener); return; }//met private void showFilteredItems( String query ) { this.query = query; loader.onContentChanged(); } private void createListData() { for( int i = 0; i < 100 ; i ++ ) { listData.add( "String "+ i ); } } public List<String> getData() { List<String> listFilteredData = new ArrayList<String>(); for( String string : listData ) { if( query == null || string.contains( query ) ) { listFilteredData.add( string ); } } return listFilteredData; }//met private class LoaderCallBacks implements LoaderCallbacks< List<String>> { @Override public void onLoadFinished(Loader<List<String>> loader, List<String> listData) { adapter.setListData( listData ); }// met @Override public void onLoaderReset(Loader<List<String>> listData) { adapter.setListData( new ArrayList<String>() ); }// met @Override public Loader<List<String>> onCreateLoader(int arg0, Bundle arg1) { return loader; }// met }//class private class StringAdapter extends ArrayAdapter< String > { private List<String> listDataToDisplay = new ArrayList<String>(); private LayoutInflater mInflater; public StringAdapter( List<String> listData ) { super( getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1, listData ); listDataToDisplay = listData; mInflater = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); }//cons private void setListData( List<String> newListData ) { this.listDataToDisplay.clear(); this.listDataToDisplay.addAll( newListData ); notifyDataSetChanged(); }//met /** * Populate new items in the list. */ @Override public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView == null) { view = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false); } else { view = convertView; } ((TextView)view.findViewById( android.R.id.text1)).setText( listDataToDisplay.get( position ) ); return view; } }//inner class }//class class StringLoader extends AsyncTaskLoader<List<String>> { SearchFragment fragment = null; public StringLoader(Context context, SearchFragment fragment) { super(context); this.fragment = fragment; }// cons @Override public List<String> loadInBackground() { return fragment.getData(); }// met }// class
xml-файл для меню поиска фрагментов res/menu / menu_search.XML-код:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:baselineAligned="false" android:orientation="horizontal" > <FrameLayout android:id="@+id/fragment" android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" /> </LinearLayout>
и xml-файл макета res / layout / search_in_fragments.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:baselineAligned="false" android:orientation="horizontal" > <FrameLayout android:id="@+id/fragment" android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" /> </LinearLayout>
использовать
ActionBar
иSearchView
. Вы сможете обрабатывать поиск без какого-либо подключения к деятельности. Просто установитеOnQueryTextListener
к SearchView.MenuItem item = menu.add("Search"); SearchView sv = new SearchView(getActionBar().getThemedContext()); item.setActionView(sv); item.setIcon(R.drawable.ic_search); item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItem.SHOW_AS_ACTION_IF_ROOM); sv.setOnQueryTextListener(new OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { //... return false; } @Override public boolean onQueryTextChange(String newText) { //... return false; } });
посмотреть этой сообщение для получения более подробной информации о пользовательском поиске.
A
Fragment
не может существовать за пределамиActivity
, но иFragment
связан сandroid.intent.action.SEARCH
или любое другоеintent-filter
.так что без использования
Activity
чтобы обернуть theFragment
, то, что вы просите не возможно.
в случае фрагментов в ViewPager, я мог бы сделать это, заблокировав кнопку поиска, когда я не на фрагменте, где я хочу дать строку поиска. В деятельности:
@Override public boolean onSearchRequested() { if (mPager.getCurrentItem() == mAdapter.getPosition(FragmentType.VIDEO)) return super.onSearchRequested(); else return false; }
и в случае отсутствия кнопки физического поиска, я добавил элемент действия в фрагмент, который запускает этот код:
@Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.search_item) { return getSherlockActivity().onSearchRequested(); } return super.onOptionsItemSelected(item); }
Я нашел обходной путь :) вы можете переопределить этот метод (startActivity (Intent)) в своей BaseActivity, а затем проверить, является ли действие ACTION_SEARCH, а затем выполнить свою специальную работу :D
@Override public void startActivity(Intent intent) { try { if (intent.getAction().equals(Intent.ACTION_SEARCH)) toast("hello"); } catch (Exception e) { } }
другие решения..... мне это не нравится. Это более легко для меня.Но это мое idea.Im ждем твоего мнения
public interface SearchImpl { public void searchQuery(String val); }
фрагмент
public class MyFragment extends Fragment implements SearchImpl { View view; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.fragment_almanca, container, false); return view; } @Override public void searchQuery(String val) { Log.e("getted", val); } }
Acitivty
@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search)); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { Log.e("setted", "" + query); try { MyFragment myFGM=new MyFragment(); myFGM.searchQuery(query); } catch (Exception e) { e.printStackTrace(); } return false; } @Override public boolean onQueryTextChange(String newText) { return false; } }); return super.onCreateOptionsMenu(menu); }
возможно,
пожалуйста, реализует вид поиска на вашей деятельности, "onQueryTextChange" в деятельности также будет слушать поиск во фрагменте, вы можете проверить видимость фрагмента в "onQueryTextChange", если он виден вы можете вызвать свой метод поиска для фрагмента, его работа идеально подходит в моем коде