问题
Edit: The problem is that the fragment is getting detached from the Activity. However, I don't know how to prevent this.
I have an application that uses a tab bar. When one of the tabs is clicked it loads a list of topics in the space below the tab bar. One of the topics is 'Directions'. Upon clicking this the space is now replaced with a layout that has another bar at the top (below the tab bar) that includes a 'Back' button and a 'Get Directions' button. Below that bar is a google map of the location. If I click 'Back' I am returned to the list as expected. Then if I click 'Directions' in the list I go back to the map as expected. Then if I click 'Back' again instead of returning me to the list again it crashes the app. It shows a NullPointerException that points to the line FragmentTransaction ft = getFragmentManager().beginTransaction(); inside the Back Button's setOnClickListener. Not sure why it's losing track of the fragmentManager.
Here is the file for tab that initially loads the list:
package org.childrensmuseum.visittcmindy;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.GradientDrawable.Orientation;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
public class InfoTab extends Fragment {
private ArrayList<InfoPage> pages;
private TCMSQLiteHelper sqliteHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sqliteHelper = new TCMSQLiteHelper(this.getActivity());
pages = sqliteHelper.getAllPages();
InfoPage directions = new InfoPage();
directions.setPage_id(101);
directions.setTitle("Directions");
InfoPage calendar = new InfoPage();
calendar.setPage_id(102);
calendar.setTitle("Calendar");
pages.add(1, directions);
pages.add(2, calendar);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.infolist, container, false);
ListView infoList = (ListView)v.findViewById(R.id.infolistview);
int[] colors = {0xFFCFE8FF,0xFFCFE8FF};
infoList.setDivider(new GradientDrawable(Orientation.RIGHT_LEFT, colors));
infoList.setDividerHeight(2);
ArrayList<String> valueList = new ArrayList<String>();
for(InfoPage page : pages){
valueList.add(page.getTitle());
}
String[] values = new String[valueList.size()];
values = valueList.toArray(values);
infoList.setAdapter(new InfoAdapter(getActivity(), values));
infoList.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View v, int p,
long id) {
long page_id = pages.get(p).getPage_id();
// If Directions
if(page_id==101){
DirectionsView directions = new DirectionsView();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.realtabcontent, directions);
ft.commit();
} else {
PageDetails details = PageDetails.newInstance(pages.get(p).getPage_id());
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.realtabcontent, details);
ft.commit();
}
}
});
return v;
}
private class InfoAdapter extends ArrayAdapter<String> {
public InfoAdapter(Context context, String[] values) {
super(context, R.layout.explorecell, values);
// TODO Auto-generated constructor stub
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View row = inflater.inflate(R.layout.infocell,parent,false);
InfoPage page = pages.get(position);
if(position % 2 != 0){
row.setBackgroundColor(getResources().getColor(R.color.listblue));
} else {
row.setBackgroundColor(getResources().getColor(R.color.white));
}
String page_icon = "page_" + page.getPage_id();
ImageView iv = (ImageView)row.findViewById(R.id.infoImage);
TextView tv = (TextView)row.findViewById(R.id.infoText);
String title = page.getTitle();
String[] titleParts = title.split("[ ]");
String newTitle = "";
for(int i=0;i<titleParts.length;i++){
titleParts[i] = titleParts[i].substring(0,1).toUpperCase() + titleParts[i].substring(1);
newTitle += titleParts[i];
if(i<titleParts.length-1){
newTitle += " ";
}
}
iv.setImageResource(getActivity().getResources().getIdentifier(page_icon, "drawable","org.childrensmuseum.visittcmindy"));
tv.setText(newTitle);
return row;
}
}
@Override
public void onDestroy() {
super.onDestroy();
if(sqliteHelper != null) {
sqliteHelper.close();
}
}
}
Here is the file for the DirectionsView that is called when you click that item in the list:
package org.childrensmuseum.visittcmindy;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import android.app.Dialog;
import android.content.Intent;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
public class DirectionsView extends Fragment implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
private static View v;
private MainApplication main;
private LocationClient mLocationClient;
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
main = MainApplication.getInstance();
mLocationClient = new LocationClient(getActivity(), this, this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if(v != null){
ViewGroup parent = (ViewGroup) v.getParent();
if(parent != null){
parent.removeView(v);
}
}
try {
mLocationClient.connect();
v = inflater.inflate(R.layout.directions, container, false);
GoogleMap mMap;
mMap = ((SupportMapFragment) getFragmentManager().findFragmentById(R.id.directionsmap)).getMap();
Geocoder coder = new Geocoder(getActivity());
List<Address> address;
try {
Log.d("DEBUG","2");
String museumAddress = "The Children's Museum of Indianapolis, 3000 North Meridian St, Indianapolis, IN 46208";
address = coder.getFromLocationName(museumAddress, 1);
Address museumLocation = address.get(0);
mMap.addMarker(new MarkerOptions()
.position(new LatLng(museumLocation.getLatitude(), museumLocation.getLongitude()))
.title(getResources().getString(R.string.museumTitle)));
} catch(Exception e) {
Log.d("DEBUG","Error Adding Marker: "+e.getLocalizedMessage());
}
Button backButton = (Button)v.findViewById(R.id.backtoexhibits);
backButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
InfoTab infoTab = new InfoTab();
ft.replace(R.id.realtabcontent, infoTab);
ft.commit();
}
});
Button directionsButton = (Button)v.findViewById(R.id.getdirections);
directionsButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
openDirectionsDialog();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
} catch (InflateException e){
/* map is already there, just return view as it is */
e.printStackTrace();
}
return v;
}
@Override
public void onConnectionFailed(ConnectionResult result) {
// TODO Auto-generated method stub
}
@Override
public void onConnected(Bundle connectionHint) {
// TODO Auto-generated method stub
}
@Override
public void onDisconnected() {
// TODO Auto-generated method stub
}
private void openDirectionsDialog() throws UnsupportedEncodingException{
final Dialog dialog = new Dialog(getActivity());
dialog.setContentView(R.layout.getdirections);
dialog.setTitle("Get Directions");
Button useAddressBtn = (Button)dialog.findViewById(R.id.useAddress);
Button useLocationBtn = (Button)dialog.findViewById(R.id.useLocationBtn);
final EditText addressText = (EditText)dialog.findViewById(R.id.addressText);
final String museumAddress = URLEncoder.encode("The Children's Museum of Indianapolis, 3000 North Meridian St, Indianapolis, IN 46208","UTF-8");
useAddressBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String address;
try {
address = URLEncoder.encode(addressText.getText().toString(),"UTF-8");
String url = "http://maps.google.com/maps?saddr="+address+"&daddr="+museumAddress;
Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(url));
intent.setClassName("com.google.android.apps.maps", "com.google.android.maps.MapsActivity");
startActivity(intent);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
useLocationBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Location mCurrentLocation = mLocationClient.getLastLocation();
double lat = mCurrentLocation.getLatitude();
double lng = mCurrentLocation.getLongitude();
String url = "http://maps.google.com/maps?saddr="+lat+","+lng+"&daddr="+museumAddress;
Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(url));
intent.setClassName("com.google.android.apps.maps", "com.google.android.maps.MapsActivity");
startActivity(intent);
}
});
dialog.show();
}
}
Edit:
Here's the stack trace
12-11 14:07:03.547: E/AndroidRuntime(1846): FATAL EXCEPTION: main
12-11 14:07:03.547: E/AndroidRuntime(1846): Process: org.childrensmuseum.visittcmindy, PID: 1846
12-11 14:07:03.547: E/AndroidRuntime(1846): java.lang.NullPointerException
12-11 14:07:03.547: E/AndroidRuntime(1846): at org.childrensmuseum.visittcmindy.DirectionsView$1.onClick(DirectionsView.java:80)
12-11 14:07:03.547: E/AndroidRuntime(1846): at android.view.View.performClick(View.java:4424)
12-11 14:07:03.547: E/AndroidRuntime(1846): at android.view.View$PerformClick.run(View.java:18383)
12-11 14:07:03.547: E/AndroidRuntime(1846): at android.os.Handler.handleCallback(Handler.java:733)
12-11 14:07:03.547: E/AndroidRuntime(1846): at android.os.Handler.dispatchMessage(Handler.java:95)
12-11 14:07:03.547: E/AndroidRuntime(1846): at android.os.Looper.loop(Looper.java:137)
12-11 14:07:03.547: E/AndroidRuntime(1846): at android.app.ActivityThread.main(ActivityThread.java:4998)
12-11 14:07:03.547: E/AndroidRuntime(1846): at java.lang.reflect.Method.invokeNative(Native Method)
12-11 14:07:03.547: E/AndroidRuntime(1846): at java.lang.reflect.Method.invoke(Method.java:515)
12-11 14:07:03.547: E/AndroidRuntime(1846): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
12-11 14:07:03.547: E/AndroidRuntime(1846): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
12-11 14:07:03.547: E/AndroidRuntime(1846): at dalvik.system.NativeStart.main(Native Method)
Edit 2:
Per suggestions, I tried replacing
FragmentTransaction ft = getFragmentManager().beginTransaction();
with
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
There was no change in the error.
回答1:
I recommend this: http://developer.android.com/training/implementing-navigation/lateral.html
It's a tutorial on using a viewPager and fragmentPagerAdapter with navigation tabs, so that you don't have to manually set up an onClickListener to change fragments. This should prevent fragments from being detached.
回答2:
You have mixed up support fragment and fragment. You must be using ActionBarSherlock library for this.
So you need to extend SherlockFragment and not Fragment.
After that, you need to use the edit that you gave:
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
This should make it work.
来源:https://stackoverflow.com/questions/20527417/android-getfragmentmanager-returns-null