Статьи

Загрузка данных Twitter в Android с помощью списков

В моей предыдущей статье мы создали ваше первое приложение для Android с основной функцией Hello World. Что ж, похоже, настоящий Hello World в мобильном мире — это канал Twitter.

Из этого туториала вы узнаете, как создать простое приложение для Android, чтобы отобразить список твитов из API поиска на основе JSON, предоставленного Twitter. Мы будем работать через:

  • отображение списка предметов
  • настройка внешнего вида каждого элемента списка
  • доступ к удаленным сервисам и анализ данных
  • создание отзывчивых пользовательских интерфейсов

Для получения инструкций о том, как настроить среду разработки Android, ознакомьтесь с предыдущей статьей , которая проведет вас от установки программного обеспечения и создания рабочего пространства до запуска скелетного приложения в эмуляторе Android.

Отображение списков

Чтобы начать работу над нашим новым проектом, создайте новый проект Android в Eclipse ( Файл -> Создать -> Другое -> Android и выберите Android Project ) , ввод соответствующего имени проекта, имени приложения, имени пакета и имени для отдельного запуска.

Вы можете увидеть варианты, которые я использовал ниже:

Настройка Android-проекта

Чтобы проверить, правильно ли вы создали проект, запустите приложение-скелет в эмуляторе, выделив имя проекта в Eclipse и выбрав « Выполнить» -> «Запуск от имени» -> « Приложение Android» .

В настоящее время созданная вами Activityandroid.app.ActivityonCreateContentViewR.layout.mai/res/layout/main.xmlandroid.app.ListActivity Android SDK предоставляет удобный способ быстрого отображения списка данных с помощью суперкласса с именем Activity Это действие уже предоставляет ContentViewListView

Теперь измените суперкласс TwitterFeedActivityListActivityContentViewonCreate

Теперь ListView ListAdaptorsListViewListActivitysetListAdaptor

Создайте несколько примеров данных, содержащих две строки, и предоставленный Android SDK адаптер ( ArrayAdaptorListViewsListAdaptorsCursor AdaptorsListViewsimple_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

Настройте свой код, как указано выше, и подтвердите следующие результаты в эмуляторе:

Эмулятор Android

Настройка элементов списка

Базовый клиент Twitter должен отображать не менее двух полей в строке: автора твита и его содержимого. Чтобы достичь этого, вам придется выйти за пределы встроенного макета и Tweet

Начните с создания класса с именем StringsTweet Затем создайте и заполните объект package com.sitepoint.mytwitter;

public class Tweet {
String author;
String content;
}

 /res/layout/list_item.xml

Создайте XML-файл макета в TextViewsLinearLayout Чтобы отобразить их один над другим, используйте 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

Создайте закрытый класс (внутри TweetListAdaptorArrayAdaptorArrayList Этот класс следует использовать для хранения TweetTextViewsgetView

Это отображение переопределяет метод ListAdaptorViewpublic View getView(int position, View convertView, ViewGroup parent) {

 View

Кроме того, он должен — если возможно — повторно использовать любые объекты convertViewiew Если для каждого элемента в списке необходимо создать новый объект V Views Кэширование View

Полная реализация пользовательского TweetListAdaptor Обратите внимание на «оператор if», проверяющий, может ли переданный convertView Если ViewListViewViews

Это достигается с помощью View Здесь вы увидите, как ссылаться на файл макета, используя сгенерированный класс LayoutServiceR

После того, как представление создано (или повторно использовано), конкретный твит извлекается из R.layout.list_itemView Затем вы можете получить ссылки на два элемента ArrayListListView Когда у вас есть ссылки, вы можете установить их с соответствующим содержанием и автором из объекта 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 Twitter Feed

Доступ к удаленным сервисам и анализ данных

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, который повторяется для извлечения ArrayListTweet

 text

Теперь замените фиктивные данные, которые вы ранее использовали, вызовом метода from_userprivate 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, отображаются в виде списка, как показано ниже:

Эмулятор Android MyTwitter

Создание отзывчивых пользовательских интерфейсов

Код в его текущем состоянии может вызвать появление диалогового окна «Приложение не отвечает» (ANR), предлагающее пользователю выйти из приложения. Это может произойти из-за длительной работы по созданию удаленного запроса данных, выполняемого в таких методах, как onCreate

Длительные задачи никогда не должны выполняться в главном потоке приложения (который управляет циклом событий пользовательского интерфейса). Вместо этого они должны быть порождены в дочерние потоки для выполнения работы.

Хотя Java-класс TweetListAdaptor adaptor = new TweetListAdaptor(this,R.layout.list_item, loadTweets());

Элементы пользовательского интерфейса могут изменять свое состояние только из основного потока, поскольку инструментарий пользовательского интерфейса Android не является поточно-ориентированным, поэтому фоновый поток должен возвращать сообщение в основной поток, чтобы манипулировать пользовательским интерфейсом.

К счастью, Android SDK предоставляет удобный класс onCreate Это достигается путем ThreadAsyncTaskAsyncTaskdoInBackground

Когда onPostExecuteAsyncTaskdoInBackgroun По завершении метод onPostExecute

Чтобы использовать это в своем приложении, вам нужно реализовать частный класс в ActivityMyTaskAsyncTask Вы можете переопределить метод doInBackgroundloadTweets

Вместо того, чтобы возвращать ArrayListActivity Затем в onPostExecuteList Adaptor onCreate Метод onCreateMyTask

Чтобы продемонстрировать, как пользовательский интерфейс отвечает во время выполнения фонового запроса, я также добавил ProgressDialogAsyncTask Полный список 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 .