Access spinner declared in a fragment - java

I have a spinner that i populate with an array list from a resource. I have it populated and the code is compiling correctly. My problem now is that I can't seem to figure out how to access the spinner from my main class. For instance, I have my class "CreateExerciseActivity" where I have my method "createExercise"
public class CreateExerciseActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_create_exercise_activiy);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
public void createExercise(View view){
EditText name = (EditText) findViewById(R.id.editText1);
DataBaseWrapper dbHandler = new DataBaseWrapper(this);
Exercise exercise = new Exercise(name.getText().toString(), category);
dbHandler.addExercise(exercise);
name.setText("");
}
}
And below is the code for my fragment where I initialize and populate the spinner
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
private Spinner spinner;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater
.inflate(R.layout.fragment_create_exercise_activiy,
container, false);
loadSpinnerCategories(rootView);
return rootView;
}
private void loadSpinnerCategories(View view){
spinner = (Spinner) view.findViewById(R.id.category_spinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getActivity().getBaseContext(), R.array.categories,
android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
}
}
I want to know how I can access my spinner from the first class "CreateExerciseActivity" now that it has been populated from the fragment. I want to be able to take the option someone has selected and enter it into the database along with exercise name in the method "createExercise".

There's many ways of doing this.
Usually, you want to respond to some type of event in the fragment. For example, a button is pressed. From the fragment you can call the activity like this:
CreateExerciseActivity activity = (CreateExerciseActivity) getActivity();
activity.createExercise(....);
A better way would be for the activity to implement an interface IOptionSelectedListener for example. The interface could have a method called OnOptionSelected(value). Then you could do:
IOptionSelectedListener listener = (IOptionSelectedListener) getActivity();
listener.OnOptionSelected(....);
You could also pass the activity reference to the newInstance() method of the fragment, instead of calling getActivity().
The activity could also have a reference to the fragment and call a method on the fragment to get the actual value of the spinner.

Related

How do I update my Fragment after data modification in my activity (SQLite)?

I have a ListView in a Fragment that is populated when the app starts.
I put a ParcelableArrayList in a Bundle in my newInstance method, and I get it back in my OnCreateView after passing the ArrayList in the newInstance method in my Activity (which is the data read from the SQLite database).
This part works, as I display my data in my Fragment correctly.
I implemented a button that removes all data from the table, and I would now like to update my view after I cleaned the table.
The remove all button is handled in my main activity where I call my database handler to empty the table.
What is the best way to do that ? Here are the parts of the code that seem relevant to me :
My Fragment class :
public class MainFragment extends Fragment {
public static final String RECETTES_KEY = "recettes_key";
private List<Recette> mRecettes;
private ListView mListView;
public MainFragment() {
// Required empty public constructor
}
public static MainFragment newInstance(List<Recette> r) {
MainFragment fragment = new MainFragment();
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(RECETTES_KEY, (ArrayList<? extends Parcelable>) r);
fragment.setArguments(bundle);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mRecettes = getArguments().getParcelableArrayList(RECETTES_KEY);
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_main, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
configureListView();
}
// Configure ListView
private void configureListView(){
this.mListView = getView().findViewById(R.id.activity_main_list_view);
RecetteAdapter adapter = new RecetteAdapter(getContext(), mRecettes);
mListView.setAdapter(adapter);
}
}
Relevant parts from my Main acivity :
This is in my OnCreate method :
mDatabaseHandler = new DatabaseHandler(this);
mRecettes = mDatabaseHandler.readRecettes();
mDatabaseHandler.close();
This is in the method I use to show a fragment :
if (this.mMainFragment == null) this.mMainFragment = MainFragment.newInstance(mRecettes);
this.startTransactionFragment(this.mMainFragment);
Let me know if I should add more of my code, this is my first time posting :)
Lucile
In your R.layout.fragment_main, you can add an id to the root view, say with android:id#+id/fragment_root
And whenever you want to change the fragment view:
In activity:
MainFragment fragment = (MainFragment) getSupportFragmentManager().getFragmentById(R.id.fragment_root);
fragment.updateList(mRecettes);
And then create the new method updateList() in your MainFragment
public void updateList(List<Recette> recettes) {
mRecettes.clear();
mRecettes.addAll(recettes);
configureListView();
}
Also you can tag your fragment when you add it to your transaction instead of using its id, and then use getSupportFragmentManager().getFragmentByTag()

Access method from Dialog to MainActivity

