1page.title=Implementing Adaptative UI Flows 2parent.title=Designing for Multiple Screens 3parent.link=index.html 4 5trainingnavtop=true 6previous.title=Supporting Different Screen Densities 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>Содержание урока</h2> 17 18<ol> 19 <li><a href="#TaskDetermineCurLayout">Определение текущего макета</a></li> 20 <li><a href="#TaskReactToLayout">Дальнейшие действия в зависимости от текущего макета</a></li> 21 <li><a href="#TaskReuseFrag">Повторное использование фрагментов в других активностях</a></li> 22 <li><a href="#TaskHandleConfigChanges">Обработка изменений конфигурации экрана</a></li> 23</ol> 24 25<h2>Дополнительные материалы</h2> 26 27<ul> 28 <li><a href="{@docRoot}guide/practices/tablets-and-handsets.html">Поддержка планшетных ПК и мобильных телефонов</a></li> 29</ul> 30 31<h2>Упражнение</h2> 32 33<div class="download-box"> 34<a href="http://developer.android.com/shareables/training/NewsReader.zip" class="button">Загрузить учебное приложение</a> 35<p class="filename">NewsReader.zip</p> 36</div> 37 38 39</div> 40</div> 41 42<p>Алгоритм пользовательского интерфейса зависит от макета, который в данный момент отображается. Например, если приложение работает в двухпанельном режиме, то при нажатии на элемент в левой панели содержание отобразится в правой. В однопанельном режиме содержание откроется отдельно (в другой активности).</p> 43 44 45<h2 id="TaskDetermineCurLayout">Определение текущего макета</h2> 46 47<p>Так как в реализации макетов существуют отличия, первое, что необходимо сделать, – определить, какой макет отображается в данный момент. Например, работает ли приложение в однопанельном или двухпанельном режиме. Для этого создадим запрос о том, существует ли данное представление и отображается ли оно в настоящий момент:</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>Обратите внимание: представленный выше код содержит запрос о том, доступна ли панель article, поскольку это удобнее, чем писать отдельные запросы для каждого макета.</p> 66 67<p>Кроме того, для работы с учетом существующих компонентов можно также проверять их доступность, прежде чем выполнять с ними какие-либо операции. Например, в учебном приложении News Reader есть кнопка, которая служит для доступа в меню, однако она отображается только в операционных системах Android версии ниже, чем 3.0, потому что в последующих версиях ее функцию выполняет элемент <PH>{@link android.app.ActionBar}</PH> на уровне API 11 и выше. Чтобы проверить наличие этой кнопки, добавим прослушиватель событий с помощью следующего кода:</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">Дальнейшие действия в зависимости от текущего макета</h2> 79 80<p>Результаты некоторых операций зависят от текущего макета. Например, если в приложении News Reader в двухпанельном режиме нажать на заголовок в списке, то статья откроется в правой панели. Если же интерфейс работает в однопанельном режиме, будет запущена отдельная активность:</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>Аналогично, в двухпанельном режиме должна отображаться панель действий с навигационными вкладками, а в однопанельном навигация должна быть реализована с помощью раскрывающегося списка. Приложение должно проверять, какой из этих вариантов следует использовать:</p> 100 101<pre> 102final String CATEGORIES[] = { "Лучшие статьи", "Политика", "Экономика", "Новости технологий" }; 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">Повторное использование фрагментов в других активностях</h2> 128 129<p>Одним из примеров повторяющегося фрагмента является реализация части интерфейса как панели в одних конфигурациях и как отдельной активности в других. Например, если приложение News Reader работает на достаточно большом экране, текст новостной статьи отображается в правой панели, а если на маленьком, то он открывается в отдельной активности.</p> 130 131<p>В таких случаях следует повторно использовать подкласс <PH>{@link android.app.Fragment}</PH> в нескольких активностях. Например, в двухпанельном макете используется подкласс <code>ArticleFragment</code>:</p> 132 133{@sample development/samples/training/multiscreen/newsreader/res/layout/twopanes.xml all} 134 135<p>Он же (без макета) используется при работе на маленьком экране (активность <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>Результат будет таким же, как если бы мы объявили фрагмент в макете XML, однако в этом случае макет XML не требуется, так как фрагмент article является единственным компонентом этой активности.</p> 143 144<p>При создании фрагментов важно не привязывать их строго к конкретной активности. Для этого можно определить интерфейс с абстрактным описанием всех необходимых способов взаимодействия фрагмента с активностью, в которой он содержится. Затем этот интерфейс нужно реализовать в самой активности.</p> 145 146<p>Например, именно так работает фрагмент <code>HeadlinesFragment</code> в приложении 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>Затем, когда пользователь выбирает заголовок, фрагмент оповещает об этом не указанную в коде активность, а заданный ею прослушиватель:</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>Этот метод рассматривается подробнее в разделе <a 182href="{@docRoot}guide/practices/tablets-and-handsets.html">Поддержка планшетных ПК и мобильных телефонов</a>.</p> 183 184 185<h2 id="TaskHandleConfigChanges">Обработка изменений конфигурации экрана</h2> 186 187<p>При реализации отдельных частей интерфейса с помощью разных активностей нужно учитывать, что интерфейс должен уметь реагировать на определенные изменения конфигурации, такие как поворот экрана.</p> 188 189<p>Например, на типичном планшетном ПК с размером экрана 7 дюймов под управлением ОС Android 3.0 или более поздней версии при вертикальной ориентации статья в приложении News Reader открывается с помощью отдельной активности, а при горизонтальной используется двухпанельный макет.</p> 190 191<p>Это означает, что если пользователь держит планшетный ПК вертикально и на экране запущена активность для просмотра статьи, приложение должно уметь определить, что ориентация была изменена на горизонтальную. Затем оно должно соответствующим образом отреагировать на изменение, то есть завершить эту активность и вернуться к основной активности, чтобы содержание отобразилось в двухпанельном макете:</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