Enter EditText on a Fragment and fill also all other Fragments - android

I have implemented the Android Tutorial from the Android Training Site and everything works so far.
Inside of a Fragment, I added an EditText field, where the user can enter a simple text string.
What I want to achieve is: If the user entered something, and then swipes to left or right, the input should be "copied" the new fragment as well.
As in the training, I have got a ScreenSlidePageFragment.java class and a ScreenSlideActivity.java class.
I've implemented an Interface in the ScreenSlidePageFragment, as mentioned here:
Communicator mCallback;
public interface Communicator {
void sendData(String inpString);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (Communicator) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "must Implement OnEditChangedListener");
}
}
But at the end, I just managed to receive it on the ScreenSlideActivity. How do I update a specified fragment?
In ScreenSlideActivity I tried to send it back to the new fragment.
#Override
public void sendData(String inpString) {
WHATFRAGMENT.changeData(inpString);
}
But how do I access another one (WHATFRAGMENT), which are created in the ScreenSlidePagerAdapter:
#Override
public Fragment getItem(int position) {
return NewReservationSlidePageFragment.create(position);
}
Do I need to create a whole Fragment and overwrite the new position? Or is it even necessary to work around the Activity? Is it possible to use FragmentTransaction somehow?
What would be the simplest solution for that?

You can achieve with your interface and public void setUserVisibleHint(boolean isVisibleToUser)
Add one more method in your interface like this,
public interface Communicator {
void sendData(String inpString);
String getData();
}
And in you activity create one String variable Globally to store the value.
And assign the data coming form sendData in that
#Override
public void sendData(String inpString) {
this.value = inpString;
//WHATFRAGMENT.changeData(inpString);
}
and implement getData too in your Activity
#Override
public void getData() {
return this.value;
}
Add setUserVisibleHint in your fragment
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
if(isVisibleToUser) {
mCallback.sendData("[string from your edit text]");
} else {
String value = mCallback.getData();
}
}
Now you can see the data which you edited in one fragment can show in other visible fragment.

Related

How can a BottomSheetDialogFragment communicate with its host fragment?

I have a button in my fragment which opens a BottomSheetDialogFragment. I want to notify the host fragment if the user selected an item on the BottomSheetDialogFragment. In order to achieve this, I have made an interface in my BottomSheetDialogFragment. However, that interface only communicates with the host activity, not the fragment. How can I send the information from the dialog to the fragment?
This is my interface:
public interface BottomSheetListener {
void onButtonClicked(int index);
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
mListener = (BottomSheetListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement BottomSheetListener");
}
}
getParentFragment will return the parent fragment, if the current fragment is attached to a fragment else it will return null if it is attached directly to an Activity
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
mListener = (BottomSheetListener) getParentFragment();
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement BottomSheetListener");
}
}
When you use a lot of fragments, nested fragments or dialogfragments it becomes messy for communicate between them. I am suggesting to use ViewModel with LiveData for passing and updating data.
first add this to build gradle :
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
then create ViewModel class :
public class YourViewModel extends AndroidViewModel {
private MutableLiveData<Integer> yourMutableLiveData=new MutableLiveData<>();
public YourViewModel(#NonNull Application application) {
super(application);
}
public MutableLiveData<Integer> getYourMutableLiveData() {
return yourMutableLiveData;
}
}
This the fragment you want set value :
public class FragmentA extends Fragment{
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
YourViewModel yourViewModel =new ViewModelProvider(getActivity()).get(YourViewModel.class);
yourViewModel.getYourMutableLiveData().setValue(0);
}
}
And this is the fragment you want to get value when updated :
public class FragmentB extends Fragment{
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
YourViewModel yourViewModel =new ViewModelProvider(getActivity()).get(YourViewModel.class);
yourViewModel.getYourMutableLiveData().observe(getViewLifecycleOwner(), new Observer<Integer>() {
#Override
public void onChanged(Integer integer) {
}
});
}
}
It can work on dialog fragment as well as I tested.
Notes :
-Do not pass context or any view into view model.
-Remember that onActivityCreated comes after onCreateView.
-Do not set this key to
YourViewModel yourViewModel =new ViewModelProvider(this).get(YourViewModel.class);
in fragment if you want to pass data fragment to fragment but you can pass in activity.
-You can set more than one observer to the data.

Calling Fragment method from attached RecyclerView.Adapter

