介绍搜索是各种应用程序的一个基本功能需求. 在我们的案例中,我们拥有一个餐厅的应用程序,需要它能让用户可以方便和快速的搜索菜品清单,找到他们想要的东西. 在本文中,我将会描述我在对UI进行持续的维护时先我们现有的餐厅应用程序加入本地搜索功能过程. 我会详细讲述我选择的UI方案及其理由, 此外还涉及到像activity中加入一个GestureOverlayView. 图 1: 餐厅应用程序的搜索视图截图 |
1 2 3 4 5 6 7 8 9 10 11 12 | /** * Builds a table of items matching the searchTerm in their name or description */ public Cursor searchMenuItems(String searchTerm) { SQLiteDatabase db = getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(TABLES.MENU); Cursor c = qb.query(db, null , "(" +MenuColumns.NAME+ " LIKE '%" +searchTerm+ "%') " + "OR (" +MenuColumns.DESCRIPTION+ " LIKE '%" + searchTerm+ "%')" , null , null , null , null ); return c; } |
代码示例 1: 查询数据库的方法
接下来,我们需要在主activity的操作栏中设置上搜索选项. 更多有关设置操作栏的信息可以阅读这篇文章: 为 Android 设备构建动态UI. 搜索功能的处理将完全在我们的应用程序里面; 我们并不想要在搜索一开始的时候列出已经安装在设备上的应用程序清单,或者发送一个intent来让另外一个搜索应用程序来进行处理.
向 MainActivity 类添加如下这个字符串变量. 我们会使用这个变量来向搜索的intent发送所要查询的字符串:
1 2 | /* Search string label */ public final static String SEARCH_MESSAGE= "com.example.restaurant.MESSAGE" ; |
代码示例 2: 向搜索intent中添加扩展数据的类变量
更新 MainActivity 的 onCreateOptionsMenu 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /** * Initialize the action menu on action bar */ public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.action_bar, menu); //set up the search MenuItem searchItem = menu.findItem(R.id.action_search); SearchView mSearchView = (SearchView) searchItem.getActionView(); searchItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW); //set up the query listener mSearchView.setOnQueryTextListener( new OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { //start the search intent Intent searchIntent = new Intent(MainActivity. this , SearchResultsActivity. class ); searchIntent.putExtra(SEARCH_MESSAGE, query); startActivity(searchIntent); return false ; } @Override public boolean onQueryTextChange(String query) { //do nothing in our case return true ; } }); return super .onCreateOptionsMenu(menu); } |
代码示例 3: 操作栏初始化代码
以及 SearchResultsActivity 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public class SearchResultsActivity extends Activity{ TextView mQueryText; GridView searchListResults; SearchAdapter adapter; Vector<com.example.restaurant.MenuFactory.MenuItem> searchList; @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.search_query_grid_results); mQueryText = (TextView) findViewById(R.id.txt_query); //setup the grid view searchListResults = (GridView)findViewById(R.id.search_results); searchList= new Vector<com.example.restaurant.MenuFactory.MenuItem>(); //get and process search query here final Intent queryIntent = getIntent(); doSearchQuery(queryIntent); adapter= new SearchAdapter( this ,searchList); searchListResults.setAdapter(adapter); //Listener for grid view searchListResults.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View v, int position, long id){ FragmentTransaction ft = getFragmentManager().beginTransaction(); Fragment prev = getFragmentManager().findFragmentByTag( "dialog" ); if (prev != null ) { ft.remove(prev); } ft.addToBackStack( null ); DialogFragment newFragment = SearchResultsDialogFragment.newInstance(searchList.elementAt(position)); newFragment.show(ft, "dialog" ); } }); } |
代码示例 4: 主要的搜索结果类 (下面还会有)
当我们构建这个列表是,我们也将会处理没有查询到任何匹配项的情况下应该怎么做. 如果没有匹配,我们会查搜索的人显示一个消息对话框,让他们知晓,并且关闭搜索的activity,他们就不会看到一个空白的界面了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /** * Builds the found item list. */ private void doSearchQuery( final Intent queryIntent) { //Get the query text String message= queryIntent.getStringExtra(MainActivity.SEARCH_MESSAGE); //Set the UI field mQueryText.setText(message); RestaurantDatabase dB= new RestaurantDatabase( this ); MenuFactory mMF= MenuFactory.getInstance(); Cursor c= dB.searchMenuItems(message); Set<String> categories = new HashSet<String>(); while (c.moveToNext()) { String category = c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.CATEGORY)); categories.add(category); //build a new menu item and add it to the list MenuItem item= mMF. new MenuItem(); item.setCategory(category); item.setName(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.NAME))); item.setDescription(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.DESCRIPTION))); item.setNutrition(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.NUTRITION))); item.setPrice(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.PRICE))); item.setImageName(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.IMAGENAME))); searchList.add(item); } c.close(); //Handle the case of not finding anything if (searchList.size()== 0 ){ Intent intent = new Intent(SearchResultsActivity. this , OrderViewDialogue. class ); intent.putExtra(OrderViewActivity.DIALOGUE_MESSAGE, "Sorry, no matching items found." ); startActivity(intent); SearchResultsActivity. this .finish(); } } |
代码示例 4 续
类的这一个部分是网格视图的适配器, 这里我们能够只做相对很小的修改实现对来自主菜单代码本身的重用. 我们也能够适配布局文件,因此保持UI在视觉上的一致性具有无需重头开始,只要轻松的对代码进行回收利用这中好处. 你之前可能已经意识到了,我也重用了 OrderViewDialogue, 这事我志气啊为购物车写的一个类,但是在这里也能起作用.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | /** * SearchAdapter to handle the grid view of found items. Each grid item contains * a view_grid_item which includes a image, name, and price. */ class SearchAdapter extends BaseAdapter { private Vector<com.example.restaurant.MenuFactory.MenuItem> mFoundList; private LayoutInflater inflater; public SearchAdapter(Context c, Vector<com.example.restaurant.MenuFactory.MenuItem> list) { mFoundList= list; inflater = LayoutInflater.from(c); } public int getCount() { return mFoundList.size(); } public Object getItem( int position) { return mFoundList.get(position); } public long getItemId( int position) { return 0 ; } // create a new ItemView for each item referenced by the Adapter public View getView( int position, View convertView, ViewGroup parent) { View v = convertView; ImageView picture; TextView name; TextView price; if (v == null ) { v = inflater.inflate(R.layout.view_grid_item, parent, false ); v.setTag(R.id.picture, v.findViewById(R.id.picture)); v.setTag(R.id.grid_name, v.findViewById(R.id.grid_name)); v.setTag(R.id.grid_price, v.findViewById(R.id.grid_price)); } picture= (ImageView) v.getTag(R.id.picture); name= (TextView) v.getTag(R.id.grid_name); price= (TextView) v.getTag(R.id.grid_price); final MenuItem foundItem = (MenuItem) mFoundList.get(position); InputStream inputStream = null ; AssetManager assetManager = null ; try { assetManager = getAssets(); inputStream = assetManager.open(foundItem.imageName); picture.setImageBitmap(BitmapFactory.decodeStream(inputStream)); } catch (Exception e) { Log.d( "ActionBarLog" , e.getMessage()); } finally { } name.setText(foundItem.name); price.setText(foundItem.price); return v; } } } |