【问题标题】:Two activities using the same fragment - problem使用相同片段的两个活动 - 问题
【发布时间】:2021-12-26 19:34:10
【问题描述】:

两个活动使用相同的片段。该片段有一个文本视图。 主要活动将“消息 1”写入文本视图并显示出来。

主活动中的一个按钮启动第二个活动“结果”。

第二个活动将“消息 2”写入文本视图并显示出来。

第二个活动中的按钮确实设置了 Result Activity.RESULT_OK 然后完成()。

主活动获得“onActivityResult”结果 OK 并将“消息 3”写入文本视图。 但是“消息 3”不会出现在文本视图中。而是显示“消息 1”。

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"
/>

【问题讨论】:

  • 问题是你有一个静态的 TextView (private static TextView textView1;)。这是一个单一的全局值,在片段之间不是唯一的。不要使用这样的静态视图 - 您无法保证当前保存在静态变量中的视图与显示的 Fragment 相关联。如果您想在活动和片段之间或片段之间传递数据,请尝试使用片段可以观察到的activity-level ViewModel。
  • 在活动之间共享,使用意图或拥有一些与活动/片段完全分开的数据存储库/事实来源。
  • 我不想在活动之间共享数据。我希望每个活动都有自己的片段,它可以在 TextView 中写入内容。它与在 xml 文件中静态添加到每个活动的片段相同。您可能会写到问题在于 TextView 是静态的,但如果它不是静态的,我不知道如何在其中设置文本。

标签: android fragment


【解决方案1】:

警告:我怀疑您发布的内容不是您真正想要做的,而是某种解决方法,因此此答案可能会或可能不会真正解决您的用例。但是,它确实会产生您在问题中要求的行为。您说您不是尝试在活动之间发送数据,但您希望第一个活动中的消息响应第二个活动中的操作而改变,这意味着信息可以共享。

主要答案: 下面的示例在 Activity 和 Fragment 之间使用共享 ViewModel 并使用 Intent 跨 Activity 进行数据传输,具有您在问题中描述的行为。

ViewModel 允许在 Activity 和 Fragment 之间共享数据,因为 Fragment 可以观察 LiveData 并在 Activity 更改它时做出响应。由于问题调用startActivityForResult 并处理结果,因此我使用它们来处理传回数据以更改消息。

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);
        });
    }
}

【讨论】:

  • 非常感谢您向我展示了如何使用 ViewModel。该应用程序现在完全按照我的预期工作。
  • 如果片段中没有 TextView 而是包含一个包含字符串列表的 RecyclerView,则可以使用 ViewModel。
  • 是的,您可以有一个LiveData&lt;List&lt;String&gt;&gt; 并让您的活动观察字符串列表并在列表更改时触发 RecyclerView 的更新。
  • 或者让片段观察列表,然后活动更新它。
  • 由于我是新手,我还有一个问题。在 MainActivity 中实例化一个 viewModel。在 DisplayFragment 中,再次实例化了一个 viewModel。现在是否为 MainActivity 实例化了 2 个视图模型。解释将不胜感激。谢谢。
猜你喜欢
  • 1970-01-01
  • 2021-08-10
  • 2015-11-19
  • 1970-01-01
  • 1970-01-01
  • 2018-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多