精彩继续~~~前面介绍了 ,到了主页面后面我们按照主页面中从上到下的顺序来接着介绍,在这里就来看SearchActivity的实现。
(一)首先我们先根据布局文件来分析下整个页面的布局:布局文件search.xml
search.xml
14 5 9 1814 17 23 27 29 32 35 36 37 40 44 4543
第一个LinearLayout是页面最上面的search输入框search按钮。紧接着是一个手势视图内嵌套了一个FixedViewFlipper用来显示查询之前的提示、查询成功后的结果(ListView)和查询不到结果的文字提示。最后一个还是一个LinearLayout中的一个Spinner用来显示查询方式的下拉列表。
(二)SearchActivity
首先给出源码方便大家学习:
SearchActivity
1 /** 2 * Allows content searching on Jamendo services 3 * 4 * @author Lukasz Wisniewski 5 */ 6 public class SearchActivity extends Activity { 7 8 enum SearchMode{ 9 Artist, 10 Tag, 11 UserPlaylist, 12 UserStarredAlbums 13 }; 14 15 private Spinner mSearchSpinner; 16 private ListView mSearchListView; 17 private EditText mSearchEditText; 18 private Button mSearchButton; 19 private ViewFlipper mViewFlipper; 20 private GestureOverlayView mGestureOverlayView; 21 22 private PlaylistRemote[] mPlaylistRemotes = null; 23 24 private SearchMode mSearchMode; 25 26 /** 27 * Launch this Activity from the outside 28 * 29 * @param c context from which Activity should be started 30 */ 31 public static void launch(Context c){ 32 Intent intent = new Intent(c, SearchActivity.class); 33 c.startActivity(intent); 34 } 35 36 /** Called when the activity is first created. */ 37 @Override 38 public void onCreate(Bundle savedInstanceState) { 39 super.onCreate(savedInstanceState); 40 requestWindowFeature(Window.FEATURE_NO_TITLE); 41 setContentView(R.layout.search); 42 43 mSearchSpinner = (Spinner)findViewById(R.id.SearchSpinner); 44 ArrayAdapter adapter = ArrayAdapter.createFromResource( 45 this, R.array.search_modes, android.R.layout.simple_spinner_item); 46 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 47 mSearchSpinner.setAdapter(adapter); 48 49 mSearchButton = (Button)findViewById(R.id.SearchButton); 50 mSearchButton.setOnClickListener(mSearchButtonListener); 51 52 mSearchEditText = (EditText)findViewById(R.id.SearchEditText); 53 mSearchListView = (ListView)findViewById(R.id.SearchListView); 54 55 mViewFlipper = (ViewFlipper)findViewById(R.id.SearchViewFlipper); 56 if(mSearchListView.getCount() == 0){ 57 mViewFlipper.setDisplayedChild(2); // search list hint 58 } 59 60 mGestureOverlayView = (GestureOverlayView) findViewById(R.id.gestures); 61 mGestureOverlayView.addOnGesturePerformedListener(JamendoApplication 62 .getInstance().getPlayerGestureHandler()); 63 } 64 65 @Override 66 protected void onResume() { 67 super.onResume(); 68 boolean gesturesEnabled = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("gestures", true); 69 mGestureOverlayView.setEnabled(gesturesEnabled); 70 } 71 72 @SuppressWarnings("unchecked") 73 @Override 74 protected void onRestoreInstanceState(Bundle savedInstanceState) { 75 mSearchMode = (SearchMode) savedInstanceState.getSerializable("mode"); 76 if(mSearchMode != null){ 77 if(mSearchMode.equals(SearchMode.Artist) 78 || mSearchMode.equals(SearchMode.Tag) 79 || mSearchMode.equals(SearchMode.UserStarredAlbums)){ 80 AlbumAdapter adapter = new AlbumAdapter(this); 81 adapter.setList((ArrayList) savedInstanceState.get("values")); 82 mSearchListView.setAdapter(adapter); 83 mSearchListView.setOnItemClickListener(mAlbumClickListener); 84 } 85 86 if(mSearchMode.equals(SearchMode.UserPlaylist)) { 87 PlaylistRemoteAdapter adapter = new PlaylistRemoteAdapter(this); 88 adapter.setList((ArrayList ) savedInstanceState.get("values")); 89 mSearchListView.setAdapter(adapter); 90 mSearchListView.setOnItemClickListener(mPlaylistClickListener); 91 } 92 93 mViewFlipper.setDisplayedChild(savedInstanceState.getInt("flipper_page")); 94 } 95 super.onRestoreInstanceState(savedInstanceState); 96 } 97 98 @Override 99 protected void onSaveInstanceState(Bundle outState) {100 if(mSearchMode != null){101 outState.putSerializable("mode", mSearchMode);102 if(mSearchMode.equals(SearchMode.Artist) 103 || mSearchMode.equals(SearchMode.Tag) 104 || mSearchMode.equals(SearchMode.UserStarredAlbums)){105 AlbumAdapter adapter = (AlbumAdapter)mSearchListView.getAdapter();106 outState.putSerializable("values", adapter.getList());107 }108 109 if(mSearchMode.equals(SearchMode.UserPlaylist)) {110 PlaylistRemoteAdapter adapter = (PlaylistRemoteAdapter)mSearchListView.getAdapter();111 outState.putSerializable("values", adapter.getList());112 }113 114 outState.putInt("flipper_page", mViewFlipper.getDisplayedChild());115 }116 super.onSaveInstanceState(outState);117 }118 119 private OnClickListener mSearchButtonListener = new OnClickListener(){120 121 @Override122 public void onClick(View v) {123 new SearchingDialog(SearchActivity.this,124 R.string.searching,125 R.string.search_fail)126 .execute(mSearchSpinner.getSelectedItemPosition()); 127 }128 129 };130 131 private OnItemClickListener mAlbumClickListener = new OnItemClickListener(){132 133 @Override134 public void onItemClick(AdapterView adapterView, View view, int position,135 long time) {136 Album album = (Album)adapterView.getItemAtPosition(position);137 PlayerActivity.launch(SearchActivity.this, album);138 }139 140 };141 142 private OnItemClickListener mPlaylistClickListener = new OnItemClickListener(){143 144 @Override145 public void onItemClick(AdapterView adapterView, View arg1, int position,146 long arg3) {147 PlaylistRemote playlistRemote = (PlaylistRemote) adapterView.getItemAtPosition(position);148 PlayerActivity.launch(SearchActivity.this, playlistRemote);149 }150 151 };152 153 /**154 * Allows cancelling search query155 * 156 * @author Lukasz Wisniewski157 */158 private class SearchingDialog extends LoadingDialog {159 160 private Integer mSearchMode;161 private BaseAdapter mAdapter;162 163 public SearchingDialog(Activity activity, int loadingMsg, int failMsg) {164 super(activity, loadingMsg, failMsg);165 }166 167 @Override168 public Integer doInBackground(Integer... params) {169 mSearchMode = params[0];170 switch(mSearchMode){171 case 0:172 // artist search173 albumSearch(0); 174 break;175 case 1:176 // tag search177 albumSearch(1); 178 break;179 case 2:180 // playlist search181 playlistSearch(); 182 break;183 case 3:184 // starred album search185 albumSearch(3); 186 break;187 default:188 }189 return mSearchMode;190 }191 192 @Override193 public void doStuffWithResult(Integer result) {194 mSearchListView.setAdapter(mAdapter);195 196 if(mSearchListView.getCount() > 0){197 mViewFlipper.setDisplayedChild(0); // display results198 } else {199 mViewFlipper.setDisplayedChild(1); // display no results message200 }201 202 // results are albums203 if(mSearchMode.equals(0) || mSearchMode.equals(1) || mSearchMode.equals(3)){204 mSearchListView.setOnItemClickListener(mAlbumClickListener);205 }206 207 // results are playlists208 if(mSearchMode.equals(2)){209 mSearchListView.setOnItemClickListener(mPlaylistClickListener);210 }211 }212 213 private void albumSearch(int id){214 JamendoGet2Api service = new JamendoGet2ApiImpl();215 String query = mSearchEditText.getText().toString();216 Album[] albums = null;217 try {218 switch (id) {219 case 0:220 albums = service.searchForAlbumsByArtist(query);221 SearchActivity.this.mSearchMode = SearchMode.Artist;222 break;223 case 1:224 albums = service.searchForAlbumsByTag(query);225 SearchActivity.this.mSearchMode = SearchMode.Tag;226 break;227 case 3:228 albums = service.getUserStarredAlbums(query);229 SearchActivity.this.mSearchMode = SearchMode.UserStarredAlbums;230 break;231 232 default:233 return;234 }235 236 AlbumAdapter albumAdapter = new AlbumAdapter(SearchActivity.this); 237 albumAdapter.setList(albums);238 albumAdapter.setListView(mSearchListView);239 mAdapter = albumAdapter;240 241 } catch (JSONException e) {242 e.printStackTrace();243 } catch (WSError e) {244 publishProgress(e);245 this.cancel(true);246 }247 }248 249 private void playlistSearch(){250 JamendoGet2Api service = new JamendoGet2ApiImpl();251 String user = mSearchEditText.getText().toString();252 try {253 mPlaylistRemotes = service.getUserPlaylist(user);254 if(mPlaylistRemotes != null){255 PlaylistRemoteAdapter purpleAdapter = new PlaylistRemoteAdapter(SearchActivity.this); 256 purpleAdapter.setList(mPlaylistRemotes);257 mAdapter = purpleAdapter;258 SearchActivity.this.mSearchMode = SearchMode.UserPlaylist;259 }260 261 } catch (JSONException e) {262 e.printStackTrace();263 } catch (WSError e) {264 publishProgress(e);265 this.cancel(true);266 }267 }268 269 }270 271 }
一、首先还是看onCreate方法:
1 /** Called when the activity is first created. */ 2 @Override 3 public void onCreate(Bundle savedInstanceState) { 4 super.onCreate(savedInstanceState); 5 requestWindowFeature(Window.FEATURE_NO_TITLE); 6 setContentView(R.layout.search); 7 8 mSearchSpinner = (Spinner)findViewById(R.id.SearchSpinner); 9 ArrayAdapter adapter = ArrayAdapter.createFromResource(10 this, R.array.search_modes, android.R.layout.simple_spinner_item);11 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);12 mSearchSpinner.setAdapter(adapter);13 14 mSearchButton = (Button)findViewById(R.id.SearchButton);15 mSearchButton.setOnClickListener(mSearchButtonListener);16 17 mSearchEditText = (EditText)findViewById(R.id.SearchEditText);18 mSearchListView = (ListView)findViewById(R.id.SearchListView);19 20 mViewFlipper = (ViewFlipper)findViewById(R.id.SearchViewFlipper);21 if(mSearchListView.getCount() == 0){22 mViewFlipper.setDisplayedChild(2); // search list hint23 }24 25 mGestureOverlayView = (GestureOverlayView) findViewById(R.id.gestures);26 mGestureOverlayView.addOnGesturePerformedListener(JamendoApplication27 .getInstance().getPlayerGestureHandler());28 }
oncreate中先是给SearchSpinner设置适配器,然后给SearchButton添加监听事件,最后设置GestureOverlayView的监听事件。有关gesture的请介绍移步
整个方法都很简单明了,这是SearchActiviy主要功能肯定是search所以重点就落在了SearchButton的onclick事件上。下面我们来看mSearchButtonListener:
1 private OnClickListener mSearchButtonListener = new OnClickListener(){ 2 3 @Override 4 public void onClick(View v) { 5 new SearchingDialog(SearchActivity.this, 6 R.string.searching, 7 R.string.search_fail) 8 .execute(mSearchSpinner.getSelectedItemPosition()); 9 }10 11 };
到这还没有看到真正的实现代码,onClick中只有一个SearchingDialog,继续看:
1 /** 2 * Allows cancelling search query 3 * 4 * @author Lukasz Wisniewski 5 */ 6 private class SearchingDialog extends LoadingDialog{ 7 8 private Integer mSearchMode; 9 private BaseAdapter mAdapter; 10 11 public SearchingDialog(Activity activity, int loadingMsg, int failMsg) { 12 super(activity, loadingMsg, failMsg); 13 } 14 15 @Override 16 public Integer doInBackground(Integer... params) { 17 mSearchMode = params[0]; 18 switch(mSearchMode){ 19 case 0: 20 // artist search 21 albumSearch(0); 22 break; 23 case 1: 24 // tag search 25 albumSearch(1); 26 break; 27 case 2: 28 // playlist search 29 playlistSearch(); 30 break; 31 case 3: 32 // starred album search 33 albumSearch(3); 34 break; 35 default: 36 } 37 return mSearchMode; 38 } 39 40 @Override 41 public void doStuffWithResult(Integer result) { 42 mSearchListView.setAdapter(mAdapter); 43 44 if(mSearchListView.getCount() > 0){ 45 mViewFlipper.setDisplayedChild(0); // display results 46 } else { 47 mViewFlipper.setDisplayedChild(1); // display no results message 48 } 49 50 // results are albums 51 if(mSearchMode.equals(0) || mSearchMode.equals(1) || mSearchMode.equals(3)){ 52 mSearchListView.setOnItemClickListener(mAlbumClickListener); 53 } 54 55 // results are playlists 56 if(mSearchMode.equals(2)){ 57 mSearchListView.setOnItemClickListener(mPlaylistClickListener); 58 } 59 } 60 61 private void albumSearch(int id){ 62 JamendoGet2Api service = new JamendoGet2ApiImpl(); 63 String query = mSearchEditText.getText().toString(); 64 Album[] albums = null; 65 try { 66 switch (id) { 67 case 0: 68 albums = service.searchForAlbumsByArtist(query); 69 SearchActivity.this.mSearchMode = SearchMode.Artist; 70 break; 71 case 1: 72 albums = service.searchForAlbumsByTag(query); 73 SearchActivity.this.mSearchMode = SearchMode.Tag; 74 break; 75 case 3: 76 albums = service.getUserStarredAlbums(query); 77 SearchActivity.this.mSearchMode = SearchMode.UserStarredAlbums; 78 break; 79 80 default: 81 return; 82 } 83 84 AlbumAdapter albumAdapter = new AlbumAdapter(SearchActivity.this); 85 albumAdapter.setList(albums); 86 albumAdapter.setListView(mSearchListView); 87 mAdapter = albumAdapter; 88 89 } catch (JSONException e) { 90 e.printStackTrace(); 91 } catch (WSError e) { 92 publishProgress(e); 93 this.cancel(true); 94 } 95 } 96 97 private void playlistSearch(){ 98 JamendoGet2Api service = new JamendoGet2ApiImpl(); 99 String user = mSearchEditText.getText().toString();100 try {101 mPlaylistRemotes = service.getUserPlaylist(user);102 if(mPlaylistRemotes != null){103 PlaylistRemoteAdapter purpleAdapter = new PlaylistRemoteAdapter(SearchActivity.this); 104 purpleAdapter.setList(mPlaylistRemotes);105 mAdapter = purpleAdapter;106 SearchActivity.this.mSearchMode = SearchMode.UserPlaylist;107 }108 109 } catch (JSONException e) {110 e.printStackTrace();111 } catch (WSError e) {112 publishProgress(e);113 this.cancel(true);114 }115 }116 117 }
好长一段代码。。。。看来是顺藤摸到瓜了,这就是整个SearchActivity中真正实现search过程的地方。
SearchingDialog这个内部类继承了LoadingDialog,关于LoadingDialog在上一篇博客中涉及到了这里就不赘述了,想看的可以移步 。
1. 首先看doInBackground(Integer... params),主要是根据获取searchSpinner选中的searchMode来调用不同search方法,mSearchMode = params[0];中的params[0]是调用在这个内部类的execute方法传进来的mSearchSpinner.getSelectedItemPosition()。其中涉及到了两个方法,albumSearch()和playlistSearch()。两个方法实现方法大体一样在这就以albumSearch为例分析下:
首先看albumSearch():
1 private void albumSearch(int id){ 2 JamendoGet2Api service = new JamendoGet2ApiImpl(); 3 String query = mSearchEditText.getText().toString(); 4 Album[] albums = null; 5 try { 6 switch (id) { 7 case 0: 8 albums = service.searchForAlbumsByArtist(query); 9 SearchActivity.this.mSearchMode = SearchMode.Artist;10 break;11 case 1:12 albums = service.searchForAlbumsByTag(query);13 SearchActivity.this.mSearchMode = SearchMode.Tag;14 break;15 case 3:16 albums = service.getUserStarredAlbums(query);17 SearchActivity.this.mSearchMode = SearchMode.UserStarredAlbums;18 break;19 20 default:21 return;22 }23 24 AlbumAdapter albumAdapter = new AlbumAdapter(SearchActivity.this); 25 albumAdapter.setList(albums);26 albumAdapter.setListView(mSearchListView);27 mAdapter = albumAdapter;28 29 } catch (JSONException e) {30 e.printStackTrace();31 } catch (WSError e) {32 publishProgress(e);33 this.cancel(true);34 }35 }
首先是JamendoGet2Api service = new JamendoGet2ApiImpl();获得从读取服务器的封装api,这里先不讨论api。然后根据传入的id值调用不同的方法来获取相应的内容,以id=0为例albums = service.searchForAlbumsByArtist(query); SearchActivity.this.mSearchMode = SearchMode.Artist;调用api的searchForAlbumsByArtist方法根据用户输入的mSearchEditText.getText()的查询内容来从服务器读取相应的ablum。然后设置mSearchMode。最后将获得的ablum传递给适配器albumAdapter。如果数据读取过程中捕获到WSError就调用publishProgress显示在也面上并结束线程。 这里我们要看一下这个适配器AlbumAdapter的具体内容:
AlbumAdapter
1 /** 2 * Adapter representing albums 3 * 4 * @author Lukasz Wisniewski 5 */ 6 public class AlbumAdapter extends ArrayListAdapter{ 7 8 public AlbumAdapter(Activity context) { 9 super(context);10 }11 12 @Override13 public View getView(int position, View convertView, ViewGroup parent) {14 View row=convertView;15 16 ViewHolder holder;17 18 if (row==null) {19 LayoutInflater inflater = mContext.getLayoutInflater();20 row=inflater.inflate(R.layout.album_row, null);21 22 holder = new ViewHolder();23 holder.image = (RemoteImageView)row.findViewById(R.id.AlbumRowImageView);24 holder.albumText = (TextView)row.findViewById(R.id.AlbumRowAlbumTextView);25 holder.artistText = (TextView)row.findViewById(R.id.AlbumRowArtistTextView);26 holder.progressBar = (ProgressBar)row.findViewById(R.id.AlbumRowRatingBar);27 28 row.setTag(holder);29 }30 else{31 holder = (ViewHolder) row.getTag();32 }33 34 holder.image.setDefaultImage(R.drawable.no_cd);35 holder.image.setImageUrl(mList.get(position).getImage(),position, getListView());36 holder.albumText.setText(mList.get(position).getName());37 holder.artistText.setText(mList.get(position).getArtistName());38 holder.progressBar.setMax(10);39 holder.progressBar.setProgress((int) (mList.get(position).getRating()*10));40 41 return row;42 }43 44 /**45 * Class implementing holder pattern,46 * performance boost47 * 48 * @author Lukasz Wisniewski49 */50 static class ViewHolder {51 RemoteImageView image;52 TextView albumText;53 TextView artistText;54 ProgressBar progressBar;55 }56 }
AlbumAdapter继承了ArrayListAdapter(之前介绍过),主要就是实现了ArrayListAdapter中的抽象方法getView,jamendo通过基类ArrayListAdapter提供的抽象方法分离了适配器的具体实现。AlbumAdapter没什么特别的就是把获得数据显示在相应的view上。为了避免每次调用重复创建convertView,使用了ViewHolder进行缓存配合setTag提高效率,ViewHolder是listview常用的优化方法。另外其中用到了RemoteImageView,一个ImageView的扩展类具体移步 。
2. 关注doStuffWithResult(Integer result)方法,这是实现的LoadingDialog的一个抽象方法,当doInBackground方法返回值不为null时调用。
1 @Override 2 public void doStuffWithResult(Integer result) { 3 mSearchListView.setAdapter(mAdapter); 4 5 if(mSearchListView.getCount() > 0){ 6 mViewFlipper.setDisplayedChild(0); // display results 7 } else { 8 mViewFlipper.setDisplayedChild(1); // display no results message 9 }10 11 // results are albums12 if(mSearchMode.equals(0) || mSearchMode.equals(1) || mSearchMode.equals(3)){13 mSearchListView.setOnItemClickListener(mAlbumClickListener);14 }15 16 // results are playlists17 if(mSearchMode.equals(2)){18 mSearchListView.setOnItemClickListener(mPlaylistClickListener);19 }20 }
首先是给listview设置刚刚获取到数据的Adapter,判断listview中是否有数据,如果有mViewFlipper就显示第一个view-SearchListView,如果没有就显示第二个view-no_results。然后根据search类型不同设置相应的item点击事件。如果search的结果是albums 就设置mAlbumClickListener,如果是playlists就设置mPlaylistClickListener。简单说就是当检索结果是专辑(ablum)就设置点击事件:点击后播放该ablum,如果是播放list就设置点击事件:显示playlist的具体内容。两个监听器源码给出:
mAlbumClickListener mPlaylistClickListener
1 private OnItemClickListener mAlbumClickListener = new OnItemClickListener(){ 2 3 @Override 4 public void onItemClick(AdapterView adapterView, View view, int position, 5 long time) { 6 Album album = (Album)adapterView.getItemAtPosition(position); 7 PlayerActivity.launch(SearchActivity.this, album); 8 } 9 10 };11 12 private OnItemClickListener mPlaylistClickListener = new OnItemClickListener(){13 14 @Override15 public void onItemClick(AdapterView adapterView, View arg1, int position,16 long arg3) {17 PlaylistRemote playlistRemote = (PlaylistRemote) adapterView.getItemAtPosition(position);18 PlayerActivity.launch(SearchActivity.this, playlistRemote);19 }
二、 Search状态的保存。由于search页面是基于本search页面的activity的跳转,也就是说从search跳转后有可能会再返回search页面,为了避免在返回时因为searchActivity被kill掉而没有数据,所以有必要保存。主要实现如下:
1 @SuppressWarnings("unchecked") 2 @Override 3 protected void onRestoreInstanceState(Bundle savedInstanceState) { 4 mSearchMode = (SearchMode) savedInstanceState.getSerializable("mode"); 5 if(mSearchMode != null){ 6 if(mSearchMode.equals(SearchMode.Artist) 7 || mSearchMode.equals(SearchMode.Tag) 8 || mSearchMode.equals(SearchMode.UserStarredAlbums)){ 9 AlbumAdapter adapter = new AlbumAdapter(this);10 adapter.setList((ArrayList) savedInstanceState.get("values"));11 mSearchListView.setAdapter(adapter);12 mSearchListView.setOnItemClickListener(mAlbumClickListener);13 }14 15 if(mSearchMode.equals(SearchMode.UserPlaylist)) {16 PlaylistRemoteAdapter adapter = new PlaylistRemoteAdapter(this); 17 adapter.setList((ArrayList ) savedInstanceState.get("values"));18 mSearchListView.setAdapter(adapter);19 mSearchListView.setOnItemClickListener(mPlaylistClickListener);20 }21 22 mViewFlipper.setDisplayedChild(savedInstanceState.getInt("flipper_page"));23 }24 super.onRestoreInstanceState(savedInstanceState);25 }26 27 @Override28 protected void onSaveInstanceState(Bundle outState) {29 if(mSearchMode != null){30 outState.putSerializable("mode", mSearchMode);31 if(mSearchMode.equals(SearchMode.Artist) 32 || mSearchMode.equals(SearchMode.Tag) 33 || mSearchMode.equals(SearchMode.UserStarredAlbums)){34 AlbumAdapter adapter = (AlbumAdapter)mSearchListView.getAdapter();35 outState.putSerializable("values", adapter.getList());36 }37 38 if(mSearchMode.equals(SearchMode.UserPlaylist)) {39 PlaylistRemoteAdapter adapter = (PlaylistRemoteAdapter)mSearchListView.getAdapter();40 outState.putSerializable("values", adapter.getList());41 }42 43 outState.putInt("flipper_page", mViewFlipper.getDisplayedChild());44 }45 super.onSaveInstanceState(outState);46 }
不做太多解释了,onSaveInstanceState(Bundle outState) 保存状态,onRestoreInstanceState(Bundle savedInstanceState)回复状态。
到这里SearchActivity就介绍完了,SearchActivity相对比较简单,有些内容在之前介绍过这里就没有赘述,大家可以看看以前的博客。
今天头脑有点晕,写的可能有点乱,将就这看吧。
休息~休息一下~~~