0

I've been developing a project for class and I have reached a point that after much searching in google still cannot find the solution.

This is the error I get

06-04 15:07:55.398    3480-3480/com.example.chema.agenda E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.chema.agenda, PID: 3480
java.lang.NullPointerException
        at com.example.chema.agenda.MainActivity_detalleFragment.onActivityCreated(MainActivity_detalleFragment.java:50)
        at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1797)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:979)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1138)
        at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:740)
        at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1501)
        at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:458)
        at android.os.Handler.handleCallback(Handler.java:808)
        at android.os.Handler.dispatchMessage(Handler.java:103)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:5296)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:824)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:640)
        at dalvik.system.NativeStart.main(Native Method)

This is the class where I get the error when Im rotating from landscape to portrait.

public class MainActivity_detalleFragment extends Fragment {

public MainActivity_detalleFragment() {
}


public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // retain this fragment

}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    ViewGroup root = (ViewGroup) inflater
            .inflate(R.layout.fragment_detalle, null);


    return root;

}
public void onActivityCreated(Bundle savedInstanceState){
    super.onActivityCreated(savedInstanceState);
    TextView campoTexto = (TextView)getActivity().findViewById(R.id.textViewNombre);
    TextView campoTexto2 = (TextView)getActivity().findViewById(R.id.textView8);
    ImageView im = (ImageView)getActivity().findViewById(R.id.imageView);
    im.setImageBitmap(MainActivity.getContacto().getImagen());
    campoTexto.setText(MainActivity.getContacto().getName() +" "+  MainActivity.getContacto().getApellidos());
    campoTexto2.setText(MainActivity.getContacto().getPhoneNumber());


}

}

Exactly in this lane im.setImageBitmap(MainActivity.getContacto().getImagen());

And this is my main activity

public class MainActivity extends ActionBarActivity implements FragmentListar.OnItemClickedListener{
private DrawerLayout desplegable;
private ActionBarDrawerToggle toggle;
Integer[] imgid = {
        R.mipmap.create_contact,
        R.mipmap.delete_action_bar,
        R.mipmap.edit_contact
};
public ListView opciones;
private boolean dosFragmentos;
static Contact contacto;

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    String[] valores = getResources().getStringArray(R.array.despl);
    CustomListAdapter adapter = new CustomListAdapter(this, valores, imgid);
    desplegable = (DrawerLayout) findViewById(R.id.drawer_layout);
    opciones = (ListView) findViewById(R.id.left_drawer);
    opciones.setAdapter(adapter);

    toggle = new ActionBarDrawerToggle(this, desplegable, R.mipmap.white_background,
            R.string.drawer_open, R.string.drawer_close);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().setHomeButtonEnabled(true);

    FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
    tx.replace(R.id.content_frame, Fragment.instantiate(MainActivity.this, "com.example.chema.agenda.FragmentListar"));
    tx.commit();

    opciones.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            TextView c = (TextView) view.findViewById(R.id.item);
            String item = c.getText().toString();
            if (item.equals("Crear")) {
                desplegable.setDrawerListener(new DrawerLayout.SimpleDrawerListener() {
                    @Override
                    public void onDrawerClosed(View drawerView) {
                        super.onDrawerClosed(drawerView);
                        if(getResources().getConfiguration().orientation== Configuration.ORIENTATION_LANDSCAPE){
                            FragmentManager fragMgr = getSupportFragmentManager();
                            Fragment currentFragment = fragMgr.findFragmentById(R.id.content_frame2);
                            FragmentTransaction fragTrans = fragMgr.beginTransaction();
                            fragTrans.remove(currentFragment);

                            FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
                            tx.replace(R.id.content_frame_landscape, Fragment.instantiate(MainActivity.this, "com.example.chema.agenda.CountFragment"));
                            tx.commit();
                        }else {
                            FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
                            tx.replace(R.id.content_frame, Fragment.instantiate(MainActivity.this, "com.example.chema.agenda.CountFragment"));
                            tx.commit();
                        }
                    }
                });
                desplegable.closeDrawer(opciones);
            }
            if (item.equals("Borrar")) {
                desplegable.setDrawerListener(new DrawerLayout.SimpleDrawerListener() {
                    @Override
                    public void onDrawerClosed(View drawerView) {
                        super.onDrawerClosed(drawerView);
                        FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
                        tx.replace(R.id.content_frame, Fragment.instantiate(MainActivity.this, "com.example.chema.agenda.FragmentDelete"));
                        tx.commit();
                    }
                });
                desplegable.closeDrawer(opciones);
            }
        }
    });



}

