Users have a crash. I know why but I do not know how to fix it. I am newbie in android dev.
Situation:
Android: Fragment inside Activity. Fragment has an EditText. Activity has a button. User tap the button. Inside Button.OnClick() I want to get text Fragment.EditText.getText();
Some users have a crash here EditText.getText(). i.e. EditText is null.
How I do:
In Activity:
public class MyAcrivity extends AppCompatActivity implements OnFragmentInteractionListener {
final MyFrag myFrag= MyFrag.newInstance();
public void run(final View view) {
//some users have crash here because getEt() return null
final String str = myFrag.getEt().getText().toString();
}
}
In Fragment:
public class MyFrag extends Fragment {
private EditText et;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_myfrag, container, false);
et = (EditText) view.findViewById(R.id.et);
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
et = (EditText) view.findViewById(R.id.et);
}
public EditText getEt() {
// return (EditText) getView().findViewById(R.id.et); here getView() could be nul too
return et;
}
}
I know getView() could be null (already googled it).
Init "View" inside onCreateView useless. Crash still happend.
Init "View" inside onViewCreated useless. Crash still happend.
I can NOT reproduce this crash in emulator or my smartphone. I have stable work of my app. BUT some users have the crash and Fabric(crashlytics) is sending messages about it.
People! Help! How to obtain some View from Fragment correctly? I can not to find answers from lifecycle of Fragment. Please explain to me what is wrong.
From your code it seems you have only created instance of fragment and trying to access the view .But to get the view of the fragment you have to add fragment to your activity.
Do this in your activity
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
MyFrag myFrag= MyFrag();
ft.replace("YOUR_FRAGMENT_CONTAINER's id", myFrag);
ft.commit();
Just a hunge, try to do the newInstance() call in the activity lifecycle. Otherwise, there might not be a trustable context to attach fragments. Try to do so in the onCreate().
And something that might be unrelated, but did you properly attach that fragment to the activity? Chances are you are already doing, since some users are correctly accessing it. Anyway, here's the fragment documentation, and a code block to attach it.
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment myFrag = MyFrag.newInstance();
fragmentTransaction.add(R.id.fragment_container, myFrag);
fragmentTransaction.commit();
Use butterknife like this:
class ExampleActivity extends Activity {
#Bind(R.id.user) EditText username;
#Bind(R.id.pass) EditText password;
#BindString(R.string.login_error)
String loginErrorMessage;
#OnClick(R.id.submit) void submit() {
// TODO call server...
}
#Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}
Add the fragment to the Activity in Activity.onCreate() method.
Get the reference to the Activity during Fragment.onAttach (Context) in the Fragment.
Create a method in Activity like enableButton(), which will enable the button like button.setEnabled (true).
Call this method from Fragment onCreateView after initializing the EditText.
This way you can ensure that EditText is initialized before the button is clicked.
Also remember to make the Activity reference null in Fragment.onDetach() to prevent any leak.
If you use getActivity.findViewById in Fragment.
public class MyFrag extends Fragment {
private EditText et;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_myfrag, container, false);
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
et = (EditText) getActivity.findViewById(R.id.et);
}
public EditText getEt() {
// return (EditText) getView().findViewById(R.id.et); here getView() could be nul too
return et;
}
}
Related
I have 2 Fragments - ButtonFragment and ListViewFragment - in my Activity MainActivity.
ButtonFragment contains a Button, ListViewFragment contains a ListView.
Each time I click on the ButtonFragment Button I want the ListViewFragment to show/hide.
How do I code this properly?
Currently my code looks like this:
MainActivity.java
public class MainActivity extends Activity implements Communicator {
ButtonFragment buttonFrag;
ListViewFragment listviewFrag;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonFrag= new ButtonFragment();
listviewFrag = new ListViewFragment();
manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.button_fragment, buttonFrag, "Fragment1");
transaction.add(R.id.listview_fragment, listviewFrag, "Fragment2");
transaction.commit();
}
}
ButtonFragment.java
public class DynamicButtonsFragment extends Fragment implements View.OnClickListener {
Button btn;
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.button_fragment_layout, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onClick(View v) {
//?? hide listview fragment from here ??
}
}
ListViewFragment.java
public class ListViewFragment1 extends Fragment {
protected ArrayAdapter<String> adapter1;
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.list_view_fragment, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
}
So my question is where do I implement the showing/hiding of ListViewFragment? I feel like I should send data to the MainActivity through the onClick method of ButtonFragment. But I do not know how to do so.
Or do I only add code in the MainActivity since the MainActivity has access to all the Fragments?
I am having trouble becase the Button is in a Fragment, not part of the MainActivity. I haven't really seen cases like this...
Can someone please help?
You cannot show/hide a Fragment directly. You may show/hide a UI object like Listview. If you like, you can show/hide Fragment indirectly by using the FragmentTransaction, and you can call its method add, remove or replace.
A link for sample code is Fragments
Do this ..
android.app.Fragment fragment = getActivity().getFragmentManager().findFragmentByTag("YOUR_FRAGMENT_TAG");
getActivity().getFragmentManager().beginTransaction().hide(fragment);
inside your click event!
One more thing when you add fragments like this..
transaction.add(R.id.button_fragment, buttonFrag, "Fragment1");
transaction.add(R.id.listview_fragment, listviewFrag, "Fragment2");
you're expected to provide the container id instead of the id of the fragment.
Example: For MainActivity container use R.id.containerMain
If you in fragment want to do some MainActivity function , you can try
#Override
public void onClick(View v) {
//?? hide listview fragment from here ??
((MainActivity)getActivity()).hidelistView();
//hidelistView you should imp in your MainActivity
}
If you have fragments within the same layout, you can use the following code:
http://www.java2s.com/Code/Android/Core-Class/Demonstrationofhidingandshowingfragments.htm
If not, than you can use several possibilities...
You can use an Intent to send data to MainActivity.
You can have a singleton instance where you store pointer to your MainActivity.
You can also use Handler to send messages, but the ways discribed above are easier to implement and should be enough for you.
i have a activity A with fragment FA, from this fragment i go to FAB, this last fragment is a FragmentPagerAdapter.
When i go from A to FA to FB, press back button and return to FA, and go again to FB this fragment is not showing anything.
My getView method from pager adapter its not called.
This is my transactions code:
From A to FA:
private void setFragment() {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment replyGroupsFragment = new ReplyGroupsFragment();
Bundle bundle = new Bundle();
bundle.putString("title", reportName);
replyGroupsFragment.setArguments(bundle);
fragmentManager.beginTransaction().replace(R.id.container, replyGroupsFragment, "ReplyGroupsFragment").commit();
getSupportActionBar().setTitle(getString(R.string.info));
getSupportActionBar().setDisplayShowTitleEnabled(true);
}
From FA to FB (Use butterkniffe)
#OnItemClick(R.id.listViewReplyGroup)
void listClick(int position) {
List<DomainReplie> domainReplieArrayList = generateRepliesList(position);
setRepliesFragment((ArrayList<DomainReplie>) domainReplieArrayList);
}
And my PagerFragment contains this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ReplyGroupsActivity replyGroupsActivity = (ReplyGroupsActivity) getActivity();
List<DomainReplie> replies = getArguments().getParcelableArrayList("replies");
adapter = new PagerAdapter(replyGroupsActivity.getSupportFragmentManager(), this, replies);
setActionBar();
}
#Override
public void onResume() {
super.onResume();
FirextApplication.getInstance().getBus().register(this);
}
#Override
public void onPause() {
super.onPause();
FirextApplication.getInstance().getBus().unregister(this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.work_containter_replies, container, false);
ButterKnife.inject(this, view);
pager.setAdapter(adapter);
return view;
}
Life cyrcle on both case are equals, but in second round screen doesnt show anything.
A little bit late, but maybe it will help others.
I had the same problem. Finally I've solved it and I made the same mistake as you... Here is the line which is causing the problem:
fragmentManager.beginTransaction().replace(R.id.container, replyGroupsFragment, "ReplyGroupsFragment").commit();
You are calling replace(), but replace() just replaces current fragment - you have to call add() for adding another fragment to back stack - that's also what fixes ButterKnife to load/update views again.
Note: ButterKnife changes a lot! Use bind() instead of inject()
First time posting here and also a new developer for Android Apps.
Desire Function of Example App:
1) Activity starts in fullscreen A
2) Fragment B then populates/inflates container of A fullscreen
3) Fragment B has a button, button is press
4) Fragment B is now replaced with Fragment C
5) Fragment C is now full screen and has data that is inputted by users, user then hits button to send to next Fragment
6) Fragment C is replaced with Fragment D and presents data to view which was inputted from Fragment C
Summary Functionality:
I am trying to keep everything on one screen going from
A (Activity) -> B (Fragment replace) -> C (Fragment replace to type data) -> D (Fragment replace and see last fragment data)
Problem/Issue
My code crashes when I try to populate the last screen with data obviously. It seems to throw an error in MainActivity at this specific line when I debug it
CartFragmentCode addCartInfoTextFragment = (CartFragmentCode) getSupportFragmentManager().findFragmentById(R.id.maincontainer);
I am definitely passing the values correctly through the interface I created, but during the debug process, I found out that my current program is trying to populate information into the same container since it seems like it didn't commit yet to replace the fragment to assign the variables to the right UI.
I was checking if I was able to replace the Fragment C with D, and I was able to only if I remove that above line of code.
Code Below
Main
public class MainActivity extends FragmentActivity implements OrderFragmentCode.FragUIListeners{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Fragment AllMenu = new AllMenuFragmentCode();
FragmentManager fragManager = getSupportFragmentManager(); //getSupportFragmentManager setup
FragmentTransaction ft = fragManager.beginTransaction(); //setup fragmentTranscation = ft
ft.add(R.id.maincontainer, AllMenu);
ft.commit();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void onCartButtonClicked(String editFood, String editType, String editName){
CartFragmentCode addCartInfoTextFragment = (CartFragmentCode) getSupportFragmentManager().findFragmentById(R.id.maincontainer);
addCartInfoTextFragment.UpdateCartTexts(editFood, editType, editName);
}}
Fragment A
public class AllMenuFragmentCode extends Fragment implements OnClickListener {
private Button Order;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.all_menu_fragment, container, false);
Order = (Button) view.findViewById(R.id.orderButton);
Order.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v) {
Fragment OrderFragmentCode = new OrderFragmentCode();
FragmentManager fragManager = getFragmentManager(); //getSupportFragmentManager setup
FragmentTransaction ft = fragManager.beginTransaction(); //setup fragmentTranscation = ft
ft.replace(R.id.maincontainer, OrderFragmentCode);
ft.addToBackStack(null);
ft.commit();
}}
Fragment C
public class OrderFragmentCode extends Fragment implements OnClickListener {
FragUIListeners activityCallback;
public interface FragUIListeners {
public void onCartButtonClicked(String foodText, String typeText, String nameText);
}
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
activityCallback = (FragUIListeners) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement FragUIListeners");
}
}
private EditText editTextFood;
private EditText editTextType;
private EditText editTextName;
private Button AddToCartButton;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.order_fragment, container, false);
editTextFood = (EditText) view.findViewById(R.id.editText);
editTextType = (EditText) view.findViewById(R.id.editText2);
editTextName = (EditText) view.findViewById(R.id.editText3);
AddToCartButton = (Button) view.findViewById(R.id.addToCart);
AddToCartButton.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v) {
Fragment Cart = new CartFragmentCode();
FragmentManager fragManager = getFragmentManager();
FragmentTransaction ft = fragManager.beginTransaction();
ft.replace(R.id.maincontainer, Cart);
ft.addToBackStack(null);
ft.commit();
activityCallback.onCartButtonClicked(editTextFood.getText().toString(), editTextType.getText().toString(), editTextName.getText().toString());
}}
Fragment D
public class CartFragmentCode extends Fragment{
private TextView foodView;
private TextView typeView;
private TextView nameView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.cart_fragment, container, false);
foodView = (TextView) view.findViewById(R.id.foodView);
typeView = (TextView) view.findViewById(R.id.typeView);
nameView = (TextView) view.findViewById(R.id.nameView);
return view;
}
public void UpdateCartTexts(String editfood, String edittype, String editname)
{
foodView.setText(editfood);
typeView.setText(edittype);
nameView.setText(editname);
}}
--Final Say--
Sorry for the long post, this has been bugging me for HOURS and I even tried to use bundles and setting the arguements but I wasnt able to get that working either (I thought if i were able to obtain values correctly I could work around and use this to assign values to my textviews through getArguments on my last Fragment).
Please help!!!! Thanks!!!
In the line CartFragmentCode addCartInfoTextFragment = (CartFragmentCode) getSupportFragmentManager().findFragmentById(R.id.maincontainer);
findFragmentById sholule be passed that you use as 2nd argument in add while adding fragment to container for e.g. ft.add(R.id.maincontainer, AllMenu); if you want to get this fragment then you use findFragmentById(AllMenu) which is the tag that is associated with the fragment. R.id.maincontainer is the container in which that fragment is being displayed.
I guess the problem is you call onCartButtonClicked inside fragment C (where fragment C still exist) so the fragment D isn't created and take C place yet.
Try to call onCartButtonClicked by a Handler in your MainActivity
This is was one of my resource at the time to mimic a solution (he was using two fragments in one activity, while my app is trying to use one activity and replacing the main framelayout): http://www.techotopia.com/index.php/Using_Fragments_in_Android_-_A_Worked_Example
After playing around with example codes and using the available documentations. I cannot use these two lines to transfer the information as suggested in my MainActivity:
CartFragmentCode addCartInfoTextFragment = (CartFragmentCode) getSupportFragmentManager().findFragmentById(R.id.maincontainer);
addCartInfoTextFragment.UpdateCartTexts(editFood, editType, editName);
I think I finally found a workaround or a solution? I am not sure if this is the correct way of passing information to fragments or other classes, but here it is...
I basically Took away those two lines and rework my MainActivity's Click method and changed it to this...
public void onCartButtonClicked(String editFood, String editType, String editName){
CartFragmentCode.passFood = editFood;
CartFragmentCode.passType = editType;
CartFragmentCode.passName = editName;
}
I then went to my last fragment Code which was OrderFragmentCode.java and declared my Strings to retrieve them at the top of the Class and they were...
public static String passFood;
public static String passType;
public static String passName;
I then took the those values and setText them to my textViews. Wallah! It works as intented, however I am not sure if this is the right way to do things... If you guys are able to find a more professional way to handle data across multiple fragments, please let me know!
I have created a MainActivity which consists of 3 tabs which are scrollable (by using ViewPager). Now each of these 3 tabs is a Fragment. Also, I am using ActionBarSherlock (ABS).
For the 1st Fragment, I have created the following class:
public class Fragment_1 extends SherlockFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View v = inflater.inflate(R.layout.fragment1, container, false);
return v;
/* R.layout.fragment1 only contains a TextView. */
}
}
For the 2nd Fragment, I want to extend a FragmentActivity as shown below.
public class Fragment_2 extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment2);
}
}
The 3rd Fragment is same as the first one.
But when I launch the app, it crashes. So, am I wrong in extending FragmentActivity for the class Fragment_2? Is it illegal to extend a FragmentActivity or even an Activity to fragment classes? If not, what is the problem here?
Thanks.
EDIT: After #CommonsWare's answer I updated my class as follows:
public class Fragment_2 extends SherlockFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button send = (Button) findViewById(R.id.bSend); //Error at this line
//some error free code
FragmentTransaction t = getSupportFragmentManager().beginTransaction(); //Error at this line
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View v = inflater.inflate(R.layout.fragment2, container, false);
return v;
}
My final two questions are:
How should I access my R.id.bSend Button in the Fragment_2 class.
Eclipse is giving the suggestion to change getSupportFragmentManager() to getFragmentManager(). Is that change all right?
Thanks!
Is it illegal to extend a FragmentActivity or even an Activity to fragment classes?
Fragment is a Java class. Your fragment implementations (for use as pages in your ViewPager) must inherit from Fragment, directly or indirectly.
Activity does not inherit from Fragment. FragmentActivity does not inherit from Fragment. Hence, you cannot inherit from Activity or FragmentActivity and somehow also inherit from Fragment.
in my app I'm using one activity and two fragments. The app uses a layout with a container so the fragments are added via transactions. The first fragment contains a listview and the other fragment a detail view for the listview items.
Both fragments use setRetainInstance(true). The fragments are added via a replace transaction and addToBackStack(null) is set. The listfragment contains an instance variable which holds some infos for the list. Now I'm changing to detail and press back and the instance variable is null. I read about setRetainInstance and addToBackStack and removed addToBackStack, but even then the instance variable is null.
Does anyone know what I might be doing wrong?
regards,
Thomas
setRetainInstance(true) will tell the FragmentManager to keep the fragment around when the containing Activity is killed and rebuilt for some reason. It doesn't guarantee that the Fragment instance will stick around after a transaction to add or replace. It sounds like your adapter is being garbage collected and you're not creating a new one.
A more generally easy solution would be to make a viewless Fragment to retain your ListAdapter. The way you do this is to create the Fragment, set the retain instance to true, and return null in the method onCreateView(). To add it, just called addFragment(Fragment, String) via the FragmentTransaction. You never remove or replace it, so it will always stay in memory for the length of the app. Screen rotations won't kill it.
Whenever your ListFragment is created, in onCreateView() get the FragmentManager and use either the method findFragmentById() or FindFragmentByTag() to retrieve your retained fragment from memory. Then get the adapter from that fragment and set it as your adapter for the list.
public class ViewlessFragment extends Fragment {
public final static string TAG = "ViewlessFragment";
private ListAdapter mAdapter;
#Override
public ViewlessFragment() {
mAdapter = createAdater();
setRetainInstance(true);
}
#Override
public void onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return null;
}
public ListAdapter getAdapter() {
return mAdapter;
}
}
public class MyListFragment extends ListFragment {
final public static String TAG = "MyListFragment";
#Override
public void onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View returnView = getMyView();
final ViewlessFragment adapterFragment = (ViewlessFragment) getFragmentManager().findFragmentByTag(ViewlessFragment.TAG);
setListAdapter(ViewlessFragment.getAdapter());
return returnView;
}
}
public class MainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle icicle) {
// ... setup code...
final FragmentManager fm = getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
ViewlessFragment adapterFragment = fm.findFragmentByTag(ViewlessFragment.TAG);
if(adapterFragment == null) {
ft.add(new ViewlessFragment(), ViewlessFragment.TAG);
}
ft.add(R.id.fragmentContainer, new MyListFragment(), MyListFragment.TAG);
ft.commit();
}
}