I have a main Activity in which I created a ViewPager that instantiate 3 other Fragments. One of these is a GridView which makes a popup appear when the user click on one item. Then, in this popup, I have a simple button.
What I want to do is: when the user click on this button, I would like to access a method in my main Activity (that should change the current item of my ViewPager) and then dismiss the popup.
I tried everything I could, but I cannot achieve this... I can set up the click event on my popup and dismiss it easily, but I didn't find out how I can access a method (or even a variable) from my popup to my main Activity.
I will put my most relevant code in here so you can understand the structure of my classes (hopefully...).
My main Activity:
public class MainActivity extends FirstActivity{
private ViewPager mViewPager;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
// Set an Adapter on the ViewPager
mViewPager.setAdapter(new MainActivity_Adapter(getSupportFragmentManager()));
public void onSaveInstanceState(Bundle savedInstanceState)
{
menuBar.onSaveInstanceState(savedInstanceState);
}}
My ViewPager activity:
public class MainActivity_Adapter extends FragmentPagerAdapter{
public MainActivity_Adapter(FragmentManager fm)
{
super(fm);
}
#Override
public Fragment getItem(int position)
{
// Set the color background for each page
switch (position)
{
case 0:
return MainActivity_Inventory.newInstance();
case 1:
return MainActivity_Map.newInstance();
default:
return MainActivity_AR.newInstance();
}
}
// The number of Splash Screens to display
#Override
public int getCount()
{
return 3;
}}
My "Inventory" Fragment
public class MainActivity_Inventory extends Fragment implements View.OnClickListener{
public static MainActivity_Inventory newInstance()
{
MainActivity_Inventory frag = new MainActivity_Inventory();
Bundle b = new Bundle();
frag.setArguments(b);
return frag;
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Select the layout
int layout;
layout = R.layout.activity_inventory_01;
// Inflate the layout resource file
View view = getActivity().getLayoutInflater().inflate(layout, container, false);
// Set the grid view
GridView gridview = (GridView) view.findViewById(R.id.inventory_gridView);
gridview.setAdapter(new InventoryImageAdapter(super.getActivity()));
gridview.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
public void onItemClick(AdapterView parent, View v, int position, long id)
{
// Create a popup to show item details
createPopup();
}
});
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
}
public void createPopup()
{
DialogFragment newFragment = new PopupActivity_Inventory();
newFragment.show(getFragmentManager(), "itemDetails");
}
#Override
public void onClick(View v)
{
}}
And my popup dialog fragment:
public class PopupActivity_Inventory extends DialogFragment{
#Override
public Dialog onCreateDialog(final Bundle savedInstanceState)
{
// Build the alert dialog
final Dialog dialog = new Dialog(this.getActivity());
// Get the layout inflater
final LayoutInflater inflater = getActivity().getLayoutInflater();
// Set up the dialog box
dialog.setContentView(inflater.inflate(R.layout.activity_inventory_popup_01, null));
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.getWindow().setGravity(Gravity.TOP);
//dialog.getWindow().getAttributes().y = 100;
(dialog.findViewById(R.id.brick_button_01)).setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
// When button is clicked, ACCESS MAIN ACTIVITY!
dialog.dismiss();
}
});
return dialog;
}}
I really hope you can help me with this... I really need to get it working. Thank you very much!
If you need further details or explanation, please just tell me.
The best thing to do is use EventBus library. I have a demo app in which you can add items to RecyclerView from anywhere within the app using EventBus. You can use it as a reference to simply do something else instead of current task. Here is the link to the repo:
https://github.com/code-crusher/android-demos/tree/master/EventBusDemo
And if you want to understand how it works you can refer to my article, it explains how to make communications like this easy:
https://medium.com/#code_crusher/eventbus-for-android
Hope it helps. Happy coding :)
Read this https://developer.android.com/training/basics/fragments/communicating.html
Look for "To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity...."
Another way to achieve it is using an EventBus and post events by Fragments to be caught by Activities.

GetText from Fragment returns null but setText works

