make Simple Fragment always get InstantiationException - android

hello guys i just try to make a simple fragment start up using support library v4 here...
this is my main activity
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
Fragment testFragment = manager.findFragmentById(R.id.listTestFragment);
transaction.show(testFragment);
transaction.commit();
}
}
and my activity_main.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">
<fragment android:name="com.drikvy.testfragmentgame.ListTestFragment"
android:id="#+id/listTestFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
and my ListTestFragment.java
public class ListTestFragment extends Fragment {
String[] data = {"aaaa", "bbbb", "cccc", "dddd"};
MainActivity activity;
public ListTestFragment(MainActivity activity) {
this.activity = activity;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.fragment_lores_list, null, false);
ListView lvLores = (ListView) view.findViewById(R.id.lv_saved_lores);
lvLores.setAdapter(new ArrayAdapter<String>(activity, android.R.id.text1, data));
return view;
}
}
sadly android runtime said that i havent empty constructor and that my fragment class is not public class etc
what's wrong?
UPDATE
Thanks to Sir Miro for pointing out the main problem in my code through his answer.
I just want to make clear some matters here:
first of all, i didnt know that by putting the fragment in the xml layout would require the fragment class to provide an empty constructor, that's why im always getting instantiate exception error in logcat
the reason behind why i didnt put an empty constructor in my fragment class is that because from my previous attempts for making fragments i had successfully show the fragments by using non-empty constructor and i usually add activity parameters inside it for context utilization within the fragment class. but i didnt noticed that none of these fragments was declared on my xml layout! and thats when the error start to popped up
so in conclusion we have to provide an empty constructor or no constructor at all if we have declared the fragments on our xml layout. but if we didnt declared them on our xml layout then we can deliberately add any kinds of constructor with any parameters we could possibly wanted within our fragment class!

You can't pass in your activity from the fragment constructor. Fragment has a method called onAttach which is called when the fragment is attached to an activity. You can set the activity variable from onAttach.
Try this as your Fragment code:
public class ListTestFragment extends Fragment {
String[] data = {"aaaa", "bbbb", "cccc", "dddd"};
MainActivity activity;
public ListTestFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.fragment_lores_list, null, false);
ListView lvLores = (ListView) view.findViewById(R.id.lv_saved_lores);
lvLores.setAdapter(new ArrayAdapter<String>(activity, android.R.id.text1, data));
return view;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.activity = (MainActivity) activity;
}
#Override
public void onDetach() {
super.onDetach();
this.activity = null;
}
}
And also you won't need all that code in your activity. Android will initialize and show the fragment for you automatically.

Related

Instantiate a custom activity class with a ListView object from MainActivity

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);
}

Is it the correct way? : To change a view/layout of Fragment on click

