I've got an activity that loads 2 different fragments and switches between them when needed. The problem is I cant acces any elements that are in the fragments. I need to be able to set the typeface and text in the toolbar as well as be able to set the checkboxes.
fragment XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="3">
<include
layout="#layout/toolbar_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:id="#+id/cycle_button"
android:button="#null"
android:background="#drawable/cycle"
android:layout_weight="1"
android:layout_gravity="center_vertical" />
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:id="#+id/walk_button"
android:button="#null"
android:background="#drawable/walk"
android:layout_weight="1"
android:layout_gravity="center_vertical" />
java file:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowTitleEnabled(false);
TextView appTitle = (TextView) toolbar.findViewById(R.id.toolbar_title);
appTitle.setText("title");
CheckBox cycle = (CheckBox) getView().findViewById(R.id.cycle_button);
CheckBox walk = (CheckBox) findViewById(R.id.walk_button);
}
the findViewById doesn't work on the fragment and it just keeps returning null.
You could send the desired values for your elements from your activity to the fragment when you're instantiating them.
But there are several methods you can choose to communicate between your activity and a fragment:
Therefore have a look at the guide Communicating with Fragments
1.) Bundle - Activity can construct a fragment and set arguments
2.) Methods - Activity can call methods on a fragment instance
3.) Listener - Fragment can fire listener events on an activity via an interface
Especially a Fragment with Arguments would be helpful to you:
In certain cases, your fragment may want to accept certain arguments.
A common pattern is to create a static newInstance method for creating
a Fragment with arguments. This is because a Fragment must have only a
constructor with no arguments. Instead, we want to use the
setArguments method such as:
public class DemoFragment extends Fragment {
// Creates a new fragment given an int and title
// DemoFragment.newInstance(5, "Hello");
public static DemoFragment newInstance(int someInt, String someTitle) {
DemoFragment fragmentDemo = new DemoFragment();
Bundle args = new Bundle();
args.putInt("someInt", someInt);
args.putString("someTitle", someTitle);
fragmentDemo.setArguments(args);
return fragmentDemo;
}
}
This sets certain arguments into the Fragment for later access within > onCreate. You can access the arguments later by using:
public class DemoFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get back arguments
int SomeInt = getArguments().getInt("someInt", 0);
String someTitle = getArguments().getString("someTitle", "");
}
}
Now we can load a fragment dynamically in an Activity with:
// Within the activity
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
DemoFragment fragmentDemo = DemoFragment.newInstance(5, "my title");
ft.replace(R.id.your_placeholder, fragmentDemo);
ft.commit();
This pattern makes passing arguments to fragments for initialization fairly straightforward.
After instantiating your fragment you can go ahead and retrieve your desired views from the fragment's layout during inflating and set the values included in the fragment's arguments.
public class DemoFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
final View view = inflater.inflate(R.layout.your_fragment_layout, container, false);
// Retrieve views here via findElementById etc.. and set the attributes from the fragment's arguments
return view;
}
}
In Fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle avedInstanceState) {
View rootView = inflater.inflate(R.layout.yourlayout, null);
Toolbar toolbar = (Toolbar) rootView.findViewById(R.id.toolbar);
//...[other views get here]...
return rootView;
}
In fragment view creation is done in .onCreateView() method.
You can use ViewHolder pattern- save references to all views for accessing them in a future.
If you want to access activity from fragment, define interface in fragment and implement it in activity, so you can make a call like:
((MyInterface) getActivity()).changeToolbar()
For passing values into fragment use static method when you create fragement. Bear in mind, that data use pass must implement Parceleable interface
Related
I've studied all the fragment getArgument null pointer questions already answered and I can't seem to find a solution that works for me. My fragment is functioning properly (buttons doing proper function) except that it does not get any arguments passed in. I have set up my constructor and my calls to it various ways, and cannot avoid the NullPoint. I think it must be something around the lifecycle of the fragment, and the fragment appearing onscreen somehow not being the initiated one, but I can't get to the bottom of it. Any help will be greatly appreciated!
Fragment code:
public class BottomBar extends Fragment {
ImageButton goHome, goPending, goActive, goHelp, goChallenge;
BottomListener activityCallback;
int chalType;
String title;
String text;
//constructor log.d prints out that it has been reached
public static BottomBar init(int chalType, String ttl, String txt){
BottomBar bot = new BottomBar();
Bundle args = new Bundle();
args.putInt("chalType", chalType);
args.putString("title", ttl);
args.putString("text", txt);
bot.setArguments(args);
Log.d("Bottom Bar Init", "called");
return bot;
}
#Override
public void onAttach(Activity activity){
super.onAttach(activity);
try{
activityCallback = (BottomListener) activity;
} catch (ClassCastException e){
Log.d("bottom bar onAttach","class cast");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.bottombarfrag, container, false);
goHome = (ImageButton) view.findViewById(R.id.goHome);
goPending = (ImageButton) view.findViewById(R.id.goPending);
goChallenge = (ImageButton) view.findViewById(R.id.challenge);
goActive = (ImageButton) view.findViewById(R.id.goActive);
goHelp = (ImageButton) view.findViewById(R.id.getInfo);
Bundle argsin = getArguments();
//never enters this loop... argsin always null
if(argsin!=null){
Log.d("bottombar oncreate", "argsin found");
chalType = argsin.getInt("chalType", 0);
title = argsin.getString("title");
text = argsin.getString("text");
}
//.... button activities, functioning properly ....
return view;
}
and main activity code that calls it:
public class MainActivity extends FragmentActivity {
Button btn, paydemo;
TextView tv1, tv2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String ttl = "Trial1 Home";
String txt = "string1text";
BottomBar bot = BottomBar.init(0, ttl, txt);
getSupportFragmentManager().beginTransaction().add(android.R.id.content, bot).commit();
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.clickbtn);
paydemo = (Button) findViewById(R.id.PayPalDemo);
tv1 = (TextView) findViewById(R.id.textView1);
tv2 = (TextView) findViewById(R.id.textView3);
tv1.setText("try1");
tv2.setText("");
// ...other button actions
}
Follow up:
I've found that I can get the fragment that I want, however it appears as a second instance on the fragment (located on top of display, not where desired), and the original instance is there, but not functioning properly (still with no args). I think that when I initialize the fragment in my activity I am not overwriting the existing fragment that is added by the xml. Maybe I need to initialize the Support Fragment Manager and link it to the proper instance to replace it? Thoughts?
xml where originally intended fragment is located (this is the fragment I want, but not the fragment that is being communicated with).
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Second Layout"
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="#+id/retBut"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Return" />
<fragment
android:id="#+id/bottombar"
android:name="com.example.trial1.BottomBar"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:layout_marginTop="0dp"
/>
</LinearLayout>
This one is tricky. Take a look at your activity: you are setting fragment and after that calling setContentView. setContentView removes all views from android.R.id.content, i think this breaks fragment's lifecycle somehow. Moreover it looks like a concept mistake: setting fragment and wiping it out after that. I suggest making a fragment container inside R.layout.activity_main, set activity content and after that put you fragment in this container.
So I came up with a functioning answer. I'm not convinced it's the proper way to do it, but it is working.
I created an inherited class for all the classes that reference this fragment, and created a set of public variables to represents the arguments passed to the fragment. I then had each class write the public variables in the super class instead of initiating a new fragment. The fragment then referenced the same public variables to pull information from.
Again, don't think it is the proper way to do it - the argument passing exists to be efficient - but it is working for me now. If anyone has a better idea for how I could accomplish this I'm all ears.
Thanks for all the help
I have a Fragment, and I want to set that whole fragment as root view of my activity. I have everything ready, and I'm instantiating my fragment programatically. I've tried (in my activity):
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FeedFragment fragment = [...];
setContentView(fragment.getView());
}
But I've got a null pointer exception. In other words, how can I make my fragment act like an activity? I only target ICS+, I don't need to support older versions, if it makes any difference.
Try this
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.all_lecturer_frag, container, false);
......
return rootView;
}
A Fragment, by design, is intended to be a tool to help you reuse screen space and as such, fragments have to be present inside a container. So while a fragment cannot technically be a root view, you can have a fragment be the only view inside the Activity. For this, you should inflate the view for your fragment programmatically inside the onCreateView() method of the fragment. then you could have something like this in your activity's layout xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.package.fragment_name
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
And then, within your activity, all you have to do is:
setContentView(R.layout.main);
Since, the fragment is defined in the layout xml, it cannot be removed from the activity's layout (although the layout itself can be changed) and is tied to it.
Also, on a side note, notice that the root view is a FrameLayout and not the fragment itself. But in this manner, your fragment can be tied to the activity. But don't forget that the Fragment will still retain it's lifecycle separate from the activity's.
EDIT: If you need to create your fragment instance programmatically, you have to do:
getFragmentManager().beginTransaction().add(R.id.frame_layout, your_fragment).commit();
This is the only way to add your fragment programmatically. But also keep in mind that the Fragment's layout is not tied to the activity's layout. But you can use the Fragment's lifecycle to behave similarly as an Activity.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.xxx);
//initializations...
if (savedInstanceState == null) {
// During initial setup, plug in the fragment.
YourFragment details = new YourFragment();
getFragmentManager().beginTransaction().add(R.id.your_root_frame_layout, details).commit();
}
}
I asked a question about how to add a Fragment that contained something drawn using OpenGL ES
here. Someone was kind enough to answer that for me, but unfortunately today I encountered another problem. As I mentioned in my other question, my purpose is to add other Fragments next to the one that contains OpenGL and because I am a beginner in Android development I don't seem to understand how this is done.
Here's what I want: right now, my code is exactly the one from my other question. I also have this Fragment:
public class TextFragment extends Fragment
{
private TextView textview;
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.text_fragment,
container, false);
textview = (TextView) view.findViewById(R.id.textView1);
return view;
}
}
together with its layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/frag2">
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Fragment Two"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
and I want to add this to my main activity, where right now I only have the OpenGL Fragment. Here's my main activity:
public class FragmentExampleActivity extends FragmentActivity implements ToolbarFragment.ToolbarListener
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
if (savedInstanceState == null)
{
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container, new OpenGLES20ActivityFrag())
.addToBackStack(null)
.commit();
}
}
}
and the Fragment that has OpenGL in it and that I have already added to the main activity:
public class OpenGLES20ActivityFrag extends Fragment
{
private GLSurfaceView mGLView;
public OpenGLES20ActivityFrag()
{
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
mGLView = new MyGLSurfaceView(this.getActivity());
return mGLView;
}
}
What I tried and failed: using another call to the .add method inside getSupportFragmentManager() or adapting this bit of code for my second Fragment
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frag2, TextFragment)
.addToBackStack(null)
.commit();
that gave me an 'expression expected' error in the add method. I tried adding this constructor to my second Fragment
public TextFragment()
{
super();
}
and then inside the add method I put .add(R.id.frag2, new TextFragment())
which still didn't work.
In order to dynamically add a Fragment to a layout, what you need is a container (like in your case, it was R.id.main_container). Thus, if you want to add multiple fragments, what you need is multiple containers, like so:
<LinearLayout android:orientation="vertical" android:layout_height="fill_parent" android:layout_width="fill_parent">
<FrameLayout android:id="#+id/main_container_1" android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent"/>
<FrameLayout android:id="#+id/main_container_2" android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent"/>
</LinearLayout>
(this snippet is from How to split the screen with two equal LinearLayouts? )
And then you would need to add the two Fragments:
if (savedInstanceState == null)
{
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container_1, new OpenGLES20ActivityFrag())
.commit();
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container_2, new TextFragment())
.commit();
}
Please note that with multiple Fragments on a single Activity, it's better not to add them to the backstack, because then you'd have to press Back as many times as there are Fragments, and in this case it's more reasonable to navigate between the "views" or states of the application with Activities, and not by replacing the Fragments.
(considering the backstack doesn't change, I don't think the backstack listener needs to be removed, but that's done so that if you press Back, you don't end the Activity, but the Fragments within it first if you have them added to the backstack. But the Activity doesn't end when it contains no fragments, and you'd have an "empty view", hence why that was added.)
Please also check if the rotation works and data is maintained even after the activity reconstruction, because there's a chance you need to set the retain instance state to true explicitly on the Fragments for that to work.
i have an application that is only fragment based, and actually (i'm just creating basic content) i have only one fragment, declared directly into the layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:name="com.italialinux.fragments.MainFragment"
android:id="#+id/main_screen_fragment"
android:layout_width="0dp"
android:layout_height="match_parent"
/>
</FrameLayout>
And this layout is loaded inside main activity, with the following code:
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_screen);
}
}
And the MainFragment class is the following:
public class MainFragment extends Fragment {
final static private long ONE_SECOND = 1000;
final static private long TWENTY_SECONDS = ONE_SECOND * 20;
private final static String TAG = "MainFragment";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
View view = inflater.inflate(R.layout.new_reminder, container, false);
return view;
}
And the new_reminder layout contains just two EditText and several labels.
The problem is that i cannot inflate the layout, since the container variable passed in onCreateView is null.
I saw many questions on SO, like the following:
Android Fragment is given a null container in onCreateView()
And all says that i need to use a transaction, but what i don't understood is:
if i have a fragment that is immutable, in a layout, and doesn't need to change (like in that case, and like in many examples where you have a ListFragment that contains a list of items), how i can inflate the layout inside the current view?
With ListFragment it works, but what if i don't want to use it?
The onCreateView method is called correctly.
In your main layout, try using android:layout_width="match_parent" instead of android:layout_width="0dp".
And you shouldn't be calling onSaveInstanceState(savedInstanceState) in onCreate; it is called by the framework when your fragment is going down to allow it to save its state.
I'm new to Android developing and of course on Fragments.
I want to access the controls of my fragment in main activity but 'findViewById' returns null.
without fragment the code works fine.
Here's part of my code:
The fragment:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:ignore="HardcodedText" >
<EditText
android:id="#+id/txtXML"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:ems="10"
android:scrollbars="vertical">
</EditText>
</LinearLayout>
the onCreate of MainActivity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.main);
this.initialisePaging();
EditText txtXML = (EditText) findViewById(R.id.txtXML);}
on this point the txtXML is null.
What's Missing in my code or what should I do?
Try like this on your fragments on onCreateView
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (container == null) {
return null;
}
LinearLayout ll = (LinearLayout )inflater.inflate(R.layout.tab_frag1_layout, container, false);
EditText txtXML = (EditText) ll.findViewById(R.id.txtXML);
return ll;
}
You should inflate the layout of the fragment on onCreateView method of the Fragment then you can simply access it's elements with findViewById on your Activity.
In this Example my fragment layout is a LinearLayout so I Cast the inflate result to LinearLayout.
public class FrgResults extends Fragment
{
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
//some code
LinearLayout ll = (LinearLayout)inflater.inflate(R.layout.frg_result, container, false);
//some code
return ll;
}
}
I'm late, but for anyone else having this issue. You should be inflating your view in the onCreateView method. Then override the onCreateActivity method and you can use getView().findViewById there.
#Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.inflate(R.layout.fragment, container, false);
}
You can't access the the view of fragment in activity class by findViewById instead what you can do is...
You must have an object of Fragment class in you activity file, right? create getter method of EditText class in that fragment class and access that method in your activity.
create a call back in Fragment class on the event where you need the Edittext obj.
1) Try this:
Eclipse menu -> Project -> Clean...
update
2) If you have 2 or more instances of 'main' layout, check if all of them have a view with 'txtXML' id
3)
A Fragment is a piece of an application's user interface or behavior that can be placed in an Activity. Interaction with fragments is done through FragmentManager, which can be obtained via Activity.getFragmentManager() and Fragment.getFragmentManager().
The Fragment class can be used many ways to achieve a wide variety of results. It is core, it represents a particular operation or interface that is running within a larger Activity. A Fragment is closely tied to the Activity it is in, and can not be used apart from one. Though Fragment defines its own lifecycle, that lifecycle is dependent on its activity: if the activity is stopped, no fragments inside of it can be started; when the activity is destroyed, all fragments will be destroyed.
Study this. you must use FragmentManager.
If you want use findViewById as you use at activities onCreate, you can simply put all in overrided method onActivityCreated.
All the answers above tell you how you should "return the layout" but don't exactly tell you how to reference the layout that was returned so I was unable to use any of the solutions given. I used a different approach to solve the problem. In the Fragment class that handles the fragment, got to the onViewCreated() class and create a context variable in it that saves the context of the parent activity (main activity in my case).
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Context fragmentContext = (MainActivity) view.getContext();
}
Once that is done, you can use the new context to access items on your fragment from inside the onViewCreated() method.
EditText editText = context.findViewById(R.id.textXML);