简体   繁体   中英

How to make a method communicate with both an Activity and a Fragment?

I have an app consisting of (among other things) one Activity and one Fragment. In the Activity som data is displayed using TextViews, and in the Fragment the user can input some data using EditText-views. I have a method which takes data from both the Activity and Fragment, performs some calculations and finally displays a result in both the Fragment and the Activity. This method currently works when I call if after editing the text in any of the EditText-views in the Fragment. However, I also want to call it each time I update the data in the Activity but when I attempt this, I can not fetch the EditText data because they return null. So my question is: what is good practice, or the ”right way” to do when dealing with methods that are supposed to be reached from both an Activity and a Fragment? I would greatly appreciate if someone could lead me onto the right track.

I have read the official documentation on Fragments and there ViewModel was mentioned. But this doesn't seem suitable in for my application since I want the Activity to be involved. Do I need to use this or can I go through my main Activity? I've also read about Interfaces, but I'm not sure which one would suit my project best. I'm currently using Interfaces, but I'm not sure if I'm doing it correctly.

I've also watched this video and read these following questions:

"My God"'s reply to this question was helpful but I'm still not sure what is the best thing to do in my case, as I have a Fragment where user can input data, and the same fragment should also view data as a result of that input. (Maybe my first mistake is building the app like this?)

I provide some code in case it is helpful.

public class AccuracyFragment extends Fragment {
    EditText editTextLevel, editTextAccuracy; 
    private OnFragmentInteractionListener mListener;

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

    @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_accuracy, container, false);
        editTextAccuracy = view.findViewById(R.id.text_accuracy_character);
        editTextLevel = view.findViewById(R.id.text_level_character);


        TextWatcher watcher = new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
//Doing nothing

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
//Doing nothing

            }

            @Override
            public void afterTextChanged(Editable editable) {
                updateFragment(Float.valueOf(editTextLevel.getText().toString()), Float.valueOf(editTextAccuracy.getText().toString()));
            }
        };

        editTextLevel.addTextChangedListener(watcher);
        editTextAccuracy.addTextChangedListener(watcher);

        return view;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null; //I don’t know what this does
    }

    @Override
    public void onResume() {
        super.onResume();
//        updateFragment(); //Should I have this? 
    }

    public interface OnFragmentInteractionListener {
        String[] onAccuracyFragmentInputChanged(float levelFromFragment, float accuracyFromFragment);    }

    public void updateFragment(float level, float accuracy) {

        //Complicated method doing things with editTextLevel and editTextAccuracy. However, it doesn’t work when this method is called from outside AccuracyFragment – EditTexts are null


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
    }

    @Override
    public void onStop() {
        super.onStop();
    }
}
 
public class MainActivity extends AppCompatActivity implements AccuracyFragment.OnFragmentInteractionListener, AdapterView.OnItemSelectedListener {




    AccuracyFragment accuracyFragment;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main_new); 
        selectedID = null;
        textView1 = (TextView) findViewById(R.id.text_1);
textView2 =  (TextView) findViewById(R.id.text_2); 

// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state, then we don't need to do anything and should return or else we could end up with overlapping fragments.
if (savedInstanceState != null) {
    return;
}

// Create a new Fragment to be placed in the activity layout
accuracyFragment = new AccuracyFragment();       
 }
}
//This is the core method which takes the value from two EditTexts in the Fragment, and two TextViews in the MainActivity
    private String[] getRequiredAccuracy(float firstValueFromActivity, float secondValueFromActivity, float firstValueFromFragment, float secondValueFromFragment) {

//This methods uses parameters from the Activity, and two from the Fragment, and is intended to be called from both the Activity and from the Fragment itself

        String returnValues[] = {s, q, r, c}; //This method is too complex to show, but it will end up outputting some values
        return returnValues;
    }



    public void methodCalledUponClick(View view) {
//After showing a Dialog with some choices, I intend to call the method from fragment:
        accuracyFragment.updateFragment();
    }

    @Override
    public String[] onAccuracyFragmentInputChanged(float levelFromFragment, float accuracyFromFragment) {
        String returnValues[] = {"0", "0", "0"};
        if (selectedID != null) {
            if (textView1.length() == 0 || textView2.length() == 0) {
                //Do nothing if any of these are empty
            } else {
                returnValues = getRequiredAccuracy(Float.valueOf(textView1.getText().toString()), Float.valueOf(textView2.getText().toString()), levelFromFragment, accuracyFromFragment);
            }
        }
        return returnValues;
    }

}

}

You can go ahead with callback/Interface to communicate with fragment and activity simultaneously.

For Creating callback/Interface:

public interface CallBackListener {
void onCallBack(String value);// pass any parameter in your onCallBack which you want to return 
}

In Fragment Class:

public class AccuracyFragment extends Fragment {

private CallBackListener callBackListener;

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    //getActivity() is fully created in onActivityCreated and instanceOf differentiate it between different Activities
    if (getActivity() instanceof CallBackListener)
        callBackListener = (CallBackListener) getActivity();
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    EditText editText = (EditText) view.findViewById(R.id.edittext);
    editText.addTextChangedListener(new TextWatcher() {

    @Override
    public void afterTextChanged(Editable s) {}

     @Override    
     public void beforeTextChanged(CharSequence s, int start,
      int count, int after) {
     }

    @Override    
    public void onTextChanged(CharSequence s, int start,
     int before, int count) {
        if(callBackListener != null)
                callBackListener.onCallBack(s.toString());
        }
    });
  }
}

In your Activity:

public class MainActivity extends AppCompatActivity  implements CallBackListener
 {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

}

 @Override
 public void onCallBack(String value) {
    Toast.makeText(mContext,"onCallback Called",Toast.LENGTH_LONG).show();
 }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM