Search code examples
androidandroid-fragmentsactionbarsherlockandroid-menuslidingmenu

How can I make a fragment replace actionbar menu behavior?


Before I start, yes I have read countless related questions. I still can't seem to track down the issue.

I have a SherlockFragmentActivity:

package com.kicklighterdesignstudio.floridaday;

import android.os.Bundle;
import android.support.v4.app.Fragment;

public class FragmentActivity extends BaseActivity {

public static final String TAG = "FragmentActivity";

public static final int SCHEDULE_FRAGMENT = 0;
public static final int MAP_FRAGMENT = 1;
public static final int FOOD_FRAGMENT = 2;
public static final int TWITTER_FRAGMENT = 3;
public static final int HASHTAG_FRAGMENT = 4;

private Fragment mContent;

public FragmentActivity() {
    super(R.string.main_activity_title);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.content_frame);
    switchContent(SCHEDULE_FRAGMENT);
    setBehindContentView(R.layout.menu_frame);

}

public void switchContent(int id) {
    getFragment(id);
    getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, mContent)
            .commit();
    getSlidingMenu().showContent();
}

private void getFragment(int id) {
    switch (id) {
    case 0:
        mContent = new ScheduleFragment();
        break;

    case 1:
        mContent = new FoodFragment();
        break;

    case 2:
        mContent = new MapFragment();
        break;

    case 3:
        mContent = new TwitterFragment();
        break;

    case 4:
        mContent = new HashtagFragment();
        break;
    }
}

}

It extends BaseActivity:

package com.kicklighterdesignstudio.floridaday;

import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.ListFragment;

import com.actionbarsherlock.view.MenuItem;
import com.slidingmenu.lib.SlidingMenu;
import com.slidingmenu.lib.app.SlidingFragmentActivity;

public class BaseActivity extends SlidingFragmentActivity {

//  private int mTitleRes;
protected ListFragment mFrag;

public BaseActivity(int titleRes) {
//      mTitleRes = titleRes;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

//      setTitle(mTitleRes);
    setTitle("");

    // set the Behind View
    setBehindContentView(R.layout.menu_frame);
    if (savedInstanceState == null) {
        FragmentTransaction t = this.getSupportFragmentManager().beginTransaction();
        mFrag = new MenuFragment();
        t.replace(R.id.menu_frame, mFrag);
        t.commit();
    } else {
        mFrag = (ListFragment) this.getSupportFragmentManager().findFragmentById(
                R.id.menu_frame);
    }

    // customize the SlidingMenu
    SlidingMenu sm = getSlidingMenu();
    sm.setShadowWidthRes(R.dimen.shadow_width);
    sm.setShadowDrawable(R.drawable.shadow);
    sm.setBehindOffsetRes(R.dimen.slidingmenu_offset);
    sm.setFadeDegree(0.35f);
    sm.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);
    sm.setBehindScrollScale(0.0f);

    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case android.R.id.home:
        toggle();
        return true;
    default:
        return super.onOptionsItemSelected(item);
    }
}

}

Right now, I'm only concerned with ScheduleFragment:

package com.kicklighterdesignstudio.floridaday;

import java.util.ArrayList;

import android.app.Activity;
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.ListView;

import com.actionbarsherlock.app.SherlockFragment;

public class ScheduleFragment extends SherlockFragment implements OnItemClickListener {

public static final String TAG = "ScheduleFragment";

private ArrayList<ScheduleItem> schedule;
private FloridaDayApplication app;

public ScheduleFragment() {
//      setRetainInstance(true);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    app = (FloridaDayApplication) activity.getApplication();
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    return inflater.inflate(R.layout.schedule_fragment, container, false);
}

@Override
public void onStart() {
    super.onStart();
    getSherlockActivity().getSupportActionBar().setTitle(R.string.schedule_fragment);

    schedule = app.getSchedule();

    ListView scheduleListView = (ListView) getActivity().findViewById(R.id.schedule_list);
    ScheduleItemAdapter adapter = new ScheduleItemAdapter(getActivity(), schedule);
    scheduleListView.setAdapter(adapter);
    scheduleListView.setOnItemClickListener(this);
}

@Override
public void onItemClick(AdapterView<?> a, View v, int id, long position) {

    Fragment newFragment = new ScheduleItemFragment(id);

    FragmentTransaction transaction = getSherlockActivity().getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.content_frame, newFragment).addToBackStack(null).commit();

}

}

