18

I am writing an application that uses NFC to read some data stored on it. My application uses Fragments and Fragment don't come with onNewIntent() method. Since, the data I am reading is done with my separate class which handles NFC related operation, the only thing I need to do is update the TextView inside the Fragment. However this implementation can also be used to pass new Intent to the Fragment.

Here is my current implementation which makes use of an interface. I am calling the listener after new Intent is received and NFC related checks succeeds. This is the FragmentActivity which hosts Fragment.

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);

}
}

This is MyBalance Fragment which has TextView that needs to be updated whenever NFC is read:

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);
}
}

This code works perfectly like expected for my application but, I want to know if there is better way to handle new Intent from Fragments?

4

3 回答 3

4

这是一个老问题,但让我回答一下,以防有人碰到它。

首先,您的代码中有一个错误:

您不能按照自己的方式注册Fragments为听众Activity。原因是ActivityandFragments可以被系统销毁并在以后从保存的状态重新创建(请参阅有关重新创建Activity 的文档)。发生这种情况时,将创建theActivity和 the 的新实例,但将 设置为侦听器的代码将不会运行,因此永远不会被调用。这是 Android 应用程序中非常常见的错误。FragmentFragmentonNewBalanceRead()

为了传达来自Activityto的事件,Fragment我看到至少有两种可能的方法:

基于接口:

Fragments 之间有一种官方推荐的通信方式。这种方法类似于您现在所做的,因为它使用由Fragmentor实现的回调接口Activity,但它的缺点是紧密耦合和大量丑陋的代码。

基于事件总线:

更好的方法(恕我直言)是利用事件总线-“主组件”(Activity在您的情况下)将“更新”事件发布到事件总线,而“从属组件”(Fragment在您的情况下)将自身注册到事件总线onStart()(取消注册in onStop()) 以接收这些事件。这是一种更简洁的方法,不会在通信组件之间添加任何耦合。

我所有的项目都使用Green Robot 的 EventBus,我不能高度推荐它。

于 2016-10-19T18:39:39.987 回答
1

至少有一种选择:来自Activity.onNewIntent 文档

在接收到新意图之前,活动将始终暂停,因此您可以指望在此方法之后调用 onResume()。

请注意,getIntent() 仍然返回原始 Intent。您可以使用 setIntent(Intent) 将其更新为这个新的 Intent。

FragmentActivity.onNewIntent 文档有所不同,但我不认为它与上述陈述相矛盾。我还假设Fragment.onResume将在FragmentActivity.onResume之后调用,尽管文档对我来说似乎有点挑剔,尽管我的测试证实了这个假设。基于此,我像这样更新了活动中的 Intent(Kotlin 中的示例)

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

在 Fragment.onResume 我可以像这样处理新意图

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

这样,活动就不需要知道它拥有哪些片段。

于 2017-07-23T15:17:27.157 回答
-2

不,没有更好的办法。片段可以比活动更长寿,并且根本不一定与它们绑定,因此提供新的意图是没有意义的。

顺便说一句,您的代码中有一些错误:)

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);

添加空检查。用户可能从未选择过导航项。

于 2013-10-27T09:42:19.500 回答