I have two fragments, MyFirst fragment and MySecond fragment. MySecond fragment extends from MyFirst fragment.
classed are like this:
public class MyFirstFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
// Check view and map, request to recreate if each of them is null
if(MyFirstFragment.this.getView() == null || googleMap == null) {
Toast.makeText(getActivity().getApplicationContext(), R.string.my_message, Toast.LENGTH_LONG).show();
return;
}
}
}
public class MySecondFragment extends MyFirstFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
}
}
My problem is super.onActivityCreated(savedInstanceState); in onActivityCreated() method of MySecondFragment calls its super. Since I want to hide functionality of this method in super class (Which is not useful for this fragment) I have added onActivityCreated() method in MySecondFragment.
The problem is if I remove this line then I have run-time error that throws "SuperNotCalledException".
What you think? seems I have to extend Fragment class instead of extending MyFirstFragment.
I have some variables in MyFirstFragment that I need them in MySecondFragment.
You can write a new Function testFunction(Bundle savedInstanceState) in your MyFirstFragment in which call super.onActivityCreated(savedInstanceState);
And in your MySecondFragment's onActivityCreated call this testFunction(Bundle savedInstanceState) rather than super.onActivityCreated(savedInstanceState);
i.e.
in MyFirstFragment
testFunction(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
}
in MySecondFragment
public void onActivityCreated(Bundle savedInstanceState) {
super.testFunction(savedInstanceState);
...
}
i don't clearly understand your basic requirment to bypass the MyFirstFragment's onActivityCreated but you can do this way.
You need to call the Fragment's implementation regardless. If you don't want to call MyFirstFragment's implementation there's a few solutions:
The cleanest one, give MyFirstFragment and MySecondFragment a common ancestor, which will be pretty much MyFirstFragment , except for onActivityCreated(). Both activities will inherit directly from it.
call super, but add a variable to the bundle to let MyFirstFragment know that its implementation is not to be called this time.
Just extend Fragment in MySecondFragment
You should always call the super method. Just set the variables you need in the MySecondFragment.onActivityCreated() method and use them afterwards. To hide functionality you can always check the calling object class but this goes against the OOP inheritance principles.
Related
Unlike typical android projects starting with MainActivity with all the code of the layout object in it. This architecture requires me to have the initial code in a custom object. Here's a structure for better understanding.
java/MainActivity.java
java/User.java
layout/activity_main.xml
layout/user.xml
Now I also need a reference to User object within MainActivity and it looks like this.
public class MainActivity extends AppCompatActivity {
public Users users; // instantiate custom class and show
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
My User class looks like this.
public class User extends AppCompatActivity {
ListView userList;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.user_list, container, false); // Inflate the layout for this fragment
userList = view.findViewById(R.id.userList);
return view;
}
}
layout/user.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context="org.puremvc.java.demos.android.employeeadmin.view.components.UserList">
<ListView
android:id="#+id/userList"
android:layout_width="395dp"
android:layout_height="715dp"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="8dp" />
</FrameLayout>
So, in other words, MainActivity just acts like a stage doing nothing except just providing a reference to the Initial object. Now I do need MainActivity to be there, can't point User to be a launcher in the manifest. Responsibilities are to be taken care of by User class.
Question: How to instantiate CustomClass User and show.
Context: The MainActivity class has to be minimalistic and clean, no User related code (ListView), all logic lies in the custom class.
P.S. There can be lateral approaches, as long as I have a reference to user Object in MainActivity and it's displayed on launch, I'll accept the answer.
As per my understanding: there should be two approaches.
First, by using the Fragment inside your Activity. Write all initialize and data flow codes inside the fragment and just initialize and start the fragment from the Activity. So when the Activity will start, it will give all its tasks to the fragment with its Context and rest thing Fragment will do.
Like below:
public class MainActivity extends AppCompatActivity {
public Users users; // instantiate custom class and show
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//the fragment stuff
FragmentManager fm= getFragmentManager() //or get SupportFragmentManager when the Fragment comes from Support lib
FragmentTransaction ft= fm.beginTransaction();
Fragment fragment= new UserListFragment();
ft.add(R.id.fragment_container, fragment);
ft.commit();
}
}
OR, the second approach should by using Interface and communicate both Activity and the Custom Class (or you can call it Controller) with it.
Its nothing, but a simple MVC design pattern which I never recommend.
You can write one Interface like below:
public interface IController{
public void initialize(Activity activity, Bundle savedInstanceState);
public void engage();
public void disengage();
}
Then, make an instance of this Controller inside your Activity/BaseActivity and use like below:
public MainActivity(IController controller){
this.controller = controller;
}
Then call each callback methods from their appropriate place to make them work inside the Controller.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//give the priviledge of onCreate to initialize
controller.initialize(this, savedInstanceState);
}
Then in your Controller class, just write the same program which you supposed to write inside Activity:
public class Your_Controller implements IController {
#Override
public void initialize(Activity activity, Bundle savedInstanceState) {
//do super where needed
//make one class level Activity instance to work in other methods
act = activity;
//just initialize views like below
TextView tv = (TextView) activity.findViewById(R.id.abc);
}
I have 2 Fragments - ButtonFragment and ListViewFragment - in my Activity MainActivity.
ButtonFragment contains a Button, ListViewFragment contains a ListView.
Each time I click on the ButtonFragment Button I want the ListViewFragment to show/hide.
How do I code this properly?
Currently my code looks like this:
MainActivity.java
public class MainActivity extends Activity implements Communicator {
ButtonFragment buttonFrag;
ListViewFragment listviewFrag;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonFrag= new ButtonFragment();
listviewFrag = new ListViewFragment();
manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.button_fragment, buttonFrag, "Fragment1");
transaction.add(R.id.listview_fragment, listviewFrag, "Fragment2");
transaction.commit();
}
}
ButtonFragment.java
public class DynamicButtonsFragment extends Fragment implements View.OnClickListener {
Button btn;
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.button_fragment_layout, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onClick(View v) {
//?? hide listview fragment from here ??
}
}
ListViewFragment.java
public class ListViewFragment1 extends Fragment {
protected ArrayAdapter<String> adapter1;
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.list_view_fragment, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
}
So my question is where do I implement the showing/hiding of ListViewFragment? I feel like I should send data to the MainActivity through the onClick method of ButtonFragment. But I do not know how to do so.
Or do I only add code in the MainActivity since the MainActivity has access to all the Fragments?
I am having trouble becase the Button is in a Fragment, not part of the MainActivity. I haven't really seen cases like this...
Can someone please help?
You cannot show/hide a Fragment directly. You may show/hide a UI object like Listview. If you like, you can show/hide Fragment indirectly by using the FragmentTransaction, and you can call its method add, remove or replace.
A link for sample code is Fragments
Do this ..
android.app.Fragment fragment = getActivity().getFragmentManager().findFragmentByTag("YOUR_FRAGMENT_TAG");
getActivity().getFragmentManager().beginTransaction().hide(fragment);
inside your click event!
One more thing when you add fragments like this..
transaction.add(R.id.button_fragment, buttonFrag, "Fragment1");
transaction.add(R.id.listview_fragment, listviewFrag, "Fragment2");
you're expected to provide the container id instead of the id of the fragment.
Example: For MainActivity container use R.id.containerMain
If you in fragment want to do some MainActivity function , you can try
#Override
public void onClick(View v) {
//?? hide listview fragment from here ??
((MainActivity)getActivity()).hidelistView();
//hidelistView you should imp in your MainActivity
}
If you have fragments within the same layout, you can use the following code:
http://www.java2s.com/Code/Android/Core-Class/Demonstrationofhidingandshowingfragments.htm
If not, than you can use several possibilities...
You can use an Intent to send data to MainActivity.
You can have a singleton instance where you store pointer to your MainActivity.
You can also use Handler to send messages, but the ways discribed above are easier to implement and should be enough for you.
This question already has answers here:
NullPointerException accessing views in onCreate()
(13 answers)
Closed 8 years ago.
EDIT
Changed title.
SDK Guide document says, Activity.onCreate complete after Fragment.onCreateView and Fragment.onAcvityCreated.
But If I try findViewById for a view of the fragment it returns null.
How can I access contents of the fragment?
I'm very new to Android UI dev.
Below is a sample code generated by Eclipse IDE.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();
// this is null
View rootView = findViewById(R.id.txtView);
}
}
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
}
}
First of all, I want to access inner contents of the 'fragment_main'.
Can I do this with findViewById?
I found that calling findViewById for a view of the fragment at onCreate call is not working.
How do I know when the Fragment views are ready at the Activity level?
I read How to implement OnFragmentInteractionListener
Am I needed to manually implement a event listener for this?
I think short answer is 'impossible' or 'not works like that'.
If one want to manage inner contents of fragments just delete fragments and move all the contents to the activity layout.
But If I try findViewById for a view of the fragment it returns null.
You can not just access the view of the fragment in your activity's oncreate or where ever, you can call view of the fragment in your activity.
I found that calling findViewById for a view of the fragment at onCreate call is not working.
That is because the view is not inflated yet in your fragment thus returning null.
Have a look at the fragment life cycle:
(source: xamarin.com)
As you can see onCreate is before onCreateView which you inflate your view for the fragment's layout.
solution:
you call findViewByIdit in your fragment's onActivityCreated.
sample:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
TextView sample = (TextView) getView().findViewById(your_id);
}
I have created a MainActivity which consists of 3 tabs which are scrollable (by using ViewPager). Now each of these 3 tabs is a Fragment. Also, I am using ActionBarSherlock (ABS).
For the 1st Fragment, I have created the following class:
public class Fragment_1 extends SherlockFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View v = inflater.inflate(R.layout.fragment1, container, false);
return v;
/* R.layout.fragment1 only contains a TextView. */
}
}
For the 2nd Fragment, I want to extend a FragmentActivity as shown below.
public class Fragment_2 extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment2);
}
}
The 3rd Fragment is same as the first one.
But when I launch the app, it crashes. So, am I wrong in extending FragmentActivity for the class Fragment_2? Is it illegal to extend a FragmentActivity or even an Activity to fragment classes? If not, what is the problem here?
Thanks.
EDIT: After #CommonsWare's answer I updated my class as follows:
public class Fragment_2 extends SherlockFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button send = (Button) findViewById(R.id.bSend); //Error at this line
//some error free code
FragmentTransaction t = getSupportFragmentManager().beginTransaction(); //Error at this line
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View v = inflater.inflate(R.layout.fragment2, container, false);
return v;
}
My final two questions are:
How should I access my R.id.bSend Button in the Fragment_2 class.
Eclipse is giving the suggestion to change getSupportFragmentManager() to getFragmentManager(). Is that change all right?
Thanks!
Is it illegal to extend a FragmentActivity or even an Activity to fragment classes?
Fragment is a Java class. Your fragment implementations (for use as pages in your ViewPager) must inherit from Fragment, directly or indirectly.
Activity does not inherit from Fragment. FragmentActivity does not inherit from Fragment. Hence, you cannot inherit from Activity or FragmentActivity and somehow also inherit from Fragment.
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.