Search code examples
androidfragment

Two activities using the same fragment - problem


Two activities using the same fragment. The fragment has a text view. Main activity writes “message 1” into the text view and it shows up.

A button in the main activity launches the second activity “for result”.

The Second activity writes “message 2” into the text view and it shows up.

A button in the second activity does set Result Activity.RESULT_OK and then finish().

The main activity gets the “onActivityResult” Result OK and writes “message 3” into the text view. However “Message 3” does not show up in the text view. Instead “message 1” shows up.

public class MainActivity extends AppCompatActivity {
   private static Context  context;
   private static Button   btn_main;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       context = this;
       btn_main = findViewById(R.id.btn_main);

       FragmentDisplay.setMessage1("Message 1");

       btn_main.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Intent intent = new Intent(MainActivity.this, SecondActivity.class);
               secondactivityLauncher.launch(intent);
           }
       });
   }
   
   ActivityResultLauncher<Intent> secondactivityLauncher = registerForActivityResult(
           new ActivityResultContracts.StartActivityForResult(),
           new ActivityResultCallback<ActivityResult>() {
               @Override
               public void onActivityResult(ActivityResult result) {
                   if (result.getResultCode() == Activity.RESULT_OK) {
                       FragmentDisplay.setMessage1("Message 3");
                   }

               }
           });

   public static Context getContext(){
       return context;
   }
}
public class SecondActivity extends AppCompatActivity {
    private static Button btn_second;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        btn_second = findViewById(R.id.btn_second);

        FragmentDisplay.setMessage1("Message 2");


        btn_second.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = getIntent();
                setResult(Activity.RESULT_OK, intent);
                finish();
            }
        });
    }
}
public class FragmentDisplay extends androidx.fragment.app.Fragment {

    private static TextView textView1;


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

    RecyclerView mRecyclerView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.display_fragment, null);

        textView1 = (TextView)view.findViewById(R.id.tv1);
        return view;
    }

    public static void setMessage1(String str){
        textView1.setText(str);
    }

}   // end of class
//activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main.MainActivity">

<fragment
    android:id="@+id/display_fragment"
    android:name="ddi.pos.display.FragmentDisplay"
    android:layout_width="700dp"
    android:layout_height="180dp"
    android:background="#00CC00"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true"
    android:layout_marginTop="80dp" />

<Button
    android:id="@+id/btn_main"
    android:layout_below="@+id/display_fragment"
    android:layout_marginTop="100dp"
    android:layout_marginLeft="50dp"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:background="#FFFFFF00"
    android:textSize="25sp"
    android:text="Start Second Activity"
/>
//second_activity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main.MainActivity">

<fragment
    android:id="@+id/display_fragment"
    android:name="ddi.pos.display.FragmentDisplay"
    android:layout_width="700dp"
    android:layout_height="180dp"
    android:background="#00CC00"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true"
    android:layout_marginTop="80dp" />

<Button
    android:id="@+id/btn_second"
    android:layout_below="@+id/display_fragment"
    android:layout_marginTop="100dp"
    android:layout_marginLeft="300dp"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:background="#000000"
    android:textSize="25sp"
    android:text="Finish Second Activity"
/>
//display_fragment.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CC5500"
>

<TextView android:id="@+id/tv1"
    android:background="#0055FF"
    android:layout_height="60dp"
    android:layout_width="600dp"
    android:text=""
    android:layout_marginLeft="30dp"
    android:layout_marginTop="30dp"
    android:textSize="20dp"
    android:textColor="#ff000000"
/>

Solution

  • Caveat: I suspect that what you posted is not what you actually want to do, but a workaround of some kind so this answer may or may not actually address your use-case. It does, however, produce the behavior you asked for in the question. You said you are not trying to send data between activities, but you want the message in the first activity to change in response to actions in the second activity which implies information may be shared.

    Main Answer: The example below, using a shared ViewModel between Activity and Fragment and using data transfer across activities using intents has the behavior you describe in your question.

    The ViewModel allows sharing of data between the Activity and Fragment, since the Fragment can observe the LiveData and respond when the activity changes it. Since the question calls startActivityForResult and handles the result, I used those to handle passing data back to change the message.

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
    
        ActivityResultLauncher<Intent> secondActivityLauncher = registerForActivityResult(
                new ActivityResultContracts.StartActivityForResult(),
                new ActivityResultCallback<ActivityResult>() {
                    @Override
                    public void onActivityResult(ActivityResult result) {
                        if (result.getResultCode() == Activity.RESULT_OK) {
                            // as you indicated:
                            //viewModel.setMessage("Message 3");
    
                            // or like this if you sent data
                            Intent data = result.getData();
                            if( data != null ) {
                                Bundle extras = data.getExtras();
                                if( extras != null ) {
                                    String msg = extras.getString("response");
                                    viewModel.setMessage(msg);
                                }
                            }
                        }
    
                    }
                });
    
        private MainViewModel viewModel;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            viewModel = new ViewModelProvider(this).get(MainViewModel.class);
    
            // Always initialize the message to "Message 1"
            viewModel.setMessage("Message 1");
    
            Button btn = findViewById(R.id.btn_main);
            btn.setOnClickListener(view -> {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                intent.putExtra("message", "Message 2");
                secondActivityLauncher.launch(intent);
            });
        }
    }
    

    SecondActivity.java

    public class SecondActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
    
            // This ViewModel instance is "not" the same instance as the one from MainActivity, it is
            // just to facilitate communication between the Activity and Fragment
            MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class);
    
            // as you had it with hard-coded message 2
            // viewModel.setMessage("Message 2");
    
            //  or like this if you sent the message
            Intent i = getIntent();
            Bundle b = i.getExtras();
            if( b != null ) {
                String msg = b.getString("message");
                viewModel.setMessage(msg);
            }
    
            Button btn = findViewById(R.id.btn_second);
            btn.setOnClickListener(view -> {
                Intent intent = new Intent();
                intent.putExtra("response", "Message 3");
                setResult(Activity.RESULT_OK, intent);
                finish();
            });
        }
    }
    

    MainViewModel.java

    public class MainViewModel extends ViewModel {
        private final MutableLiveData<String> message_to_display = new MutableLiveData<>();
        LiveData<String> message() { return message_to_display; }
    
        void setMessage(String msg) {
            message_to_display.postValue(msg);
        }
    }
    

    DisplayFragment.java

    public class DisplayFragment extends Fragment {
    
        public DisplayFragment() {
            // Required empty public constructor
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            return inflater.inflate(R.layout.fragment_display, container, false);
        }
    
        @Override
        public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            TextView txt = view.findViewById(R.id.tv1);
    
            // Get the ViewModel from the hosting activity, could be
            // Main or Second, and observe its message. Update the
            // TextView if the message is changed.
            MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
            viewModel.message().observe(getViewLifecycleOwner(), s -> {
                txt.setText(s);
            });
        }
    }