使用片段的 Android 搜索

有人知道如何用 Fragment实现标准 Android 搜索界面的教程或示例吗?换句话说,有没有可能把一个带有 SearchManager的标准搜索放在一个片段中?

94168 次浏览

在使用 Fragments时,仍然需要使用 Activity来控制和分配 Fragments。 这个 Activity可以像以前一样具有搜索功能。

我最近从一个“正常”的基于 Activity的应用程序,基于 Fragment的应用程序和搜索功能的工作对我来说是一样的。

你有没有尝试过,但是没有成功? 如果有的话,在你的问题中给出更多的细节。

编辑:

如果您想要进行特定于片段的搜索,那么请让您的所有 Fragments使用 startSearch方法扩展接口 MyFragment,并让您的 ActivitystartSearch方法调用当前片段的 startSearch方法。

Fragment不能存在于 Activity之外,也不能连接到 android.intent.action.SEARCH或任何其他 intent-filter

因此,如果不使用 Activity来包装 Fragment,那么您所要求的是不可能的。

简而言之,你不能。在 Fragment中创建搜索界面是不可能的,原因有几个。

  1. 创建可搜索界面时,必须在 Android 清单中指定默认的“可搜索活动”。正如我确信你知道的,一个 Fragment不能没有父 Activity而存在,因此,这种分离是不可能的。

  2. 如果你已经知道了第一点,我假设你问这个问题是希望有一些神奇的“黑客”可以完成这项工作。但是,文档指出,

    当用户在搜索对话框或小部件中执行搜索时, 系统启动您的搜索活动,并提供搜索 执行 ACTION _ SEARCH 操作的意图中的查询 Activity 从意图的 QUERY 附加值检索查询,然后 搜索您的数据并显示结果。

    负责提供搜索结果的底层内部系统需要的是 Activity,而不是 Fragment; 因此,实现完全独立于 Activity的搜索界面是不可能的,因为它需要对 底层系统本身进行更改。如果你不相信我,请查看 SearchableInfo 类的源代码:)。

话虽如此,要达到你所描述的目标似乎并不太困难。例如,您可以考虑实现您的可搜索-Activity,以便它接受 android.intent.action.SEARCH意图,(而不是立即显示在 ListView中,例如)将搜索查询传递给 Fragment。例如,考虑以下可搜索的活动:

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

当发出搜索请求时,系统将启动您的可搜索活动,执行查询,并将结果传递给某个容器 Activity (基于您的 doMySearch实现)。容器 Activity 然后将这些结果传递给所包含的可搜索 Fragment,结果将在其中显示。实现所需要的工作量比您所希望的要多一些,但是我确信有一些方法可以使它更加模块化,而且看起来这可能是您所能做的最好的工作。

附言。如果您使用这种方法,那么您可能需要特别注意哪些活动被添加/移除到了回栈中。请参阅此 邮寄以获得更多关于如何完成此操作的信息。

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

如果片段进入 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);
}

我想我做到了: 你可以实际上使用片段,并添加一个搜索图标到一个操作栏,这样搜索就可以在片段内部进行。技巧是使用操作栏、操作视图、监听器、加载程序和适配器。

虽然它完全绕过了 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/outs/search _ in _  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>

使用 ActionBarSearchView。您将能够在不连接到 Activity 的情况下处理搜索。只需将 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;
}
});

有关自定义搜索的详细信息,请参阅 这个文章。

使用标准的 ActionBarSearchViewActionViewAPI 在片段中进行搜索是非常有可能的。这也可以使用 AppCompat 支持类 v7返回到 Android 2.1(API 级别7)。

在你的片段里:

@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。只要从 @ Rookie解决方案中添加一些东西到 @ David的解决方案中,就可以让它以一种简单的方式正常工作,下面是我的片段代码:

我的碎片 :

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);,因为它使菜单项增加了一倍,因为活动膨胀了一个菜单,然后片段膨胀了另一个菜单

感谢“大卫”和“菜鸟”

是的,有可能,

请在您的活动中实现搜索视图,活动中的“ onQueryTextChange”也会监听片段中的搜索,您可以检查片段可见性在“ onQueryTextChange”中,如果它可见,您可以调用片段搜索方法,它在我的代码中工作完美

其他的解决方案... . . 我不喜欢这样。 这对我来说更容易。但这是我的主意。我在等你的意见

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

活动

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

我找到了一个工作:) 您可以在 BaseActivity 中覆盖该方法(startActivity (Inent)) ,然后检查该操作是否为 ACTION _ SEARCH,然后执行特殊的任务: D

@Override
public void startActivity(Intent intent) {
try {
if (intent.getAction().equals(Intent.ACTION_SEARCH))
toast("hello");
} catch (Exception e) {


}
}