I have defined a public method inside a fragment to set the value of an internal variable. But I cannot access (cannot see) the method outside the fragment (e.g. from Main activity class). I'm probably missing something but cannot figure it out. Every help is appreciated.
My fragment code is below (I removed import packages for brevity):
package com.example.lenovo.apptel_book;
public class my_add_fragment extends Fragment {
int temp_data;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_my_add_fragment, container, false);
}
public void set_data(int x)
{
temp_data = x;
}
}
I want to access the "set_data" method when a given button is clicked. Within the onClick method, i use the code below:
public void main_Add_Click(View v)
{
Fragment fr = new my_add_fragment();
// need to access fr.set_data() but cannot see the method!
}
Use this
my_add_fragment fr = new my_add_fragment();
Instead of this
Fragment fr = new my_add_fragment();
Related
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;
}
}
I am creating a android app in which I have a main activity which has 2 fragments, fragUp and fragDown. fragUp has two edit text fields and a button and fragDown has two text view fields. Initially main activity shows only fragUp and asks user for the input fields, when the user clicks the button then onClick method is triggered and fragDown is created below fragUp and shows the two fields entered by the user.
Summary-
FRAGMENTS- fragUp , fragDown
MAIN ACTIVITY- main activity
Main activity has two linear layouts 1.ly1 for fragUp2.ly2 for fragDown
Note- All fragments are added dynamically in main java file and not declared in main.xml file.
This is main activity code
public class MainActivity extends AppCompatActivity implements fragUp.fragInterface {
FragmentTransaction ft,ft1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
ft=getFragmentManager().beginTransaction();
fragUp fu=new fragUp();
ft.add(R.id.ly1,fu); //Fragment fragUp is added dynamically.
ft.commit();
}
public void set(String a,String b){
ft1=getFragmentManager().beginTransaction();
fragDown ob=new fragDown();
ft1.add(R.id.ly2,ob);
ft1.commit();
fragDown fd=(fragDown)getFragmentManager().findFragmentById(R.id.ly2);
fd.seta(a, b);
}
This is my fragUp java code-
public class fragUp extends Fragment {
View v;
public interface fragInterface{
public void set(String a,String b);
}
fragInterface ob;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v=inflater.inflate(R.layout.fragup,container,false);
Button b=(Button)v.findViewById(R.id.button1);
b.setOnClickListener( new OnClickListener(){
public void onClick(View v){
store();
}});
return v;
}
public void onAttach(Activity activity){
super.onAttach(activity);
try{
ob=(fragInterface)activity;
}
catch(ClassCastException e){
throw new ClassCastException();
}
}
public void store(){ with main activity
EditText et1=(EditText)v.findViewById(R.id.editText1);
EditText et2=(EditText)v.findViewById(R.id.editText2);
ob.set(et1.getText().toString(),et2.getText().toString()); //Interface implemented to share data
}
}
This is fragDown java code-
public class fragDown extends Fragment {
View v;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v=inflater.inflate(R.layout.fragdown,container,false);
return v;
}
public void seta(String a,String b){
TextView t1=(TextView)v.findViewById(R.id.textView1);
TextView t2=(TextView)v.findViewById(R.id.textView2);
t1.setText(a);
t2.setText(b);
}
}
When I run the app and press the button my app crashes and logcat says-
08-05 09:37:02.198: E/AndroidRuntime(2866): java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.fragment.fragDown.seta(java.lang.String, java.lang.String)' on a null object reference
08-05 09:37:02.198: E/AndroidRuntime(2866): at com.example.fragment.Second.set(Second.java:30)
08-05 09:37:02.198: E/AndroidRuntime(2866): at com.example.fragment.fragUp.store(fragUp.java:52)
08-05 09:37:02.198: E/AndroidRuntime(2866): at com.example.fragment.fragUp$1.onClick(fragUp.java:25)
If anyone can help me then it would be great. I spent hours debugging it can't find where I went wrong. Please help.
Why don't you pass the values to the fragment as arguments? Check the "Layout" section on this guide: http://developer.android.com/reference/android/app/Fragment.html
Here's an excerpt from that guide that could help you:
public static DetailsFragment newInstance(int index) {
DetailsFragment f = new DetailsFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
Then when setting up the fragment:
// Prepare args
Bundle arguments = new Bundle();
arguments.putString(key, value);
// Init fragment and pass args
fragment = new DetailsFragment();
fragment.setArguments(arguments);
// Commit to the fragment manager, thus
// inflating the view
getSupportFragmentManager().beginTransaction()
.add(R.id.your_id, fragment).commit();
You can access the bundled arguments on your fragment's onCreate method.
Addressing your code specifically, you should:
MainActivity.set, pass the args to your fragment:
public void set(String a,String b) {
ft1 = getFragmentManager().beginTransaction();
fragDown ob = new fragDown();
// Add args. It is a good practice
// to declare keys as public static final
// variables, accessing them accordingly. Here
// I'm just putting the string, but you should
// avoid that.
Bundle args = new Bundle();
args.putString("key_a", a);
args.putString("key_b", b);
ob.setArguments(args);
ft1.add(R.id.ly2, ob);
ft1.commit();
}
Add FragDown.onCreate method:
private String a = "";
private String b = "";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.a = getArguments().getString("key_a");
this.b = getArguments().getString("key_b");
}
Call seta on your onCreateView:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v = inflater.inflate(R.layout.fragdown, container, false);
seta(this.a, this.b)
return v;
}
this is due to the Fragment's lifecycle: http://developer.android.com/reference/android/app/Fragment.html#Lifecycle
Now, a bit of advice:
If you're serious about Android development, go read the links I posted (from top to bottom) until you understand them. Sorry, there's no way around them.
The "show me the code" kind of questions are usually considered rude and are prone to get downvoted or get hateful comments. StackOverflow is working towards a "be nice" campaign, and that's one of the main reasons I coped with it and followed up.
Asking a question in StackOverflow should really be your last resource, after you've exhausted every other option and have gone through docs, tutorials, source code, GitHub issues and Pull Requests, and so on.
Take this advice with a grain of salt :)
What I ought to do is change the view/layout of Fragment without creating another class for fragment on click of a button.
For example I have an activity - ContactsActivity and I have a fragment - ContactsFragment.
The Standard way of using Fragments:
From ContactsActivity I call ContactsFragment by -
getFragmentManager().beginTransaction().replace(android.R.id.content, new ContactsFragment())
.commit();
Code for setting View in ContactsFragment class -
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.contacts_primary, container, false);
return rootView;
}
**Now comes how I do what I want to do ** (Change the view of fragment)
I change only the view of ContactsFragment by doing a bad kind of hack.
I change the onCreateView() shown above to this -
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
//Set the view to R.layout.contacts_primary
rootView = inflater.inflate(R.layout.contacts_primary, container, false);
//Set the view to R.layout.contacts_secondary
if(getActivity().getIntent()!=null && getActivity().getIntent().getBooleanExtra("s", false)) {
rootView = inflater.inflate(R.layout.contacts_secondary, container, false);
Log.e(tag,getActivity().getIntent().getExtras().toString());
return rootView;
}
//This is the onClickListener which again calls the ContactsActivity class,
//this time with an Intent which I used above to change the view from
//R.layout.contacts_primary to R.layout.contacts_secondary
Button button = (Button)rootView.findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getActivity(), ContactsActivity.class).putExtra("s",true));
}
});
Now everything works as I want and flawlessly.
But I have a very strong feeling that either all of it is wrong and Fragments aren't supposed to work this way or I am using a hectic hack to achieve what can be done by few lines of code.
So please let me know what is it? And if there is a standard way of doing what I am trying to do.
For me passing additional argument on which base fragment decides wich layout to use seems totally ok. But there is cleaner way of doing what you want to achieve without starting another activity.
First of all pass argument to fragment by making standard static new instance method in fragment (we cannot pass this argument in constructor as android always recreates fragments using empty constructor). Something like this:
public static ContactsFragment newInstance(boolan firstView) {
ContactsFragment fragment = new ContactsFragment();
Bundle args = new Bundle();
args.putBoolean("yourArg", firstView);
fragment.setArguments(args);
return fragment;
}
Every time you have to initiate your fragment do this with this method.
Then declare interface in your fragment to communicate with your activity. Like this
public interface NewViewListener {
public void showNewView(boolen firstView);
}
Than make your activity implement it so your activity han a method where it can place new fragment in container view. In your fragments onAttach and onDetach methodsmake sure your activity implements this interface and hold reference to your activity in private NewViewListener field in your fragment. Like this:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (NewViewListener ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement NewViewListener ");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
Then in on button click method call showNewView method on your activity with whatever argument you want indicating which view you want in new fragment instance. And in your activity method showNewVew fragment in the container. Like this:
#Override
public void showNewView(boolean firstView) {
getFragmentManager().beginTransaction().replace(android.R.id.content, ContactsFragment.newInstance(firstView)
.commit();
}
In your fragments onCreateView you may get passed arguments and decide which view you want to use.
I have a class file MainActivity and i want switch to Testing.class file. So this is following code.
public static class MyFragment extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.xyz, container, false);
final TextView tt=(TextView) rootView.findViewById(R.id.txt);
Button button=(Button) rootView.findViewById(R.id.btn1);
View.OnClickListener sx = new OnClickListener()
{
public void onClick(View v)
{
call();
}
};
button.setOnClickListener(sx);
return rootView;
}
private void call()
{
// Here getting the Error //
Intent intent = new Intent(MainActivity.this, Testing.class);
startActivity(intent);
}
}
So, this is the above code , i want to switch to new class file called Testing but
i am facing an error-: No enclosing instance of the type MainActivity is accessible in scope
I think creating an new static class called MyFragment within MainActivity creating problem.
So, please help me out and give me some good solution.
Okay so you cannot call MainActivity.this since you are in the fragment class. You have to access the context in a different way. Such as
getActivity()
which returns the activity associated with the fragment. The activity is a context (Since Activity extends Context)
You can use getActivity() method inside your fragment but take care that this method might return null in case of calling it before getting onActivityCreated() method
Let's say I have this button:
<Button
android:id="#+id/idone"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="D2"
android:onClick="myMeth"/>
I have several times used this to call methods from a layout xml as it calls the method from the activity that inflated such view.
Recently with DialogFragments, well it does not work at all. I keep getting an error telling me that such method does not exist. Where is it then looking for such method? I have added it to the DialogFragment class:
public class myActivity extends DialogFragment {
public DiceDialog() {
// empty constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.myDialog, container);
getDialog().setTitle("Hello");
return view;
}
public void myMeth(View view) {
//...
}
As well as in the activity that instantiates the FragmentManager and calls the dialog:
public Class MainActiviry Extends FragmentActivity {
//...
public void onCreate(Bundle savedInstanceState) {
// ..
FragmentManager fm = getSupportFragmentManager();
MyActivity dialog = new AddDiceDialog();
dialog.show(fm, "tag");
}
public void myMeth(View view){
//...
}
And still the messag is that MyMeth is not found.
I have already read that using interfaces and listeners is the correct way to communicate between activity and dialog fragments, but what I am trying to figure out here is where that myMeth call is being made, because well,it is called.
You can implement public myMeth(View view) in your Activity, which will then check for the currently visible Fragment, and call its method.
If you want to use more then one callable method in your Fragment, you can utilize the id's of the calling views and implement a switch, calling a different fragment method according to the id of the View.