This is a canonical question for a problem frequently posted on StackOverflow.
I'm following a tutorial. I've created a new activity using a wizard. I get NullPointerException when attempting to call a method on Views obtained with findViewById() in my activity onCreate().
Activity onCreate():
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View something = findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
Layout XML (fragment_main.xml):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="packagename.MainActivity$PlaceholderFragment" >
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:id="#+id/something" />
</RelativeLayout>
The tutorial is probably outdated, attempting to create an activity-based UI instead of the fragment-based UI preferred by wizard-generated code.
The view 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.
The preferred solution is to move the code to the fragment onCreateView(), calling findViewById() on the inflated fragment layout rootView:
#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); // not activity findViewById()
something.setOnClickListener(new View.OnClickListener() { ... });
return rootView;
}
As a side note, the fragment layout will eventually be a part of the activity view hierarchy and discoverable with activity findViewById() but only after the fragment transaction has been run. Pending fragment transactions get executed in super.onStart() after onCreate().
Try OnStart() method and just use
View view = getView().findViewById(R.id.something);
or Declare any View using getView().findViewById method in onStart()
Declare click listener on view by anyView.setOnClickListener(this);
Try to shift your accessing views to the onViewCreated method of fragment because sometimes when you try to access the views in onCreate method they are not rendered at the time resulting null pointer exception.
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
View something = findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
Agreed, this is a typical error because people often don't really understand how Fragments work when they begin working on Android development. To alleviate confusion, I created a simple example code that I originally posted on Application is stopped in android emulator , but I posted it here as well.
An example is the following:
public class ContainerActivity extends FragmentActivity implements ExampleFragment.Callback
{
#Override
public void onCreate(Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
this.setContentView(R.layout.activity_container);
if (saveInstanceState == null)
{
getSupportFragmentManager().beginTransaction()
.add(R.id.activity_container_container, new ExampleFragment())
.addToBackStack(null)
.commit();
}
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
}
#Override
public void exampleFragmentCallback()
{
Toast.makeText(this, "Hello!", Toast.LENGTH_LONG).show();
}
}
activity_container.xml:
<?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>
ExampleFragment:
public class ExampleFragment extends Fragment implements View.OnClickListener
{
public static interface Callback
{
void exampleFragmentCallback();
}
private Button btnOne;
private Button btnTwo;
private Button btnThree;
private Callback callback;
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try
{
this.callback = (Callback) activity;
}
catch (ClassCastException e)
{
Log.e(this.getClass().getSimpleName(), "Activity must implement Callback interface.", e);
throw e;
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_example, container, false);
btnOne = (Button) rootView.findViewById(R.id.example_button_one);
btnTwo = (Button) rootView.findViewById(R.id.example_button_two);
btnThree = (Button) rootView.findViewById(R.id.example_button_three);
btnOne.setOnClickListener(this);
btnTwo.setOnClickListener(this);
btnThree.setOnClickListener(this);
return rootView;
}
#Override
public void onClick(View v)
{
if (btnOne == v)
{
Toast.makeText(getActivity(), "One.", Toast.LENGTH_LONG).show();
}
else if (btnTwo == v)
{
Toast.makeText(getActivity(), "Two.", Toast.LENGTH_LONG).show();
}
else if (btnThree == v)
{
callback.exampleFragmentCallback();
}
}
}
fragment_example.xml:
<?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" >
<Button
android:id="#+id/example_button_one"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="30dp"
android:text="#string/hello"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"/>
<Button
android:id="#+id/example_button_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/example_button_one"
android:layout_alignRight="#+id/example_button_one"
android:layout_below="#+id/example_button_one"
android:layout_marginTop="30dp"
android:text="#string/hello" />
<Button
android:id="#+id/example_button_three"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/example_button_two"
android:layout_alignRight="#+id/example_button_two"
android:layout_below="#+id/example_button_two"
android:layout_marginTop="30dp"
android:text="#string/hello" />
</RelativeLayout>
And that should be a valid example, it shows how you can use an Activity to display a Fragment, and handle events in that Fragment. And also how to communicate with the containing Activity.
The view "something" is in fragment and not in activity, so instead of accessing it in activity you must access it in the fragment class like
In PlaceholderFragment.class
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_main, container,
false);
View something = root .findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... });
return root;
}
You are trying to access UI elements in the onCreate() but , it is too early to access them , since in fragment views can be created in onCreateView() method.
And onActivityCreated() method is reliable to handle any actions on them, since activity is fully loaded in this state.
Add the following in your activity_main.xml
<fragment
android:id="#+id/myFragment"
android:name="packagename.MainActivity$PlaceholderFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</fragment>
Since you have declared your View in the fragment_main.xml,move that piece of code where you get the NPE in the onCreateView() method of the fragment.
This should solve the issue.
in the posted code above in the question there is a problem :
you are using R.layout.activity_main in oncreate method, but the xml files name is "fragment_main.xml" , means you are trying to get the view of fragment_main.xml file which is not being shown so it gives null pointer exception. change the code like :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);// your xml layout ,where the views are
View something = findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
You have to remember important thing is :
NullPointerException occurs when you have declared your variable and trying to retreive its value before assigning value to it.
Use onViewCreated() Method whenever using or calling views from fragments.
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
View v = view.findViewById(R.id.whatever)
}
I've got the same NullPointerException initializing a listener after calling findViewById() onCreate() and onCreateView() methods.
But when I've used the onActivityCreated(Bundle savedInstanceState) {...} it works. So, I could access the GroupView and set my listener.
I hope it be helpful.
Most popular library for finding views which is used by almost every developer.
ButterKnife
As I can their are enough answers explaining finding views with proper methodology. But if you are android developer and code frequently on daily basis then you can use butter-knife which saves a lot time in finding views and you don't have write code for it, With in 2-3 steps you can find views in milliseconds.
Add dependency in app level gradle:
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
Add plugin for butter knife:
File -> Settings -> plugins->
Then search for Android ButterKnife Zelezny and install plugin and restart your studio and you are done with it.
Now just go to Oncreate method of your activity and right click on your layout_name and tap on generate button and select butterknife injection option and your views references will be automatically created like mention below:
#BindView(R.id.rv_featured_artist)
ViewPager rvFeaturedArtist;
#BindView(R.id.indicator)
PageIndicator indicator;
#BindView(R.id.rv_artist)
RecyclerView rvArtist;
#BindView(R.id.nsv)
NestedScrollingView nsv;
#BindView(R.id.btn_filter)
Button btnFilter;
Related
I'm trying to update a TextView object's text by calling the setText() method. I provide a string value directly to it, but I can't get it to update on the UI of the app running on the Emulator.
This is taking place on a fragment (one of the fragments automatically generated when a project with a simple activity is created on Android Studio)
A couple points about my situation thus far:
I tried calling the setText() method with the runOnUiThread "pattern" to no avail.
getActivity().runOnUiThread(new Runnable()
{
#Override
public void run()
{
textView.setText("Service online");
}
});
I checked property mText of the TextView instance. It IS UPDATED. It just doesn't update on the UI :/
In short, no matter what I try to do, the UI element sticks to whatever string value is set on the XML Fragment file (or no value, if I delete the android:text attribute). Other posts on Stack Overflow similar to this issue did not help me either. Any idea what it could be?
Also, I'm posting the entire fragment related java code:
public class FirstFragment extends Fragment
{
public Gson serializer;
public TextView textView;
private NetworkManager networkManager;
private boolean serviceIsBound;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_first, container, false);
textView = (TextView) view.findViewById(R.id.main_window);
textView.setText(R.string.app_name);
return inflater.inflate(R.layout.fragment_first, container, false);
}
#Override
public void onStart() {
super.onStart();
Intent bindIntent = new Intent(getActivity(), NetworkManager.class);
getActivity().bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
#Override
public void onStop()
{
super.onStop();
getActivity().unbindService(serviceConnection);
serviceIsBound = false;
}
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.findViewById(R.id.button_first).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.v("DEV UPDATE", "Starting Request ");
if (serviceIsBound)
{
GetAPIStatusResult result = networkManager.GetAPIStatus();
if (result.GetStatus())
{
Log.v("REQUEST RESULT", "API is Fine");
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
textView.setText("Service online");
}
});
}
else
{
Log.v("REQUEST RESULT", "API is Down or a problem occurred");
textView.setText("Service down");
}
}
}
});
}
private ServiceConnection serviceConnection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName className, IBinder service)
{
NetworkManager.NetworkManagerServiceBinder binder = (NetworkManager.NetworkManagerServiceBinder) service;
networkManager = binder.GetService();
serviceIsBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg)
{
serviceIsBound = false;
}
};
}
The associated XML for the fragment:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">
<TextView
android:id="#+id/main_window"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Here will appear API status"
app:layout_constraintBottom_toTopOf="#id/button_first"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/button_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="SendRequest"
android:text="#string/SendRequest"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/main_window" />
</androidx.constraintlayout.widget.ConstraintLayout>
As user Cheticamp commented, I had an issue on my onCreateView() method, where I was calling the infalter.inflate() method twice, and not returning my view object.
I replaced the second inflate() method call with a return of my view object and it immediately worked! My UI was now being updated as expected!
You're trying to reference a view that belongs to the activity. If you want to update something in the activity you need to look at other methods rather than trying to directly reference the views.
A good place to start would be an interface you pass to the fragment that is created by the activity. Call the interface method from the fragment when you want to set the next. Then let the activity handle the updating. This is cleaner too as each view is responsible for its own elements only.
You also don't need to use runOnUIThread as onViewCreated isn't an ansychronos function you're already on the UI thread anyway.
Hopefully that helps.
Obviously, I am new to Android - XML programming... So I have a navigation drawer, and once item is selected from the drawer, a corresponding fragment on the right side will display. Inside that fragment, I have linear layouts. I'd like to get redirected to another activity once that linear layout has been tapped. I was able to make it work on activities, by using android:onClick on XML file, but can't make it work on fragment. Somebody help me please.
App interface, refer to this image:
http://i.stack.imgur.com/u6dHi.jpg
Code:
fragment_smart.xml - the display once item's selected. I am trying to use the onClick on xml.
<LinearLayout
android:id="#+id/linearLayoutsmart1"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_below="#+id/smart_title"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:orientation="horizontal"
android:weightSum="1"
android:onClick="smart_recommended_link">
Here's my Java code:
public class FragmentSmart extends Fragment {
public static final String TAG = "stats";
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View myfragment = inflater.inflate(R.layout.fragment_smart, container, false);
return myfragment;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
public void smart_recommended_link(View view) {
Intent smartRecommendedIntent = new Intent(this, SmartRecommended.class);
startActivity(smartRecommendedIntent);
}
}
The app is crashing when I clicked on the linearlayout using this code. What's the best thing to do here? Thank you!
For fragments you need to add the listener programmatically:
public class MyFragment extends Fragment implements View.OnClickListener {
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.findViewById(R.id.my_layout).setOnClickListener(this);
}
#Override
public void onClick(View v) {
// Handle click based on v.getId()
}
}
First of all, linear layouts cant trigger onClick events by default.
Check this answer for more information: LinearLayout onClick.
You can get the LinearLayout from the view in your onCreateView() like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View myfragment = inflater.inflate(R.layout.fragment_smart, container, false);
LinearLayout layout = (LinearLayout)myFragment.findViewById(R.id.linearLayoutsmart1);
// here you can set a listener of any type you want to the layout
return myfragment;
}
In fragments, you must use getActivity() method instead of this to reference the activity that the fragment is attached to.
public void smart_recommended_link(View view) {
Intent smartRecommendedIntent = new Intent(getActivity(), SmartRecommended.class);
startActivity(smartRecommendedIntent);
}
I have a single activity with a navigation drawer (the basic one provided by Eclipse new app wizard). I have a FrameLayout as a container for the different fragments of the app, which are replaced when selecting an item in the navigation drawer. They are also added to the BackStack.
These fragments contain a LinearLayout, which has some EditTexts and a Button. If the button is pressed, a new LinearLayout is created and a couple TextViews are added to it with the content of the EditTexts. The user can repeat this option more than once, so I cannot tell how many LinearLayouts I'll need, therefore I need to add them programmatically.
One of these fragments xml:
<LinearLayout
android:id="#+id/pen_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/new_pen_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/activity_vertical_margin"
android:background="#drawable/border"
android:orientation="vertical"
android:paddingBottom="#dimen/home_section_margin_bottom"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/home_section_margin_top" >
<EditText
android:id="#+id/new_pen_round"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:hint="#string/new_pen_round_hint"
android:textSize="#dimen/normal_text_size" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="2" >
<Button
android:id="#+id/new_pen_cancel_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/new_item_button_margin_right"
android:layout_weight="1"
android:background="#drawable/button_bg"
android:paddingBottom="#dimen/new_item_button_padding_bottom"
android:paddingTop="#dimen/new_item_button_padding_top"
android:text="#string/new_item_cancel_button"
android:textSize="#dimen/normal_text_size" />
<Button
android:id="#+id/new_pen_insert_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/new_item_button_margin_left"
android:layout_weight="1"
android:background="#drawable/button_bg"
android:paddingBottom="#dimen/new_item_button_padding_bottom"
android:paddingTop="#dimen/new_item_button_padding_top"
android:text="#string/new_pen_insert_button"
android:textSize="#dimen/normal_text_size" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
There are actually many other EditTexts but I removed them here to keep it short, the result is the same. It's java file:
public class PenaltiesFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_penalties, container, false);
Button insertNewPen = (Button) view.findViewById(R.id.new_pen_insert_button);
insertNewPen.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
TextView round = (TextView) getActivity().findViewById(R.id.new_pen_round);
LinearLayout layout = (LinearLayout) getActivity().findViewById(R.id.pen_layout);
int numChilds = layout.getChildCount();
CustomPenaltyLayout penalty = new CustomPenaltyLayout(getActivity(), round.getText());
layout.addView(penalty, numChilds - 1);
}
});
return view;
}
}
I removed some useless methods, which are just the default ones. CustomPenaltyLayoutis a subclass of LinearLayout which I created, it just creates some TextViews and adds them to itself.
Everything works fine here. The user inserts data in the EditText, presses the Insert button and a new layout is created and added in the fragment.
What I want to achieve is: say that I open the navigation drawer and select another page, the fragment gets replaced and if I go back to this fragment (via navigation drawer or via Back button) I want the text, that the user added, to be still there.
I do not call PenaltiesFragment.newInstance() everytime I switch back to this fragment, I instead create the PenaltiesFragment object once and keep using that one. This is what I do:
Fragment fragment;
switch (newContent) {
// various cases
case PEN:
if(penFragment == null) // penFragment is a private field of the Main Activity
penFragment = PenaltiesFragment.newInstance();
fragment = penFragment;
break;
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment)
.addToBackStack("fragment back")
.commit();
I understand that onCreateView() is called again when the fragment is reloaded, right? So that is probably why a new, blank fragment is what I see. But how do I get the inserted CustomPenaltyLayout back? I cannot create it in the onCreateView() method.
I found a solution to my problem. I replaced the default FrameLayout that Android automatically created as a container for my fragments, with a ViewPager, then created a FragmentPagerAdapter like this:
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment;
switch (position) {
// ...other cases
case PEN:
fragment = PenaltiesFragment.newInstance();
break;
// ...other cases
}
return fragment;
}
#Override
public int getCount() {
return 6;
}
}
Then the only thing left to do to keep all the views at all times has been to add this line to my activity onCreate method.
mPager.setOffscreenPageLimit(5);
See the documentation for details on how this method works.
This way, though, I had to reimplement all the back button logic, but it's still simple, and this is how I did it: I create a java.util.Stack<Integer> object, add fragment numbers to it (except when you use the back button, see below), and override onBackPressed() to make it pop the last viewed fragment instead of using the back stack, when my history stack is not empty.
You want to avoid pushing elements on the Stack when you press the back button, otherwise you will get stuck between two fragments if you keep using the back button, instead of eventually exiting.
My code:
MyAdapter mAdapter;
ViewPager mPager;
Stack<Integer> pageHistory;
int currentPage;
boolean saveToHistory;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.container);
mPager.setAdapter(mAdapter);
mPager.setOffscreenPageLimit(5);
pageHistory = new Stack<Integer>();
mPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
if(saveToHistory)
pageHistory.push(Integer.valueOf(currentPage));
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
saveToHistory = true;
}
#Override
public void onBackPressed() {
if(pageHistory.empty())
super.onBackPressed();
else {
saveToHistory = false;
mPager.setCurrentItem(pageHistory.pop().intValue());
saveToHistory = true;
}
};
I have an Android app where i'm using tabs (with ActionBarSherlock). So my main activity creates the tabs for me and from there i load in the fragment layouts.
In my MainActivity.java i create a tab (this is just a snippet):
mTabsAdapter = new TabsAdapter(this, mViewPager);
mTabsAdapter.addTab(
bar.newTab().setText("Fragment 1"),
MainMenuFragment.class, null);
My MainMenu.java looks like this:
public class MainMenuFragment extends SherlockFragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.mainmenu_fragment, container, false);
return view;
}
public void showMainMenu(View view)
{
Log.e("app", "olol: button!"); // never called!!
}
}
And this is mainmenu_fragment
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000" >
<Button
android:id="#+id/btnMenu"
android:layout_width="170dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="41dp"
android:text="#string/mainmenu"
android:onClick="showMainMenu" />
</RelativeLayout>
Now all i have to do is place the method showMainMenu(View view) somewhere. I thought this would go in the corresponding java file (MainMenuFragment.java in my case). But it only works when i put the method in the MainAvtivity.java file.
That means that all my button actions from all kinds of fragment layouts will go in one (the main) java file. Why can't i simply place it inside the java file that calls the Fragment layout..??
The short answers is (like already pointed out), you can't.
The only way to do this is by creating an onClick even listener. In the MainMenuFragment.java in the onCreate method, do something like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.scan_fragment, container, false);
Button menuButton = (Button)view.findViewById(R.id.btnMenu);
menuButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.e("app", "onclick listener");
}
});
return view;
}
You can remove the onClick attribute from the layout xml.
Now all I have to do is place the method showMainMenu() somewhere - this is wrong. Please, refer to the documentation of android:onClick :
For instance, if you specify android:onClick="sayHello", you must declare a public void sayHello(View v) method of your context (typically, your Activity).
Seems You cannot place your callback somewhere, because framework won't be able to find that callback. If You're defining it inside Activity (which is actually, a Context), it's possible for View to find it back. Actually, View.java contains the following code:
case R.styleable.View_onClick:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot " + "be used within a restricted context");
}
final String handlerName = a.getString(attr);
mHandler = getContext().getClass().getMethod(handlerName, View.class);
...
mHandler.invoke(getContext(), View.this);
Seems it's the only possible way to call callback defined in layout file with current android:onClick attribute specification.
I solved this using the following:
Fragment xml contains
android:onClick="myFunction"
Activity Contains
public void myFunction(View v)
{ getSupportFragmentManager().findViewById/Tag(...).myFunction(v); }
Fragment Code can then implement as below to have access to local data
public void myFunction(View v) {...}
Hope this helps.
The gist of the issue is this: I'm trying to Launch a DialogFragment from a FragmentActivity. This DialogFragment's view contains a FrameLayout which I would like to populate with a Fragment. Basically the FragmentActivity launches the DialogFragment, then the DialogFragment populates it's FrameLayout with a Fragment. I've scoured the internet for tutorials and I've pieced together something that (in my mind) should work. However, no matter what I try I continuously get errors. This is what I have so far:
This is my FragmentActivity's layout (file name is "activity_interact"):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/activity" >
<Button
android:id="#+id/btnLaunchDialog"
style="#style/btn" />
This is my DialogFragment's layout (file name is "dialog_overview"):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/dialog" >
<FrameLayout
android:id="#+id/frameDisplay"
style="#style/frame" />
This is my Fragment's layout (file name is "fragment_stats"):
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/table" >
<TableRow style="#style/table" >
<TextView
style="#style/display"
android:gravity="right"
android:text="#string/textStr" />
</TableRow>
Here is the java code for my FragmentActivity:
public class ActivityInteract extends FragmentActivity implements
OnClickListener {
Button btnLaunchDialog;
#Override
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.activity_interact);
btnLaunchDialog = (Button) findViewById(R.id.btnLaunchDialog);
btnLaunchDialog.setOnClickListener(this);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnLaunchDialog:
FragmentManager fm = getSupportFragmentManager();
DialogOverview dialogOverview = new DialogOverview();
dialogOverview.show(fm, "dialog_overview");
break;
}
}
}
Here is my DialogFragment's code:
public class DialogOverview extends DialogFragment implements OnClickListener {
public DialogOverview() {
}
#Override
public View onCreateView(LayoutInflater li, ViewGroup vg, Bundle b) {
View view = li.inflate(R.layout.dialog_overview, vg);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.frameDisplay, new FragmentStats());
ft.commit();
return view;
}
}
Here is my Fragment's code:
public class FragmentStats extends Fragment {
#Override
public View onCreateView(LayoutInflater li, ViewGroup vg, Bundle b) {
View view = li.inflate(R.layout.fragment_stats, vg);
return view;
}
}
And finally, here is the logcat error:
06-11 10:07:29.382: E/AndroidRuntime(30013): java.lang.IllegalArgumentException: No view found for id 0x7f060003 for fragment FragmentStats{4169c928 #1 id=0x7f060003}
I can see that it's saying that I don't have a view for the Fragment, but I do...(or do I?) I'm lost here, any help would be appreciated. Also, am I going about this the right way? Would it be more efficient to re-use a FragmentManager? (i.e. pass it from the FragmentActivity into the DialogFragment)
Update: I removed the code to load my Fragment into the DialogFragment and the DialogFragment displays without issue now. So obviously (as the logcat error suggests) there is something wrong with my Fragment itself...however, it matches examples that I've seen on the internet. Which is making me wonder: Is there an issue with nesting fragments in this way? A FragmentActivity displaying a DialogFragment that displays a Fragment makes me want to quip that "we can't go any deeper" but I don't know. Could I nest more fragments?
Actually, you CAN add nested fragment to a DialogFragment, BUT it cannot be based on a wrapped Dialog.
Instead of overriding onCreateDialog, inflate the View that contains the ViewGroup that will use the Fragment in onCreateView.
A consequence of this is that you cannot use a DialogFragment that wraps an AlertDialog - so if you want positive and negative buttons you need to manually create them in the content view.
Also, keep in mind that you cannot set the fragment in the XML. You need to declare the container view in XML and perform the fragment transaction programmatically.
public class MyDialogFragment extends DialogFragment {
[...]
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.dialog_layout, container);
if (savedInstanceState == null) {
final ChildFragment fragment = [...];
getChildFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit();
}
return view;
}
}