В моей предыдущей статье мы создали ваше первое приложение для Android с основной функцией Hello World. Что ж, похоже, настоящий Hello World в мобильном мире — это канал Twitter.
Из этого туториала вы узнаете, как создать простое приложение для Android, чтобы отобразить список твитов из API поиска на основе JSON, предоставленного Twitter. Мы будем работать через:
- отображение списка предметов
- настройка внешнего вида каждого элемента списка
- доступ к удаленным сервисам и анализ данных
- создание отзывчивых пользовательских интерфейсов
Для получения инструкций о том, как настроить среду разработки Android, ознакомьтесь с предыдущей статьей , которая проведет вас от установки программного обеспечения и создания рабочего пространства до запуска скелетного приложения в эмуляторе Android.
Отображение списков
Чтобы начать работу над нашим новым проектом, создайте новый проект Android в Eclipse ( Файл -> Создать -> Другое -> Android и выберите Android Project ) , ввод соответствующего имени проекта, имени приложения, имени пакета и имени для отдельного запуска.
Вы можете увидеть варианты, которые я использовал ниже:
Чтобы проверить, правильно ли вы создали проект, запустите приложение-скелет в эмуляторе, выделив имя проекта в Eclipse и выбрав « Выполнить» -> «Запуск от имени» -> « Приложение Android» .
В настоящее время созданная вами Activity
android.app.Activity
onCreate
ContentView
R.layout.mai
/res/layout/main.xml
android.app.ListActivity
Android SDK предоставляет удобный способ быстрого отображения списка данных с помощью суперкласса с именем Activity
Это действие уже предоставляет ContentView
ListView
Теперь измените суперкласс TwitterFeedActivity
ListActivity
ContentView
onCreate
Теперь ListView
ListAdaptors
ListView
ListActivity
setListAdaptor
Создайте несколько примеров данных, содержащих две строки, и предоставленный Android SDK адаптер ( ArrayAdaptor
ListViews
ListAdaptors
Cursor Adaptors
ListView
simple_list_item_1
Также необходимо предоставить адаптеру макет, который он может использовать для визуализации элементов в каждой строке. В приведенном ниже примере мы используем предоставленный макет Android SDK, public class TwitterFeedActivity extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] elements = {"Line 1", "Line 2"};
setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, elements));
}
}
ArrayAdaptor
Настройте свой код, как указано выше, и подтвердите следующие результаты в эмуляторе:
Настройка элементов списка
Базовый клиент Twitter должен отображать не менее двух полей в строке: автора твита и его содержимого. Чтобы достичь этого, вам придется выйти за пределы встроенного макета и Tweet
Начните с создания класса с именем Strings
Tweet
Затем создайте и заполните объект package com.sitepoint.mytwitter;
public class Tweet {
String author;
String content;
}
/res/layout/list_item.xml
Создайте XML-файл макета в TextViews
LinearLayout
Чтобы отобразить их один над другим, используйте android:orientation="vertical"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<TextView android:id="@+id/toptext" android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical" android:singleLine="true" />
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:id="@+id/bottomtext"
android:singleLine="true" />
</LinearLayout>
R
После создания XML-файла плагин Android Eclipse автоматически добавит его в качестве ссылки в созданный R
Этот /gen
/res
Это позволяет вашему Java-коду ссылаться на XML-элементы и файлы, созданные в папках R.layout.list_item
На файл, который вы только что создали, теперь можно ссылаться как Activity
Создайте закрытый класс (внутри TweetListAdaptor
ArrayAdaptor
ArrayList
Этот класс следует использовать для хранения Tweet
TextViews
getView
Это отображение переопределяет метод ListAdaptor
View
public View getView(int position, View convertView, ViewGroup parent) {
View
Кроме того, он должен — если возможно — повторно использовать любые объекты convertView
iew
Если для каждого элемента в списке необходимо создать новый объект V Views
Кэширование View
Полная реализация пользовательского TweetListAdaptor
Обратите внимание на «оператор if», проверяющий, может ли переданный convertView
Если View
ListView
Views
Это достигается с помощью View
Здесь вы увидите, как ссылаться на файл макета, используя сгенерированный класс LayoutService
R
После того, как представление создано (или повторно использовано), конкретный твит извлекается из R.layout.list_item
View
Затем вы можете получить ссылки на два элемента ArrayList
ListView
Когда у вас есть ссылки, вы можете установить их с соответствующим содержанием и автором из объекта TextView
android:id="@+id/toptext"
Теперь метод Tweet
private class TweetListAdaptor extends ArrayAdapter<Tweet> {
private ArrayList<Tweet> tweets;
public TweetListAdaptor(Context context,
int textViewResourceId,
ArrayList<Tweet> items) {
super(context, textViewResourceId, items);
this.tweets = items;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.list_item, null);
}
Tweet o = tweets.get(position);
TextView tt = (TextView) v.findViewById(R.id.toptext);
TextView bt = (TextView) v.findViewById(R.id.bottomtext);
tt.setText(o.content);
bt.setText(o.author);
return v;
}
}
который будет отображать следующее при запуске в эмуляторе:
Доступ к удаленным сервисам и анализ данных
Android SDK содержит пакеты, предназначенные для упрощения доступа к API на основе HTTP. HTTP-классы Apache включены и находятся в пакете onCreate
Вы будете использовать эти классы вместе с классами public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Tweet tweet = new Tweet();
tweet.author = "dbradby";
tweet.content = "Android in space";
ArrayList<Tweet> items = new ArrayList<Tweet>();
items.add(tweet);
TweetListAdaptor adaptor = new TweetListAdaptor(this,R.layout.list_item, items);
setListAdapter(adaptor);
}
Прежде чем какие-либо удаленные службы будут доступны из приложения Android, необходимо объявить разрешение. Это предупредит пользователя (вашего приложения) о том, что приложение может делать. Разрешения могут быть контролировать входящие SMS, читать адресную книгу или, в вашем случае, просто сделать запрос в Интернете. Эти разрешения отображаются пользователю в магазине Android Market перед установкой приложения, давая пользователю возможность выбрать, хотят ли они предоставить этому приложению заявленные разрешения.
Разрешение, которое вам нужно использовать, — ИНТЕРНЕТ и должно быть вставлено вне тега приложения в файле org.apache.http
org.json
Имея разрешение, мы можем создать частный метод в AndroidManifest.xml
</application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>Activity
Приведенный ниже код выполняет запрос и ищет результирующий массив JSON, который повторяется для извлечения ArrayList
Tweet
text
Теперь замените фиктивные данные, которые вы ранее использовали, вызовом метода from_user
private ArrayList<Tweet> loadTweets(){
ArrayList<Tweet> tweets = new ArrayList<Tweet>();
try {
HttpClient hc = new DefaultHttpClient();
HttpGet get = new
HttpGet("http://search.twitter.com/search.json?q=android");
HttpResponse rp = hc.execute(get);
if(rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
{
String result = EntityUtils.toString(rp.getEntity());
JSONObject root = new JSONObject(result);
JSONArray sessions = root.getJSONArray("results");
for (int i = 0; i < sessions.length(); i++) {
JSONObject session = sessions.getJSONObject(i);
Tweet tweet = new Tweet();
tweet.content = session.getString("text");
tweet.author = session.getString("from_user");
tweets.add(tweet);
}
}
} catch (Exception e) {
Log.e("TwitterFeedActivity", "Error loading JSON", e);
}
return tweets;
}
loadTweets
Запустите это в эмуляторе, и теперь вы должны увидеть, что реальные данные, возвращаемые из запроса API, отображаются в виде списка, как показано ниже:
Создание отзывчивых пользовательских интерфейсов
Код в его текущем состоянии может вызвать появление диалогового окна «Приложение не отвечает» (ANR), предлагающее пользователю выйти из приложения. Это может произойти из-за длительной работы по созданию удаленного запроса данных, выполняемого в таких методах, как onCreate
Длительные задачи никогда не должны выполняться в главном потоке приложения (который управляет циклом событий пользовательского интерфейса). Вместо этого они должны быть порождены в дочерние потоки для выполнения работы.
Хотя Java-класс TweetListAdaptor adaptor = new TweetListAdaptor(this,R.layout.list_item, loadTweets());
Элементы пользовательского интерфейса могут изменять свое состояние только из основного потока, поскольку инструментарий пользовательского интерфейса Android не является поточно-ориентированным, поэтому фоновый поток должен возвращать сообщение в основной поток, чтобы манипулировать пользовательским интерфейсом.
К счастью, Android SDK предоставляет удобный класс onCreate
Это достигается путем Thread
AsyncTask
AsyncTask
doInBackground
Когда onPostExecute
AsyncTask
doInBackgroun
По завершении метод onPostExecute
Чтобы использовать это в своем приложении, вам нужно реализовать частный класс в Activity
MyTask
AsyncTask
Вы можете переопределить метод doInBackground
loadTweets
Вместо того, чтобы возвращать ArrayList
Activity
Затем в onPostExecute
List Adaptor
onCreate
Метод onCreate
MyTask
Чтобы продемонстрировать, как пользовательский интерфейс отвечает во время выполнения фонового запроса, я также добавил ProgressDialog
AsyncTask
Полный список Activity
package com.sitepoint.mytwitter;
import java.util.ArrayList;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
public class TwitterFeedActivity extends ListActivity {
private ArrayList<Tweet> tweets = new ArrayList<Tweet>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new MyTask().execute();
}
private class MyTask extends AsyncTask<Void, Void, Void> {
private ProgressDialog progressDialog;
protected void onPreExecute() {
progressDialog = ProgressDialog.show(TwitterFeedActivity.this,
"", "Loading. Please wait...", true);
}
@Override
protected Void doInBackground(Void... arg0) {
try {
HttpClient hc = new DefaultHttpClient();
HttpGet get = new
HttpGet("http://search.twitter.com/search.json?q=android");
HttpResponse rp = hc.execute(get);
if(rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
{
String result = EntityUtils.toString(rp.getEntity());
JSONObject root = new JSONObject(result);
JSONArray sessions = root.getJSONArray("results");
for (int i = 0; i < sessions.length(); i++) {
JSONObject session = sessions.getJSONObject(i);
Tweet tweet = new Tweet();
tweet.content = session.getString("text");
tweet.author = session.getString("from_user");
tweets.add(tweet);
}
}
} catch (Exception e) {
Log.e("TwitterFeedActivity", "Error loading JSON", e);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
progressDialog.dismiss();
setListAdapter(new TweetListAdaptor(
TwitterFeedActivity.this, R.layout.list_item, tweets));
}
}
private class TweetListAdaptor extends ArrayAdapter<Tweet> {
private ArrayList<Tweet> tweets;
public TweetListAdaptor(Context context,
int textViewResourceId,
ArrayList<Tweet> items) {
super(context, textViewResourceId, items);
this.tweets = items;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.list_item, null);
}
Tweet o = tweets.get(position);
TextView tt = (TextView) v.findViewById(R.id.toptext);
TextView bt = (TextView) v.findViewById(R.id.bottomtext);
tt.setText(o.content);
bt.setText(o.author);
return v;
}
}
}
Теперь у вас должно быть законченное приложение, запрашивающее данные Twitter в фоновом режиме.
AsyncTask
Вы можете быть уверены, что мы расскажем об этом в следующих статьях. В то же время вы должны прочитать отличный документ Android, посвященный этим классам на http://developer.android.com .