Download the top stories from the
New York Times
API in JSON format,
and display them in a
TextView
and a touch-sensitive
ListView
.
We saw a
ListView
whose
View
s
contained a title and subtitle in
CursorAdapter
and in
Sqlite.
See the
API
(Application Program Interface),
Requesting a Key,
and the
Top
Stories API.
MainActivity.java
.
Class
MainActivity
contains the inner class
DownloadTask
.
activity_main.xml
is a
RelativeLayout
containing a
TextView
.
The
TextView
is scrollable because of
android:scrollbars="vertical"
and the
setMovementMethod
in
useTheResult
.
strings.xml
dimens.xml
AndroidManifest.xml
has
uses-permission
INTERNET
.
build.gradle
(Module: app).
Get a key from the New York Times.
ListView
.
activity_main.xml
,
replace the
TextView
with
<ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="wrap_content"/> <TextView android:id="@android:id/empty" android:layout_width="match_parent" android:layout_height="match_parent" android:text="No articles."/>
ArticleAdapter.java
.
It uses a layout file on your hard disk,
~/Library/Android/sdk/platforms/android-22/data/res/layout/simple_list_item_2.xml
which contains an object of the deprecated class
TwoLineListItem
.
But I was too lazy to write my own layout file containing a
LinearLayout
.
package edu.nyu.scps.top; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; import android.widget.TwoLineListItem; public class ArticleAdapter extends BaseAdapter { private Context context; String[] title; String[] abs; public ArticleAdapter(Context context, String[] title, String[] abs) { this.context = context; this.title = title; this.abs = abs; } @Override public int getCount() { return title.length; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { TwoLineListItem twoLineListItem; if (convertView != null) { twoLineListItem = (TwoLineListItem)convertView; } else { LayoutInflater inflater = LayoutInflater.from(context); twoLineListItem = (TwoLineListItem)inflater.inflate(android.R.layout.simple_list_item_2, null); } TextView text1 = (TextView)twoLineListItem.findViewById(android.R.id.text1); text1.setText(title[position]); TextView text2 = (TextView)twoLineListItem.findViewById(android.R.id.text2); text2.setText(abs[position]); return twoLineListItem; } }
useTheResult
to
the following.
When you touch an item,
it will launch a web browser with the full article.
That’s because when you search for an
Activity
that’s capable of performing an
ACTION_VIEW
with a piece of information whose
Uri
starts with
http:
,
the
Intent
will find the web browser for you.
See the browser’s
AndroidManifest.xml
,
lines
69
and
83.
private void useTheResult(String json) { TextView empty = (TextView)findViewById(android.R.id.empty); int n; //number of articles String[] title; String[] abs; //"abstract" is Java keyword final String[] url; if (json == null) { empty.setText("Couldn't get JSON string from server."); return; } try { JSONObject jSONObject = new JSONObject(json); JSONArray results = jSONObject.getJSONArray("results"); n = results.length(); title = new String[n]; abs = new String[n]; url = new String[n]; for (int i = 0; i < n; ++i) { JSONObject result = results.getJSONObject(i); title[i] = result.getString("title"); abs[i] = result.getString("abstract"); url[i] = result.getString("url"); } } catch (JSONException exception) { empty.setText(exception.toString()); return; } ArticleAdapter adapter = new ArticleAdapter(this, title, abs); ListView listView = (ListView)findViewById(R.id.listView); listView.setAdapter(adapter); listView.setEmptyView(empty); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Uri uri = Uri.parse(url[position]); Intent intent = new Intent(Intent.ACTION_VIEW, uri); //find web browser startActivity(intent); //To return to here, touch Android back button. } }); }
TwoLineListItem
.
Create your own layout file containing a
LinearLayout
as we did in
CursorAdapter.
ListView
?
What about in landscape orientation?
If they’re still too long,
display each article’s thumbnail image and title instead.
See WeatherGrid
for downloading an icon.
The JSON will be parsed in the second thread.
article.xml
to replace
simple_list_item_2.xml
.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal" android:paddingBottom="8dp" android:paddingLeft="0dp" android:paddingRight="?attr/listPreferredItemPaddingRight" android:paddingTop="8dp"> <RelativeLayout android:layout_width="40dp" android:layout_height="40dp"> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true"/> </RelativeLayout> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
ArticleAdapter
:
Bitmap[] bitmap;Initialize the field with an extra argument of the constructor of class
ArticleAdapter
.
public ArticleAdapter(/* etc. */, Bitmap[] bitmap) { //etc. this.bitmap = bitmap;Change the
getView
method of the adapter to
@Override public View getView(int position, View convertView, ViewGroup parent) { LinearLayout linearLayout; if (convertView != null) { linearLayout = (LinearLayout)convertView; } else { LayoutInflater inflater = LayoutInflater.from(context); linearLayout = (LinearLayout)inflater.inflate(R.layout.article, null); } ImageView imageView = (ImageView)linearLayout.findViewById(R.id.imageView); if (bitmap[position] != null) { imageView.setImageBitmap(bitmap[position]); } TextView textView = (TextView)linearLayout.findViewById(R.id.textView); textView.setText(title[position]); return linearLayout; }
n
,
title
,
abs
,
and
url
in
useTheResult
will become fields of class
MainActivity
.
Add an additional fields to this class:
Bitmap[] bitmap;Add this method to class
MainActivity
.
private Bitmap downloadThumbnail(String urlString) { HttpURLConnection connection = null; InputStream inputStream = null; byte[] byteArray = null; try { URL url = new URL(urlString); connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("GET"); connection.setDoInput(true); connection.connect(); //Read the response. inputStream = connection.getInputStream(); byte[] buffer = new byte[1024]; ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); while (inputStream.read(buffer) != -1) { outputStream.write(buffer); } byteArray = outputStream.toByteArray(); } catch (Exception exception) { Log.e("myTag", "downloadThumbnail", exception); } finally { try { inputStream.close(); } catch (Exception exception) { Log.e("myTag", "downloadThumbnail", exception); return null; } try { connection.disconnect(); } catch (Exception exception) { Log.e("myTag", "downloadThumbnail", exception); return null; } } if (byteArray == null || byteArray.length == 0) { return null; } return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length); }
doInBackground
to
@Override protected Void doInBackground(String... urlString) { String json; //Must be declared outside of try block, //so we can mention them in finally block. HttpURLConnection httpURLConnection = null; BufferedReader bufferedReader = null; try { URL url = new URL(urlString[0]); httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setRequestMethod("GET"); httpURLConnection.connect(); InputStream inputStream = httpURLConnection.getInputStream(); if (inputStream == null) { return null; } InputStreamReader inputStreamReader = new InputStreamReader(inputStream); bufferedReader = new BufferedReader(inputStreamReader); String line; StringBuffer buffer = new StringBuffer(); while ((line = bufferedReader.readLine()) != null) { buffer.append(line); } if (buffer.length() == 0) { json = null; } json = buffer.toString(); } catch (IOException exception) { Log.e("myTag", "doInBackground ", exception); return null; } finally { if (httpURLConnection != null) { httpURLConnection.disconnect(); } if (bufferedReader != null) { try { bufferedReader.close(); } catch (final IOException exception) { Log.e("myTag", "doInBackground ", exception); } } } if (json == null) { //empty.setText("Couldn't get JSON string from server."); return null; } try { JSONObject jSONObject = new JSONObject(json); JSONArray results = jSONObject.getJSONArray("results"); n = results.length(); Log.d("myTag", "n = " + n); title = new String[n]; abs = new String[n]; url = new String[n]; bitmap = new Bitmap[n]; for (int i = 0; i < n; ++i) { JSONObject result = results.getJSONObject(i); title[i] = result.getString("title"); abs[i] = result.getString("abstract"); url[i] = result.getString("url"); try { JSONArray multimedia = result.getJSONArray("multimedia"); JSONObject medium = multimedia.getJSONObject(0); String thumbnailUrl = medium.getString("url"); bitmap[i] = downloadThumbnail(thumbnailUrl); } catch (JSONException exception) { bitmap[i] = null; } } } catch (JSONException exception) { //empty.setText(exception.toString()); return null; } return null; }
onPostExecute
is
Void v
and the return type is
void
.
It calls
useTheResult
with no argument.
The
AsyncTsk
is
<String, Void, Void>
.
useTheResult
is
private void useTheResult() { TextView empty = (TextView) findViewById(android.R.id.empty); ArticleAdapter adapter = new ArticleAdapter(this, title, abs, bitmap); ListView listView = (ListView) findViewById(R.id.listView); listView.setAdapter(adapter); listView.setEmptyView(empty); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView> parent, View view, int position, long id) { Uri uri = Uri.parse(url[position]); Intent intent = new Intent(Intent.ACTION_VIEW, uri); //find web browser startActivity(intent); //To return to here, touch Android back button. } }); }