I am a beginner in writing Android app.
I am confused when passing parameters from 1st activity to 2nd activity when 2nd activity is using fragment.
Here is what i would like to do. I would like to switch to webview activity that show corresponding webpage from the 1st activity parameter: routeName.
e.g. if routeName = A, display A.html. otherwise display default.html
Here are my coding:
In 1st activity:
Intent intent = new Intent(getActivity(), RouteInfoWebActivity.class);
intent.putExtra("routeName", routeName);
startActivity(intent);
WebView Activity and Fragment class
public class RouteInfoWebActivity extends FragmentHolder {
private RouteInfoWebFragment routeInfoWebFragment;
public RouteInfoWebActivity() {
routeInfoWebFragment = new RouteInfoWebFragment(getIntent().getExtras().getString("routeName","defaultKey"));
}
protected Fragment fragment() { return routeInfoWebFragment; }
#Override
public void onBackPressed() {
RouteInfoWebFragment routeWeb = (RouteInfoWebFragment) routeInfoWebFragment;
WebView htmlView = routeWeb.getWebView();
if (htmlView.canGoBack()) {
htmlView.goBack();
} else {
super.onBackPressed();
}
}
}
public class RouteInfoWebFragment extends LocalWebFragment {
String url = "";
public RouteInfoWebFragment(String routeName) {
If (routename == 'A') {
url == 'A.html';
} else {
url = 'default.html';
}
super("file:///android_asset/"+url, R.layout.route_web);
}
#Override
public View onCreateView(final LayoutInflater inflater,
final ViewGroup container,
final Bundle savedInstanceState) {
final View routeWebView = super.onCreateView(inflater, container, savedInstanceState);
return routeWebView;
} // onCreateView
However, I saw the warning in Fragment class when doing so:
Avoid non-default constructor in Fragment.
As I have set all corresponding properties in "LocalWebFragment" which I do not want to change it.
I know in Fragment I can use new instance to pass parameters before linking it to activity. However, wondering how can it be done on my case please? Is there any easy way to do so apart from creating a new Fragment parent?
Thank you for your help.
You can pass the arguments in the form of a bundle and call setArguments(Bundle bundle) on your fragment object. In your fragment you can get the values by calling getArguments() and extracting what you want from that bundle.
EDIT
First of all, you need to get a object of your fragment and set the arguments.
Fragment fragment = new RouteInfoWebFragment();
Bundle bundle = new Bundle();
bundle.putString("routeName", routeName);
fragment.setArguments(bundle);
Then in your fragment class you can simply get the value of routeName
Bundle arguments = getArguments();
String routeName = arguments.getString("routeName");
Related
This is how my application runs.
The application has several fragments as view pager connecting through main activity. From Second Fragment, I launched custom fragment dialog and upon ok button click inside that fragment dialog, I am fetching the result into main activity. I am using android.support.v4 library.
public class MainActivity extends FragmentActivity implements MyDialog.Communicator {
private static String callBackMessage;
#Override
public Fragment getItem(int pos) {
switch (pos) {
case 0:
return FirstFragment.newInstance();
case 1:
return SecondFragment.newInstance(MainActivity.this);
default:
return ThirdFragment.newInstance();
}
}
#Override
public void onDialogMessage(String callBackMessage) {
MainActivity.callBackMessage = callBackMessage;
}
public static String getCallBackMessage() {
return callBackMessage;
}
This is my MyDialog Fragment class, here I am creating some rows and giving checkboxes to be selected.
private void performOKButtonFunctionality() {
String msg = checkedBoxesCount + "";
communicator.onDialogMessage(msg);
SecondFragment secondFragment = new SecondFragment();
secondFragment.testFunction(row);
dismiss();
}
Now because of my very early days into android and its fragment life cycle, I am calling a method on Second fragment after instantiating it. the row parameter just passes the row number.
Now inside the Second Fragment Class I have some thing like,
public void testFunction(String callBackRow) {
callBackRowNo = Integer.parseInt(callBackRow);
String callBackMessage = MainActivity.getCallBackMessage();
getTableData(callBackMessage + "");
}
In order to save the SecondFragment previous state, I am saving its context by making it static like this,
private static Context activityContext;
public static SecondFragment newInstance(Context context) {
activityContext = context;
SecondFragment f = new SecondFragment();
Bundle b = new Bundle();
f.setArguments(b);
return f;
}
Now doing so, I am able to use my Second Fragment only one time after clicking the ok button from Fragment Dialog. but upon clicking again, the app crashes at this point.
FragmentManager myDialogManager = getActivity().getSupportFragmentManager();
The raised exception is:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.support.v4.app.FragmentManager android.support.v4.app.FragmentActivity.getSupportFragmentManager()' on a null object reference
Now, my questions are:
How do we reenter into the oncreate method of the fragment where it just restores its previous state rather than saving it explicitly before launching
fragment dialog.
How does the fragment dialog pass parameter to the fragment which can be used to update the fragment field.
Is there some easy way to achieve this?
Before asking I searched the SO but could not find anything where this scenario was asked or explained.
I am badly stuck here and any solution/direction is much appreciated.
try getChildFragmentManager() instead of getSupportFragmentManager()
when do you make fragment with newInstace, you can put argsments like this.
public static MyFragment newInstance(int value) {
MyFragment f = new MyFragment();
Bundle args = new Bundle();
args.putInt("value", value);
f.setArguments(args);
return f;
}
it also needs constructor MyFragment()
public MyFragment() {}
get values from args like this
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (getArguments() != null) {
value = getArguments().getInt("value");
}
return inflater.inflate(R.layout.your_layout, container, false);
}
I am creating a android app in which I have a main activity which has 2 fragments, fragUp and fragDown. fragUp has two edit text fields and a button and fragDown has two text view fields. Initially main activity shows only fragUp and asks user for the input fields, when the user clicks the button then onClick method is triggered and fragDown is created below fragUp and shows the two fields entered by the user.
Summary-
FRAGMENTS- fragUp , fragDown
MAIN ACTIVITY- main activity
Main activity has two linear layouts 1.ly1 for fragUp2.ly2 for fragDown
Note- All fragments are added dynamically in main java file and not declared in main.xml file.
This is main activity code
public class MainActivity extends AppCompatActivity implements fragUp.fragInterface {
FragmentTransaction ft,ft1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
ft=getFragmentManager().beginTransaction();
fragUp fu=new fragUp();
ft.add(R.id.ly1,fu); //Fragment fragUp is added dynamically.
ft.commit();
}
public void set(String a,String b){
ft1=getFragmentManager().beginTransaction();
fragDown ob=new fragDown();
ft1.add(R.id.ly2,ob);
ft1.commit();
fragDown fd=(fragDown)getFragmentManager().findFragmentById(R.id.ly2);
fd.seta(a, b);
}
This is my fragUp java code-
public class fragUp extends Fragment {
View v;
public interface fragInterface{
public void set(String a,String b);
}
fragInterface ob;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v=inflater.inflate(R.layout.fragup,container,false);
Button b=(Button)v.findViewById(R.id.button1);
b.setOnClickListener( new OnClickListener(){
public void onClick(View v){
store();
}});
return v;
}
public void onAttach(Activity activity){
super.onAttach(activity);
try{
ob=(fragInterface)activity;
}
catch(ClassCastException e){
throw new ClassCastException();
}
}
public void store(){ with main activity
EditText et1=(EditText)v.findViewById(R.id.editText1);
EditText et2=(EditText)v.findViewById(R.id.editText2);
ob.set(et1.getText().toString(),et2.getText().toString()); //Interface implemented to share data
}
}
This is fragDown java code-
public class fragDown extends Fragment {
View v;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v=inflater.inflate(R.layout.fragdown,container,false);
return v;
}
public void seta(String a,String b){
TextView t1=(TextView)v.findViewById(R.id.textView1);
TextView t2=(TextView)v.findViewById(R.id.textView2);
t1.setText(a);
t2.setText(b);
}
}
When I run the app and press the button my app crashes and logcat says-
08-05 09:37:02.198: E/AndroidRuntime(2866): java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.fragment.fragDown.seta(java.lang.String, java.lang.String)' on a null object reference
08-05 09:37:02.198: E/AndroidRuntime(2866): at com.example.fragment.Second.set(Second.java:30)
08-05 09:37:02.198: E/AndroidRuntime(2866): at com.example.fragment.fragUp.store(fragUp.java:52)
08-05 09:37:02.198: E/AndroidRuntime(2866): at com.example.fragment.fragUp$1.onClick(fragUp.java:25)
If anyone can help me then it would be great. I spent hours debugging it can't find where I went wrong. Please help.
Why don't you pass the values to the fragment as arguments? Check the "Layout" section on this guide: http://developer.android.com/reference/android/app/Fragment.html
Here's an excerpt from that guide that could help you:
public static DetailsFragment newInstance(int index) {
DetailsFragment f = new DetailsFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
Then when setting up the fragment:
// Prepare args
Bundle arguments = new Bundle();
arguments.putString(key, value);
// Init fragment and pass args
fragment = new DetailsFragment();
fragment.setArguments(arguments);
// Commit to the fragment manager, thus
// inflating the view
getSupportFragmentManager().beginTransaction()
.add(R.id.your_id, fragment).commit();
You can access the bundled arguments on your fragment's onCreate method.
Addressing your code specifically, you should:
MainActivity.set, pass the args to your fragment:
public void set(String a,String b) {
ft1 = getFragmentManager().beginTransaction();
fragDown ob = new fragDown();
// Add args. It is a good practice
// to declare keys as public static final
// variables, accessing them accordingly. Here
// I'm just putting the string, but you should
// avoid that.
Bundle args = new Bundle();
args.putString("key_a", a);
args.putString("key_b", b);
ob.setArguments(args);
ft1.add(R.id.ly2, ob);
ft1.commit();
}
Add FragDown.onCreate method:
private String a = "";
private String b = "";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.a = getArguments().getString("key_a");
this.b = getArguments().getString("key_b");
}
Call seta on your onCreateView:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v = inflater.inflate(R.layout.fragdown, container, false);
seta(this.a, this.b)
return v;
}
this is due to the Fragment's lifecycle: http://developer.android.com/reference/android/app/Fragment.html#Lifecycle
Now, a bit of advice:
If you're serious about Android development, go read the links I posted (from top to bottom) until you understand them. Sorry, there's no way around them.
The "show me the code" kind of questions are usually considered rude and are prone to get downvoted or get hateful comments. StackOverflow is working towards a "be nice" campaign, and that's one of the main reasons I coped with it and followed up.
Asking a question in StackOverflow should really be your last resource, after you've exhausted every other option and have gone through docs, tutorials, source code, GitHub issues and Pull Requests, and so on.
Take this advice with a grain of salt :)
I sort of figured out how to send the data from a fragment -> activity -> a second fragment. However, when the second fragment tries to pull the data, it runs into a null error. The code that first sends the data is shown below:
Intent i = new Intent(getActivity(), scoring_two.class);
i.putExtra("Cv",Cv);
dicstr_twotank_frag.this.startActivity(i);
The code for the scoring_two activity is below:
package edu.UDayton.www;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
//This is simply the placeholder activity for allowing the fragment to attach to something.
//Fragements deal with screen rotations more easily than activities, which is why all the
//code is the fragment scoring. But fragments can't exist by themselves so there needs to
//be a dummy activity to hold them.
public class scoring_two extends FragmentActivity{
#Override
protected void onSaveInstanceState(Bundle saveInstanceState) {
super.onSaveInstanceState(saveInstanceState);
}
#Override
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.rel5);
Intent intent = getIntent();
Double Cv = intent.getDoubleExtra("Cv", 1.0);
Fragment scoring = new Fragment();
Bundle bundle = new Bundle();
bundle.putDouble("Cv", Cv);
scoring.setArguments(bundle);
}
}
And the scoring fragment:
package edu.UDayton.www;
public class scoring extends Fragment {
public TextView textOverallScore;
Bundle bundle;
Double Cv;
//Button declarations
Button main;
//This creates a view that the fragment will use to obtain the actual layout for the activity
public View rootView;
//This is the beginning of activity initialization. Since this is an Android fragment, the first
//step is to attach it to an actual activity. The activity in this case is essentially a placeholder.
//All of the real work of this system comes from this Android fragment
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
}
//This code preserves certain values if you exit the app and come back to it
#Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
//This retains all of the data in the app upon screen rotation. Normally the activity is destroyed and
//re-created upon rotation. This prevents this from happening.
setRetainInstance(true);
}
//These lines obtain the layout view "scoring" to use for the fragment.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState) {
View rootView = getActivity().getLayoutInflater().inflate(R.layout.scoring, null);
//Identify the text that needs to change
textOverallScore=(TextView)rootView.findViewById(R.id.textOverallScore);
//Receives data
bundle = getArguments();
Cv = bundle.getDouble("Cv", 1.0);
textOverallScore.setText(Cv+"");
//Identifies the button
main = (Button) rootView.findViewById(R.id.buttonMain);
//Sends user back to main screen
main.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(getActivity(), systems.class);
scoring.this.startActivity(i);
}
});
return rootView;
}
}
I believe the error occurs at the following line.
Cv = bundle.getDouble("Cv", 1.0);
If I comment out the line, and just have Cv = 0.0 or something else, it makes the proper change to the output file. Any help would be appreciated, thanks!
After the help from #dhun, I have updated the code for the scoring fragment to the following:
public class scoring extends Fragment {
public TextView textOverallScore;
//Button declarations
Button main;
//This creates a view that the fragment will use to obtain the actual layout for the activity
public View rootView;
//This is the beginning of activity initialization. Since this is an Android fragment, the first
//step is to attach it to an actual activity. The activity in this case is essentially a placeholder.
//All of the real work of this system comes from this Android fragment
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);}
public Bundle getBundle() {
Bundle bundle = new Bundle();
return bundle;
}
//This code preserves certain values if you exit the app and come back to it
#Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
//This retains all of the data in the app upon screen rotation. Normally the activity is destroyed and
//re-created upon rotation. This prevents this from happening.
setRetainInstance(true);
}
//These lines obtain the layout view "scoring" to use for the fragment.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState) {
View rootView = getActivity().getLayoutInflater().inflate(R.layout.scoring, null);
//Identify the text that needs to change
textOverallScore=(TextView)rootView.findViewById(R.id.textOverallScore);
//Receives data
Double Cv = getBundle().getDouble("Cv",1.36);
textOverallScore.setText(Cv + "");
//Identifies the button
main = (Button) rootView.findViewById(R.id.buttonMain);
//Sends user back to main screen
main.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(getActivity(), systems.class);
scoring.this.startActivity(i);
}
});
return rootView;
}
}
And now it no longer crashes, but it only passes the final default value through, it does not receive the Cv value (or it cannot find it).
I think the bundle you are getting in scoring fragment's onCreatView() by
bundle = getArguments();
returns null bundle and that is why you are getting null pointer exception at the line you have mentioned. Try implementing callback interface to send data back and forth between the activity and fragment. Let me give you a good example. You should refer to this answer posted here and make your code like that and try.
https://stackoverflow.com/a/16578326/4150528
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.
I have an enum describing three different sports:
public enum MatchType {
S1(0, "Sport1", "xml stream address", R.id.match_list, R.layout.fragment_match_list, R.color.separator_sport1),
S2(0, "Sport2", "xml stream address", R.id.match_list, R.layout.fragment_match_list, R.color.separator_sport2),
S3(0, "Sport3", "xml stream address", R.id.match_list, R.layout.fragment_match_list, R.color.separator_sport3);
...getters/setters
}
I then have fragment with
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
matchesArrayAdapter = new MatchListAdapter(getActivity(), new ArrayList<Match>());
return inflater.inflate(matchType.getLayout(), container, false);
}
Also in my fragment I have an AsyncTask where I have this
#Override
protected void onPostExecute(final List<Match> matches) {
if (matches != null) {
matchListView = (ListView) getActivity().findViewById(matchType.getRId());
[setup listeners]
matchesArrayAdapter.matchArrayList = matches;
matchListView.setAdapter(matchesArrayAdapter);
}
}
EDIT:
In my Activity I have an AppSectionsPagerAdapter with
public Fragment getItem(int i) {
MatchListSectionFragment fragment = new MatchListSectionFragment();
Bundle bundle = new Bundle();
bundle.putInt(Constants.MATCH_TYPE, i);
fragment.setArguments(bundle);
return fragment;
}
EDIT 2:
Here's my onCreate and onCreateView from my fragment:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
matchType = MatchType.getMatchType(bundle.getInt(Constants.MATCH_TYPE));
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
matchesArrayAdapter = new MatchListAdapter(getActivity(), new ArrayList<Match>());
return inflater.inflate(matchType.getLayout(), container, false);
}
The AsyncTask reads an xml stream for each of the sports in my enum but my problem is that tab #1 is overwritten with data from tab #2 and subsequently tab #3.
Before I had a fragment defined for each sport but surely that can't be necessary?
How do I go about using the same fragment with the same layout for each sport?
When instantiating your fragment in your Activity set the Fragment's arguments with a Bundle.
Bundle myBundle = new Bundle();
myBundle.putInt(MY_EXTRA, 1);
myFragment.setArguments(myBundle);
In your bundle put some Extra that will be read in the fragment's onCreate() callback.
int i = getArguments().getInt(MY_EXTRA);
I am on mobile.
Put three FrameLayouts in a LinearLayout, named frame1, frame2 and frame 3. This will be your main Activity's layout.
Then in the Activity's oncreate() method, call getFragmentManager().getFragmentTransaction().
Instantiate the three fragments and send them the data, preferably through a Bundle.
On the Fragment Transaction call the add() or replace() method for each fragment, the first parameter is the id of the respective FrameLayout, the second parameter is the fragment itself.
Call commit().
You should create the newInstance method in your fragment, also you should store MatchType instansce in you fragment.
MatchType matchType;
public static MyFragment newInstance(MatchType matchType) {
MyFragment fragment = new MyFragment();
fragment.matchType = matchType;
return fragment;
}
In your Activity you should to create 3 instances of MyFragment with this method (with related to each fragment it owns MatchType). Then in onCreateView method you should insert data to your views from matchType.
Sorry, I'm on mobile. And sorry for my English.
Update
Check your variable matchType. Maybe it declared as static?