Haciendo un cliente de Twitter en Android.

3
20018

Haciendo un cliente de Twitter en Android

0. Índice de contenidos.

1. Entorno

Este tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 17′ (2.8 GHz Intel Core Duo, 8GB DDR3 SDRAM)
  • Sistema Operativo: Mac OS X Lion 10.8.5
  • Android Studio

2. Introducción

En este tutorial vamos a ver cómo crear un sencillo cliente de Twitter en Android con el IDE Android Studio, con la finalidad de explicar el uso de los Fragment en Android y ver las ventajas que nos proporcionan frente al desarrollo usando únicamente con Actividades.

Para realizar este tutorial usaremos el sistema de build Gradle, que viene integrado con Android Studio y usaremos dos librerías: twitter4j, que nos ayudará a conectarnos a la API de Twitter a través de un wrapper Java y Picasso, que nos permitirá descargar imágenes de una manera sencilla, además de ofrecernos un sistema de caché y evitará leaks de memoria.

3. Creando un proyecto Android con Android Studio

Android Studio es un IDE basado en IntelliJ 13 desarrollado por Google y JetBrains, que se puede obtener gratuitamente en
la página oficial
, para realizar este tutorial también necesitaremos el
SDK de Android
instalado.

Una vez tengamos todo instalado, creamos un nuevo proyecto con Android Studio y seguimos el Wizard, en la primera pantalla marcaremos la casilla Fragment en Support Mode, para que nos añada la librería support_v4 que nos permitirá trabajar con Fragment en cualquier versión de Android ( ya que los fragment no fueron añadidos a Android hasta la versión 3.0 ).

creacion_proyecto

creacion_proyecto

creacion_proyecto

Eliminamos el código que nos ha creado por defecto y dejamos únicamente el siguiente código:

public class TimelineActivity extends FragmentActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_timeline);
    }
}

Abrimos el archivo .xml que contiene la vista de la aplicación
res/layout/activity_timeline.xml
y añadimos una vista
ProgressBar
mediante el InterfaceBuilder que nos ofrece Android Studio, la barra de progreso indicará al usuario que se está cargando la información.

Ejecutamos la aplicación y el resultado debería ser el siguiente:

pantalla_loading

4. Integración con la API de Twitter

Para integrarnos con Twitter vamos a usar la fantástica librería twitter4j la cual nos ofrecerá una jerarquía de clases Java con las diferentes entidades que usa la API de Twitter, además de abstraernos de crear las conexiones y parte de la autenticación con los servicios.

Para integrarla en nuestro proyecto abrimos el archivo
build.gradle
y en la sección de dependencias de proyecto ( la parte de dependencias de abajo ) añadimos la dependencia de twitter4j

dependencies {
        compile 'com.android.support:support-v4:19.0.0'
        compile 'com.android.support:appcompat-v7:+'
        compile 'org.twitter4j:twitter4j-core:3.0.5'
        compile 'org.twitter4j:twitter4j-async:3.0.5'
}

También es importante que agreguemos los permisos para conectarnos a internet en el fichero
AndroidManifest.xml
:

    <!-- Permission - Internet Connect -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Network State Permissions -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Para poder usar la Api de Twitter necesitaremos una Api Key y permiso del usuario para poder acceder a la información de su cuenta, de momento vamos a utilizar nuestra cuenta e inyectaremos nuestro token directamente, para ello, vamos a la sección de developers de Twitter y creamos una nueva aplicación:

api_twitter

Una vez dentro, le damos a crear nuestro Token de Acceso y copiamos los cuatro valores ( Consumer Key, Consumer Secret, Access Token, Access Token Secret ) :

api_twitter

Una vez tengamos los datos necesarios, tan solo necesitamos indicárselos a la librería para que pueda conectarse con la API de Twitter, para ello hay
varias maneras
, creo que la más fácil es hacerlo programáticamente a traves del configurationBuilder.

Vamos a crear una clase que nos permitirá obtener el Timeline del usuario con el que hayamos realizado los pasos anteriores:

public class TweetRepository {
    private static TweetRepository instance;

    private TweetRepository() {

    }

    public static TweetRepository getInstance() {
        if (instance == null) {
            instance = new TweetRepository();
        }
        return instance;
    }

    private ConfigurationBuilder getConfiguration() {
        ConfigurationBuilder cb = new ConfigurationBuilder();
        cb.setDebugEnabled(true)
                .setOAuthConsumerKey("PEGAR LA CLAVE OBTENIDA")
                .setOAuthConsumerSecret("PEGAR LA CLAVE OBTENIDA")
                .setOAuthAccessToken("PEGAR LA CLAVE OBTENIDA")
                .setOAuthAccessTokenSecret("PEGAR LA CLAVE OBTENIDA");

        return cb;
    }

    public void getTimelineAsync(TwitterListener listener) {
        AsyncTwitterFactory factory = new AsyncTwitterFactory((getConfiguration().build()));
        AsyncTwitter asyncTwitter = factory.getInstance();
        asyncTwitter.addListener(listener);
        asyncTwitter.getHomeTimeline();
    }
}

