пятница, 17 февраля 2012 г.

Android Training-Проектирование для различных экранов-Реализация адаптивного UI потока


В зависимости от разметки, которую ваше приложение использует в данный момент, UI поток может отличаться. Например, если ваше приложение сейчас в двухпанельном режиме, нажатие на элемент в левой панеле будет просто отображать содержание в правой панеле, если же выбран однопанельный режим, содержание должно быть отображено в другой activity.

Определение текущей разметки.


Так как ваша реализация каждой разметки будет иметь небольшие отличия, первое что вам стоит сделать это определить какую разметку пользователь видит в текущий момент. Для примера, вы хотите узнать, пользователь в одно- или двухпанельном режиме. Вы можете сделать это, сделав запрос о существовании и видимости данного вида:
public class NewsReaderActivity extends FragmentActivity {
    boolean mIsDualPane;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);

        View articleView = findViewById(R.id.article);
        mIsDualPane = articleView != null && 
                        articleView.getVisibility() == View.VISIBLE;
    }
}

Обратите внимание, что код делает запрос о доступности панели "article", что более гибко, чем создание жестко заданного запроса о специфической разметке.

Другой пример того, как вы можете адаптироваться к существованию различных компонентов - это проверить доступны ли они перед выполнением операций над ними Для примера, в нашем приложении News Reader , существует кнопка, открывающая меню, но она существует только если устройство работает под управлением Android версии ниже 3.0 . Таким образом для добавления обработчика нажатия на клавишу делаем так:
Button catButton = (Button) findViewById(R.id.categorybutton);
OnClickListener listener = /* создаем свой обработчик тут/;
if (catButton != null) {
    catButton.setOnClickListener(listener);
}
Реакция в соответствии с выбранной разметкой.

Некоторые действия могут заканчиваться различным результатом в зависимости от текущей разметки. Для примера, в News Reader приложении, нажатие на заголовок из списка заголовков открывает статью в правой панеле в двухпанельном режиме и запускает отдельную activity если UI в однопанельном режиме:
@Override
public void onHeadlineSelected(int index) {
    mArtIndex = index;
    if (mIsDualPane) {
        /* отображает статью в правой панеле */
        mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
    } else {
        /* запускает отдельную activity */
        Intent intent = new Intent(this, ArticleActivity.class);
        intent.putExtra("catIndex", mCatIndex);
        intent.putExtra("artIndex", index);
        startActivity(intent);
    }
}
Кроме того, если приложение находится в двухпанельном режиме, оно должно установить панель с вкладками для навигации,а в однопанельном режиме настроить навигацию при помощи spinner виджета. Так, Ваш код выбирает необходимый вариант:
final String CATEGORIES[] = 
{ "Top Stories", "Politics", "Economy", "Technology" };
public void onCreate(Bundle savedInstanceState) {
    ....
    if (mIsDualPane) {
        /* использование вкладок для навигации */
    actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
        int i;
        for (i = 0; i < CATEGORIES.length; i++) {
            actionBar.addTab(actionBar.newTab().setText(
                CATEGORIES[i]).setTabListener(handler));
        }
        actionBar.setSelectedNavigationItem(selTab);
    }
    else {
        /* использование списка навигации (spinner) */
     actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST);
        SpinnerAdapter adap = new ArrayAdapter(this, 
                R.layout.headline_item, CATEGORIES);
        actionBar.setListNavigationCallbacks(adap, handler);
    }
}
Повторное использование Fragments в других activity.

Повторяющийся паттерн в проектировании для нескольких экранов содержит часть вашего интерфейса которая реализована как панель на одних конфигурациях экрана и как отдельная activity на других. Для примера, в News Reader примере, текст новостной статьи отображается на в правой панеле на больших экранах и как отдельная activity на маленьких.

В случаях наподобии этого вы можете обычно избежать дублирование кода путем повторного использования одного подкласса Fragment в различных activity. Для примера, ArticleFragment используется в разметке с двумя панелями:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>
И используется повторно(без разметки) в activity для маленьких экранов (ArticleActivity):
ArticleFragment frag = new ArticleFragment();
getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();

Естественно, это имеет такой же эффект, как и объявление фрагмента в XML разметке, но в этом случае XML разметка необязательна, т.к. фрагмент статьи это только компонент этой activity.
Одна важная вещь, которую стоит помнить при разработке собственных фрагментов-это не создавать сильную связь с конкретной activity. Вы можете обычно сделать это определяя интерфейс, который выносит на абстрактный уровень все методы взаимодействия фрагмента с host activity, и затем реализовать этот интерфейс в host activity. Например в News Reader приложении HeadlinesFragment:
public class HeadlinesFragment extends ListFragment {
    ...
    OnHeadlineSelectedListener mHeadlineSelectedListener = null;

    /* Должно быть реализовано в host activity */
    public interface OnHeadlineSelectedListener {
        public void onHeadlineSelected(int index);
    }
    ...

    public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener listener) {
        mHeadlineSelectedListener = listener;
    }
}
Затем, когда пользователь выбирает заголовок, фрагмент обращается к обработчику, указанному activity-хозяином :
public class HeadlinesFragment extends ListFragment {
    ...
    @Override
    public void onItemClick(AdapterView<?> parent, 
                            View view, int position, long id) {
        if (null != mHeadlineSelectedListener) {
            mHeadlineSelectedListener.onHeadlineSelected(position);
        }
    }
    ...
}
Эта методика обсуждается подробнее в следующем руководстве: Supporting Tablets and Handsets.
Ручное управление конфигурациями экрана:
Если вы используете различные activity для реализации различных частей вашего интерфейса вам стоит помнить что может понадобиться реагировать на определенные изменения конфигурации(например поворот экрана) чтобы держать интерфейс в актуальном состоянии.
Например на типичном 7" планшете под управлением Android 3.0 или выше, News Reader приложение использует отдельную activity для отображения новостной статьи если устройство находится в портретном режиме, но использует двухпанельную разметку в альбомном режиме.
Это означает, что когда пользователь находится в портретном режиме и activity для просмотра статьи на экране, мы должны отслеживать смену ориентации экрана в альбомный режим и реагировать соответствующим образом - завершать работу activity и возвращаться в главное activity для отображения содержимого в двухпанельном режиме:
public class ArticleActivity extends FragmentActivity {
    int mCatIndex, mArtIndex;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCatIndex = getIntent().getExtras().getInt("catIndex", 0);
        mArtIndex = getIntent().getExtras().getInt("artIndex", 0);

        //Если нужен двухпанельный режим...
        if (getResources().getBoolean(R.bool.has_two_panes)) {
            finish();
            return;
        }
        ...
}
Оригинальный материал доступен по адресу.





Комментариев нет:

Отправить комментарий