I recently started coding my first Android project using Android Studio 3.1.2 and SDK 19.
One of my fragments contains a RecyclerView with a custom RecyclerView.Adapter attached. On the CardView the Adapter gets by its ViewHolder, there can be a button. The target is, if the button is pressed, a method of my fragment should be called, though it's an instance of a custom subclass of Fragment:
From RequestingFragment:
public abstract class RequestingFragment extends Fragment implements RequestCallbacks {
public final static void startRequest(final RequestOperation, String param) {
//this is the guy i want to call
}
//these are the RequestCallbacks, they're all getting called in startRequest()
public void onSuccess(JSONObject json, String parsingkey) { }
public void onError() { }
public void onFinished() { }
Now one of my RequestingFragments contains a RecyclerView, on which a custom ErrorCompactAdapter is attached. Inside the Adapters ViewHolder, where I load the layout for the single CardViews, there's a button, which should call startRequest() onClick from my RequestingFragment
From ErrorCompactAdapter:
public class ErrorCompactAdapter extends RecyclerView.Adapter<ErrorCompactAdapter.ErrorCompactViewHolder> {
private Context context;
private ArrayList<Error> errors;
public ErrorCompactAdapter(Context context, ArrayList<Error> errors) {
this.context = context;
this.errors = errors;
}
public void onBindViewHolder(ErrorCompactViewHolder, int position) {
//...
holder.errorTakeOverButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//here's were i'm stuck
}
});
//...
}
}
My first approach was to change the context attribute of ErrorCompactAdapter to a RequestingFragment, so that I can call startRequest() on this.
private Context context; // private RequestingFragment attacher;
public void onClick(View v) {
attacher.startRequest(/*params*/);
}
But i'm very unsure, if the fragment that contains the RecyclerView will be the one which receives the response of the request, or if a somehow "pseudo-anonymous" Fragment will receive the response and simply does nothing with it then. Can someone enlight me, if this is the correct path? Thanks in advance.
Pass the Fragment in you ErrorCompactAdapter class's constructor. This works for me the way I want. I had the same issue.
RequestingFragment mFragment;
public ErrorCompactAdapter(Context context, ArrayList<Error> errors,
RequestingFragment fragment)
{
this.context = context;
this.errors = errors;
this.mFragment = fragment;
}
// While passing the fragment into your adapter, do it this way.
ErrorCompactAdapter errorCompactAdapter = new ErrorCompactAdapter(
context, errors, RequestingFragment.this);
holder.errorTakeOverButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// use you method of fragment here
mFragment.startRequest();
}
});

How to Update Current Fragment in ViewPager using Interfaces with ClassCastException?