ScheduleFragment displays a list of schedule items. When clicked, a new fragment is displayed (ScheduleItemFragment) to show details of the item and a map to its location:

package com.kicklighterdesignstudio.floridaday;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.actionbarsherlock.app.SherlockFragment;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.model.MarkerOptions;

@SuppressLint("ValidFragment")
public class ScheduleItemFragment extends SherlockFragment {

public static final String TAG = "ScheduleItemFragment";

private int scheduleItemId;
private FloridaDayApplication app;
private ScheduleItem scheduleItem;
private MapView mapView;
private GoogleMap map;

public ScheduleItemFragment(int id) {
    scheduleItemId = id;
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    app = (FloridaDayApplication) activity.getApplication();
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.schedule_item_fragment, container, false);
    setHasOptionsMenu(true);

    // Initialize MapView
    mapView = (MapView) v.findViewById(R.id.map_view);
    mapView.onCreate(savedInstanceState);

    return v;
}

@Override
public void onStart() {
    super.onStart();
    scheduleItem = app.getSchedule().get(scheduleItemId);
    getSherlockActivity().getSupportActionBar().setTitle(scheduleItem.getTitle());

    // Map Stuff
    map = mapView.getMap();
    if (map != null) {
        map.getUiSettings();
        map.setMyLocationEnabled(true);

        try {
            MapsInitializer.initialize(getActivity());
        } catch (GooglePlayServicesNotAvailableException e) {
            e.printStackTrace();
        }

        // Add marker to the map
        MarkerOptions options = new MarkerOptions().position(scheduleItem.getLocation().getPosition()).title(
                scheduleItem.getLocation().getTitle());
        map.addMarker(options);

        // Adjust Camera Programmatically
        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(
                scheduleItem.getLocation().getPosition(), 14);
        map.animateCamera(cameraUpdate);
    } else {
        Log.i(TAG, "Map is null");
    }

    // Other Views
    TextView title = (TextView) getSherlockActivity().findViewById(R.id.title);
    TextView time = (TextView) getSherlockActivity().findViewById(R.id.time);
    TextView description = (TextView) getSherlockActivity().findViewById(R.id.description);

    title.setText(scheduleItem.getTitle());
    time.setText(scheduleItem.getDateTimeString());
    description.setText(scheduleItem.getDescription());

}

// TODO: Make this work
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case android.R.id.home:
        getFragmentManager().popBackStack();
        return true;
    default:
        return super.onOptionsItemSelected(item);
    }
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
}

@Override
public void onDestroy() {
    mapView.onDestroy();
    super.onDestroy();
}

@Override
public void onLowMemory() {
    mapView.onLowMemory();
    super.onLowMemory();
}

@Override
public void onPause() {
    mapView.onPause();
    super.onPause();
}

@Override
public void onResume() {
    mapView.onResume();
    super.onResume();
}

@Override
public void onSaveInstanceState(Bundle outState) {
    mapView.onSaveInstanceState(outState);
    super.onSaveInstanceState(outState);
}

}

Thanks to BaseActivity, the icon in my ActionBar is clickable. It toggles the Sliding Menu. When viewing a ScheduleItemFragment, I would like the icon to return to the previous item in the backstack (which you can see I'm trying to do.) No matter what I try, the icon always toggles the Sliding Menu. Any thoughts on how to guarantee my Fragment gains control of the ActionBar menu clicks?


Solution

  • you need to call setHasOptionsMenu(true); in onCreate, not onCreateView

    also I'm interested what happens when you debug, does onOptionsItemSelected still get called?

    edit: add the following to your fragment

    @Override
    public void onCreate(Bundle savedInstanceState) {
        setHasOptionsMenu(true);
        super.onCreate(savedInstanceState);
    }
    

    edit1: there may be a better way to do this but I'm not exactly sure how your fragments are setup out, create a public field in baseActivity public bool isItemFragmentOnTop now onOptionsItemSelected in the case of the home button getting press do this

    if (isItemFragmentOnTop){
       getFragmentManager().popBackStack();
       isItemFragmentOnTop = false;
    } else {
       toggle();
    }
    return true;
    

    then in your fragment you can call ((BaseActivity)getActivity).isItemFragmentOnTop = true; to make the home button pop the back stack, you would want to do this when you display your fragment onItemClick in your list view.