public void onBackPressed() {
    Log.d("CDA", "onBackPressed Called");
    Intent setIntent = new Intent(this, MainActivity.class);

    startActivity(setIntent);
}

public void OnItemClicked(int id) {
    DatabaseHandler d1 = new DatabaseHandler(this.getApplicationContext());
    contacto = d1.getContact(id);


    FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
    if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
        tx.replace(R.id.content_frame2, Fragment.instantiate(MainActivity.this, "com.example.chema.agenda.MainActivity_detalleFragment"));
        tx.commit();
    }

    else {
        if ((getResources().getConfiguration().screenLayout &
                Configuration.SCREENLAYOUT_SIZE_MASK) ==
                Configuration.SCREENLAYOUT_SIZE_LARGE) {
            tx.replace(R.id.content_frame2, Fragment.instantiate(MainActivity.this, "com.example.chema.agenda.MainActivity_detalleFragment"));
            tx.commit();

        } else {
            tx.replace(R.id.content_frame, Fragment.instantiate(MainActivity.this, "com.example.chema.agenda.MainActivity_detalleFragment"));
            tx.commit();
        }
    }

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}



@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (toggle.onOptionsItemSelected(item)) {

    }
    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}
protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    toggle.syncState();
}

public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    toggle.onConfigurationChanged(newConfig);


    // Checks the orientation of the screen

}
static public Contact getContacto(){
    return contacto;
}

And my FragmentListar class:

public class FragmentListar extends ListFragment {

private List<Contact> c1;
OnItemClickedListener mListener = sDummyCallbacks;

private static OnItemClickedListener sDummyCallbacks = new OnItemClickedListener() {
    @Override
    public void OnItemClicked(int id) {
    }
};

public FragmentListar() {
    c1 = null;
}


public interface OnItemClickedListener {
    public void OnItemClicked(int id);
}

public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnItemClickedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement OnItemClickedListener");
    }
}

@Override
public void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);

    DatabaseHandler d1 = new DatabaseHandler(getActivity().getApplicationContext());
    this.c1 = d1.getAllContacts();

    d1.close();

    // Estable cemos el Adapter a la Lista del Fragment
    setListAdapter(new ContactListAdapter(getActivity(),
            c1));
}

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    // TODO Auto-generated method stub
    super.onListItemClick(l, v, position, id);

    // Mostramos un mensaje con el elemento pulsado


    mListener.OnItemClicked(c1.get(position).getID());

}

I don't really know why MainActivity_detalleFragment is called once my MainActivity restarts on rotation. It should be FragmentListar the one called.

Fixing contacto to be non-static

