Вопрос по java, android – Фрагменты и уведомления: предназначайтесь для различных действий от Уведомления; в зависимости от конфигурации экрана

12
Question:

Как решить чтоActivity Notification должен запускаться, если цель может зависеть от конфигурации (размер экрана, ориентация и т. д.); как это часто бывает, когда кто-то используетFragments?

Details:

Давайте рассмотримОбразец NewsReader это демонстрирует, как использоватьFragments, чтобы создать приложение, которое хорошо работает с экранами разных размеров и ориентациями. Это приложение имеет следующую структуру:

A HeadlinesFragment. An ArticleFragment. A "main" activity (NewsReaderActivity). In dual pane mode, this activity contains both the fragments. In single-pane mode, it only contains the HeadlinesFragment. An ArticleActivity. This activity is only used in single pane mode; and it contains the ArticleFragment.

Теперь предположим, что я должен улучшить это приложение, чтобы добавить фонService он прослушивает обновления новостей и уведомляет пользователя через уведомления в строке состояния о появлении новых новостей. Список разумных требований может выглядеть так:

If there are multiple news updates, clicking on the notification should always take the user to the headlines list. If there's only one update, clicking on the notification should open up the brand new news article.

Обратите внимание, что эти требования относятся к различным целевым действиям в зависимости от текущей конфигурации. Особенно,

Requirement (1) in either mode = NewsReaderActivity. Requirement (2) in dual-pane mode = NewsReaderActivity. Requirement (2) in single-pane mode = ArticleActivity.

Что было бы элегантным способом для достижения (2) и (3) выше? Я думаю, что можно смело исключить возможностьService исследует текущую конфигурацию, чтобы решить, какую деятельность следует направить сPendingIntent.

Одним из решений, о котором я подумал, было пропустить (2) и всегда делать (3), то есть всегда запускатьArticleActivity если есть только одно обновление новостей. Этот фрагмент из ArticleActivity выглядел многообещающе:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //...
    //...
    // If we are in two-pane layout mode, this activity is no longer necessary
    if (getResources().getBoolean(R.bool.has_two_panes)) {
        finish();
        return;
    }

    //...
    //...
}

Этот код гарантирует, что если кто-то просматриваетArticleActivity, но переключается на конфигурацию, где она больше не требуется (например, из портретной в альбомную); тогда действие просто закрывается.

Однако в нашем случае это не сработает, поскольку намерение будет иметьFLAG_ACTIVITY_NEW_TASK флаг установлен; мы бы создали новую задачу, а «предыдущая» отсутствует активность в стеке. Итак, звоняfinish() будет просто очистить весь стек.

Итак, как решить, какую операцию запустить из уведомления, если эта операция зависит от конфигурации экрана?

Ваш Ответ

3   ответа
5

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

Прежде всего вы контролируете внешний вид фрагментов, используя макеты XML для различных конфигураций, например:

одна панель (res / layout):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >

        <FrameLayout
                android:id="@+id/mainFragment"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent" />

</LinearLayout>

двойная панель (res / layout-xlarge-land):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >

        <FrameLayout
                android:id="@+id/mainFragment"
                android:layout_weight="2"
                android:layout_width="0dp"
                android:layout_height="fill_parent" />

        <FrameLayout
                android:id="@+id/detailsFragment"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="fill_parent" />

</LinearLayout>

В NewsReaderActivity вам нужно только проверить, какие фрагменты существуют в макете:

boolean isMainFragment = (findViewById(R.id.mainFragment) != null);
if (isMainFragment) {
    mainFragment = new ListFragment();
}

boolean isDetailFragment = (findViewById(R.id.detailsFragment) != null);
if (isDetailFragment) {
    detailFragment = new DetailsFragment();
}

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (isMainFragment ) {
    transaction.add(R.id.mainFragment, mainFragment);
}
if (isDetailFragment ) {
    transaction.add(R.id.detailFragment, detaislFragment);
}  
transaction.commit();

Затем, когда вы находитесь в режиме одной панели (detailsFragment не существует) и хотите показать экран подробностей, вы просто снова запускаете ту же операцию, но включаете параметр в намерение указать, какой контент необходим:

void onViewDetails() {
    Intent i = new Intent(this, NewsReaderActivity.class);
    i.putExtra("showDetails", true);
    startActivity(i);
}

В onCreate () вашей активности вы выбираете фрагмент в зависимости от этого параметра:

boolean isDetailFragment = (findViewById(R.id.detailsFragment) != null);
if (!isDetailFragment) {
    boolean showDetails = getIntent().getBooleanExtra("showDetails", false);
    if (showDetails) {
       mainFragment = new DetailsFragment();
    }
    else {
       mainFragment = new ListFragment();
    }
}

Now when you finally launch the activity from the notification then you just don't care about the current screen orientation anymore!

Просто включите & quot; showDetails & quot; отметьте свое намерение и установите его соответствующим образом, точно так же, как это сделано вonViewDetails(), В этом случае ваша активность будет отображать оба фрагмента, когда вы находитесь в режиме двойной панели (вы все равно можете сделать какое-то специальное поведение, если «showDetails» имеет значение «истина») или в противном случае, когда вы находитесь в конфигурации, требующей одного режима боли, тогда указанный вами фрагмент в & quot; showDetails & quot; флаг показан.

Я надеюсь, что это поможет и даст вам хорошее понимание этого подхода.