In my MainActivity I call a Fragment and set the boolean mFragment to true when its active. Now I set some Text in the TextViews declared in the Fragment and it works fine. But when a Button declared in the Fragment calls a Method in the MainActivity suddently mFragment is false and I can't use getText from the TextViews because they are null. I have no idea why this is so.
Main Activity
public class MainActivity extends AppCompatActivity implements SensorEventListener {
MainFragment mainFragment;
HistoryActivity historyFragment;
boolean mFragment = false;
//....
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainFragment = new MainFragment();
historyFragment = new HistoryActivity();
if(!mFragment) {
getMainFragment();
}
//...SenserManager and so on
#Override
public void onSensorChanged(SensorEvent event){
if (mFragment) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
DecimalFormat df = new DecimalFormat("0.000");
//This works constantly:
mainFragment.textViewX.setText("X = " + df.format(x));
mainFragment.textViewY.setText("Y = " + df.format(y));
mainFragment.textViewZ.setText("Z = " + df.format(z));
}
}
public void writeEntry(){ //called in Fragment
if(mFragment) {
//This doesn't work (isn't even called because mFragment is false)
String x = (String) mainFragment.textViewX.getText();
String y = (String) mainFragment.textViewY.getText();
String z = (String) mainFragment.textViewZ.getText();
}
}
public void getMainFragment() {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, mainFragment);
transaction.addToBackStack(null);
transaction.commit();
mFragment = true;
}
Fragment
public class MainFragment extends Fragment {
TextView textViewX;
TextView textViewY;
TextView textViewZ;
Button button;
MainActivity mainActivity = new MainActivity();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
textViewX = (TextView) view.findViewById(R.id.textViewX);
textViewY = (TextView) view.findViewById(R.id.textViewY);
textViewZ = (TextView) view.findViewById(R.id.textViewZ);
button = (Button) view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mainActivity.writeEntry();
}});
return view;
}
}
Use callback interface to interact from fragment back to activity. Please refer. It's a very recommended method. http://developer.android.com/training/basics/fragments/communicating.html#Implement
Your architecture has many flaws. You create a fragment instance and are then calling methods on it. You can't do this in android for components such as activities, fragments, services etc (You can only do this for Java classes). You cannot simply create an object of fragment like this. All Fragments, Activities and Services in Android must go through their respective lifecycles so that they have a valid context attached to them.
Also the fragment activity interaction should be done via interfaces. Not by simply calling methods on their objects which must not be created in the first place. You might get it working but ultimately it will keep causing you problems. I recently answered two questions this and this which had the same problem. Seems like lot of people have this doubt.
Your communication and a few point is wrong.
With below usage probably you wanted to access current MainAcitivity's method or fields. But this isn't the current instance of MainAcitivity. You just have instantiated a new one (also without lifecycle of activity)
MainActivity mainActivity = new MainActivity();
So this why the mFragment boolen variable is false and also other items are null, like textviews.
You could refer below links
http://developer.android.com/intl/es/training/basics/fragments/communicating.html
http://www.truiton.com/2015/12/android-activity-fragment-communication/

How to update custom RecyclerView from FragmentDialog?