El método getTimelineAsync nos devolverá de manera asincrona los últimos 20 Tweets ( clase Status en twitter4j ) pertenecientes al Timeline del usuario, al ser un método asíncrono tenemos que pasarle por parámetro la clase que manejará el Callback, las comunicaciones con servicios de Internet en Android tienen que realizarse en Threads distintos al de la UI de manera asíncrona, o sino Android lanzará una Excepción.

5. Mostrando la lista de Tweets

Una vez tengamos la lista de Tweets en memoria tenemos que mostrarla de alguna manera en pantalla, para ello vamos a usar el tipo de Fragment ListFragment el cual funciona de una manera bastante parecida a ListView, nos permitirá mostrar una lista de Vistas, las vistas pueden ser del tipo que queramos, en este caso vamos a mostrar una lista sencilla de TextViews que contendrán el texto de los Tweets.

En la clase
TimelineActivity
añadimos el siguiente código:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_timeline);

    //Buscamos a ver si hay algún fragmento en el contenedor
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.container);

    // En caso de que no haya ningún fragmento, obtenemos los Tweets del Timeline de la API de twtitter y manejamos el Callback
    if (fragment == null) {
        TweetRepository.getInstance().getTimelineAsync(timelineListener); // => timelineListener
    } else {
        View progressBar = findViewById(R.id.progressBar);
        progressBar.setVisibility(View.GONE);
    }
}

// Esta clase interna es la encargada de manejar el callback,  tiene dos métodos para manejar la posibilidad de éxito y de error.
TwitterListener timelineListener = new TwitterAdapter() {

    @Override
    public void gotHomeTimeline(ResponseList<Status> statuses) {
        showTimeline(statuses);
    }

    @Override
    public void onException(TwitterException te, TwitterMethod method) {
        showError();
    }
};

A continuación deberemos crear el método showTimeline, que será el encargado de insertar el ListFragment y pasarle los Tweets que deberá mostrar:

private void showTimeline(ResponseList<Status> statuses) {
    // Creamos un array de Strings con el texto de los Status( Tweets )
    String[] tweets = new String[statuses.size()];
    int counter = 0;
    for (Status status : statuses) {
        tweets[counter] = status.getText();
        counter++;
    }

    // Lo guardamos en un bundle, el cual le pasaremos al Fragment
    final Bundle bundle = new Bundle();
    bundle.putStringArray("tweets", tweets);

    // Debido a que el callback se está ejecutando en otro Thread distinto al Thread de UI, necesitamos mandar un mensaje
    // Al Thread de UI para poder actualizar la vista, para ello usamos el método runOnUiThread de la clase Activity
    this.runOnUiThread(new Runnable() {
        @Override
        public void run() {

            // Ocultamos la barra de progreso
            View progressBar = findViewById(R.id.progressBar);
            progressBar.setVisibility(View.GONE);

            // Insertamos el TimelineFragment
            Fragment fragment = new TimelineFragment();
            //Añadimos el bundle con los tweets que hemos creado anteriormente
            fragment.setArguments(bundle);
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.container, fragment)
                    .commit();
        }
    });
}

Para comunicarse entre Actividades y Fragment, Android prefiere el uso de Bundles sobre variables globales/estáticas, esto facilita el crear Actividades y Fragments reusables ya que solo usan la información que se les pasa, y no información guardada en estados globales.

Y el TimelineFragment:

public class TimelineFragment extends ListFragment{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //Obtenemos los tweets
        String[] tweets = getArguments().getStringArray("tweets");

        // El ArrayAdapter creará la jerarquia de vistas a partir de un Array de Objetos
        // En este caso usaremos un layout que nos proporciona Android que simplemente mostrará
        // La lista de Strings, el último parámetro es el array con los objetos a mostrar
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
                inflater.getContext(),
                android.R.layout.simple_list_item_1,
                tweets
        );

        // Este método es parte de la clase ListFragment y nos permitirá indicarle cual es el adaptador de la vista.
        setListAdapter(arrayAdapter);

        return super.onCreateView(inflater, container, savedInstanceState);
    }
}

Arrancamos la aplicación y ya deberíamos ver la lista de Tweets de nuestro Timeline:

timeline

Creando la vista de detalle

El siguiente paso es crear una vista de detalle de cada Tweet mostrando el usuario que lo ha publicado y el número de veces que ha sido hecho favorito y retwitteado, gracias a la librería twitter4j además de cada Tweet( Status ) tenemos un objeto User que contiene alguna la información del usuario que ha publicado el Tweet, como puede ser el nick o la url con la imagen de perfil.

Vamos a crear una clase llamada StatusFragment:

