如何在Activity和Fragment之间实现回调?

16

我在我的活动中有这个界面。

public interface LogoutUser {
    void logout();
}

我的片段实现了这个接口,因此在我的片段中,我有这个:

@Override
public void logout() {
    // logout
}

在我的活动中,我调用了

mLogoutUser.logout();

mLogoutUser是一个类型为LogoutUser接口的对象。

我的问题在于mLogoutUser对象为空。如何初始化它?

谢谢!


1
mLogoutUser = yourFragment; - dymmeh
4个回答

21

正如我在评论中所说,我使用了我的碎片中的onAttach方法解决了这个问题。但是这种方式需要在碎片中声明回调字段(在这种情况下为mLogoutUser),并以以下方式初始化:

public class MyFragment extends ListFragment {
    LogoutUser mLogoutUser;

    // Container Activity must implement this interface
    public interface LogoutUser {
        public void logout();
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try {
            mLogoutUser = (LogoutUser) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString()
                    + " must implement LogoutUser");
        }
    }

    ...
}

与其他片段通信中了解更多信息。


但如果您的情况是在活动中声明的字段,您可以使用onAttachFragment方法从您的活动初始化监听器字段,如下所示:

@Override
public void onAttachFragment(Fragment fragment) {
    super.onAttachFragment(fragment);

    mLogoutUser = (LogoutUser) fragment;
}

另外,您可以使用事件总线来实现片段和活动之间的通信。一个选择是来自Square的Otto库


1
非常感谢 @androidevil。你救了我的一天!顺便说一下,如果我们有多个片段附加,我们可以在onAttachFragment中使用if(fragment instanceof fragmentone){}。 - Kishore
这不是最好的方式,你正在违反开闭原则。实际上,如果你想在其他活动中重复使用这个片段,由于你所做的实例检查,你是不能这样做的。 - Ehsan
由于onAttach(Activity activity)已被弃用,并且现在有了onAttach(Context context),我认为最初的建议现在是可接受的。 - C. Skjerdal

16

示例:如何在 Fragment 和 Activity 之间创建回调

public interface CallBackListener {
    void onCallBack();// pass any parameter in your onCallBack which you want to return
}

CallBackFragment.class

:回调片段类
public class CallBackFragment extends Fragment {

    private CallBackListener callBackListener;

    public CallBackFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment

        return inflater.inflate(R.layout.fragment_call_back, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        //getActivity() is fully created in onActivityCreated and instanceOf differentiate it between different Activities
        if (getActivity() instanceof CallBackListener)
            callBackListener = (CallBackListener) getActivity();
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        Button btn = (Button) view.findViewById(R.id.btn_click);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(callBackListener != null)
                    callBackListener.onCallBack();
            }
        });
    }
}

CallbackHandlingActivity.class

public class CallbackHandlingActivity extends AppCompatActivity implements CallBackListener
{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_all_user);

    }

    @Override
    public void onCallBack() {
        Toast.makeText(mContext,"onCallback Called",Toast.LENGTH_LONG).show();
    }
}

感谢您提供这个完整的示例答案! - Jakub S.
@Nepster 为避免内存泄漏,是否有必要在 Fragment.onDestroy() / Fragment.onStop() 中将 callBackListener 置为空? - Ehtesham Hasan
1
谢谢!工作顺利!那么,有没有关于从Activity到Fragment创建回调的示例? - Serjaru

5

Android碎片 - 与活动通信

您需要使用getFragmentById()getFragmentByTag()获取对碎片的引用。

getFragmentManager().findFragmentById(R.id.example_fragment);

4
感谢您的信任。另一种方法是使用onAttach()函数。 - androidevil
@androidevil 怎样使用 onAttach()? - DroidLearner
onAttach() 自 API 23 起已被弃用。 - zeeshan
1
据我所知,onAttach(Activity activity)已被弃用,推荐使用onAttach(Context context)。 - Juan Carlos Iturriagagoitia

0
您可以使用kotlinx的Channel在片段和活动之间发送数据或回调,反之亦然。
在您的MainActivity中:
val loginPromptChannel = Channel<LoginPromptState>()
val loginStateFlow = loginPromptChannel.receiveAsFlow()

//onCreate
lifecycleScope.launchWhenStarted {
            loginStateFlow.collect() { state ->
                when (state) {
                    is LoginPromptState.Login -> {
                        //smooth scroll to login fragment
                        binding.viewpager.setCurrentItem(2, true)
                    }

                }
            }
        }
//create sealed a class 
sealed class LoginPromptState {
        object Login : LoginPromptState()
    }

在你的片段中发送回调,例如:
lifecycleScope.launch {
    val channelLogin = (activity as MainActivity).loginPromptChannel
    channelLogin.send(MainActivity.LoginPromptState.Login)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接