Error: User Rate Limit ExceededFragmentError: User Rate Limit Exceededsomething similarError: User Rate Limit ExceededFragmentError: User Rate Limit ExceededActivityError: User Rate Limit ExceededFragmentError: User Rate Limit ExceededActivityError: User Rate Limit Exceeded curioustechizen
7

Это действительно хороший вопрос!

I think one can safely rule out the possibility of the Service probing for the current configuration to decide what activity to target with the PendingIntent.

Я согласен с тем, что было бы предпочтительнее сохранять решения о пользовательском интерфейсе на уровне пользовательского интерфейса, но наличие решения для службы, безусловно, было бы целесообразным выбором. Вы можете использовать статический метод в классе уровня пользовательского интерфейса, чтобы технически оставить код решения вне службы (например, статическийcreateArticlePendingIntent() метод наNewsReaderActivity что сервис использует для создания своегоNotification).

So, how does one decide what activity to launch from a notification, if the activity to launch depends on screen configuration?

ИспользоватьgetActivity() PendingIntent заNewsReaderActivity в вашемNotificationс достаточным количеством дополнений, которыеNewsReaderActivity знает, что именно в этом "показать статью" сценарий. Прежде чем это вызываетsetContentView(), пусть это определит,ArticleActivity это правильный ответ. Если так,NewsReaderActivity звонкиstartActivity() запускатьArticleActivityзатем звонитfinish() чтобы избавиться от себя (или нет, если вы хотите, чтобы НАЗАД из статьи перейти кNewsReaderActivity).

Или используйтеgetActivity() PendingIntent заICanHazArticleActivity в вашемNotification. ICanHazArticleActivity имеетTheme.NoDisplay, поэтому он не будет иметь пользовательского интерфейса. Принимает решение о запускеNewsReaderActivity или жеArticleActivity, звонкиstartActivity() на правильный ответ, а затем звонитfinish(), Преимущество по сравнению с предыдущим решением состоит в том, что нет краткой вспышкиNewsReaderActivity если конечный пункт назначенияArticleActivity.

Или используйтеcreateArticlePendingIntent() Вариант, который я упомянул в первом пункте моего ответа.

Могут быть и другие варианты, но это то, что приходит на ум.

Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededServiceError: User Rate Limit ExceededafterError: User Rate Limit ExceededR.bool.dual_paneError: User Rate Limit Exceeded curioustechizen
Error: User Rate Limit Exceededfinish()Error: User Rate Limit Exceeded curioustechizen
Error: User Rate Limit ExceededTheme.NoDisplayError: User Rate Limit ExceededActivityError: User Rate Limit ExceededActivityError: User Rate Limit Exceeded curioustechizen
Error: User Rate Limit ExceededR.bool.dual_paneError: User Rate Limit ExceededgetResources().getBoolean()Error: User Rate Limit ExceededContextError: User Rate Limit Exceededfinish()Error: User Rate Limit ExceededstartActivity()Error: User Rate Limit Exceeded
1

Подход деятельности без пользовательского интерфейса, упомянутый впринятый ответ это то, что я решил. Я попробовал другой вариант, но это не сработало. Что я попробовал, так это:

  1. In the Service, build an Intent stack with the Intent for NewsReaderActivity at the bottom of the stack and that for ArticleActivity at the top.
  2. Use PendingIntent.getActivities() and pass in the stack as created in Step1 to obtain PendingIntent representing the full stack.
  3. In ArticleActivity, we have the following code:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //...
        //...
        // If we are in two-pane layout mode, this activity is no longer necessary
        if (getResources().getBoolean(R.bool.has_two_panes)) {
            finish();
            return;
        }
    
        //...
        //...
    }
    

Итак, вы первая цельArticleActivity - который делает своего рода самоанализ, чтобы решить, полезно ли это в текущей конфигурации. Если нет, он просто уходит сfinish(), ПосколькуNewsReaderActivity уже присутствует "до"ArticleActivity в бэк-стеке, это приведет вас кNewsReaderActivity.

Это казалось идеальным решением - за исключением одного момента, который я упустил из виду:PendingIntent.getActivities() работает только с API 11 или выше. В библиотеке поддержки есть приблизительный эквивалент:TaskStackBuilder, Можно добавить Intents в стек с помощьюaddNextIntent()и, наконец, позвонитьgetPendingIntent() достичь чего-то похожего наPendingIntent.getActivities() (Я предполагаю).

Однако на устройствах до HC это приведет только кtop-most Activity из стека, запускаемого в новой задаче (выделено мое):

On devices running Android 3.0 or newer, calls to the startActivities() method or sending the PendingIntent generated by getPendingIntent(int, int) will construct the synthetic back stack as prescribed. On devices running older versions of the platform, these same calls will invoke the topmost activity in the supplied stack, ignoring the rest of the synthetic stack and allowing the back key to navigate back to the previous task.

Таким образом, на устройствах до HC нажатие на ArticleActivity вернет вас к задаче, которая выполнялась до нашей. Это не то, что мы хотим.

Я, вероятно, поделюсь своим проектом в ближайшее время. Я также беспокоился о том, чтобы запускать новые задачи даже тогда, когда выполнял уточнения внутри приложения (например, уведомление «новая новостная статья», когда я читаю статью). Надеюсь, я опубликую это как отдельный вопрос.

Похожие вопросы