public void OnItemClicked(int id) {

    Bundle bundle = new Bundle();
    bundle.putString("id", Integer.toString(id));
    // set Fragmentclass Arguments
    MainActivity_detalleFragment fragobj = new MainActivity_detalleFragment();
    fragobj.setArguments(bundle);

    FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
    if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
        tx.replace(R.id.content_frame2, Fragment.instantiate(MainActivity.this, "com.example.chema.agenda.MainActivity_detalleFragment"));
        tx.commit();
    }

    else {
        if ((getResources().getConfiguration().screenLayout &
                Configuration.SCREENLAYOUT_SIZE_MASK) ==
                Configuration.SCREENLAYOUT_SIZE_LARGE) {
            tx.replace(R.id.content_frame2, Fragment.instantiate(MainActivity.this, "com.example.chema.agenda.MainActivity_detalleFragment"));
            tx.commit();

        } else {
            tx.replace(R.id.content_frame, Fragment.instantiate(MainActivity.this, "com.example.chema.agenda.MainActivity_detalleFragment"));
            tx.commit();
        }
    }

}
  • Just out of curiosity. Why do you need to know that? – Emanuel Moecklin Jun 04 '15 at 01:37
  • Well I'm developing a contact app. When I start mainActivity it gets contacts already created from sqlite database. Since in my portrait mainactivity xml I just have the list while in the landscape I've both list and details if you click in any contact. If I add android:configChanges="keyboardHidden|orientation|screenSize" in the manifest it doesnt work out pretty well – Jose Maria Garcia Jun 04 '15 at 01:40
  • I'd like to have different xml for portrait and landscape but since main activity resets everytime you rotate your device and everytime that happens it loads contacts from the bbdd every xml gets overwrite with my main_activity layout – Jose Maria Garcia Jun 04 '15 at 01:46
  • 1
    In that case your approach is wrong. Use two fragments for the the list and the detail view. Use a Loader to retrieve the list which will retain its search results across orientation changes. See http://developer.android.com/reference/android/support/v4/content/Loader.html – Emanuel Moecklin Jun 04 '15 at 02:44
  • Yeah I use two frameLayouts , one for the list and one for the detail. The problem I'm facing is that when I go back from the two fragments landscape(list and detail) to the one fragment portrait(list) my app stops working due to null point exception – Jose Maria Garcia Jun 04 '15 at 10:33
  • Fix the NullPointerExceptions then ;-). I'm sure you don't need to know which xml layout was active before an orientation change to do that. Post some code and the stack trace and maybe someone can help you fix it. – Emanuel Moecklin Jun 04 '15 at 12:13
  • Already updated with some code. The main problem is MainActivity_detalleFragment is called when it shouldnt' . – Jose Maria Garcia Jun 04 '15 at 13:17

1 Answers1

0

Never ever use static methods to retrieve information from an Activity!

What happens here is that contacto is populated once you click an item in your list. That variable and whatever it points to is not retained across a screen orientation meaning after you rotate the screen it will be Null, hence the NullPointerException.

There's honestly a couple of things wrong with your code, the question though is about the NPE so I'll focus on that one here.

The quick'n'dirty way to fix this is to check whether getContacto() returns Null or not. Also make the MainActivity.getContacto() a non-static method (getActivity().getContacto()).

What you need to dig in though is why the detail fragment is retained at all as the layout should probably not contain the detail fragment but the list fragment, right?

What you need to consider is using a Loader as mentioned in my comment to the question. Loader results are retained across orientation changes and you'd not run into this issue at all. Also it's obviously more efficient if you don't have to reload after each orientation change.

Also to consider is why you need to retrieve the contact in the Activity. As far as I can tell from your code you need it in the detail fragment so why not just move this code into your fragment?

DatabaseHandler d1 = new DatabaseHandler(this.getApplicationContext());
contacto = d1.getContact(id);

Of course the fragment needs to know the id but that's a good thing since that's really information that the detail fragment should have.

Emanuel Moecklin
  • 28,488
  • 11
  • 69
  • 85
  • Yeah I know it will be null but as far as I can see MainActivity shouldn't be calling fragmentdetails until I click on one item of the list so it shouldn't be a problem, right? About the non-static method. How would I call then 'getContacto()' from MainActivity_detalleFragment? Finally, you're right about the Database , I should not call that on the MainActivity. Thanks Emanuel , I really appreciate your answer! – Jose Maria Garcia Jun 04 '15 at 17:28
  • Regarding the last thing you said, about passing the id to the Fragment. Would this work? Posting it on the main post. – Jose Maria Garcia Jun 04 '15 at 17:58