Antes que nada , déjenme decirles que es la primera vez que realizo una entrada en un blog , asi que disculpen la calidad del contenido , recién me estoy informando de como usar blogger XD , ahora pasemos a lo importante : el código....
Aqui doy por hecho que ya sabes configurar el entorno de trabajo de android , eclipse y que ya has podido levantar tu "HolaMundo" , ejemplos de esos hay bastantes, aqui le di prioridad al uso de :
- Layouts en android
- Personalizar una lista
- Pasar de una interfaz a otra
- Listar contenido remoto , en este caso videos de youtube
Espero les sea de ayuda , así que vamos a empezar.
Entorno de desarrollo:
Entorno de desarrollo:
- Sistema Operativo : Windows 7 32 bits
- Jdk : 1.6
- Eclipse : Spring Tool Suite 2.7.1.
- Plataforma Android : 2.3.3 API 10
Libreriás requeridas
* Google Guice 2.0 : guice-2.0-no_aop.jar
http://code.google.com/p/google-guice/downloads/detail?name=guice-2.0-no_aop.jar&can=2&q=
* Roboguice 1.1.2 : roboguice-1.1.2.jar
http://repo1.maven.org/maven2/org/roboguice/roboguice/1.1.2/roboguice-1.1.2.jar
* Google Guice 2.0 : guice-2.0-no_aop.jar
http://code.google.com/p/google-guice/downloads/detail?name=guice-2.0-no_aop.jar&can=2&q=
* Roboguice 1.1.2 : roboguice-1.1.2.jar
http://repo1.maven.org/maven2/org/roboguice/roboguice/1.1.2/roboguice-1.1.2.jar
Para esta aplicación he usado Roboguice , que es similar a la inyección de dependencias de spring , me parece sencillo y útil , y me permite mantener el mismo tipo de estructura al igual que los proyectos de spring (daos , services , vistas , etc).
Aqui una vista de la estructura del proyecto :
Explicaré las clases del paquete "config"
1.- CanalYoutubeGoogleGuiceModule
package com.midominio.canalyoutube.config;
import roboguice.config.AbstractAndroidModule;
import com.midominio.canalyoutube.dao.VideoDao;
import com.midominio.canalyoutube.dao.impl.VideoDaoImpl;
import com.midominio.canalyoutube.service.VideoService;
import com.midominio.canalyoutube.service.impl.VideoServiceImpl;
public class CanalYoutubeGoogleGuiceModule extends AbstractAndroidModule {
@Override
protected void configure() {
//Iniciando Daos
bind(VideoDao.class).to(VideoDaoImpl.class);
//Iniciando Servicios
bind(VideoService.class).to(VideoServiceImpl.class);
}
}
Esta clase es el núcleo de google guice , simplemente heredamos de AbstractAndroidModule y colocamos una instancia de la clase que queremos quede como si fuera un bean de spring , con la sentencia bind
decimos que se setee un VideoDaoImpl.class cada vez que coloque como atributo una variable de tipo VideoDao , lo mismo para los servicios.
2.- CanalYoutubeRoboGuiceApplication
package com.midominio.canalyoutube.config;
import java.util.List;
import roboguice.application.RoboApplication;
import com.google.inject.Module;
public class CanalYoutubeRoboGuiceApplication extends RoboApplication{
private Module module = new CanalYoutubeGoogleGuiceModule();
@Override
protected void addApplicationModules(List<Module> modules) {
modules.add(module);
}
public void setModule(Module module) {
this.module = module;
}
}
Esta clase de RoboGuice es la instancia principal , aqui seteamos el módulo creado anteriormente
3.- Constantes
package com.midominio.canalyoutube.config;
public interface Constantes {
public static String USER_ID = "whatdafaqshow";
public static String FEED_URL ="http://gdata.youtube.com/feeds/api/users/";
public static String TIPO_VIDEO_SUBIDAS_TOTALES= "uploads";
public static int MAX_VIDEOS_POR_PAGINA = 15;
}
Aqui definimos las variables importantes , como el canal de donde vamos a buscar ( uno de los que me gusta Whatdafaqshow ), la url de donde sacar los datos , el tipo de consulta , en este caso , los videos subidos al canal , y la cantidad de videos por página (de momento solo puse 15 ya que las consultas a la api de google traen de 25 en 25 como máximo)
Clases del paquete DAO :
4.- Video
package com.midominio.canalyoutube.dao.domain;
import java.util.List;
public class Video {
String titulo;
String ruta;
List<Miniatura> miniaturas;
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
public String getRuta() {
return ruta;
}
public void setRuta(String ruta) {
this.ruta = ruta;
}
public List<Miniatura> getMiniaturas() {
return miniaturas;
}
public void setMiniaturas(List<Miniatura> miniaturas) {
this.miniaturas = miniaturas;
}
}
La consulta a la api de google es un xml el cual tendremos que parsear y colocarlo en clases para su fácil manejo , en este caso la clase video , contendrá el titulo , la ruta del video y las miniaturas(imágenes) del video.
5.- Miniatura
package com.midominio.canalyoutube.dao.domain;
public class Miniatura {
String ruta;
String width;
String height;
String time;
public String getRuta() {
return ruta;
}
public void setRuta(String ruta) {
this.ruta = ruta;
}
public String getWidth() {
return width;
}
public void setWidth(String width) {
this.width = width;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
6.- VideoDao
package com.midominio.canalyoutube.dao;
import java.util.List;
import com.midominio.canalyoutube.dao.domain.Video;
public interface VideoDao {
public List<Video> obtenerVideos();
public List<Video> obtenerVideos(int start_index,int max_results);
}
La interfaz plantilla de los métodos para recuperar los videos , en este caso 2 , donde se traen los videos con los parámetros por defecto , y otro donde se traen los videos con una paginación personalizada
7.- VideoDaoImpl
package com.midominio.canalyoutube.dao.impl;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.midominio.canalyoutube.config.Constantes;
import com.midominio.canalyoutube.dao.VideoDao;
import com.midominio.canalyoutube.dao.domain.Miniatura;
import com.midominio.canalyoutube.dao.domain.Video;
import com.midominio.canalyoutube.util.LoggerUtil;
public class VideoDaoImpl implements VideoDao {
@Override
public List<Video> obtenerVideos(){
int start_index = 1;
int max_results = Constantes.MAX_VIDEOS_POR_PAGINA;
return obtenerVideos(start_index,max_results);
}
@Override
public List<Video> obtenerVideos(int start_index,int max_results) {
List<Video> lista = null;
String ruta = Constantes.FEED_URL + Constantes.USER_ID + "/" + Constantes.TIPO_VIDEO_SUBIDAS_TOTALES + "?start-index="+start_index+"&max-results="+max_results;
URL url = null;
URLConnection connection = null;
HttpURLConnection httpConnection = null;
try {
lista = new ArrayList<Video>();
url = new URL(ruta);
connection = url.openConnection();
httpConnection = (HttpURLConnection)connection;
int responseCode = httpConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream in = httpConnection.getInputStream();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
// Parse the earthquake feed.
Document dom = db.parse(in);
Element docEle = dom.getDocumentElement();
NodeList nl = docEle.getElementsByTagName("entry");
if (nl != null && nl.getLength() > 0) {
for (int i = 0 ; i < nl.getLength(); i++) {
Video x = new Video();
Element entry = (Element)nl.item(i);
//Seteando el titulo
Element title = (Element)entry.getElementsByTagName("title").item(0);
x.setTitulo(title.getFirstChild().getNodeValue());
//Seteando las miniaturas
Element mediaGroup = (Element)entry.getElementsByTagName("media:group").item(0);
NodeList thumbnails = mediaGroup.getElementsByTagName("media:thumbnail");
List<Miniatura> listaMiniaturas = new ArrayList<Miniatura>();
for(int j = 0; j<thumbnails.getLength();j++){
Element e = (Element)thumbnails.item(j);
Miniatura m = new Miniatura();
m.setRuta(e.getAttribute("url"));
m.setWidth(e.getAttribute("width"));
m.setHeight(e.getAttribute("height"));
m.setTime(e.getAttribute("time"));
listaMiniaturas.add(m);
}
x.setMiniaturas(listaMiniaturas);
//Seteando la url del video
NodeList datosVideo = mediaGroup.getElementsByTagName("media:content");
Element e = (Element)datosVideo.item(datosVideo.getLength()-1);
x.setRuta(e.getAttribute("url"));
lista.add(x);
}
}
}
} catch (MalformedURLException e) {
LoggerUtil.v(VideoDaoImpl.class, e.getMessage());
lista = null;
} catch (IOException e) {
LoggerUtil.v(VideoDaoImpl.class, e.getMessage());
lista = null;
} catch (ParserConfigurationException e) {
LoggerUtil.v(VideoDaoImpl.class, e.getMessage());
lista = null;
} catch (SAXException e) {
LoggerUtil.v(VideoDaoImpl.class, e.getMessage());
lista = null;
}
return lista;
}
}
Aqui lo que hacemos es parsear el xml recibido y tomar los datos de los videos , para entender como es el xml de una llamada a youtube , haremos la prueba accediendo a ésta url
http://gdata.youtube.com/feeds/api/users/whatdafaqshow/uploads?start-index=1&max-results=15
Se puede colocar tambien http://gdata.youtube.com/feeds/api/users/whatdafaqshow/uploads , pero usará los parametros por defecto , desde el primer video y máximo de resultados , 25
En mozilla colocamos ver codigo fuente y dicho codigo lo guardamos como xml , el cual contiene
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:app='http://purl.org/atom/app#'
xmlns:media='http://search.yahoo.com/mrss/' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'
xmlns:gd='http://schemas.google.com/g/2005' xmlns:yt='http://gdata.youtube.com/schemas/2007'>
<id>http://gdata.youtube.com/feeds/api/users/whatdafaqshow/uploads</id>
<updated>2011-11-27T22:30:11.578Z</updated>
<category scheme='http://schemas.google.com/g/2005#kind' term='http://gdata.youtube.com/schemas/2007#video'/>
<title type='text'>Uploads by whatdafaqshow</title>
<logo>http://www.youtube.com/img/pic_youtubelogo_123x63.gif</logo>
<link rel='related' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/users/whatdafaqshow'/>
<link rel='alternate' type='text/html' href='http://www.youtube.com/profile?user=whatdafaqshow#p/u'/>
<link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/users/whatdafaqshow/uploads'/>
<link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/users/whatdafaqshow/uploads/batch'/>
<link rel='self' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/users/whatdafaqshow/uploads?start-index=1&max-results=25'/>
<link rel='next' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/users/whatdafaqshow/uploads?start-index=26&max-results=25'/>
<author>
<name>whatdafaqshow</name>
<uri>http://gdata.youtube.com/feeds/api/users/whatdafaqshow</uri>
</author>
<generator version='2.1' uri='http://gdata.youtube.com'>YouTube data API</generator>
<openSearch:totalResults>108</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<openSearch:itemsPerPage>25</openSearch:itemsPerPage>
<entry>
<id>http://gdata.youtube.com/feeds/api/videos/aQujLSfJ13A</id>
<published>2011-11-20T22:41:26.000Z</published>
<updated>2011-11-27T22:27:03.000Z</updated>
<category scheme='http://schemas.google.com/g/2005#kind' term='http://gdata.youtube.com/schemas/2007#video'/>
<category scheme='http://gdata.youtube.com/schemas/2007/categories.cat' term='People' label='Gente y blogs'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='whatdafaqshow'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='what'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='da'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='faq'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='mox'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='wdfshow'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='videos'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='graciosos'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='la'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='historia'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='de'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='internet'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='whatdafaq'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='wdf'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='niños'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='malignos'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='rusos'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='japoneses'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='raros'/>
<title type='text'>Niños Malignos!</title>
<content type='text'>PAGINA WEB : http://www.whatdafaqshow.com
FB Oficial http://www.facebook.com/wdfshow
Mox FB: http://www.facebook.com/moxwdf
Twitter: http://bit.ly/iytacG
Episodio 105 de WDF!?
</content>
<link rel='alternate' type='text/html' href='http://www.youtube.com/watch?v=aQujLSfJ13A&feature=youtube_gdata'/>
<link rel='http://gdata.youtube.com/schemas/2007#video.responses' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/aQujLSfJ13A/responses'/>
<link rel='http://gdata.youtube.com/schemas/2007#video.related' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/aQujLSfJ13A/related'/>
<link rel='http://gdata.youtube.com/schemas/2007#mobile' type='text/html'
href='http://m.youtube.com/details?v=aQujLSfJ13A'/>
<link rel='self' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/users/whatdafaqshow/uploads/aQujLSfJ13A'/>
<author>
<name>WHATDAFAQSHOW</name>
<uri>http://gdata.youtube.com/feeds/api/users/whatdafaqshow</uri>
</author>
<gd:comments>
<gd:feedLink href='http://gdata.youtube.com/feeds/api/videos/aQujLSfJ13A/comments' countHint='1915'/>
</gd:comments>
<media:group>
<media:category label='Gente y blogs' scheme='http://gdata.youtube.com/schemas/2007/categories.cat'>People</media:category>
<media:content url='http://www.youtube.com/v/aQujLSfJ13A?version=3&f=user_uploads&app=youtube_gdata' type='application/x-shockwave-flash' medium='video' isDefault='true' expression='full' duration='381' yt:format='5'/>
<media:content url='rtsp://v8.cache1.c.youtube.com/CigLENy73wIaHwlw18knLaMLaRMYDSANFEgGUgx1c2VyX3VwbG9hZHMM/0/0/0/video.3gp' type='video/3gpp' medium='video' expression='full' duration='381' yt:format='1'/>
<media:content url='rtsp://v3.cache6.c.youtube.com/CigLENy73wIaHwlw18knLaMLaRMYESARFEgGUgx1c2VyX3VwbG9hZHMM/0/0/0/video.3gp' type='video/3gpp' medium='video' expression='full' duration='381' yt:format='6'/>
<media:description type='plain'>PAGINA WEB : http://www.whatdafaqshow.com
FB Oficial http://www.facebook.com/wdfshow
Mox FB: http://www.facebook.com/moxwdf
Twitter: http://bit.ly/iytacG
Episodio 105 de WDF!?
</media:description>
<media:keywords>whatdafaqshow, what, da, faq, mox, wdfshow, videos, graciosos, la, historia, de, internet, whatdafaq, wdf, niños, malignos, rusos, japoneses, raros</media:keywords>
<media:player url='http://www.youtube.com/watch?v=aQujLSfJ13A&feature=youtube_gdata_player'/>
<media:thumbnail url='http://i.ytimg.com/vi/aQujLSfJ13A/0.jpg' height='360' width='480' time='00:03:10.500'/>
<media:thumbnail url='http://i.ytimg.com/vi/aQujLSfJ13A/1.jpg' height='90' width='120' time='00:01:35.250'/>
<media:thumbnail url='http://i.ytimg.com/vi/aQujLSfJ13A/2.jpg' height='90' width='120' time='00:03:10.500'/>
<media:thumbnail url='http://i.ytimg.com/vi/aQujLSfJ13A/3.jpg' height='90' width='120' time='00:04:45.750'/>
<media:title type='plain'>Niños Malignos!</media:title><yt:duration seconds='381'/>
</media:group>
<gd:rating average='4.8676705' max='5' min='1' numRaters='9431' rel='http://schemas.google.com/g/2005#overall'/>
<yt:statistics favoriteCount='712' viewCount='366645'/>
</entry>
:
:
</feed>
Aqui lo que nos interesa son los tags <entry> y dentro de estos , el tag "title" y los grupos "media:thumbnail" y "media:content" , que es donde encontraremos las miniaturas y las ruta del video respectivamente
NOTA : para el tag < media : content >, tomamos el ultimo tag que es el que contiene la ruta del archivo .3gp
Clases del paquete "SERVICE"
8.- VideoService
package com.midominio.canalyoutube.service;
Es la interfaz plantilla para comunicar el DAO , con la capa Vista
import java.util.List;
import com.midominio.canalyoutube.view.domain.ItemVideo;
public interface VideoService {
public List<ItemVideo> obtenerVideos();
}
9.- VideoServiceImpl
package com.midominio.canalyoutube.service.impl;
import java.util.ArrayList;
import java.util.List;
import com.google.inject.Inject;
import com.midominio.canalyoutube.dao.VideoDao;
import com.midominio.canalyoutube.dao.domain.Video;
import com.midominio.canalyoutube.service.VideoService;
import com.midominio.canalyoutube.view.domain.ItemVideo;
public class VideoServiceImpl implements VideoService {
@Inject
VideoDao videoDao;
@Override
public List<ItemVideo> obtenerVideos() {
List<ItemVideo> rpta = null;
List<Video> lista = videoDao.obtenerVideos();
if(lista!=null){
int dim = lista.size();
if(dim>0){
rpta = new ArrayList<ItemVideo>();
for(int i = 0;i<dim ;i++){
Video x = lista.get(i);
ItemVideo aux = new ItemVideo();
aux.setTitulo(x.getTitulo());
aux.setRuta(x.getRuta());
try{
aux.setMiniatura(x.getMiniaturas().get(1).getRuta());
}catch(NullPointerException npe){
aux.setMiniatura("");
}
rpta.add(aux);
}
}
}
return rpta;
}
}
Aqui , la anotación destacable es @Inject , que sirve para insertar una instancia de la Clase VideoDaoImpl , definida en la configuración.
En esta clase tomamos un objeto video del xml y lo convertimos en un objeto ItemVideo , que es para la capa Vista , en las miniaturas , elegimos en este caso la segunda , porque la primera es demasiado grande y descuadra el diseño.
Clases del paquete "view"
10.- ItemVideo
package com.midominio.canalyoutube.view.domain;
import android.os.Parcel;
import android.os.Parcelable;
import com.midominio.canalyoutube.util.LoggerUtil;
public class ItemVideo implements Parcelable {
private String titulo;
private String ruta;
private String miniatura;
public ItemVideo(){}
public ItemVideo(Parcel source){
LoggerUtil.v(ItemVideo.class,"Parseando desde la fuente");
titulo = source.readString();
ruta = source.readString();
miniatura = source.readString();
}
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
public String getRuta() {
return ruta;
}
public void setRuta(String ruta) {
this.ruta = ruta;
}
public String getMiniatura() {
return miniatura;
}
public void setMiniatura(String miniatura) {
this.miniatura = miniatura;
}
@Override
public String toString() {
return titulo;
}
@Override
public int describeContents() {
return hashCode();
}
@Override
public void writeToParcel(Parcel destino, int flags) {
LoggerUtil.v(ItemVideo.class, "Parseando : "+ flags);
destino.writeString(titulo);
destino.writeString(ruta);
destino.writeString(miniatura);
}
public static final Parcelable.Creator<ItemVideo> CREATOR = new Parcelable.Creator<ItemVideo>() {
public ItemVideo createFromParcel(Parcel in){
return new ItemVideo(in);
}
public ItemVideo[] newArray(int size){
return new ItemVideo[size];
}
};
}
Esta clase será usada como tipo de dato de la lista de videos mostrada en el móvil , aqui lo destacable , es la implementación de Parcelable , esta implementación , nos permitirá pasar este tipo de dato entre Activitys (como las ventanas de una aplicacion de escritorio , o las páginas de una aplicacion web) de Android , para ello se debe crear una variable estática CREATOR , con el método writeToParcel , donde se escribe los atributos de la clase , y el método createFromParcel , lee el Parcel y coloca la lectura en los atributos , para ello la clase debe tener un constructor que tenga una clase Parcel de parámetro.
NOTA : El orden de lectura y escritura de atributos en el Parcel , debe ser el mismo
Ahora vamos a las vistas
11.- Inicio
Código Inicio.java
package com.midominio.canalyoutube.view;
import java.util.List;
import roboguice.activity.RoboActivity;
import roboguice.inject.InjectView;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;
import com.google.inject.Inject;
import com.midominio.canalyoutube.service.VideoService;
import com.midominio.canalyoutube.view.domain.ItemVideo;
import com.midominio.canalyoutube.view.list.ItemVideoListAdapter;
public class Inicio extends RoboActivity {
@Inject
VideoService videoService;
@InjectView(R.id.listaEpisodios)
ListView listaEpisodios;
ItemVideoListAdapter adaptador;
List<ItemVideo> listaVideos;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.inicio);
listaVideos = videoService.obtenerVideos();
adaptador = new ItemVideoListAdapter(this,R.layout.inicio_lista_itemvideo,listaVideos);
listaEpisodios.setAdapter(adaptador);
listaEpisodios.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent,View view,int position, long id) {
//Tomando el objeto
ItemVideo v = (ItemVideo)listaEpisodios.getItemAtPosition(position);
//Colocando el objeto en la vista
Intent i = new Intent(view.getContext(),RepVideo.class);
i.putExtra("itemVideo",v);
startActivityForResult(i, 0);
}
});
}
Código : inicio.xml
<?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="fill_parent">
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="@string/lista_episodios" style="@style/tituloActivity" />
<ListView android:id="@+id/listaEpisodios" android:layout_height="wrap_content" android:layout_width="match_parent" />
</LinearLayout>
Aquí lo destacable son las anotaciones @Inject y @InjectView , que insertan un objeto y un componente de vista respectivamente , en @InjectView , se debe tomar el recurso de una plantilla determinada , en este caso de la plantilla inicio.xml.
La clase ItemVideoListAdapter , es una extension de ArrayAdapter , para que cada item de la lista se muestre de la forma <Foto><Texto> ya que el ListView por defecto de android , solo muestra texto .
También se ha colocado un evento a cada item de la lista , para que cuando se le clickee vaya a otra Activity llevando los datos del video, usando Intent y su método putExtra
NOTA :
- No se debe heredar de Activity sino de RoboActivity , de lo contrario los @Inject no funcionarán.
- Al tomar por id , en el xml es android:id="@+id/listaEpisodios" y en el java es @InjectView(R.id.listaEpisodios)ListView listaEpisodios;
Esto reemplaza a este tipo de sentencias
TextView tt = (TextView) v.findViewById(R.id.texto);
11.- ItemVideoListAdapter
Código ItemVideoListAdapter.java
package com.midominio.canalyoutube.view.list;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.midominio.canalyoutube.util.LoggerUtil;
import com.midominio.canalyoutube.view.R;
import com.midominio.canalyoutube.view.domain.ItemVideo;
public class ItemVideoListAdapter extends ArrayAdapter<ItemVideo> {
Context context;
private List<ItemVideo> items;
public ItemVideoListAdapter(Context context, int textViewResourceId, List<ItemVideo> items) {
super(context, textViewResourceId, items);
this.items = items;
this.context = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.inicio_lista_itemvideo, null);
}
ItemVideo item = items.get(position);
if (item != null) {
//poblamos la lista de elementos
TextView tt = (TextView) v.findViewById(R.id.texto);
ImageView im = (ImageView) v.findViewById(R.id.icono);
//Seteando la imagen
if (im!= null) {
try {
URL url = new URL(item.getMiniatura());
URLConnection conn = url.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
Bitmap bm = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
im.setImageBitmap(bm);
} catch (MalformedURLException e) {
LoggerUtil.v(ItemVideoListAdapter.class,e.getMessage());
im.setImageResource(R.drawable.icon);
} catch (IOException e) {
LoggerUtil.v(ItemVideoListAdapter.class,e.getMessage());
im.setImageResource(R.drawable.icon);
}
}
if (tt != null) {
tt.setText(item.getTitulo());
}
}
return v;
}
}
Código : inicio_lista_itemvideo.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight" android:padding="6dip">
<ImageView android:id="@+id/icono" android:layout_width="wrap_content" android:layout_height="fill_parent"
android:layout_alignParentTop="true" android:layout_alignParentBottom="true" android:layout_marginRight="6dip "/>
<TextView android:id="@+id/texto" android:layout_width="fill_parent" android:layout_height="fill_parent"
android:layout_toRightOf="@id/icono" android:layout_alignParentRight="true" android:layout_centerVertical="true"
android:singleLine="true" android:ellipsize="marquee" />
</RelativeLayout>
Aquí en el método getView() asignaremos la imagen y el título , cabe destacar que al ser una imagen remota , la deberemos cargar como mapa de bits.
Como son items personalizados , tambien debe tener un xml para la estructura de la interfaz.
NOTAS : como estamos sobreescribiendo la forma de mostrar la lista , tambien necesita un archivo de layout
11.- RepVideo
Código : RepVideo.java
package com.midominio.canalyoutube.view;
import roboguice.activity.RoboActivity;
import roboguice.inject.InjectView;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.MediaController;
import android.widget.TextView;
import android.widget.VideoView;
import com.midominio.canalyoutube.view.domain.ItemVideo;
public class RepVideo extends RoboActivity{
@InjectView(R.id.video)
VideoView videoView;
@InjectView(R.id.txt_titulo)
TextView txtTitulo;
@InjectView(R.id.txt_ruta)
TextView txtRuta;
@InjectView(R.id.btnInicio)
Button play;
@InjectView(R.id.btnPausa)
Button pause;
@InjectView(R.id.btnRegresar)
Button regresar;
int flagPausaInicio;
ItemVideo v;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.repvideo);
flagPausaInicio = 0; //0 es que no se ha reproducido
Intent i = getIntent();
v = (ItemVideo) i.getParcelableExtra("itemVideo");
txtTitulo.setText(v.getTitulo());
txtRuta.setText(v.getRuta());
MediaController mc = new MediaController(this.getBaseContext());
mc.setAnchorView(videoView);
videoView.setMediaController(mc);
videoView.requestFocus();
play.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if(flagPausaInicio==0){
Uri video = Uri.parse(v.getRuta());
videoView.setVideoURI(video);
videoView.start();
flagPausaInicio = 1;
}else{
videoView.resume();
}
}
});
pause.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
videoView.stopPlayback();
}
});
regresar.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent intent = new Intent();
setResult(RESULT_OK, intent);
finish();
}
});
}
}
Código : repvideo.xml
<?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="fill_parent">
<Button android:text="@string/btnRegresar"
android:id="@+id/btnRegresar"
android:layout_width="fill_parent" android:textSize="16px" android:layout_height="40px" />
<TextView android:layout_width="fill_parent" android:id="@+id/txt_titulo"
android:layout_height="wrap_content" style="@style/tituloActivity" />
<TextView android:layout_width="fill_parent" android:id="@+id/txt_ruta"
android:layout_height="wrap_content" />
<RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content"
android:id="@+id/frame">
<VideoView android:id="@+id/video" android:layout_width="226px"
android:layout_height="194px" android:keepScreenOn="true" />
</RelativeLayout>
<Button android:text="@string/btnInicio" android:id="@+id/btnInicio"
android:layout_width="fill_parent" android:textSize="16px" android:layout_height="40px" />
<Button android:text="@string/btnPausa" android:id="@+id/btnPausa"
android:layout_width="fill_parent" android:textSize="16px" android:layout_height="40px" />
</LinearLayout>
Al pulsar cualquier item de la lista , nos llevará a esta interfaz donde se reproducirá el video seleccionado , con la clase Intent , obtendremos el objeto enviado desde la interfaz principal , tambien cuenta con un botón para pausar y reproducir el video (aquí hacemos uso de un flag , en caso de que no se haya cargado el video , usa el método start() , si se continua de una pausa , usa el método resume), y un botón para regresar al listado
NOTAS:
- El video a reproducir debe ser la ruta del archivo .3gp
- Este Activity solo funciona en un móvil real , no en el emulador
12.- LoggerUtil
package com.midominio.canalyoutube.util;
import android.util.Log;
public class LoggerUtil {
public static void v(String tag,String mensaje){
Log.v(tag,mensaje);
}
public static void v(Class<?> clase,String mensaje){
Log.v(clase.getSimpleName(),mensaje);
}
}
Esta clase permite insertar en el log usando como tag el nombre de la clase , o un nombre personalizado
NOTA : para ver el log se debe activar la perspectiva DDMS en Eclipse
12.- AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.midominio.canalyoutube.view"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="10" />
<application android:name="com.midominio.canalyoutube.config.CanalYoutubeRoboGuiceApplication"
android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Inicio" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".RepVideo" android:label="@string/app_name"></activity>
</application>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>
Habrá que hacer algunos cambios a este fichero de configuración , como agregar la segunda "Activity" , el parámetro android:name, esto debido a que estamos usando Roboguice , y es parte de la configuración que nos pide.
También debemos agregar el tag de uses-permission , para poder acceder a Internet.
13.- Styles.xml y Strings.xml
Al igual que css , en styles.xml , podemos definir estilos para varios componentes de android , en este caso para nuestra etiqueta de texto.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="tituloActivity">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:typeface">monospace</item>
<item name="android:background">#79B900</item>
<item name="android:textColor">#FFF</item>
<item name="android:textSize">16px</item>
<item name="android:textStyle">bold</item>
</style>
</resources>
Al igual que css , en styles.xml , podemos definir estilos para varios componentes de android , en este caso para nuestra etiqueta de texto.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="tituloActivity">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:typeface">monospace</item>
<item name="android:background">#79B900</item>
<item name="android:textColor">#FFF</item>
<item name="android:textSize">16px</item>
<item name="android:textStyle">bold</item>
</style>
</resources>
En el caso de strings.xml , nos sirve para definir textos para la aplicación
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Canal Youtube</string>
<string name="lista_episodios">Lista de Episodios</string>
<string name="btnRegresar">Regresar</string>
<string name="btnInicio">Play</string>
<string name="btnPausa">Pause</string>
</resources>
<resources>
<string name="app_name">Canal Youtube</string>
<string name="lista_episodios">Lista de Episodios</string>
<string name="btnRegresar">Regresar</string>
<string name="btnInicio">Play</string>
<string name="btnPausa">Pause</string>
</resources>
Interfaz Final -
Si todo nos compiló correctamente , la aplicación debería verse de la siguiente forma
Inicio.java
RepVideo.java
NOTAS FINALES :
- De momento no cuento con un smartphone , sino de hecho probaba el video youtube , disculpen por eso.
- Varios de éstos códigos , están en otros tutoriales de Internet , solo quise agruparlos en uno ya que no encontré un tutorial parecido
- Si tienen correcciones y/o mejoras del código , bienvenido sea.
Bueno , espero les haya sido de utilidad.
PD : si pensaron que no les daba el código , se equivocaron , aqui el enlace
http://www.mediafire.com/?hyu91hw6r7ssfpb
Avisen si falla el enlace
Saludos a todos




DOC EL CONDIGO NO FUNCIONA EN EL EMULADOR NECESARIAMENTRE TIENE K KORRER EN EL DISPOSITIVO???
ResponderEliminarGuau! Es demasiado bueno para ver como gran colección sobre RoboGuice. Necesito másRoboGuice Tutorials
ResponderEliminarNo funciona la aplicacion
ResponderEliminar