• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    &#64;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 &amp;&amp;
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&#64;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 &lt; 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    &#64;Override
171    public void onItemClick(AdapterView&lt;?&gt; 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    &#64;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