startActivityForResult的替代方案

Bra*_*ane 4 android

编辑:将Fragment更改为Partial,当我写这篇文章时,我对Fragment对象一无所知.

我有一个部分包含一个按钮来调出联系人列表.这样做需要打电话

startActivityForResult( new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI), MY_REQUEST_CODE );
Run Code Online (Sandbox Code Playgroud)

并在我的Activity中处理结果,如:

public void onActivityResult( int requestCode, int resultCode, Intent data ) {
    if (resultCode == RESULT_OK) {
        switch (requestCode) {
            case MY_REQUEST_CODE: {
                Address address = contact_address( data );
                if (address != null) {
                    // do something with address
                }
            } break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

根据我在Activity布局中包含部分的方式,在其他部分中可能有几层深度,并且可能有多个部分实例.

我想避免将MY_REQUEST_CODE的ID一直传播到调用活动的部分 - 或其任何变体,比如将onClickListener分配给按钮 - 我不希望顶级UI关注部分如何完全是建造的.

有没有一种标准的方法来实现这一目标?在我看来,如果可以使onActivityResult接受Uri而不是int代码,则可以避免传播.我希望我在这里遗漏了一些明显的东西......

Com*_*are 12

我有一个片段,其中包含一个按钮以显示联系人列表.这样做需要调用[ startActivityForResult()]

最简单的答案是你打电话startActivityForResult()Fragment,而不是Activity.结果应该回到片段自己的onActivityResult().

话虽这么说,你的方法似乎是对我推荐的几乎完全倒置.碎片不应该是开始活动.片段既不知道也不关心如何处理片段本身之外的事物的特定UI事件(例如,按钮点击).在这种情况下,活动应该负责获得联系,片段只是告诉活动"嘿,这里按钮gots按下,哟,给我一个联系回".这不仅对于关注点分离很重要,而且对于测试很重要,因此您可以使用模拟的联系人选择等来测试此行为.

根据我在Activity布局中包含该片段的方式,它可能在其他UI片段中有几层深度

您无法在Android中的片段中嵌套片段.如果你尝试,你会得到不可靠的结果.

我不希望顶级UI关心片段的构造方式.

活动绝对必须知道片段是如何构建的,因为活动是构建构造的活动.

脱下袖口,这是我接近它的方式:

  1. 为超出片段本身边界的片段引发的UI事件建立一个侦听器接口.出于这个答案的目的,我会称之为OnFooEventListener.

  2. 当活动创建/配置片段时,活动会向片段提供OnFooEventListener实例.如果活动实现了接口,那么这可能是活动本身.

  3. 为异步pick-a-contact事件建立一个侦听器接口,该片段希望某人代表它执行该事件.出于这个答案的目的,我会称之为OnContactPickedListener.它会有一个像onContactPicked()包含Uri所选联系人的方法.

  4. OnFooEventListener,requestContact()用户单击按钮时会调用类似的内容.requestContact()OnContactPickedListener实例作为参数.

  5. 活动将执行startActivityForResult()调用,并在其中onActivityResult()调用关联的onContactPicked()方法OnContactPickedListener.HashMap在请求进行过程中,活动会将这些内容缓存在某个或某个内容中.

现在,我们在活动和片段之间有明确的分离.片段仍然可以由任意数量的活动托管(例如,一个用于大屏幕,一个用于普通屏幕).可以通过生产方式(ACTION_PICK)或其他方式提供联系以进行测试(例如,作为测试用例的一部分建立的值).该活动可以毫无问题地处理任何数量的此类片段.


Bra*_*ane 3

我不认为 Commonsware 的解决方案回答了我的问题,因为它要求部分的每个容器都为完全包含在部分中的事件添加处理程序。

我特别不想为该部分的每个实例实现处理程序。

所以我想出了一个解决方案,尽管我承认这感觉也不对。

首先,我对 Activity 进行子类化,并创建一个小型框架,用于将侦听器与 startActivityForResult() 和 onActivityResult() 相关联。

public class BaseActivity extends Activity {
    // assume that we'll never start more than one activity at a time from our activity (a safe assumption?)
    private static final int
        LISTENED_REQUEST_CODE = 1000000000;

    public static interface ActivityResultListener {
        public void onResultCode( int resultCode, Intent data );
    }
    private ActivityResultListener
        activity_result_listener_;

    public void startActivityForResult( Intent intent, ActivityResultListener listener ) {

        // paranoia
        if (activity_result_listener_ != null) {
            Log.e( TAG, "Activity trying to start more than one activity at a time..." );
            return;
        }

        activity_result_listener_ = listener;
        startActivityForResult( intent, LISTENED_REQUEST_CODE );
    }

    public void onActivityResult( int requestCode, int resultCode, Intent data ) {
        if (requestCode == LISTENED_REQUEST_CODE) {
            if (activity_result_listener_ != null) {
                ActivityResultListener listener = activity_result_listener_;
                activity_result_listener_ = null;
                listener.onResultCode( resultCode, data );
                return;
            }
        }

        super.onActivityResult(requestCode, resultCode, data);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在部分内部,我调用重载的 startActivityForResult() 并实现一个侦听器:

public void onFinishInflate() {
    ImageButton contact_button = (ImageButton)findViewById(R.id.contact_button);
    contact_button.setOnClickListener( new OnClickListener() {
        @Override
        public void onClick(View view) {
            ((BaseActivity)getContext()).startActivityForResult( new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI), 
                new BaseActivity.ActivityResultListener() {
                    @Override
                    public void onResultCode( int resultCode, Intent data ) {
                        if (resultCode == BaseActivity.RESULT_OK) {
                            add_contact_address( data );
                        }
                    }
                });
        }
    } ); 
}
Run Code Online (Sandbox Code Playgroud)

所以现在我可以在任何地方使用这个部分,而不必为每个实例定义侦听器。

我看到的缺点是子类化 Activity 会阻止我使用其他 Activity 类型。这可以被重新设计成接口/实现,但随后又开始遭受非 DRY 逻辑的困扰。