如何跟踪跨片段的数据变化?

How to keep track of data changes across Fragments?

我有一个应用程序,其中有一个 Activity(主),在布局中有一个 ViewPager2。 ViewPager2 的适配器设置了 2 个片段。我们将其中之一称为片段 A。

当片段 A 打开时,用户可以选择从自身内部打开另一个片段,即片段 B。 (通过交易;我在 Fragment A 中有一个空的 FrameLayout)。 Fragment B 提供了一个对话框来编辑 Fragment A 中 RecyclerView 显示的数据。Fragment A 本身也有一些选项来更改其 RecyclerView 中的数据。

此数据来自共享首选项。现在,问题来了——因为 Fragments 只能通过接口与 Activity 通信,所以我可以将数据保存在 Activity 中,但我别无选择,只能重新加载整个Fragment A 中的 RecyclerView,因为 Fragment A 不知道它在哪个位置获得了新数据。

请指教。以下是我提到的片段和Activity。

MainActivity.java:

package com.coffeetech.kittycatch;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity implements FoodEditorFragment.SendFoodFromEditor{
    ViewPager2 viewPager;
    TabLayout tab_menu;
    SwipeAdapter adapter;

    ArrayList<Food> foods;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewPager=findViewById(R.id.viewpager);
        adapter=new SwipeAdapter(this);
        viewPager.setAdapter(adapter);

        //SETTING TABS
        tab_menu = findViewById(R.id.tab_menu);
        new TabLayoutMediator(tab_menu, viewPager, true, new TabLayoutMediator.TabConfigurationStrategy() {
            @Override
            public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                switch(position){
                    case 0:
                        tab.setText("INVENTORY");
                        break;
                    case 1:
                        tab.setText("SHOPPING LIST");
                }
            }
        }).attach();



    }

    @Override
    public void onBackPressed() {
        super.onBackPressed(); //TODO: CONFIGURE THIS
    }

    @Override
    public void sendFood(Food food, int position) { //TODO: CODE TO REPLACE DATA TO SPECIFIC POSITION
        loadData();


        saveData();
    }

    @Override
    public void sendFood(Food food) { //TODO: CODE TO ADD DATA TO POSITION 0
        loadData();


        saveData();
    }

    public void loadData(){
        //TODO: LOAD THE LIST HERE
    }

    public void saveData(){
        //TODO: SAVE THE DATA HERE
    }
}

ViewPager2 的 FragmentStateAdapter:

package com.coffeetech.kittycatch;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;

public class SwipeAdapter extends FragmentStateAdapter {

    Fragment fragment;

    public SwipeAdapter(FragmentActivity fa){
        super(fa);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        switch (position){
            case 0: //START INVENTORY FRAGMENT
                fragment = new InventoryFragment();
                break;

            case 1: //START SHOPPING LIST FRAGMENT
                fragment = new ShoppingListFragment();
                break;
        }
        return fragment;

    }

    @Override
    public int getItemCount() {
        return 2;
    }
}

片段'A':

package com.coffeetech.kittycatch;

import android.content.Context;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;

public class InventoryFragment extends Fragment {

    //GLOBAL VARIABLES
    RecyclerView recyclerView;
    FoodAdapter foodAdapter;
    ArrayList<Food> foods = new ArrayList<Food>();
    FloatingActionButton add_button;
    FrameLayout frameLayout;
    Food food;
    int q; //for use in decrease and functions

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


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onPause() { //TODO:CODE TO SAVE DATA
        saveData();
        super.onPause();
    }

    @Override
    public void onResume() { //TODO:CODE TO LOAD DATA
        loadData();
        super.onResume();
        //food constructor takes arguments -> (String name, int type, int quantity, int min_quantity)
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View v = inflater.inflate(R.layout.fragment_inventory, container, false);
        foodAdapter = new FoodAdapter(foods);
        recyclerView=v.findViewById(R.id.recycler_view);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setAdapter(foodAdapter);

       //setting up the frameLayout
        frameLayout = v.findViewById(R.id.food_editor_frame);
        //setting up the Add button
        add_button=v.findViewById(R.id.add_button);
        add_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {  //for new food addition to list
                openFoodEditorFragment();
            }
        });

        foodAdapter.setOnFoodcardClickListener(new FoodAdapter.OnFoodcardClickListener() {
            @Override
            public void deleteFood(int position) { //code that deletes current food
                foods.remove(position);
                foodAdapter.notifyItemRemoved(position);
            }

            @Override
            public void onEdit(int position, int mode) { //code that runs the edit of each food, mode=1 for edit
                openFoodEditorFragment(position,foods.get(position)); //TODO: SEE IF YOU NEED 'mode' AT ALL
            }


            @Override
            public void decrease(int position) {
                food = foods.get(position);
                q=food.getQuantity();
                food.setQuantity(q-1);
                foodAdapter.notifyItemChanged(position);
            }

            @Override
            public void increase(int position) {
                food = foods.get(position);
                q=food.getQuantity();
                food.setQuantity(q+1);
                foodAdapter.notifyItemChanged(position);
            }

            @Override
            public void setSeekBar(int position,int progress) {
                food = foods.get(position);
                food.setQuantity(progress);
                Toast.makeText(getContext(),"Quantity set to "+progress+"%",Toast.LENGTH_SHORT).show();
                foodAdapter.notifyItemChanged(position);
            }
        });
        return v;
    }

    public void openFoodEditorFragment(int position,Food food){ //code for Editor in Edit Old mode

        //creating new Bundle and passing each member data of 'food'
        Bundle bundle = new Bundle();
        bundle.putString("name",food.getName());
        bundle.putInt("type",food.getType());
        bundle.putInt("quantity",food.getQuantity());
        bundle.putInt("min_quantity",food.getMin_quantity());
        bundle.putInt("mode",1);
        bundle.putInt("position",position);

        //setting up arguments for Fragment
        FoodEditorFragment foodEditorFragment = new FoodEditorFragment();
        foodEditorFragment.setArguments(bundle);

        //creating the Fragment
        FragmentTransaction transaction=getFragmentManager().beginTransaction();
        transaction.replace(R.id.food_editor_frame,foodEditorFragment);
        transaction.commit();
    }

    public void openFoodEditorFragment(){ //code for Editor in Add New mode

        //creating new Bundle and passing each member data of 'food'
        Bundle bundle = new Bundle();
        bundle.putInt("position",0);
        bundle.putInt("mode",0);

        //setting up arguments for Fragment
        FoodEditorFragment foodEditorFragment = new FoodEditorFragment();
        foodEditorFragment.setArguments(bundle);

        //creating the Fragment
        FragmentTransaction transaction=getFragmentManager().beginTransaction();
        transaction.replace(R.id.food_editor_frame,foodEditorFragment);
        transaction.commit();
    }


    protected void setData(int position, Food food){
        foods.remove(position);
        foods.add(position,food);
        foodAdapter.notifyItemChanged(position);
    }

    protected void setData(Food food){
        foods.add(0,food);
        foodAdapter.notifyItemInserted(0);
        Toast.makeText(getContext(),"Added to the top",Toast.LENGTH_SHORT).show();
    }

    void loadData(){
        //TODO: CODE TO LOAD DATA
    }

    void saveData(){
        //TODO: CODE TO SAVE DATA
    }
}

