3

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.

LoneWolfPR
  • 3,978
  • 12
  • 48
  • 84
  • 2
    post the stack trace. since your using fragment from support library. and use `getSupportFragmentManager()` since you have `import android.support.v4.app.Fragment;` – Raghunandan Dec 11 '13 at 18:45
  • @Raghunandan yer fast! but maybe you should post that as an answer – petey Dec 11 '13 at 18:49
  • 1
    @petey i will once op posts stack trace and the cause is confirmed – Raghunandan Dec 11 '13 at 18:51
  • posted the stack trace. – LoneWolfPR Dec 11 '13 at 19:08
  • @LoneWolfPR what is line 80 `DirectionsView.java`? did u use `getSupportFragmentManager()`? – Raghunandan Dec 11 '13 at 19:09
  • Line 80 is `FragmentTransaction ft = getFragmentManager().beginTransaction();` I tried using it, but it says the method is undefined. Also, if that was the problem wouldn't the back button fail the first time I click it, not just when I go back into the fragment after clicking it once? Edit: I mean I tried using `getSupportFragmentManager()` – LoneWolfPR Dec 11 '13 at 19:26
  • @LoneWolfPR getSupportFragmentManger is right. but if you say it works first time. I guess it has something do it fragment lifecycle – Raghunandan Dec 11 '13 at 20:10
  • Do you have a suggestion as to why getSupportFragmentManager throws an error when I try to use it. If I use `FragmentTransaction ft = getSupportFragmentManager().beginTransaction();` it says: `The method getSupportFragmentManager() is undefined for the type DirectionsView` – LoneWolfPR Dec 11 '13 at 20:13
  • In order to use `getSupportFragmentManager()` I had to use it with `getActivity()`. After doing that it still throws the same error as above. – LoneWolfPR Dec 11 '13 at 20:33
  • Make sure the activity containing this fragment is a FragmentActivity. – jesobremonte Dec 12 '13 at 13:45
  • It is a FragmentActivity. – LoneWolfPR Dec 12 '13 at 13:49
  • Instead of replacing with a new instance the previous fragment when clicking back, try doing getFragmentManager().popBackStackImmediate(); instead? This may consequently fix the second back press nullpointer issue. – jesobremonte Dec 12 '13 at 14:03
  • When I do that the first time I go into the directions page the back button doesn't function at all. So if I click another tab so I can get away, then go back to the info tab and click directions, then click back I get the same null pointer error. – LoneWolfPR Dec 12 '13 at 14:51
  • I've discovered the problem is that somehow the directionsview fragment is becoming detached from the Activity. Now I just need to figure out why. – LoneWolfPR Dec 12 '13 at 17:07

2 Answers2

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.

Daniel Handojo
  • 612
  • 5
  • 19
0

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.

shiladitya
  • 2,290
  • 1
  • 23
  • 36