What I ought to do is change the view/layout of Fragment without creating another class for fragment on click of a button.
For example I have an activity - ContactsActivity and I have a fragment - ContactsFragment.
The Standard way of using Fragments:
From ContactsActivity I call ContactsFragment by -
getFragmentManager().beginTransaction().replace(android.R.id.content, new ContactsFragment())
.commit();
Code for setting View in ContactsFragment class -
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.contacts_primary, container, false);
return rootView;
}
**Now comes how I do what I want to do ** (Change the view of fragment)
I change only the view of ContactsFragment by doing a bad kind of hack.
I change the onCreateView() shown above to this -
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
//Set the view to R.layout.contacts_primary
rootView = inflater.inflate(R.layout.contacts_primary, container, false);
//Set the view to R.layout.contacts_secondary
if(getActivity().getIntent()!=null && getActivity().getIntent().getBooleanExtra("s", false)) {
rootView = inflater.inflate(R.layout.contacts_secondary, container, false);
Log.e(tag,getActivity().getIntent().getExtras().toString());
return rootView;
}
//This is the onClickListener which again calls the ContactsActivity class,
//this time with an Intent which I used above to change the view from
//R.layout.contacts_primary to R.layout.contacts_secondary
Button button = (Button)rootView.findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getActivity(), ContactsActivity.class).putExtra("s",true));
}
});
Now everything works as I want and flawlessly.
But I have a very strong feeling that either all of it is wrong and Fragments aren't supposed to work this way or I am using a hectic hack to achieve what can be done by few lines of code.
So please let me know what is it? And if there is a standard way of doing what I am trying to do.
For me passing additional argument on which base fragment decides wich layout to use seems totally ok. But there is cleaner way of doing what you want to achieve without starting another activity.
First of all pass argument to fragment by making standard static new instance method in fragment (we cannot pass this argument in constructor as android always recreates fragments using empty constructor). Something like this:
public static ContactsFragment newInstance(boolan firstView) {
ContactsFragment fragment = new ContactsFragment();
Bundle args = new Bundle();
args.putBoolean("yourArg", firstView);
fragment.setArguments(args);
return fragment;
}
Every time you have to initiate your fragment do this with this method.
Then declare interface in your fragment to communicate with your activity. Like this
public interface NewViewListener {
public void showNewView(boolen firstView);
}
Than make your activity implement it so your activity han a method where it can place new fragment in container view. In your fragments onAttach and onDetach methodsmake sure your activity implements this interface and hold reference to your activity in private NewViewListener field in your fragment. Like this:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (NewViewListener ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement NewViewListener ");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
Then in on button click method call showNewView method on your activity with whatever argument you want indicating which view you want in new fragment instance. And in your activity method showNewVew fragment in the container. Like this:
#Override
public void showNewView(boolean firstView) {
getFragmentManager().beginTransaction().replace(android.R.id.content, ContactsFragment.newInstance(firstView)
.commit();
}
In your fragments onCreateView you may get passed arguments and decide which view you want to use.

Can a fragment extend a fragment activity? [Android]

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.

getSupportFragmentManager().findFragmentById() returns null

I am trying to implement fragment communication in android like the one in the android guide http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
but my application is crashing as the getSupportFragmentManager().findFragmentById() returns null. What is the issue with my implementation.
The code is given below:
The program is just to send an input text from one fragment to another fragment textView area through a button click from first fragmnet.I have an activity_main.xml and two fragment layout (two separate xml files rather than part of in activity_main.xml)
Frag1.java
public class Frag1 extends Fragment {
public Frag1(){
}
buttonClickListener buttonListener;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
buttonListener = (buttonClickListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnButtonPressListener");
}
}
View myFragmentView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
myFragmentView = inflater.inflate(R.layout.frag1, container, false);
//SetValue Button
Button setValueButton = (Button) myFragmentView.findViewById(R.id.setValueButton);
setValueButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
buttonListener.onButtonPressed("Message received");
}
});
return myFragmentView;
}
}
Frag2.java
public class Frag2 extends Fragment {
View myFragmentView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
myFragmentView = inflater.inflate(R.layout.frag2, container, false);
return myFragmentView;
}
void setMessage(String msg){
TextView txt=(TextView)myFragmentView.findViewById(R.id.textView1);
txt.setText(msg);
}
}
buttonClickListener.java
public interface buttonClickListener {
public void onButtonPressed(String msg);
}
MainActivity.java
public class MainActivity extends FragmentActivity implements
ActionBar.TabListener, buttonClickListener {
SectionsPagerAdapter mSectionsPagerAdapter;
#Override
public void onButtonPressed(String msg) {
// TODO Auto-generated method stub
Frag2 fragmentObj=(Frag2) getSupportFragmentManager().findFragmentById(R.layout.frag2);
fragmentObj.setMessage(msg);
}
Please tell me where did I go wrong?
EDIT:
I am using fragment creation using the template generated by Android Plug-in eclipse IDE.
So the fragments are created using android.support.v4.app.Fragment
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch(position)
{
case 0:
return new Frag1();
case 1:
return new Frag2();
}
return fragment;
}
The codebase is kept here for reference
https://skydrive.live.com/redir?resid=D37E0F56FEC9B499!259
Try This it works for me How to put Google Maps V2 on a Fragment Using ViewPager
<fragment
android:id="#+id/map"
android:layout_width="wrap_content"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
GoogleMap mGoogleMap = ((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map)).getMap();
You should have added the fragment Frag2 by calling
getSupportFragmentManager().beginTransaction().add(R.id.frag2_view, new Frag2(), "tag").commit();
at your MainActivity, where R.id.frag2_view is a layout defined in your main_layout.
To get that Fragment, you should then call
Frag2 obj = (Frag2)getSupportFragmentManager().findFragmentById(R.id.frag2_view);
passing the layout id you used to add the fragment in the main_layout.
Hope it helps.
EDIT:
Since you use a ViewPager, you should use R.id.pager as the ID.
I just tried with your example and it worked.
Frag2 fragmentObj=(Frag2) getSupportFragmentManager().findFragmentById(R.id.pager);
EDIT 2:
Despite it worked, I don't really think this is the correct way, since R.id.pager its from ViewPager and you can't find, let's say, frag4 or frag5.
Ignore my answer please. I'm not sure how to do that with ViewPager, sorry.
I was having a similar problem and here is the solution.
To get the reference to the proper fragment inside the viewpager just call:
getSupportFragmentManager().findFragmentByTag(tag);
The tag param you can build it using the following syntax: "android:switcher:pager_id:index", where pager_id is the id of the ViewPager in the XML layout and the index is the position of your fragment inside the ViewPager starting by zero. See the original response here: https://stackoverflow.com/a/7393477/2423274
Found the right solution for this question.
Hope more people see this.
Frag2 fragmentObj=(Frag2) mSectionsPagerAdapter.getItem(2);
You should use your Adapter (where you populate your fragments) as a source for get the Fragment reference.
I used this to get a child fragment from a fragment adapter...
Fragment parent = MainActivity.this.getSupportFragmentManager().findFragmentById(R.id.main_container);
if (parent instanceof PagerFragment) {
// do something with parent
Fragment child = ((PagerFragment) f).adapter.getItem(childPosition);
}

Calling a method from layout xml with DialogFragment.. how does that work?

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.

Categories

Resources