I created custom compound view where I incorporate functionality to take pictures.
I'm calling it like this (from view):
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
((Activity)mContext).startActivityForResult(intent, index);
This part works good. What I don't know how to do is how do I implement onActivityResult inside my custom view?
Or should I catch this inside Activity and than re-route into my view? Doesn't look like very nice solution..
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);
There is no way to catch onActivityResult from your view, only from Activity.
And its not safe to assume that's Context object is Activity. In general you should not rely on this fact. Even if it seems reasonable in case with views, you still should use only methods available trough Context interface. That's because your can't predict all side-effects on the Activity, when you're calling Activity specific functions.
Just make the same method inside your custom view
And inside the activitys onActivityResult call yourView.onActivityResult(...) and process the result inside your view..
Also as guys mentioned you must not always end up with Context being of Activity class. Usually when it is from inflated view.
But if you construct your view only in code and always use the activity instance you are good.
Related
This is a famous problem of getting result from activity to a nested fragment, the activity could send the result to only the Fragment that has been attached directly to Activity but not the nested one. That's the reason why onActivityResult of nested fragment would never been called no matter what.
In my case i have one activity that contains one fragment which has a viewPager with bunch of fragments.
In one of this fragments of the viewPager i try to start a camera intent, the result returned by this intent is never sent to the last fragment, i tried severals answers that i found but none of them is robust, one of the solutions was to create an EventBus object to diffuse the resultCode from activity to all the fragments but it doesn't work for this case, any help??
I might have a working solution for you since I used to be in a very similar situation.
In short terms I stopped relying on Fragment's onActivityResult() and implemented a working logic myself.
The only onActivityResult() you should trust is the one of your Activity.
You may create an interfacelike this one
public interface ActivityResultDispatcher {
// Pass anything you want, Bundle, Intent, ecc...
// For the sake of simplicity I'm using Bundle...
public void dispatchResultData(final Bundle data);
}
and have your Fragment classes implement it.
The communication between your Activity and your primary Fragmentshould be very easy that you can do something like this (in your Activity):
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
final Bundle bundle = new Bundle();
// put data inside Bundle
this.primaryFrament.dispatchResultData(bundle);
}
Now, your primary Fragment is the one that holds the ViewPager with the other Frament classes.
So in your primary Fragment you will do:
#Override public void dispatchResultData(final Bundle data) {
for (int i = 0; i < MyViewPager.NUM_FRAGMENTS; i++) {
final ActivityResultDispatcher subFragment = (ActivityResultDispatcher) this.retrieveFragment(i);
// some fragments in ViewPager may be dynamically deleted
if (subFragment != null) {
subFragment.dispatchResultData(data);
}
}
}
The function retrieveFragment(final int position) is a very cozy piece of code I had found on the web.
An implementation for a ViewPager works like this:
public Fragment retrieveFragment(final int position) {
return this.fragmentManager.findFragmentByTag("android:switcher:" + R.id.my_pager_id + ":" + position);
}
All your active Fragment classes in the ViewPager will receive the Bundle and you can decide what to do with it.
You may pass more information to the dispatcher, such as
public void dispatchResultData(final Bundle data, final int requestCode);
and have your Fragment decide if it should answer to the function call.
You have basically built a complete fully-working onActivityResult() utility and can be sure that all of your Fragments (if instantiated) will receive the result.
I have a two pane fragment design with the detail fragment dealing with the item clicked on the leftmost, listfragment.
Since I have many different list items, they have different GUI's each one of them, I want to put the handling of the GUI parts in different separate classes.
I'm using reflections to get the code slimmed inside the ItemDetailFragment:(code below is stripped down)
*//get class gui_handler for the object and get its constructor:*
Constructor<?> ctor = mListItem.getmGuiHandler().getConstructor(View.class);
*//create an object of the gui_handler class, pass the rootView as arg:*
gui_handler_base handlerObject = (gui_handler_base) ctor.newInstance(root);
*//run setup-method containing findViewById() and more GUI related stuff:*
handlerObject.setupGUI(mListItem, getActivity());
In setupGUI:
public void setupGUI(MyListItem item, final Activity activity) {
buttonRun.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
final String FILENAME = "XXX";
Intent I = new Intent(activity,KTActivity.class);
Bundle b = new Bundle();
b.putSerializable("FileName",FILENAME);
I.putExtras(b);
activity.startActivityForResult(I,1);
return;
}
});
#Override
public void onActivityResult(int requestCode, int resultCode,
Intent data) {
// I WANT TO END UP HERE WHEN THE ACTIVITY RETURNS;
}
I have one onActivityResult method in the gui_handler_base class and one in the ItemDetailFragment, but none of them are called.
How can I do this??
Your question is quite ambiguous. Why are you using onActivityResult? activity.startActivityForResult(I,1); why? WHy not just implement the onclicklistener class in your fragment class and be done with it? It seems like in trying to reduce code you created more headaches for yourself. Also take out numbers in your code and use constants. You have to call the intent and use the intent in the same fragment. Further more why are you extracting out the GUI functionality from the fragments? You shouldn't be. even if you are creating custom functionality, you would still deal with a fragment's functionality in the fragment.
//if you want to try this… normally when receiving a camera intent this is what you can do.
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case yourResultCode:
doSomethingWithYourResult;
break;
]
I have a problem with my fragments. I've only three fragments, and in the right side I have a fragment with a historical listView, and I want to update when I take a picture. But this fragment only update when I move to left side. But I want to update when I'm in the middle too, or for example when I end up to take a picture.
I can't call onCreateView(...) obviously. What's the easiest solution if I want to call from MainAcitivty to FragmentAdapter?
(FragmentAdapter; where is my onCreateView with inflater.inflate(R....)
Thanks a lot
Create a method in your fragment to update the listview and make it static or create a static instance of your fragment and call the method in your activity when your app comes back from the camera inside your onactivityresult call.
EDIT:
you could just set up a static method in your fragment and call it anywhere like this.
in your fragment and in this sample lets say its called MyFragment:
public static void doSomething(Intent data){
//do something with data from camera
}
then in your activity's onActivityResult:
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
MyFragment.doSomething(data);
}
im my App i use TabHost. and ActivityGroup to load activities under the tab. on my 2nd tab i open activityGroup "TabGroupActivity"... and from here i open a child activity "childActivity2". from the "childActivity2" i want to open an normal activity which has a theme dialog. and when i return from my normal activity i want to run the onActivityResult() in my childActivity2.
But the onActivityResult() in ChildActivity2 is not working.
the code where in childActivity2 to start the normal activity is
data.putInt("doctorId", doctor_id);
Intent createSchedule = new Intent(ScheduleWeekly.this, CreateSchedule.class).putExtras(data);
startActivityForResult(createSchedule, 1);
this is my onActivityResult()
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode==Activity.RESULT_OK)
{ Log.e("get","result");
.................
......
}
}
Your problem is same as mine. The problem is that the onActivityResult function won't directly trigger from a child activity in activity group, even your intent is from that child activity.
The solution is divided in three steps.
First, you have to let your parent activity, that is your ActiviyGroup class to call the startActivityForResult function in the position you need to jump out the current activity. In your child activity, when you need to lunch your normal activity, instead call:
startActivityForResult(intent, 0);
You should call:
getParent().startActivityForResult(intent,0);
This will let the ActivityGroup to take care of the call back. In your case, since your have three level nested, you may have to try whether the parent or grandparent should take care of the call back and make proper modification about getParent() part.
Second, after you make the parent class of current activity start the intent, you will need to add onAcivityResult() function into BOTH parent class and the current child class. In current class you just write normal call back handle message as you do now. But in parent class, the onActivityResult() function will catch the call back from the normal activity and deliver the intent to the current class.
Third, This step is for the parent onActivityResult class, in that class, your need:
public void onActivityResult(int requestCode, int resultcode, Intent data)
{
super.onActivityResult(requestCode, resultcode, data);
switch (resultcode)
{
case RESULT_OK:
MyChildActivity CA = (MyChildActivity) getLocalActivityManager().getCurrentActivity();
CA.onActivityResult(requestCode, resultcode, data);
}
}
As you can see, the onActivityResult function in parent ActivityGroup class is just catch the call back, get the child activity you which needs to jump to another activity, and transfer the data to it. You may not need exactly onActivityResult function in your child activity as state in Step 2, but I think this is a better way to do it.
Hope this help!
I am new to android so please excuse the newbie question. I have a game I am trying to port from an old Java applet to android. My goal is to get this functional and then post an article on a site like CodeProject (or a better one if there are ones more appropriate). The idea is to show that a person brand new to android development can create an app in a reasonable amount of time.
I am making some progress but have run into a problem. I have the main activity in which the user interacts with. I then created a menu item that in turn starts a second activity (call it child) with a modest number of checkbox's, seekbar's etc to fill in parameters. I can successfully pass the class containing all the options from main to child. But I cannot get the child to pass this data back to the main.
First here is my main code that starts the child activity:
public void addBalls()
{
Intent myIntent = new Intent(this, GameOptions.class);
Bundle b = new Bundle();
b.putSerializable("options", gameParams);
myIntent.putExtras(b);
startActivityForResult(myIntent,STATIC_OPTIONS_VALUE);
}
The data passed to the child (and hopefully back again) is:
public class GameOptionParams implements Serializable
{
private static final long serialVersionUID = 1L;
public int speedBarPosition;
public int vgravityBarPosition;
public int mgravityBarPosition;
public int viscosityBarPosition;
public int restititionBarPosition;
public boolean trace;
public boolean collide;
public boolean mush;
public boolean wrap;
public boolean flicker;
}
And here is the expected return (again in main)
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode)
{
case (STATIC_OPTIONS_VALUE) :
{
if (resultCode == Activity.RESULT_OK)
{
//retrieve intended options
Bundle b = data.getExtras();
gameParams = (GameOptionParams) b.getSerializable("options");
}
break;
}
}
}
The child activity successfully receives the gameParams data. It then interacts with the user to update the values and then I attempt to return it but it does not seem to get sent to main. Here is the child code in the onStop() override.
Maybe this code should not be in the onStop() override but I can't determine where else to place it.
#Override
public void onStop()
{
super.onStop();
//read widget values
gameParams.speedBarPosition = speedBar.GetPosition();
gameParams.vgravityBarPosition = vgravityBar.GetPosition();
gameParams.mgravityBarPosition = mgravityBar.GetPosition();
gameParams.viscosityBarPosition = viscosityBar.GetPosition();
gameParams.restititionBarPosition = restititionBar.GetPosition();
//todo save to persistent
Intent resultIntent = new Intent(this, TiltBall2ImpactActivity.class);
Bundle b = new Bundle();
b.putSerializable("options", gameParams);
resultIntent.putExtras(b);
setResult(Activity.RESULT_OK, resultIntent);
}
Back in the main onActivityResult override I always see requestCode=0, resultCode=0, data=null. I assume this is a typical newbie problem, I have been reading the sdk documentation, user forums etc and have come close to a solution but just not quite there yet. Any help would be appreciated.
Since this is sort of a setting menu for the game, I assume you are going to need these values for more than one activity. If so you extend the android.app.Application class.
In that class you can create attributes to hold your values. In any activity you can call
MyApplication myApp = (MyApplication)getApplicationContext();
where myApp is a singleton. So you will get the values you set from another activity.
You will need to add this code to your application tag in the manifest file for it to work
android:name=".MyApplication"
If you need to keep these values for next startup of the application, you need to use SharedPreferences. This is a good tutorial for that
http://saigeethamn.blogspot.com/2009/10/shared-preferences-android-developer.html
Assuming in your 'child' activity, the user has to press an 'OK' or 'Save' button then put the code to set the gameParams parameters in the button's onClick(...) handler.
Use the default constructor for instantiating the Intent, example...
Intent resultIntent = new Intent();
...then after creating the Bundle and adding gameParams to it and calling setResult(...), simply call finish() to terminate the 'child' activity. There aren't many occasions that I can think of to override onStop() and I suspect you don't want to be using it to attempt returning the Intent.