Динамически установить родительскую активность

Таким образом, в настоящее время у меня есть действие, которое может быть достигнуто двумя различными действиями, проблема в том, что я могу установить только одно действие в качестве родительского действия в XML-файле манифеста. Очевидно, что это плохой дизайн UX/UI, потому что действие может отослать пользователя назад к неправильному действию, в котором он был ранее, и поэтому я пытаюсь динамически установить, какое действие является родительским действием.

Проблема в том, что я не совсем уверен, как это сделать, будь то код или XML, поэтому любые указатели приветствуются.:)

7 ответов

Решение

Здесь есть две концепции: "Вверх" и "Назад". "Назад" очевиден: отведи меня туда, где я был до того, как пришел сюда. Обычно вам не нужно беспокоиться о "Назад", так как система справится с этим просто отлично. "Вверх" не так очевидно - это аналог Zoom Out - от элемента к коллекции, от детализации к более широкой картине.

Что из этого подходит вашему варианту использования?


Согласно комментарию ниже: кнопка "вверх" извлекает пункт назначения из манифеста Android, но его можно настроить программно.

Для будущих читателей вот пример того, как на самом деле реализовать официальное / правильное решение согласно руководствам разработчика (выделите абзац, начинающийся с "Это подходит, когда родительское действие может отличаться... ").

Обратите внимание, что это решение предполагает использование библиотеки поддержки для реализации ActionBar и что вы можете по крайней мере установить исходную активность "по умолчанию" в XML-файле манифеста в качестве откатной, если активность, из которой вы отказываетесь, находится в "задаче", которая не принадлежит вашему приложению (см. связанные документы для пояснения).

// Override BOTH getSupportParentActivityIntent() AND getParentActivityIntent() because
// if your device is running on API 11+ it will call the native
// getParentActivityIntent() method instead of the support version.
// The docs do **NOT** make this part clear and it is important!

@Override
public Intent getSupportParentActivityIntent() {
    return getParentActivityIntentImpl();
}

@Override
public Intent getParentActivityIntent() {
    return getParentActivityIntentImpl();
}

private Intent getParentActivityIntentImpl() {
    Intent i = null;

    // Here you need to do some logic to determine from which Activity you came.
    // example: you could pass a variable through your Intent extras and check that.
    if (parentIsActivityA) {
        i = new Intent(this, ActivityA.class);
        // set any flags or extras that you need.
        // If you are reusing the previous Activity (i.e. bringing it to the top
        // without re-creating a new instance) set these flags:
        i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        // if you are re-using the parent Activity you may not need to set any extras
        i.putExtra("someExtra", "whateverYouNeed");
    } else {
        i = new Intent(this, ActivityB.class);
        // same comments as above
        i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        i.putExtra("someExtra", "whateverYouNeed");
    }

    return i;
}

ПРИМЕЧАНИЕ. Если вы не зададите родительское действие по умолчанию в XML-файле манифеста, вам также потребуется реализовать onCreateSupportNavigateUpTaskStack(), поскольку система не будет знать, как сгенерировать backstack для вашей задачи. Я не привел ни одного примера для этой части.

Мои мысли о finish() тип решения

В поисках решения этой проблемы я наткнулся на несколько ответов, которые защищают стратегию переопределения onOptionsItemSelected() и перехват android.R.id.home кнопка, чтобы они могли просто finish() электрический ток Activity вернуться к предыдущему экрану.

Во многих случаях это приведет к желаемому поведению, но я просто хочу отметить, что это определенно не то же самое, что правильная навигация UP. Если вы переходили к дочернему действию через одно из родительских занятий, то да finish() вернет вас к предыдущему экрану, но что если вы введете дочернюю активность через уведомление? В таком случае finish() Если вы нажмете кнопку "ВВЕРХ", вы вернетесь обратно на домашний экран или в любое приложение, которое вы просматривали до того, как нажмете уведомление, а вместо этого оно должно было отправить вас в соответствующую родительскую активность в вашем приложении.

Таким образом, вы можете динамически переходить к родительской активности:

