I have an android Activity with swipe navigation (implemented with ViewPager).
public class UberActivity extends FragmentActivity {
ViewPager mViewPager;
protected void onCreate(Bundle savedInstanceState) {
//... some stuff
myfragment1=new MyFragment1();
myfragment2=new MyFragment2();
myfragment3=new MyFragment3();
myfragment4=new MyFragment4();
}
public void onChoiceSelected(){
mViewPager.post(new Runnable(){public void run(){
myfragmen1.update();
myfragmen2.update();
myfragmen3.update();
myfragmen4.update();
}});
}
}
public class Fragment4 extends Fragment {
View v;
#Override
public View onCreateView(LayoutInflater layoutInflater, ViewGroup container,
Bundle savedInstanceState) {
v=layoutInflater.inflate(R.layout.mylayout,container,false);
return v;
}
public void update(){
((TextView)v.findViewById(R.id.textView1)).setText("new text");
}
I will get a NullPointerException on update(), unless I beforehand swipe to it (so that its layout is actually inflated before calling update()).
The problem here is that Fragment4 is instanced but its OnCreateView() is not called. How should I check if OnCreateView() is called? I could put a boolean there, but I don't think this is a best practice...
Just add an if-statement around 'v', checking if it is null or not.
public void update(){
if (v != null) {
((TextView)v.findViewById(R.id.textView1)).setText("new text");
}
}
Related
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
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
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.
I am trying to understand when i should use the oncreate method or the oncreateview method.
I am a little bit confused. First i had some code including statements like findViewById() in the OnCreate() method. But it always responded with a null pointer Exception, then someone told me i should put it in the OnCreateView() method. It worked, but i do not understand when and what for code i should put in the OnCreate() or when and what i should put in the OnCreateView(). Could someone please explain this to me.
In my code, the methodology is the following:
ACTIVITY code:
#Override
public void onCreate(Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
this.setContentView(R.layout.activity_container);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (saveInstanceState == null)
{
getSupportFragmentManager().beginTransaction()
.replace(R.id.activity_container_container, new MyFragment()).addToBackStack(null).commit();
}
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
}
#Override
public void myInterface()
{
System.out.println("Do stuff;");
}
FRAGMENT code:
private int titleId;
private Button placeholderButton;
private MyInterface activity_myInterface;
public MyFragment()
{
super();
this.titleId = R.string.my_actionbar_title;
}
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try
{
activity_myInterface = (MyInterface)activity;
}
catch(ClassCastException e)
{
Log.e(this.getClass().getSimpleName(), "MyInterface interface needs to be implemented by Activity.", e);
throw e;
}
}
//this is an interface defined by me
#Override
public int getActionBarTitleId()
{
return titleId;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_myfragment, container, false);
placeholderButton = (Button)view.findViewById(R.id.myfragment_placeholderbtn);
placeholderButton.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v)
{
if(v == placeholderButton)
{
System.out.println("Do more stuff");
}
}
Where R.layout.activity_container is:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:id="#+id/activity_container_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
This way the Activity is responsible for containing the Fragment (and this fragment is created in onCreate()) and the Fragment is responsible for the display of UI and the event handling. You can have more than one fragment on an Activity, though, that's what they primarily were designed for.
If you are using an Activity, then you can just stay away from the onCreateView.
Just stick to the default activity lifecycle:
The layout (content) is inflated (created) at the onCreate method.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book);
}
Then once it has been inflated you are ready to use it and manipulate views that belong to it.
findViewById() returned null for you because the layout was not ready to be used and that's because it was not yet inflated. You can use findViewById() after setContentView() in onCreate.
findViewById() is a method that can be pretty hard on the performance (if called many times), so you should get all the views you need in onCreate and save them to an instance variable like:
TextView textView1, textView2;
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book);
textView1 = findViewById(R.id.textview1);
textView2 = findViewById(R.id.textview2);
...
}
And use them from onStart:
#Override
protected void onStart() {
super.onStart();
if (textView1 != null) textView1.setText("myText");
if (textView2 != null) textView2.setText("some other text");
}
I have a ListView in my activity.On clicking on the list item it invokes another activity.In that activity I have implemented ViewPager and fragments.
When it loads first time onResume() ,onCreate() and onCreateView() method called twice, if I clicks on first list item. (i.e. it loads first and second fragment view)
when I click on the any other List fragment except first then it calls onResume() ,onCreate() and onCreateView() methods three times (i.e. It loads previous and after and click view )
It is absoutely fine but I have google analytics code with which I have to track only current page so where I can put this code to load for only current page
My question is my googleAnalytics code tracs three or two pages at first time even user doesnot gone through those pages how to avoid this ?
My code is as below for fragment
public class MainListActivity extends Activity{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(TAG, "onCreate()");
CustomFragmentPagerAdapter adapter = new CustomFragmentPagerAdapter();
viewPager.setAdapter(adapter);
}
}
//code for fragment adapter
public class CustomFragmentPagerAdapter extends FragmentPagerAdapter {
public CustomFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int pos) {
CustomFragment customFragment = new CustomFragment();
arrayList.add(customFragment);
return customFragment;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return arrayList.size();
}
}
//code for fragment
public class CustomFragment extends Fragment{
public CustomFragment() {
super();
}
#Override
public void onResume() {
super.onResume();
Log.v(TAG, "onCreate -Resume");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(TAG, "onCreate");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
Log.v(TAG, "onCreateView");
return myAnyView;
}
}
The problem is that the onResume() method is called for all your fragments, that is including the invisible ones.
Check gorn's answer here:
How to determine when Fragment becomes visible in ViewPager
You have to override
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// Your logic
}
and do your logic in there.