public class StatusFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Obtenemos el Bundle con la información del Tweet a mostrar
        Bundle arguments = getArguments();
        Status status = (Status) arguments.getSerializable(TimelineActivity.SELECTED_STATUS);

        // Obtenemos el objeto de usuario asociado al Tweet
        User user = status.getUser();

        // Hacemos Inflate del archivo xml de vista
        View view = inflater.inflate(R.layout.fragment_status, container, false);

        TextView statusTextView = (TextView) view.findViewById(R.id.statusTextView);
        TextView favoritosTextView = (TextView) view.findViewById(R.id.favoritosTextView);
        TextView retweetsTextView = (TextView) view.findViewById(R.id.retweetsTextView);
        TextView userTextView = (TextView) view.findViewById(R.id.userTextView);
        ImageView avatarImageView = (ImageView) view.findViewById(R.id.avatarImageView);

        // Actualizamos los TextView con la información del objeto
        statusTextView.setText(status.getText());
        favoritosTextView.setText(status.getFavoriteCount() + " Favoritos ");
        retweetsTextView.setText(status.getRetweetCount() + " Retweets ");
        userTextView.setText(user.getName());

        // Usamos la libería Picasso para descargar la imagen del avatar de la URL y insertarla en el ImageView
        Picasso.with(getActivity().getApplicationContext()).load(user.getMiniProfileImageURL()).into(avatarImageView);

        return view;
    }
}

Y la vista asociada:
fragment_status.xml
, tendrá una apariencia similar a:

timeline

Se puede encontrar el código completo, incluido el del XML para crear esta vista, al final del tutorial

6. Conectando los dos Fragments

El siguiente paso es conectar el TimelineFragment con el StatusFragment, para ello vamos a modificar el TimelineFragment para que avise a la actividad que lo contiene cada vez que se hace click en un item de la lista.

Vamos a crear una interfaz que podrán implementar las actividades que contengan al TimelineFragment , además de implementar dos métodos de ListFragment que nos permitirá notificar a la actividad cuando se pulse un elemento:

public class TimelineFragment extends ListFragment{

    public static final String TWEETS_KEY ="tweets";
    OnListItemClickedCallbackHandler callbackHandler;

    // Las actividades que contengan este fragment pueden implementar esta interfaz para recibir
    // Eventos de cuando se hace click en un item
    public interface OnListItemClickedCallbackHandler{
        public void OnListItemClicked(int position);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ...
    }

    // A este método lo invoca Android en el momento en el que inserta el Fragment en la actividad
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            // Intentamos castear la actividad, en caso de que salte la excepción significa que la actividad no implementa la interfaz
            // Por lo tanto no recibirá callbacks
            callbackHandler = (OnListItemClickedCallbackHandler) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        // Si la actividad implementa la interfaz llamamos al método que tenga la actividad para manejar los eventos
        if(callbackHandler != null){
            callbackHandler.OnListItemClicked(position);
        }
    }
}

Por último implementar la interfaz en la Actividad padre
TimelineActivity
:

public class TimelineActivity extends FragmentActivity implements TimelineFragment.OnListItemClickedCallbackHandler {

    // Resto del código de la clase

    @Override
    public void OnListItemClicked(int position) {
      // Obtenemos el Status del array de Status(Tweet) que tenemos en memoria
      Status status = statuses.get(position);

      // Creamos un bundle que contendrá el Status(Tweet) seleccionado
      Bundle bundle = new Bundle();
      bundle.putSerializable(SELECTED_STATUS, status);

      // Creamos el fragment
      Fragment fragment = new StatusFragment();
      fragment.setArguments(bundle);

      // Y lo añadimos al contenedor
      getSupportFragmentManager()
              .beginTransaction()
              .replace(R.id.container, fragment) // Usamos replace porque ya tenemos un fragment en el contenedor
              .addToBackStack("Status") // Haciendo esto permitimos que el usuario pueda volver al fragment anterior pulsando Back
              .commit();
    }

}

Y el resultado:

status

7. Conclusiones:

Como hemos podido comprobar, es muy fácil crear unidades de código reusables gracias a los Fragments, esto nos permitiría usar el mismo Fragment en pantallas totalmente distintas, e incluso combinar los distintos Fragment en una misma pantalla ( veremos cómo hacer una pantalla para tablets usando ambos Fragments ).

Esto aumenta la mantenibilidad y la reutilización de código, además de ayudar a disminuir el trabajo de nuestras Actividades, y permitir liberarlas de trabajo.


Puedes descargar el código de esta aplicación
aquí
con algunas correcciones para permitir cambios de orientación.

3 COMENTARIOS

  1. muy buen tutorial.. pero mi pregunta es como realizar el listview pero con la imagen del usuario… es decir que el timeline de los tweets tenga la imagen del usuario el nombre, favoritos y retweets

  2. Excelente tutorial mil gracias, Mi pregunta es si al cargar los Tweets puede quedar con hipervinculo los que tienen y que pueda cargarse el enlace.

  3. Cómo podria obtener mas de 20 tweets, se que son capturados por getTimelineAsync, Pero donde podria poner a capturar mas de 20. Gracias…..

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad