I'm trying to add a view in fragment B from fragment A, they both belong to the same activity but they are never running simultaneously
My activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fm = getSupportFragmentManager();
Fragment_Main main = new Fragment_Main();
fm.beginTransaction()
.replace(R.id.mainContainer, main)
.commit();
/* Fragment_Main is a fragment with two buttons
* to either call fragment A or fragment B and
* mainContainer is a FrameLayout occupying the whole screen
*/
}
Fragment A:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_subjects, container, false);
ButterKnife.bind(this, view);
AddButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentB.addMyView();
}
});
return view;
}
Fragment B:
public static void addMyView(){
final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(0, 200, 0, 0);
CustomView mView = new CustomView(mContext, "english", "a1", "10:12", "14:32");
mLayout.addView(mView, params);
// CustomView is the custom view I want to add
}
Question:
I know that a fragment can't communicate with another fragment directly, how do I tell the activity to add a view permanently to FragmentB when the method addMyView() is called?
Persisting for one activity lifecycle
FragmentA should set a flag (e.g. a boolean attribute) on the Activity instance.
When FragmentB is instantiated, the Activity should pass this flag to the created instance (e.g. via Bundle). FragmentB should then decide whether or not to add the View.
Persisting permanently
FragmentA should set a SharedPreferences flag.
FragmentB should check this flag in onCreateView and potentially add the View depending on the value.
General advice:
Solving communication between instances with static values/methods is almost always bad practice.
Related
The signup page of my app is divided into three fragments. FragmentA, FragmentB and FragmentC which is attached to same activity. On clicking the next button which is present in FragmentA it will call FragmentB and same is for FragmentB and FragmentC. As I move forward the buttons of the previous fragment overlaps the current one. Below is my code. How can I avoid this situation?
MainActivity.class
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.signup_page);
init();
}
private void init() {
ViewPager2 viewPager = findViewById(R.id.signup_pager);
List<Fragment> fragmentList = new ArrayList<Fragment>();
FragmentA aFragment = new FragmentA();
FragmentB bFragment = new FragmentB();
fragmentList.add(aFragment);
fragmentList.add(bFragment);
SignupFragmentPagerAdapter signupFragmentPagerAdapter = new SignupFragmentPagerAdapter(this,fragmentList);
viewPager.setUserInputEnabled(false);
viewPager.setAdapter(signupFragmentPagerAdapter);
}
}
FragmentA.class
public class FragmentA extends Fragment {
EditText et_name;
Spinner sp_user_type;
Button bt_next;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fraga,container,false);
et_name = view.findViewById(R.id.editTextTextPersonName);
sp_user_type = view.findViewById(R.id.user_spinner);
bt_next = view.findViewById(R.id.button8);
bt_next.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FragmentB bFragment = new FragmentB();
FragmentManager manager = getParentFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.frag_a, bFragment);
transaction.addToBackStack(null);
transaction.commit();
}
});
return view;
}
}
FragmentB.class
public class FragmentB extends Fragment {
Button bt_next;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragb,container,false);
bt_next = view.findViewById(R.id.button9);
bt_next.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FragmentC cFragment = new FragmentC();
FragmentManager manager = getParentFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.frag_b, Fragment);
transaction.addToBackStack(null);
transaction.commit();
}
});
return view;
}
}
Cause of your problem:
You are making fragment transaction (from A to B) within Fragment A itself; and not through the placeholder of these fragments (which is in your case the ViewPager itself; So the MainActivity still sees that the ViewPager is at the first page and therefore shows Fragment A in the background.
You have two options to solve this:
First: Since you're disable page scrolling; I don't see any use to
the ViewPager .. so you can just have a fragment placeholder in the
main activity, and make fragment transaction on this placeholder.
Second: if you have a need to the ViewPager, instead of making
fragment transaction, you can use
viewPager.setCurrentItem(position) and set the position to the
target fragment according to the order you add them in the adapter.
You can do that from your fragment when you hit the button using:
((MainActivity)requireActivity()).myViewPager.setCurrentItem(position)
I tried to make an application that shows a fragment at start then you can change that fragment to an other one with a button in the first fragment. But when I put the button action in the fragments java, it won't start and I get the nullpointerexception error. Could you tell me why?
public class Fragment_main extends Fragment {
FragmentManager fm = getFragmentManager();
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Button button_inditas = (Button) getView().findViewById(R.id.button_inditas);
button_inditas.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fm.beginTransaction().replace(R.id.content_frame, new Fragment_1()).commit();
}
});
return inflater.inflate(R.layout.fragment_main,container,false);
}
}
But when I put the button action in the fragments java, it won't start and I get the nullpointerexception error. Could you tell me why?
Well I see some errors on your code...
First of all you can't call getView() if you haven't inflated one, it means that you MUST inflate a view as i'll put on my answer and then you can avoid the getView() and use that view itself.
And instead of returning the inflater you have to return your View
This is how your Fragment should look like:
public class Fragment_main extends Fragment {
public Fragment_main() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main,container,false);
Button button_inditas = (Button)rootView.findViewById(R.id.button_inditas);
button_inditas.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fragmentManager = getFragmentManager ();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction ();
fragmentTransaction.add (R.id.content_frame, new Fragment_1());
fragmentTransaction.commit ();
});
return rootView;
}
}
You have to inflate the view first and use the view returned by that instead of getView ().
View view = inflater.inflate (...);
and then
... = (Button) view.findView(...);
This happens because the view that is returned by getView () hasn't been created yet, so getView() returns null.
I have two fragment, "A" and "B".
When I return (BackPressed) to my fragment "A", the values of edittext aren't refresh. Why?
I have this code to call fragment "B".
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_container_pedido_novo,
container, false);
btnAddProduto = (Button) view.findViewById(R.id.btnNovoProduto);
btnAddProduto.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Fragment fragment = new FragmentContainerProduto(true);
FragmentManager frgManager = getActivity()
.getSupportFragmentManager();
frgManager.beginTransaction()
.replace(R.id.content_frame, fragment)
.addToBackStack(null).commit();
}
});
edtCodPedido = (EditText) view.findViewById(R.id.edtCodPedidoNovo);
edtVlBruto = (EditText) view.findViewById(R.id.edtVlBrutoPedidoNovo);
edtCodPedido
.setText(String.valueOf(GlobalUtil.objPedido.getCodPedido()));
edtVlBruto.setText(String.valueOf(GlobalUtil.objPedido.getVlBruto()));
return view;
}
Someone?
When you return to a fragment from the back stack Android does not re-create the fragment but re-uses the same instance of it and starts with onCreateView() in the fragment lifecycle. The key thing is you should not inflate view again in onCreateView() of the fragment to which you return, because you are using the existing fragment instance. You need to save and reuse the rootView. See here the answer of Vince Yuan How can I maintain fragment state when added to the back stack?.
Well, I just need put this code...
#Override
public void onResume() {
super.onResume();
edtVlBruto.setText(String.valueOf(GlobalUtil.objPedido.getVlBruto()));
edtCodPedido
.setText(String.valueOf(GlobalUtil.objPedido.getCodPedido()));
}
I have the following Activity:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new StartFragment())
.commit();
}
Button login = (Button) findViewById(R.id.loginButton);
login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
Intent intent = new Intent(MainActivity.this,LoginActivity.class);
startActivity(intent);
}
});
}
I get a NPE when I try to invoke findViewByID for R.id.loginButton, and I'm guessing this is because loginButton is within a separate Fragment, which I have as:
public static class StartFragment extends Fragment {
public StartFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
}
However, I am unsure of how to fix this so that I can find the loginButton ID. I haven't worked with fragments before, so I realize I may be using them/implementing them incorrectly. fragment_main contains a few buttons in a LinearLayout, and activity_main has nothing but a single FrameLayout.
Try to implement your onCreateView(...) in Fragment like
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
View something = rootView.findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... });
return rootView;
}
The Button is in the fragment layout (fragment_main.xml) and not in the activity layout (activity_main.xml). onCreate() is too early in the lifecycle to find it in the activity view hierarchy, and a null is returned. Invoking a method on null causes the NPE.
Write code to initialize button from fragment becuase your button is into fragment layout not into activity's layout.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
Button login = (Button) rootView.findViewById(R.id.loginButton);
login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
Intent intent = new Intent(MainActivity.this,
LoginActivity.class);
startActivity(intent);
}
});
return rootView;
}
And remove the login button related code from onCreate of Activity.
findViewById() works with reference to a root view.
Without having a view in the first place will throw a null pointer exception
In any activity you set a view by calling setContentView(someView);.
Thus when you call findViewById() , its with reference to the someView.
Also findViewById() finds the id only if its in that someView. So in you case null pointer exception
For fragments, adapters, activity, .... any view's findViewById() will only find if the id exixts in the view
Alternately if you are inflating a view, then you can also use inflatedView.findViewById() to get a view from that inflatedView
In short make sure you have the id in your layout you are referring to or make findViewById() call in appropriate place(Ex. adapters getView(), activity's onCreate() or onResume() or onPause() , fragments onCreateView(), ....)
Also have an idea about UI & background thread's as you cannot efficiently update UI in bg-threads
I want to remove a fragment and show a toast when I click a textView. My code shows the toast, but doesn't remove the fragment.
My method:
public void hide(View view) {
My_frag myFrag= new My_frag();
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.remove(myFrag);
transaction.commit();
Toast.makeText(getApplicationContext(), "Hello", Toast.LENGTH_LONG)
.show();
}
My_frag class:
public class My_frag extends android.support.v4.app.Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.my_frag_layout, container, false);
}
}
At some point you must create and add the Fragment correct? You are re-creating the fragment in your hide(...) method so you are trying to remove something that has never been added. Sure you may have added an instance, but not the instance you are trying to remove.
Instead, create a global variable Fragment fragToRemove in your Activity. When you create the fragment (that is where ever you do transaction.add(fragToRemove = new My_Frag);) you will hold an instance. then change your transaction.remove(myFrag) to transaction.remove(fragToRemove) and it should work just fine.
Take the instance of Fragment Transaction other than that was taken while adding fragment to activity and call remove method on that and pass the same instance of Fragment which was used at the time.
Example:
public class MainActivity extends AppCompatActivity {
FragmentTransaction fragmentTransaction;
BlankFragment blankFragment;
Button b;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
b = (Button) findViewById(R.id.activity_button);
fragmentTransaction = getSupportFragmentManager().beginTransaction();
blankFragment=new BlankFragment(); //Fragment instance
fragmentTransaction.add(R.id.main_layout,blankFragment).commit();
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.remove(blankFragment).commit(); //created different
}
});
}
In the same way you can do in fragments also and can also remove fragment X on the click of component of fragment X.