Обращение с новым элементом в фрагменте - программирование
Подтвердить что ты не робот

Обращение с новым элементом в фрагменте

Я пишу приложение, которое использует NFC для чтения некоторых данных, хранящихся на нем. В моем приложении Fragments и Fragment не используется метод onNewIntent(). Поскольку данные, которые я читаю, выполняются с помощью моего отдельного класса, который обрабатывает связанную с NFC операцию, единственное, что мне нужно сделать, это обновить TextView внутри Фрагмента. Однако эта реализация также может использоваться для передачи нового намерения в фрагмент.

Вот моя текущая реализация, которая использует интерфейс. Я вызываю слушателя после получения нового Intent и проверки на соответствие NFC успешно. Это FragmentActivity, в котором находится фрагмент.

public class Main extends FragmentActivity implements
    ActionBar.OnNavigationListener {

private Bundle myBalanceBundle;
private NFC nfcObj;
private NewBalanceListener newBlanceListener;

@Override
public void onNewIntent(Intent intent) {
    setIntent(intent);
}

@Override
protected void onResume() {
    getNFCState();
    super.onResume();
}

private void getNFCState() {
    //Other NFC related codes
    else if (nfc_state == NFC.NFC_STATE_ENABLED){
        readNFCTag();
    }
}

private void readNFCTag() {
    //Other NFC related codes
    if (getIntent().getAction().equals(NfcAdapter.ACTION_TECH_DISCOVERED)) {
        nfcObj.setTag((Tag) getIntent().getParcelableExtra(
                NfcAdapter.EXTRA_TAG));
        nfcObj.readQuickBalance();

        transitQuickReadFragment(nfcObj.getCurrentBalance());
    }
}

private void transitQuickReadFragment(String balance) {
    // Creates a balance bundle and calls to select MyBalance Fragment if it
    // is not visible. Calls listener is it is already visible.
    if (actionBar.getSelectedNavigationIndex() != 1) {
        if (myBalanceBundle == null)
            myBalanceBundle = new Bundle();

        myBalanceBundle.putString(Keys.BALANCE.toString(), balance);

        actionBar.setSelectedNavigationItem(1);
    } else {
        newBlanceListener.onNewBalanceRead(balance);
    }
}

@Override
public boolean onNavigationItemSelected(int position, long id) {
    // Other fragment related codes
    fragment = new MyBalance();
    fragment.setArguments(myBalanceBundle);
    newBlanceListener = (NewBalanceListener) fragment;
    // Other fragment related codes
}

// Interface callbacks. You can pass new Intent here if your application
// requires it.
public interface NewBalanceListener {
    public void onNewBalanceRead(String newBalance);

}
}

Это фрагмент MyBalance, который имеет TextView, который необходимо обновлять всякий раз, когда читается NFC:

public class MyBalance extends Fragment implements NewBalanceListener {

private TextView mybalance_value;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    //Other onCreateView related code

    Bundle bundle = this.getArguments();
    if (bundle != null)
        mybalance_value.setText(bundle.getString(Keys.BALANCE.toString(),
                "0.00"));
    else
        mybalance_value.setText("0.00");

    //Other onCreateView related code
}

@Override
public void onNewBalanceRead(String newBalance) {
    mybalance_value.setText(newBalance);
}
}

Этот код работает отлично, как ожидалось для моего приложения, но я хочу знать, есть ли лучший способ обработать новый Intent из фрагментов?

4b9b3361

Ответ 1

Это старый вопрос, но позвольте мне ответить на него, если кто-то столкнется с ним.

Прежде всего, у вас есть ошибка в коде:

Вы не можете зарегистрировать Fragments в качестве слушателей внутри Activity, как вы это делаете. Причина в том, что Activity и Fragments могут быть уничтожены системой и повторно созданы позже из сохраненного состояния (см. Документацию по Восстанавливать активность). Когда это произойдет, будут созданы новые экземпляры как Activity, так и Fragment, но код, устанавливающий Fragment в качестве слушателя, не будет запущен, поэтому onNewBalanceRead() никогда не будет вызываться. Это очень распространенная ошибка в приложениях Android.

Чтобы передавать события от Activity до Fragment, я вижу по крайней мере два возможных подхода:

Основанный на интерфейсе:

Существует официально рекомендованный метод связи между фрагментами. Этот подход аналогичен тому, что вы делаете сейчас, в том, что он использует интерфейсы обратного вызова, реализованные либо с помощью Fragment, либо Activity, но его недостатком является жесткая связь и много уродливого кода.

Шина событий:

Лучшим подходом (IMHO) является использование шины событий - "главный компонент" (Activity в вашем случае), сообщения "обновляют" события до шины событий, тогда как "подчиненный компонент" (Fragment в вашем случае ) регистрируется в шине событий в onStart() (отменяет регистрацию в onStop()) для получения этих событий. Это более чистый подход, который не добавляет связи между коммуникационными компонентами.

Все мои проекты используют Green Robot EventBus, и я не могу рекомендовать его достаточно высоко.

Ответ 2

Существует как минимум одна альтернатива: Документация Activity.onNewIntent:

Активность всегда будет приостановлена ​​до получения нового намерения, поэтому вы можете рассчитывать на вызов onResume() после этого метода.

Обратите внимание, что getIntent() все еще возвращает исходное намерение. Вы можете использовать setIntent (Intent), чтобы обновить его до этого нового намерения.

Документация FragmentActivity.onNewIntent отличается, но я не думаю, что это противоречит вышеприведенным утверждениям. Я также делаю предположение, что Fragment.onResume будет вызываться после FragmentActivity.onResume, хотя документация кажется мне немного суетливой, хотя мои тесты подтверждают это предположение. Исходя из этого, я обновил намерение в действии так (примеры в Котлине)

override fun onNewIntent(intent: Intent?) {
    setIntent(intent)
    super.onNewIntent(intent)
}

И в Fragment.onResume я мог бы обработать новое намерение так:

override fun onResume() {
    super.onResume()
    doStuff(activity.intent)
}

Таким образом, активность не должна знать о том, какие фрагменты она имеет.

Ответ 3

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

Btw, у вас есть несколько ошибок в вашем коде:)

if (actionBar.getSelectedNavigationIndex() != 1) {

Магические числа плохие! используйте константу.

    if (myBalanceBundle == null)
        myBalanceBundle = new Bundle();

    myBalanceBundle.putString(Keys.BALANCE.toString(), balance);
    actionBar.setSelectedNavigationItem(1);

мы уже знаем, что navigationitem установлен в 1

} else {
    newBlanceListener.onNewBalanceRead(balance);

Добавьте нулевую проверку. Пользователь никогда не мог выбрать элемент навигации.