getActionBar().setDisplayHomeAsUpEnabled(true);

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    // Respond to the action bar's Up/Home button
    case android.R.id.home:
        finish();
        return true;
    }
    return super.onOptionsItemSelected(item);
}

ПРИМЕЧАНИЕ. Он перенаправляет вас на активность или фрагмент, откуда вы пришли, независимо от того, родитель это или нет. Нажатие на кнопку "вверх" / "вверх" на панели действий просто завершит текущее действие.

Метод для переопределения является getParentActivityIntent.

Вот мой код и отлично работает.

@Override
public Intent getParentActivityIntent() {
    Intent parentIntent= getIntent();
    String className = parentIntent.getStringExtra("ParentClassSource"); 

    Intent newIntent=null;
    try {
        //you need to define the class with package name
        newIntent = new Intent(OnMap.this, Class.forName(className));

    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return newIntent;
}

От родительской деятельности;

Intent i = new Intent(DataDetailsItem.this, OnMap.class);
i.putExtra("ParentClassSource", "com.package.example.YourParentActivity");
startActivity(i);

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

Теперь для дополнительных усилий по поддержке навигации "вверх" есть несколько способов сделать это:

  1. Активность, родительская активность которой определена в AndroidManifest.

    Your Android Manifest
    ---------------------
    
    <activity
        android:name="com.example.app.DetailActivity"
        android:parentActivityName="com.example.app.MainActivity" >
        <meta-data 
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.app.MainActivity" />
    </activity>
    
    Your Detail Activity
    --------------------
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
    
        switch (item.getItemId()) {
            case android.R.id.home:
                NavUtils.navigateUpFromSameTask(this);
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
    

    Это хорошо работает, если есть только одно родительское действие, означающее, что (DetailActivity) всегда запускается из (MainActivity). В противном случае это решение не будет работать, если (DetailActivity) запускается из разных мест. Подробнее здесь: http://developer.android.com/training/implementing-navigation/ancestral.html

  2. (Проще и рекомендуется) Действия с фрагментом и бэк-стек фрагмента:

    Your Detail Activity
    --------------------
    
    protected void replaceFragment(Bundle fragmentArguments, boolean addToBackStack) {
    
        DetailFragment fragment = new DetailFragment();
        fragment.setArguments(fragmentArguments);
    
        // get your fragment manager, native/support
        FragmentTransaction tr = fragmentManager.beginTransaction();
        tr.replace(containerResId, fragment);
        if (addToBackStack) {
            tr.addToBackStack(null);
        }
        tr.commit();
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
    
        switch (item.getItemId()) {
            case android.R.id.home:
                finish();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
    

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

Надеюсь это поможет.

Чтобы узнать, как правильно использовать Навигацию вверх, см. Это Android Dev Guide.

Обратите внимание, что в приведенном выше Руководстве по разработке Android есть большой недостаток, поскольку функции NavUtils работают по-разному для ICS(и ниже) и JellyBean(и выше). Этот недостаток в NavUtils прекрасно объясняется здесь.

Вы можете переопределить кнопку "Вверх", чтобы действовать как кнопка "Назад" следующим образом:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            onBackPressed();
    }
    return super.onOptionsItemSelected(item);
}

Котлин 2020

Мое действие запускается из разных действий, поэтому AndroidManifest работает только с одним родительским действием.

Вы можете вернуться к предыдущему действию следующим образом:


supportActionBar?.setDisplayHomeAsUpEnabled(true)

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
        when(item!!.itemId){
            android.R.id.home -> {
                finish()
                return true
            }
        }
        return super.onOptionsItemSelected(item)
    }

Вам нужно будет отслеживать родительскую активность. Один из способов сделать это - сохранить его как дополнительное в намерении, которое вы создаете для запуска дочернего действия). Например, если действие A запускает действие B, сохраните намерение A в намерении, созданном для B. Затем в B onOptionsItemSelected где вы обрабатываете навигацию вверх, извлекаете намерение А и запускаете его с нужными флагами намерений.

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

Навигация по Android вверх для Действия с несколькими родителями

Другие вопросы по тегам