I have an Activity A with a fragment frag2. Inside the fragment I have a RecyclerView and Adapter to show a list of custom class objects. Adding objects to the adapter is handled programmatically. I have a button inside TwoFragment that opens a FragmentDialog. I'd like to add an object to my Adapter by confirming this dialog, but it seems that the adapter is null when called from the FragmentDialog.
The same adapter is not null, and works if I call it from the fragment OnClick.
Moreover the adapter is null only after screen rotation, it works fine before rotating.
To communicate between the two Fragments I implement a communicator class in activity A.
Activity A
public void respond(String type) {
frag2.addSupport(type);
}
frag2
public RecyclerView rv;
public ArrayList<support> supports;
public myAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supports = new ArrayList<>();
adapter = new myAdapter(supports);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View layout = inflater.inflate( R.layout.fragment_two, container, false);
layout.setId(R.id.frag2);
if (savedInstanceState!=null)
{
supports = savedInstanceState.getParcelableArrayList("supports");
}
rv = (RecyclerView) layout.findViewById(R.id.rv);
adapter = new myAdapter(supports);
rv.setAdapter(myAdapter);
rv.setLayoutManager(new LinearLayoutManager(getActivity()));
rv.setItemAnimator(new DefaultItemAnimator());
#Override
public void onClick(View v) {
int id = v.getId();
switch (id){
case R.id.button1:
addSupport(type); // THIS WORKS ALWAYS, even after screen rotate
break;
case R.id.button2:
showDialog();
break;
}
}
public void showDialog(){
FragmentManager manager = getFragmentManager();
myDialog dialog = new myDialog();
dialog.show(manager, "dialog");
}
public void addSupport(String type){
adapter.addItem(new support(type)); // this line gives null pointer on adapter, but only if called after screen rotate and only if called from the dialog
}
dialog
communicator comm;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog, null);
comm = (myCommunicator) getActivity();
create = (Button) view.findViewById(R.id.button_ok);
create.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v) {
if(v.getId()==R.id.button_ok)
{
// some controls to set type
comm.respond(type)
dismiss();
}
else {
dismiss();
}
myAdapter
public class myAdapter extends RecyclerView.Adapter<myAdapter.VH> {
private LayoutInflater inflater;
private ArrayList<support> data = new ArrayList<>();
// settings for viewholder
public myAdapter (ArrayList<support> data)
{
this.data=data;
}
public void addItem(support dataObj) {
data.add(dataObj);
notifyItemInserted(data.size());
}
}
logcat
FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference
I hope there are no mistakes, I shortened the code for better understanding. Keep in mind that everything works if I never rotate the screen.
I'm a beginner with android and I'm stuck with this for several days now. Please, help.
To understand the problem, it's as you say:
.. everything works if I never rotate the screen
So firstly to understand what happens on rotation, this is a quote from the Android Developer website:
Caution: Your activity will be destroyed and recreated each time the user rotates the screen. When the screen changes orientation, the system destroys and recreates the foreground activity because the screen configuration has changed and your activity might need to load alternative resources (such as the layout).
Ok, now to understand the error:
FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference
Essentially, in your dialog class, you have created a strong dependency by declaring :
comm = (myCommunicator) getActivity();
because comm references objects which would have been destroyed on rotation, hence the NullPointerException.
To further understand runtime changes, such as orientation changes, I'd recommend going through Handling Runtime Changes.
Update
Thank you for your answer, what would you recommend instead of comm = (myCommunicator) getActivity(); ?
The solution comes in 3 parts:
Make sure the onCreate of Activity A has the following:
#Override
public void onCreate(Bundle savedInstanceState) {
......
// find the retained fragment on activity restarts
FragmentManager fm = getFragmentManager();
frag2 = (Frag2) fm.findFragmentByTag(“frag2”);
// create frag2 only for the first time
if (frag2 == null) {
// add the fragment
frag2 = new Frag2();
fm.beginTransaction().add(frag2 , “frag2”).commit();
}
......
}
Add setRetainInstance(true) to the onCreate of frag2.
Remove the implicit referencing i.e. comm = (myCommunicator) getActivity();, and implement something more loosely coupled for dialog.
dialog
public interface Communicator {
void respond(String type);
}
Communicator comm;
....
public void addCommunicator(Communicator communicator) {
comm = communicator;
}
public void removeCommunicator() {
comm = null;
}
#Override
public void onClick(View v) {
if((v.getId()==R.id.button_ok) && (comm!=null))
{
// some controls to set type
comm.respond(type);
}
// Regardless of what button is pressed, the dialog will dismiss
dismiss();
}
This allows you do the following in frag2 (or any other class for that matter):
frag2
<pre><code>
public class Frag2 extends Fragment implements dialog.Communicator {
........
public void showDialog() {
FragmentManager manager = getFragmentManager();
myDialog dialog = new myDialog();
dialog.addCommunicator(this);
dialog.show(manager, "dialog");
}
#Override
public void respond(String type){
adapter.addItem(new support(type));
}
}

Getting Id of a Textview in a fragment OnCreate() method

I am trying to get the Id of a text view in a fragment's onCreate() method. It returns null everytime.
I want to display my contacts in a fragment. For that at the end of FetchContact() method I assign the contact list to TextView.
Since my code is returning null when I find the TextView my application gives NullPointerException.
Anyone who could tell me how to get the Id of TextView of a Fragment in onCreate() method.
I tried making an object of activity class to get the TextView, still the code returned null. No luck.
public class ContactsFragment extends Fragment {
public TextView outputtext;
Context context;
class ActivityObj extends Activity{
public TextView text;
Context objContext;
#Override
public void onCreate(Bundle savedInstanceState) {
text =(TextView)findViewById(R.id.textContact);
objContext = getApplicationContext();
super.onCreate(savedInstanceState);
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
ActivityObj obj = new ActivityObj();
super.onCreate(savedInstanceState);
outputtext =(TextView) getView().findViewById(R.id.textContact);
// outputtext= obj.text;
context=getActivity().getApplicationContext();
//fetchContacts();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.contacts_layout, container, false);
return rootView;
}
find the TextView my application gives NullPointerException.
Because in Fragment lifecycle onCreate method called before onCreateView method.
override onViewCreated which call after onCreateView method and use use first parameter of onViewCreated method for accessing views from Fragment layout:
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView text =(TextView)view.findViewById(R.id.textContact);
/// your code here....
}
use
rootView.findViewById(R.id....) in your oncreateView there is where you should do the UI related work
You should use like this on onActivityCreated method
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
TextView tv = (TextView) getActivity().findViewById(R.id.textView1);
}
}
Or use callbacks
You cannot initialize any view using findViewById before onCreateView method calls.
So you must have to wait for the callback to run. And there are two ways to get it. And it will work on onActivityCreated because it called after onCreateView
As described by ρяσѕρєя K using the View param came from onViewCreated.
You can call getView() in any callback(delegate) which call after
onCreateView()
Like
TextView tv = (TextView) getView().findViewById(R.id.tv_id);

Categories