Custom View calling startActivityForResult

You actually can do it like this:

@Override
public void onClick(View v) {
    final FragmentManager fm = ((FragmentActivity) getContext()).getSupportFragmentManager();
    Fragment auxiliary = new Fragment() {
        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            //DO WHATEVER YOU NEED
            super.onActivityResult(requestCode, resultCode, data);
            fm.beginTransaction().remove(this).commit();
        }
    };
    fm.beginTransaction().add(auxiliary, "FRAGMENT_TAG").commit();
    fm.executePendingTransactions();

    auxiliary.startActivityForResult(new Intent(getContext(), ToStartActivity.class), 3333);
}

The trick is using an auxiliary temp fragment.


I'm having the same issue, as the initial question. I know that you all posted working solution, BUT, all the solutions lack one thing: encapsulation. What do I mean - If in one activity I have 10 views that should (on some event) start another activity it should be NORMAL to be able to start that new activity from the view that needs that activity. You all are trying to convince that is better to handle all new possible activites from the initial one - than why we added different logic in each view. We may want to RE-USE code, and create one custom view that can work INDEPENDENT to where we use it (work may include showing another activity to select something for example).

I know that this is not possible (or not yet), and is a clear proof that Android SDK is not ready yet to handle real big applications.

If you want an example:in any real business app that has for example, customer list (that should be a view) ,the view should be able to launch by itself addcustomer activity, edit customer activity and so on, independent from where you put that customer list view (control) - because in big apps you need to RE-use components (you may need to show the customer list control in a order product activity, in a timesheet activity and so on.).

One possible solution could be: - start the new activity (using the view context (normally should be the parent activity). - on the new activity closing event, either call directly a method in the calling view (depending on the case, and posibilities: either static that is handling the code that you normally would run on activityresult, either try to pass the instance of the calling view to the new activity, and do the same. In this way, you can handle your new activity, without letting the containing activity to know anything about it.


You need to catch this from your activity. The startActivityForResult is called on your activity, so it'll be the one launching the Intent and getting the result. I'd say that it's overall bad to launch it directly from the view's code. A better solution would be with a clickListener (or checkChangeListener, or whatever you want), set by your activity, and calling a method like "openImageCapture".

When the Intent returns, your activity will take care of the result and update your views as needed.

Views are there just for displaying stuff on the screen and getting user input, the activity is there to do the actual work.


Here's a static function to implementing @riwnodennyk's solution, while overcoming the Fragment must be static and not in anonymous class error:

public static void myStartActivityForResult(FragmentActivity act, Intent in, int requestCode, OnActivityResult cb) {
    Fragment aux = new FragmentForResult(cb);
    FragmentManager fm = act.getSupportFragmentManager();
    fm.beginTransaction().add(aux, "FRAGMENT_TAG").commit();
    fm.executePendingTransactions();
    aux.startActivityForResult(in, requestCode);
}

public interface OnActivityResult {
    void onActivityResult(int requestCode, int resultCode, Intent data);
}

@SuppressLint("ValidFragment")
public static class FragmentForResult extends Fragment {
    private OnActivityResult cb;
    public FragmentForResult(OnActivityResult cb) {
        this.cb = cb;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (cb != null)
            cb.onActivityResult(requestCode, resultCode, data);
        super.onActivityResult(requestCode, resultCode, data);
        getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
    }
}

Usage example:

    Intent inPhonebook = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
    myStartActivityForResult((FragmentActivity) getContext(),
            inPhonebook, REQUEST_CODE_PICK_CONTACT, this::onContacts);