In the last post, I had explained how you can use Retrofit API to consume Feedly’s feed search API. In this post, I will cover the integration of a SearchView with the feed search API.
To integrate SearchView with the custom data source, you need to implement the following interfaces in the fragment or activity.
- SearchView.OnQueryTextListener: This handles firing a query with the data provider as the user starts typing in the SearchView.
- SearchView.OnSuggestionListener: This handles click or selection of the suggested result from search.
The code for the implemented methods of these interfaces is show below:-
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 | //for onQueryTextListener @Override public boolean onQueryTextSubmit(String s) { if (s.length() > 2) { loadData(s); } return true; } @Override public boolean onQueryTextChange(String s) { if (s.length() > 2) { loadData(s); } return true; } //for OnSuggestionListener @Override public boolean onSuggestionSelect(int position) { Cursor cursor = (Cursor) searchView.getSuggestionsAdapter().getItem(position); String feedName = cursor.getString(4); searchView.setQuery(feedName, false); searchView.clearFocus(); return true; } @Override public boolean onSuggestionClick(int position) { Cursor cursor = (Cursor) searchView.getSuggestionsAdapter().getItem(position); String feedName = cursor.getString(4); searchView.setQuery(feedName, false); searchView.clearFocus(); return true; } |
The layout for the customized list item which will appear when the user starts typing in the SearchView is shown below:-
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 | <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/icon_feed" android:layout_width="32dp" android:layout_height="32dp" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:layout_alignParentStart="true" android:contentDescription="@string/ImgContentDescription" /> <TextView android:id="@+id/feed_url_text" android:layout_width="wrap_content" android:layout_height="23dp" android:paddingLeft="5dp" android:layout_toRightOf="@id/icon_feed" android:paddingTop="0dp" android:paddingBottom="2dp" /> <LinearLayout android:id="@+id/subscriber_layout" android:orientation="horizontal" android:layout_below="@+id/feed_url_text" android:layout_toRightOf="@id/icon_feed" android:layout_width="wrap_content" android:layout_height="25dp"> <ImageView android:src="@drawable/icon_user_light" android:paddingLeft="5dp" android:layout_width="24dp" android:layout_height="24dp" /> <TextView android:id="@+id/subscriber_count" android:text="@string/feed_name" android:layout_width="wrap_content" android:paddingTop="2dp" android:paddingLeft="2dp" android:layout_height="23dp" /> </LinearLayout> </RelativeLayout> |
As, I am using a custom layout for the list item that appears in the search, So I have extended the SimpleCursorAdapter to bind the view to the underlying data as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class SearchFeedResultsAdaptor extends SimpleCursorAdapter { private static final String tag=SearchFeedResultsAdaptor.class.getName(); private Context context=null; public SearchFeedResultsAdaptor(Context context, int layout, Cursor c, String[] from, int[] to, int flags) { super(context, layout, c, from, to, flags); this.context=context; } @Override public void bindView(View view, Context context, Cursor cursor) { ImageView imageView=(ImageView)view.findViewById(R.id.icon_feed); TextView textView=(TextView)view.findViewById(R.id.feed_url_text); TextView subscribersView=(TextView)view.findViewById(R.id.subscriber_count); ImageTagFactory imageTagFactory = ImageTagFactory.newInstance(context, R.drawable.rss_icon); imageTagFactory.setErrorImageId(R.drawable.rss_icon); ImageTag tag = imageTagFactory.build(cursor.getString(2),context); imageView.setTag(tag); FeedReaderApplication.getImageManager().getLoader().load(imageView); textView.setText(cursor.getString(4) + " : " + cursor.getString(1)); subscribersView.setText(cursor.getString(3)); } } |
In this adapter class, I am binding the layout components to the elements returned by cursor in the overridden bindView method . I am also using the novoda image loader to load the icon image from the URL returned by Feedly service.
Since, I am using a fragment , so the code to initialize the SearchView is placed in the OnActivityCreated method as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 | @Override //list of columns public static String[] columns = new String[]{"_id", "FEED_URL", "FEED_ICON", "FEED_SUBSCRIBERS", "FEED_TITLE"}; public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); searchView = (SearchView) getView().findViewById(R.id.searchFeedView); searchView.setOnQueryTextListener(this); searchView.setOnSuggestionListener(this); mSearchViewAdapter = new SearchFeedResultsAdaptor(this.getActivity(), R.layout.search_feed_list_item, null, columns,null, -1000); searchView.setSuggestionsAdapter(mSearchViewAdapter); } |
The loadData method which is invoked from the onQueryTextChanged method loads the Feedly search results as shown below:-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | private void loadData(String searchText) { //specify endpoint and build the restadapter instance RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("http://feedly.com") .build(); //Now use restadapter to create an instance of your interface FeedlySuggestionsService searchService = restAdapter.create(FeedlySuggestionsService.class); //populate the request parameters HashMap queryMap = new HashMap(); queryMap.put("query", searchText); //implement the Callback method for retrieving the response searchService.searchFeeds(queryMap, new Callback<FeedlyResponse>() { @Override public void success(FeedlyResponse feedlyResponse, Response response) { MatrixCursor matrixCursor = convertToCursor(feedlyResponse.getResults()); mSearchViewAdapter.changeCursor(matrixCursor); } @Override public void failure(RetrofitError error) { Log.e(tag, error.toString()); } }); } |
If you notice the highlighted lines in the above method, you will see that the result returned by the REST service is in List format. Now, as the SearchView accepts only CursorAdapter as its SuggestionAdapter, so we have to convert the List into a Cursor object. To do so, I have created a convertToCursor method which iterates the List to return a MatrixCursor object. After the conversion is done, I am just replacing the existing cursor of the Suggestion Adapter with this new value. The method to convert the list to MatrixCursor is shown below:-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | private MatrixCursor convertToCursor(List<FeedlyResult> feedlyResults) { MatrixCursor cursor = new MatrixCursor(columns); int i = 0; for (FeedlyResult feedlyResult : feedlyResults) { String[] temp = new String[5]; i = i + 1; temp[0] = Integer.toString(i); String feedUrl = feedlyResult.getFeedId(); if (feedUrl != null) { int index = feedUrl.indexOf("feed/"); if (index != -1) { feedUrl = feedUrl.substring(5); } } temp[1] = feedUrl; temp[2] = feedlyResult.getIconUrl(); temp[3] = feedlyResult.getSubscribers(); temp[4] = feedlyResult.getTitle(); cursor.addRow(temp); } return cursor; } |
The above method takes the column names for the Cursor from a static String array as shown above the onActivityCreated method.
That’s it ! now you have a SearchView integrated with a REST service.
Go Top