I am trying to refresh a currently selected fragment inside of a ViewPager. I am using one MainActivity as a way to communicate between multiple fragments via interface calls. The fragment I am working with is implementing an interface call to the ViewPagerAdapter, however, when attaching the listener to the fragment I receive a ClassCastException error "Activity cannot be cast to Fragment". This is the code below:
#Override
public void onAttach(Context context) {
super.onAttach(context);
mListener = (UpdateableFragment) context; **//ERROR HERE**
}
I am trying to follow the answer posted here https://stackoverflow.com/a/17855730, however i cannot figure out where the interface call should be handled. Currently i have it implemented in the ViewPagerAdapter, so when i need to update i can just notifyDataSetChange(), but it returns the ClassCastException error.
I have tried to handle the interface call in the MainActivity i am using to communicate with the rest of my fragments and this eliminates the ClassCastException error, however, i have no way to call the ViewPagerAdapter and notifyDatasetChange. Below is my code:
CurrentFragment (in ViewPager)
private UpdateableFragment mListener;
#Override
public void onAttach(Context context) {
super.onAttach(context);
mListener = (UpdateableFragment) context; **//ERROR HERE**
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public void click(){
mListener.update();
}
public interface UpdateableFragment {
void update();
}
ViewPagerAdapter
public class ViewPagerAdapter extends FragmentPagerAdapter implements CurrentFragment.UpdateableFragment{
public ViewPagerAdapter(FragmentManager fm) {
super(fm);}
//Note: unnecessary code left out.
public void update() {
notifyDataSetChanged();
}
#Override
public int getItemPosition(Object object) {
if (object instanceof CurrentFragment.UpdateableFragment) {
((CurrentFragment.UpdateableFragment) object).update();
}
return super.getItemPosition(object);
}
Any feedback would be appreciated. Thanks.
In onAttach(Context context) , the context is means of Activity, so cast context to your interface will throw the cast exception, if you want to communication with each fragments, i think you can try Event Bus library.
Try to use onAttachFragment() instead of onAttach():
#Override
public void onAttachFragment(Fragment childFragment) {
confirmListener = (ConfirmListener) childFragment;
}

Multiple Interfaces in Single Fragment

I have a Fragment that needs to communicate more than one Action back to it's Activity. For example,
When a button is clicked, it needs to communicate the onClick back to the Activity.
2.When a user's login and password match, a boolean value is sent to the Activity notifying it to start an Intent.
My first question is, is this common where a Fragment needs to relay more that one type of Action back to the Activity? And secondly, how is this solved? Is the following a good way to do it...
I created a custom class, which extends Fragment and included the two interfaces that I need (One to pass the onClick back to the Activity and One to pass a boolean value):
public class CustomInterfaceFragment extends Fragment {
public OnClickedListener listener;
public LogInInterface loggedInListener;
static interface OnClickedListener{
public void buttonClicked(View v);
}
static interface LogInInterface{
public void userLoggedIn(boolean loggedIn);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.listener = (OnClickedListener)activity;
this.loggedInListener = (LogInInterface)activity;
}}
I then extended this custom class in my Fragment and used the appropriate methods where needed. This is the onClick method in the Fragment...
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.register_button:{
listener.buttonClicked(v);//***Pass onClick Back to Activity
break;
}
case R.id.fragment_login_loginButton:{
ParseUser.logInInBackground(userName.getText().toString(), password.getText().toString(), new LogInCallback() {
#Override
public void done(ParseUser user, ParseException e) {
if (user!=null){
boolean verified = user.getBoolean("emailVerified");
if(!verified){
Toast.makeText(getActivity(),"Please Verify",Toast.LENGTH_LONG).show();
progressDialog.dismiss();
ParseUser.logOut();
}else{
progressDialog.dismiss();
loggedInListener.userLoggedIn(true);//***Pass boolean Back to Activity
}
}else {
Toast.makeText(getActivity(),e.getMessage(),Toast.LENGTH_LONG).show();
progressDialog.dismiss();
}
}
});
}
break;
}
}
Finally I implemented the custom fragment class and its interfaces in my Activity in order to retrieve the data.
Is this a reasonable way to solve this problem or am I missing something? The application seems to work fine. I just want to know what the best programming practice would be. Thank you.
all i can say is you can bring down this two interfaces to one like this below
public interface fragmentInteractions{
public void OnClickedListener(View v);
public void userLoggedIn(boolean loggedIn);
....
....
}
and i don't think the interface here needs to be static
Elaborating on Avinash Joshi's answer :
public interface CustomListener {
void onButtonClicked();
void onLoginResult( boolean isUserLoggedIn ); // You can pass User object via this method in case its required to do some operations
}
public class MainActivity extends Activity implements CustomListener {
#Override
public void onCreate( Bundle savedInstance ) {
// Initialize UI elements
// Initialize Fragment
}
#Override
public void onButtonClicked() {
//Action to be performed on button click
}
#Override
public void onLoginResult( boolean isUserLoggedIn ) {
if( isUserLoggedIn ) {
//take user to dashboard or any other screen
//Usually with the help of SupportFragmentManager
}
else {
//Take user to signup screen with an optional toast message
//In case parameters like User name and password need not be entered by user again, you can access them as function parameters and pass them to signupFragment via bundle
}
}
}
public class LoginFragment extends Fragment {
CustomListener mCustomListener;
#Override
public void onAttach( Context context ) {
super.onAttach( Context context );
try {
mCustomListner = (CustomListener) context;
} catch ( ClassCastException e {
Log.e(TAG, "Activity must implement CustomListener")
}
}
//Rest of Fragment initialization code here
}
Here's a complete example :
http://www.truiton.com/2015/12/android-activity-fragment-communication/

onAttach callback from fragment to activity

I want to send String data from fragment to activity.
I have read the article about communicating between fragment and activity in android developer, using onAttach callback.
can anyone explain clearly how to send data from fragment to activity?
You should do something like this. First create an interface which will use to comunicate with your activity for example :
public interface OnViewSelected {
public void onViewSelected(int viewId);
}
and in your onAttach do this :
OnViewSelected _mClickListener;
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
_mClickListener = (OnViewSelected) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement onViewSelected");
}
}
In your Fragment implement OnClickListener and in your onClick() method do this :
#Override
public void onClick(View v) {
_mClickListener.onViewSelected(456);
}
After that in your Activity you have to implement the interface you created in your Fragment and it will ask you to add unimplemented methods and in your activity you will have function like this :
#Override
public void onViewSelected(int data) {
Log.d("","data : "+data); // this value will be 456.
}
That's all. : )

Categories

Resources