片段'B':

package com.coffeetech.kittycatch;

import android.content.Context;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;


public class FoodEditorFragment extends Fragment {

    private TextView name,quantity,min_quantity;
    private ImageButton save,cancel;
    private RadioGroup radioGroup;

    protected int mode,t,position;
    protected Food food = new Food();

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        sffe = (SendFoodFromEditor)context;
    }

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

    public interface SendFoodFromEditor{
        void sendFood(Food food,int position); //FOR EDITING FOOD
        void sendFood(Food food); //FOR NEW FOOD
    }

    SendFoodFromEditor sffe;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_food_editor, container, false);
        name=view.findViewById(R.id.name_editor);
        quantity=view.findViewById(R.id.quantity_editor);
        min_quantity=view.findViewById(R.id.min_quantity_editor);
        save=view.findViewById(R.id.save_button_editor);
        cancel=view.findViewById(R.id.cancel_button_editor);
        radioGroup=view.findViewById(R.id.radioGroup_editor);

        mode=getArguments().getInt("mode");
        position=getArguments().getInt("position");

        if (mode==1){ //for editing Food
            //CODE TO SETUP EDITOR ACCORDING TO INITIAL DETAILS
            name.setText(getArguments().getString("name"));
            quantity.setText(String.valueOf(getArguments().getInt("quantity")));
            min_quantity.setText(String.valueOf(getArguments().getInt("min_quantity")));
            t=getArguments().getInt("type");

            if(t==0){//for discrete food
                radioGroup.check(R.id.discrete_radioButton);
            }else{//for cont food
                radioGroup.check(R.id.cont_radioButton);
            }

        }
        setButtons();
        return view;

    }


    public void setButtons(){
        save.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {  //USE BELOW 'food' TO PASS NEW DATA TO ACTIVITY
                 try {
                        if ((!name.getText().toString().isEmpty()) && ((radioGroup.getCheckedRadioButtonId() == R.id.discrete_radioButton) || (radioGroup.getCheckedRadioButtonId() == R.id.cont_radioButton))) {
                            food.setName(name.getText().toString());
                            food.setQuantity(Integer.parseInt(quantity.getText().toString()));
                            food.setMin_quantity(Integer.parseInt(min_quantity.getText().toString()));

                            if (radioGroup.getCheckedRadioButtonId() == R.id.discrete_radioButton) {
                                food.setType(0);
                            } else if (radioGroup.getCheckedRadioButtonId() == R.id.cont_radioButton) {
                                food.setType(1);
                            }
                            //CODE TO SEND THE FOOD TO ACTIVITY
                            if (mode == 1) {
                                sffe.sendFood(food, position);
                            } else {
                                sffe.sendFood(food);
                            }
                            //CLOSE THE FRAGMENT
                            getFragmentManager().beginTransaction().remove(FoodEditorFragment.this).commit();
                        } else {
                            throw new Exception();
                        }
                    }catch (Exception e){
                        Toast.makeText(getContext(),"Please set all details",Toast.LENGTH_SHORT).show();
                    }
            }

        });

        cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) { //CODE IF USER PRESSES ON CANCEL
                //CLOSE THE FRAGMENT
                getFragmentManager().beginTransaction().remove(FoodEditorFragment.this).commit();
            }
        });
    }
}

您需要进行以下更改。

在片段 A 中,

替换

transaction.replace(R.id.food_editor_frame,foodEditorFragment);

transaction.add(R.id.food_editor_frame,foodEditorFragment);

原因:使用 replace 将删除 fragmentA 并且 FragmentA 将始终被重新创建。

在MainActivity

@Override
public void sendFood(Food food, int position){
//find fragment by Id
 Fragment fragmentA = (Fragment)
                    getSupportFragmentManager().findFragmentById(R.id.fragmetnA);

 fragment.setData(position,food)

      //code for save data here

}

 @Override
    public void sendFood(Food food) { 
        Fragment fragmentA = (Fragment)
                getSupportFragmentManager().findFragmentById(R.id.fragmetnA);

fragment.setData(food)

      //code for save data here

    }