Inside my MainActivity (Demo) I add FragmentDemo 1 to backstack. Inside this fragmentDemo 1, pressing a button opens a new FragmentDemo 2, where I have an edit text. On pressing the button on this second fragment, I want to remove it from backstack and send the data from editText back to FragmentDemo 1.
I am using a listener on Fragment 2, and implementing the methods, but when I run the code I have the following message. java.lang.ClassCastException: com.example.teacherapp.activities.Demo#5630fb7must implement Listener
Demo (Main Activity)
public class Demo extends AppCompatActivity implements FragmentInterface {
private TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
if (savedInstanceState == null) {
addFragment(new FragmentDemo1());
}
}
#Override
public void onMyFragment(Fragment fragment) {
addFragment(fragment);
}
private void addFragment(Fragment fragment){
getSupportFragmentManager()
.beginTransaction()
.add(R.id.demo_container,fragment)
.addToBackStack(null)
.commit();
}
}
FragmentInterface
public interface FragmentInterface {
void onMyFragment(Fragment fragment);
}
FragmentDemo 1
public class FragmentDemo1 extends Fragment implements FragmentInterface, FragmentDemo2.Fragment2CallBack {
Button btnFrag1;
TextView tvFrag1;
public FragmentDemo1() {
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_fragment_demo1, container, false);
return view;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
btnFrag1 = view.findViewById(R.id.fragment1_button);
tvFrag1 = view.findViewById(R.id.fragment1_tv);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getFragmentManager();
btnFrag1.setText("Frag 1: " + String.valueOf(fm.getBackStackEntryCount()));
btnFrag1.setOnClickListener(v -> {
replaceFragment(new FragmentDemo2());
});
}
#Override
public void onMyFragment(Fragment fragment) {
replaceFragment(fragment);
}
private void replaceFragment(Fragment fragment) {
getFragmentManager().
beginTransaction().
replace(R.id.demo_container, fragment).
addToBackStack(null).
commit();
}
#Override
public void onDataSent(String myData) {
Toast.makeText(getContext(), "RECEIVED. "+myData, Toast.LENGTH_LONG).show();
}
}
FragmentDemo 2
public class FragmentDemo2 extends Fragment {
private Button btnFrag2;
private EditText etFrag2;
private Fragment2CallBack listener;
public FragmentDemo2(){}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_fragment_demo2,container,false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
btnFrag2=view.findViewById(R.id.fragment2_button);
etFrag2=view.findViewById(R.id.fragment2_et);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm=getFragmentManager();
btnFrag2.setOnClickListener(v->{
String info=etFrag2.getText().toString();
listener.onDataSent(info);
fm.popBackStack();
});
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try{
listener=(Fragment2CallBack) context;
}catch (ClassCastException e){
throw new ClassCastException(context.toString()+"must implement Listener");
}
}
public interface Fragment2CallBack{
void onDataSent(String s);
}
}
The result expected is to have fragment 2 removed from backstack and open fragment 1, with received data from fragment 2.
Fragment Back Stack Example.
Refer this url https://www.dev2qa.com/android-fragment-back-stack-example/
The Problem in your existing code
The Context you try to use in FragmentDemo2 is of Demo activity (not of FragmentDemo1) so you can not type cast it to listener when you are trying to attach
If you want to achieve the same goal with existing code, follow below step
1) Expose API from FragmentDemo2 that will allow to set Listener of type Fragment2Callback
public void setListener(Fragment2Callback li) {listener = li;}
2) From FragmentDemo1, when you create FragmentDemo2 instance, you also need to set itself as listener to FragmentDemo2
FragmentDemo2 frg = new FragemtDemo2()
frg.setListener(this);
replaceFragment(frg);
3) Then replaceFragment() from FragmentDemo1
4) remove casting of listener from onAttach() inside FragmentDemo2
So now you have FragmentDemo1 as listener inside FragmentDemo2, which you can use to communicate to fragmentDemo1
I hope this information help you
Related
I am having a fragment that contains for example one button, I want to allow the Activity containing the fragment to change this button view like for example color and title or src Image.
What's the best way to do that ?
Update : sorry If I wasn't clear enough but I want the activity to change the whole view as it's likes , like setting the padding, the color or anything.
It will create the view programmatically and the fragment should replace the old view by the new one and change the view's ID so that the fragment code isn't affected. And If i created methods in the fragment that takes these views when should the Main activity call them ? since the activity has to wait for the layout of the fragment to be created.
1. You must get you fragment from your activity
val fragment = supportFragmentManager.findFragmentByTag("tag")
or
val fragment = supportFragmentManager.findFragmentById(R.id.fragment)
2. Create a method that will do what you wants in you fragment and call this method from the activity
fragment.hideButton()
or you can use interface
Try this way according to your requirement
public abstract class BaseFragment extends Fragment {
BaseActivity yourActivity;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
yourActivity = (BaseActivity) context;
}
#Override
public void onDetach() {
yourActivity = null;
super.onDetach();
}
public void refreshview(){}
public void setDada(Data data){}
#override
publicn void onStart(){
yourActivity.setCurrentOpenedFragment(this);
}
}
Create the BaseActivity class
public abstract class BaseActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void setCurrentOpenedFragment(BaseFragment currentFragment){}
}
Extends the BaseFragment in your fragment i.e.
public class YourFragment extends BaseFragment{
View view;
Button button;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment, container, false);
button = (Button)view.findViewById(R.id.button);
return view;
}
#override
publicn void onStart(){
super.onStart();
//yourActivity.setCurrentOpenedFragment(this);
}
Data data;
#override
public void setDada(Data data){
this.data = data;
}
#Override
public void refreshview() {
button.setcolor(data.getcolor());
}
}
Write the below line of code in your Activity class
public class YourActivity extends BaseActivity{
public static final byte NONE = -1;
public static final byte HOME = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activity);
changeFragment(new YourFragment(), HOME);
initview();
}
public void initview(){
Button b = (Button)findViewById(R.id.button);
b.setOnClickListener(new OnClickListener(){
if(currentFragmentOnDrawer != null){
Data data = new Data();
data.setColor(getResource().getcolor(R.color.red));
currentFragmentOnDrawer.setDada(data);
currentFragmentOnDrawer.refreshview()
}
});
}
BaseFragment currentFragmentOnDrawer;
#Override
public void setCurrentOpenedFragment(BaseFragment currentFragment) {
super.setCurrentOpenedFragment(currentFragment);
this.currentFragmentOnDrawer = currentFragment;
}
public void changeFragment(Fragment targetFragment, byte state) {
FragmentTransaction ft = getSupportFragmentManager()
.beginTransaction();
ft.replace(R.id.main_fragment, targetFragment, "" + state);
ft.addToBackStack("" + state);
ft.commit();
}
}
Hope it will help for you
So basically, i have an activity called (Profile Activity) and two fragments connected to it (Profile view and profile edit fragment). Since im completely new with android studio, java language and fragments, im trying to place both fragments into activity but in a way that only profile view fragment is shown. Edit profile fragment needs to be hidden. Im using next part of the code:
getSupportFragmentManager().beginTransaction().add(R.id.profile_fragment, profileViewFragment).commit();
getSupportFragmentManager().beginTransaction().add(R.id.profile_fragment, profileEditFragment).commit();
I already tried something with "hide" and "show", but with no success. I have imported "android.support.v4.app.FragmentManager;" Thank you.
EDIT:
Profile activity after implementing new code:
public class ProfileActivity extends AppCompatActivity implements ProfileViewFragment.ProfileViewListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
//ovo ispod je za proslijedivanje iz activita u fragment
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
ProfileViewFragment profileViewFragment = new ProfileViewFragment();
ProfileEditFragment profileEditFragment=new ProfileEditFragment();
profileViewFragment.setArguments(getIntent().getExtras());
profileEditFragment.setArguments(getIntent().getExtras());
//getSupportFragmentManager().beginTransaction().add(R.id.profile_fragment, profileEditFragment).commit();
getSupportFragmentManager().beginTransaction().add(R.id.profile_fragment, profileViewFragment).commit();
//getSupportFragmentManager().beginTransaction().replace(R.id.profile_fragment, profileViewFragment).commit();
//FragmentManager fm=getSupportFragmentManager();
//fm.beginTransaction().setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out).show(new ProfileViewFragment()).commit();
//fm.beginTransaction().setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out).show(new ProfileEditFragment()).commit();
//-----------------------------------
}
#Override
public void onOpenProfileEditor() {
ProfileEditFragment profileEditFragment=new ProfileEditFragment();
profileEditFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().replace(R.id.profile_fragment, profileEditFragment).commit();
}
#Override
public void onAttachFragment(Fragment fragment) {
if (fragment instanceof ProfileViewFragment) {
ProfileViewFragment profileFragment = (ProfileViewFragment) fragment;
profileFragment.setListener(this::onOpenProfileEditor);
}
}
}
Profile view fragment with new code:
public class ProfileViewFragment extends Fragment {
private Unbinder unbinder;
//novi kod sa stacka
private ProfileViewListener listener;
//-------------
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
FragmentProfileViewBinding viewBinding=DataBindingUtil.inflate(inflater, R.layout.fragment_profile_view, container, false);
View view=viewBinding.getRoot();
unbinder = ButterKnife.bind(this, view);
UserModel user = (UserModel) getArguments().get(ModelEnum.UserModel.name());
//viewBinding povezuje fragment i xml (proslijeduje user)
viewBinding.setUser(user);
//viewBinding.setUserGender(user);
return view;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
// #OnClick(R.id.btn_change_settings)
// public void changeSettings(){
//getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.profile_fragment, new ProfileEditFragment()).commit();
// }
#Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
//ISPOD JE NOVI KOD SA STACK OVERFLOWA
public interface ProfileViewListener{
void onProfileEditor();
}
public void setListener(ProfileViewListener listener) {
this.listener = listener;
}
#OnClick(R.id.btn_change_settings)
public void onEdit(View view){
if(listener!=null){
onOpenProfileEditor();
}
}
}
Instead of
getSupportFragmentManager().beginTransaction().add(R.id.profile_fragment, profileEditFragment).commit();
It must be
getSupportFragmentManager().beginTransaction().replace(R.id.profile_fragment, profileEditFragment).commit();
This will replace your fragment, instead of adding it.
Please, also note that you must call "add" for the first time and use "replace" afterwards.
You may find more about fragments here: https://developer.android.com/training/basics/fragments/fragment-ui
EDIT
For the new issue you have outlined, the solution is to "report" you activity that an event had happened, so it can take action. Here is how to do that.
First, we need an interface (you can add it inside you Profile fragment) and to link the activity to our fragment, if it implements that interface.
public class ProfileViewFragment extends Fragment {
...
...
private ProfileViewListener listener;
...
...
#OnClick(R.id.btn_change_settings)
public onEdit(View view) {
// If there is anyone listening, report that we need to open editor
if (listener != null) {
listener .onOpenProfileEditor();
}
}
public void setListener(ProfileViewListener listener) {
this.listener = listener;
}
// The interface
public interface ProfileViewListener {
void onOpenProfileEditor();
}
}
And in the class, we need to implement the interface and subscribe as a listener.
public class ProfileActivity extends AppCompatActivity implements ProfileViewFragment.ProfileViewListener {
...
...
#Override
public void onOpenProfileEditor() {
ProfileEditFragment profileEditFragment=new ProfileEditFragment();
profileEditFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager()
.beginTransaction
.replace(R.id.profile_fragment, profileEditFragment)
.commit();
}
#Override
public void onAttachFragment(Fragment fragment) {
if (fragment instanceof ProfileViewFragment) {
ProfileViewFragment profileFragment = (ProfileViewFragment) fragment;
profileFragment.setListener(this);
}
}
}
You may find more detail on Activity-Fragment communication here - https://developer.android.com/training/basics/fragments/communicating
I'm going from a Fragment -> Activity. In the activity I make an interface and when the user clicks a button I pass in a string to an interface object I create and call finish(). In the fragment I implement that interface and want to get that string. But in the activity I get a crash saying the listener is null.
public class SampleActivity extends AppCompatActivity
{
private Button button;
private MatchActivity.OnMatchCompleteListener onMatchCompleteListener;
public interface OnMatchCompleteListener
{
public void matchComplete(String matchID);
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.someButton);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onMatchCompleteListener.matchComplete("finished");
finish();
}
});
}
}
Fragment
public class SampleFragment extends Fragment implements SampleActivity.OnMatchCompleteListener{
private View rootView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_login , container, false);
return rootView;
}
#Override
public void matchComplete(String matchID) {
Log.d("TEST",matchID);
}
}
if you have your fragment in the R.layout.activity_main and your fragment id is R.id.fragment use this:
onMatchCompleteListener =
((SampleFragment)getFragmentManager().findFragmentById(R.id.fragment))
otherwise use this solution:
SampleFragment fragment = new SampleFragment();
onMatchCompleteListener = fragment;
getFragmentManager().beginTransaction().add(R.layout.activity_main, fragment, "TAG_SIMPLE").commit();
I was able to create a single event callback from a fragment to the activity. However, I have problem with creating two event callbacks from the same fragment.
Basically, I have a button on the fragment1 layout, when click on it, it will execute something in mainactivity, and then, it changes hide the Button at fragment1 layout, and then I need to send a Boolean from the fragment to activity, then initiate fragment2.
How can I implement the 2nd callback interface in the mainacativity.
Here is my code:
at Fragment1:
public class Fragment1 extends Fragment{
private static final String TAG = "Fragment1";
Boolean in1, in2;
Button btn1;
ListenerA mListenerA;
public interface ListenerA{
public void methodA(Boolean in1);
};
ListenerB mListenerB;
public interface ListenerB{
public void methodB(Boolean in2);
};
#Override
public void onAttach(Context context) {
super.onAttach(context);
if(context instanceof ListenerA)
mListenerA = (ListenerA)context;
if(context instanceof ListenerB)
mListenerB = (ListenerB)context;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View myView = inflater.inflate(R.layout.fragment1, container, false);
btn1 = (Button) myView.findViewById(R.id.btn1_ID);
btn1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mListenerA.methodA(true);
}
});
return myView;
}
public void showBtnMethod (Boolean showBtn){
if (showBtn==false) {
btn1.setVisibility(View.INVISIBLE);
mListenerB.methodB(true); //***** problematic line
}
}
}
and the code in the main activity is below. please note that on the first line I was able only to implement one method from the fragment.
public class MainActivity extends AppCompatActivity implements Fragment1.ListenerA {
private static final String TAG = "MainActivity";
public Fragment1 mFragment1;
public Fragment2 mFragment2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
FragmentTransaction transaction1 = getSupportFragmentManager().beginTransaction();
mFragment1 = new Fragment1();
transaction1.replace(R.id.content_fragment_ID, mFragment1);
transaction1.commit();
}
}
// this gets called by ListenerA when click on the button
public void methodA(Boolean in){
if (in==true){
Toast.makeText(MainActivity.this, "mMethodA Executed", Toast.LENGTH_SHORT).show();
triggerShowMethod();
}
};
private void triggerShowMethod(){
mFragment1.showBtnMethod(false);
};
// Problematic section is below
public void methodB(Boolean in){
if (in==true){
FragmentTransaction transaction2 = getSupportFragmentManager().beginTransaction();
mFragment2 = new Fragment2();
transaction2.replace(R.id.content_fragment_ID, mFragment2);
transaction2.commit();
Toast.makeText(MainActivity.this, "mMethodB Executed", Toast.LENGTH_LONG).show();
}
};
}
You can simply consolidate both interfaces into one:
public interface Listener{
public void methodA(Boolean in1);
public void methodB(Boolean in2);
};
and in your MainActivity:
public class MainActivity extends AppCompatActivity implements Fragment1.Listener
Your activity doesn't implement ListenerB.
public class MainActivity extends AppCompatActivity implements Fragment1.ListenerA, Fragment1.ListenerB {
}
im a new guy on this programming world so i guess this question is simple. So I have one imagebutton on my fragment and I whatI want is that when I click on it, it does a fragment transaction to another fragment; but the thing is that when I run the app and click on the imagebutton, it only "superimposes" the content of the other fragment into the one where the imagebutton is, of course what i want to do is just to go to the other fragment, ive been dealing with this for a while so I hope someone helps me, thanks.
Here is my java code of the fragment
public class inicio extends Fragment {
public inicio() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_inicio,container,false);
ImageButton luces = (ImageButton)view.findViewById(R.id.imageButton);
luces.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
interior interior= new interior();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.inicioo, interior).commit();
}
});
}
}
New code added...
public class transaction extends MainActivity
implements com.example.administradora.prueba.inicio.OnSelectedListener{
public void onButtonSelected() {
interior interior= new interior();
getSupportFragmentManager().beginTransaction().replace(R.id.inicioo, interior).commit();
}
}
but i get this error in the logcat:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.administradora.prueba/com.example.administradora.prueba.MainActivity}: java.lang.ClassCastException: com.example.administradora.prueba.MainActivity#20f8fe9b must implement OnSelectedListener
You shouldn't replace a Fragment from another Fragment. Try to do that through the Activity.
Define some interface for the Fragment to hold
public class MyFragment extends Fragment {
OnSelectedListener mCallback;
// Container Activity must implement this interface
public interface OnSelectedListener {
public void onButtonSelected();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnSelectedListener ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnSelectedListener");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_inicio,container,false);
ImageButton luces = (ImageButton)view.findViewById(R.id.imageButton);
luces.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Send the event to the host activity
if (mCallback != null) mCallback.onButtonSelected();
}
});
return view;
}
}
And swap the Fragment container in the interface implementation from the Activity.
public class MainActivity extends Activity
implements MyFragment.OnSelectedListener{
...
public void onButtonSelected() {
interior interior= new interior();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.inicioo, interior)
.commit();
}
}
The first two sections of Communicating with Other Fragments is what you are looking for.