1page.title=Cómo implementar interfaces de usuario adaptables 2parent.title=Cómo diseñar aplicaciones para varias pantallas 3parent.link=index.html 4 5trainingnavtop=true 6previous.title=Cómo admitir varias densidades de pantalla 7previous.link=screendensities.html 8 9@jd:body 10 11 12<!-- This is the training bar --> 13<div id="tb-wrapper"> 14<div id="tb"> 15 16<h2>En esta sección puedes aprender:</h2> 17 18<ol> 19 <li><a href="#TaskDetermineCurLayout">Cómo determinar el diseño actual</a></li> 20 <li><a href="#TaskReactToLayout">Cómo reaccionar en función del diseño actual</a></li> 21 <li><a href="#TaskReuseFrag">Cómo volver a utilizar fragmentos en otras actividades</a></li> 22 <li><a href="#TaskHandleConfigChanges">Cómo gestionar los cambios en la configuración de la pantalla</a></li> 23</ol> 24 25<h2>También puedes consultar:</h2> 26 27<ul> 28 <li><a href="{@docRoot}guide/practices/tablets-and-handsets.html">Cómo admitir tablets y dispositivos móviles</a></li> 29</ul> 30 31<h2>¡Pruébalo!</h2> 32 33<div class="download-box"> 34<a href="http://developer.android.com/shareables/training/NewsReader.zip" class="button">Descargar la aplicación de ejemplo</a> 35<p class="filename">NewsReader.zip</p> 36</div> 37 38 39</div> 40</div> 41 42<p>En función del diseño actual de tu aplicación, la interfaz puede variar. Por ejemplo, si tu aplicación está en modo de panel dual, haz clic en un elemento del panel izquierdo para que aparezca en el panel de la derecha. Asimismo, si está en modo de panel único, el contenido debería aparecer por sí mismo (en otra actividad).</p> 43 44 45<h2 id="TaskDetermineCurLayout">Cómo determinar el diseño actual</h2> 46 47<p>Dado que la implementación de cada diseño variará en cierta medida, probablemente una de las primeras cosas que tendrás que hacer será determinar el diseño que el usuario puede ver en ese momento. Por ejemplo, puedes determinar si el usuario utiliza el modo de "panel único" o de "panel dual". Para ello, puedes consultar si una vista determinada existe y es visible:</p> 48 49<pre class="prettyprint"> 50public class NewsReaderActivity extends FragmentActivity { 51 boolean mIsDualPane; 52 53 @Override 54 public void onCreate(Bundle savedInstanceState) { 55 super.onCreate(savedInstanceState); 56 setContentView(R.layout.main_layout); 57 58 View articleView = findViewById(R.id.article); 59 mIsDualPane = articleView != null && 60 articleView.getVisibility() == View.VISIBLE; 61 } 62} 63</pre> 64 65<p>Ten en cuenta que este código consulta la disponibilidad del panel del "artículo", lo que es mucho más flexible que incluir una consulta para un diseño determinado.</p> 66 67<p>Otro ejemplo de cómo puedes adaptar la existencia de diferentes componentes es comprobar su disponibilidad antes de realizar una operación relacionada con los mismos. Por ejemplo, en la aplicación de ejemplo News Reader, hay un botón que abre un menú, pero ese botón solo aparece en las versiones anteriores a Android 3.0 (porque <PH>{@link android.app.ActionBar}</PH> en el nivel 11 del API y en niveles superiores). Por tanto, para añadir el detector de eventos para este botón, puedes hacer lo siguiente:</p> 68 69<pre class="prettyprint"> 70Button catButton = (Button) findViewById(R.id.categorybutton); 71OnClickListener listener = /* create your listener here */; 72if (catButton != null) { 73 catButton.setOnClickListener(listener); 74} 75</pre> 76 77 78<h2 id="TaskReactToLayout">Cómo reaccionar en función del diseño actual</h2> 79 80<p>El resultado de algunas acciones puede variar en función del diseño actual. Por ejemplo, en el ejemplo de News Reader, al hacer clic en un encabezado de la lista se abrirá el artículo del panel situado a la derecha si la interfaz de usuario está en modo de panel dual, pero se iniciará una actividad independiente si esta está en modo de panel único:</p> 81 82<pre> 83@Override 84public void onHeadlineSelected(int index) { 85 mArtIndex = index; 86 if (mIsDualPane) { 87 /* display article on the right pane */ 88 mArticleFragment.displayArticle(mCurrentCat.getArticle(index)); 89 } else { 90 /* start a separate activity */ 91 Intent intent = new Intent(this, ArticleActivity.class); 92 intent.putExtra("catIndex", mCatIndex); 93 intent.putExtra("artIndex", index); 94 startActivity(intent); 95 } 96} 97</pre> 98 99<p>De igual modo, si la aplicación está en modo de panel dual, debe configurar la barra de acción con pestañas para la navegación, mientras que si la aplicación está en modo de panel único, debe configurar la navegación con un widget giratorio. Por tanto, el código debe comprobar también cuál es el caso adecuado:</p> 100 101<pre> 102final String CATEGORIES[] = { "Top Stories", "Politics", "Economy", "Technology" }; 103 104public void onCreate(Bundle savedInstanceState) { 105 .... 106 if (mIsDualPane) { 107 /* use tabs for navigation */ 108 actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS); 109 int i; 110 for (i = 0; i < CATEGORIES.length; i++) { 111 actionBar.addTab(actionBar.newTab().setText( 112 CATEGORIES[i]).setTabListener(handler)); 113 } 114 actionBar.setSelectedNavigationItem(selTab); 115 } 116 else { 117 /* use list navigation (spinner) */ 118 actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST); 119 SpinnerAdapter adap = new ArrayAdapter<String>(this, 120 R.layout.headline_item, CATEGORIES); 121 actionBar.setListNavigationCallbacks(adap, handler); 122 } 123} 124</pre> 125 126 127<h2 id="TaskReuseFrag">Cómo volver a utilizar fragmentos en otras actividades</h2> 128 129<p>Un patrón recurrente a la hora de diseñar para distintas pantallas es utilizar una parte de la interfaz que se implementa como un panel en algunas configuraciones de pantalla y como actividad independiente en otras. Por ejemplo, en el ejemplo de News Reader, el texto del artículo de noticias se presenta en el panel de la derecha en las pantallas grandes, pero es una actividad independiente en las pantallas más pequeñas.</p> 130 131<p>En otros casos similares, puedes evitar la duplicación de código reutilizando la misma <PH>{@link android.app.Fragment}</PH> en distintas actividades. Por ejemplo, <code>ArticleFragment</code> se utiliza en el diseño de panel dual:</p> 132 133{@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes.xml all} 134 135<p>A continuación, se vuelve a utilizar (sin diseño) en el diseño de actividad para pantallas más pequeñas (<code>ArticleActivity</code>):</p> 136 137<pre> 138ArticleFragment frag = new ArticleFragment(); 139getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit(); 140</pre> 141 142<p>Evidentemente, esto tiene el mismo efecto que declarar el fragmento en un diseño XML. Sin embargo, en este caso, un diseño XML sería un trabajo innecesario porque el fragmento del artículo es el único componente de esta actividad.</p> 143 144<p>Un punto muy importante que debes tener en cuenta al diseñar tus fragmentos es no crear un acoplamiento fuerte para una actividad determinada. Para ello, normalmente puedes definir una interfaz que resuma todas las formas en las que tiene que interactuar el fragmento con su actividad principal y, a continuación, la actividad principal implementa esa interfaz:</p> 145 146<p>Por ejemplo, ese es precisamente el objetivo del <code>HeadlinesFragment</code> de la aplicación News Reader:</p> 147 148<pre> 149public class HeadlinesFragment extends ListFragment { 150 ... 151 OnHeadlineSelectedListener mHeadlineSelectedListener = null; 152 153 /* Must be implemented by host activity */ 154 public interface OnHeadlineSelectedListener { 155 public void onHeadlineSelected(int index); 156 } 157 ... 158 159 public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener listener) { 160 mHeadlineSelectedListener = listener; 161 } 162} 163</pre> 164 165<p>A continuación, cuando el usuario selecciona un encabezado, el fragmento notifica el detector especificado por la actividad principal (en lugar de notificar una actividad predefinida específica):</p> 166 167<pre> 168public class HeadlinesFragment extends ListFragment { 169 ... 170 @Override 171 public void onItemClick(AdapterView<?> parent, 172 View view, int position, long id) { 173 if (null != mHeadlineSelectedListener) { 174 mHeadlineSelectedListener.onHeadlineSelected(position); 175 } 176 } 177 ... 178} 179</pre> 180 181<p>Si quieres obtener más información sobre esta técnica, puedes consultar la guía sobre <a 182href="{@docRoot}guide/practices/tablets-and-handsets.html">Cómo admitir tablets y dispositivos móviles</a>.</p> 183 184 185<h2 id="TaskHandleConfigChanges">Cómo gestionar los cambios en la configuración de la pantalla</h2> 186 187<p>Si utilizas actividades independientes para implementar partes individuales de tu interfaz, debes tener en cuenta que es posible que tengas que reaccionar ante determinados cambios en la configuración (como un cambio de rotación) para mantener la homogeneidad de tu interfaz.</p> 188 189<p>Por ejemplo, en un tablet de 7" que utilice Android 3.0 o una versión superior, el ejemplo de News Reader utiliza una actividad independiente para mostrar el artículo de noticias en el modo vertical, pero utiliza el diseño de panel dual en el modo horizontal.</p> 190 191<p>Esto significa que cuando el usuario utiliza el modo vertical y está consultando un artículo, tienes que detectar que la orientación ha cambiado al modo horizontal y reaccionar de forma adecuada finalizando la actividad. A continuación, debes volver a la actividad principal para que el contenido pueda mostrarse en el diseño de panel dual:</p> 192 193<pre> 194public class ArticleActivity extends FragmentActivity { 195 int mCatIndex, mArtIndex; 196 197 @Override 198 protected void onCreate(Bundle savedInstanceState) { 199 super.onCreate(savedInstanceState); 200 mCatIndex = getIntent().getExtras().getInt("catIndex", 0); 201 mArtIndex = getIntent().getExtras().getInt("artIndex", 0); 202 203 // If should be in two-pane mode, finish to return to main activity 204 if (getResources().getBoolean(R.bool.has_two_panes)) { 205 finish(); 206 return; 207 } 208 ... 209} 210</pre> 211 212 213