First of all I need to say, that I was cunning with title. This post is not about menu customization. It's about creating pseudo-menu.
So, requirement was to create screen-wide menu with icons. Also from image you can see, that menu appears below toolbar and doesn't overlay it.
Of course I googled this question. And appears, that Google want us to keep menu grey, flat and simple. Here is interesting article about this problem:
Android: How can you Implement a Custom Menu class.
I can't resist to quote it:
My question is to Google engineers. Why? Why is this so? Why can’t I pass in a custom Theme to my Menus? Why can’t I choose how to Style them? Yes, I can understand you not wanting some devious developer taking control of a phone by disabling the actions of all the phone’s buttons but the Menu button?My idea is pretty simple. Instead of menu I use Fragment, that I show in every activity on press on menu icon. I use Relative layout to place this MenuFragment below Toolbar. And yes, I use Toolbar from support library instead of action bar. This gives more flexibility.
But let's explore some code.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/app_bar"
layout="@layout/app_bar" />
<fragment xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fragment"
android:name="com.shakenbeer.customoptionsmenu.MainActivityFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/app_bar"
tools:layout="@layout/fragment_main" />
<FrameLayout
android:id="@+id/fragment_menu_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/app_bar"></FrameLayout>
</RelativeLayout>
This is how activity could look like. I used the same layout for every activity in my project. FrameLayout is a container for menu.
Also here is BaseActivity classm that contains logic for calling menu and choosing menu item:
public class BaseActivity extends AppCompatActivity implements MenuFragment.MenuListener {
protected Toolbar toolbar;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.app_bar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowTitleEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setElevation(0f);
}
@Override public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
@Override public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_menu: {
triggerFragmentMenu();
return true;
}
}
return super.onOptionsItemSelected(item);
}
private void triggerFragmentMenu() {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment menuFragment = fragmentManager.findFragmentById(R.id.fragment_menu_container);
if (menuFragment == null) {
MenuFragment fragment = MenuFragment.newInstance();
fragmentTransaction.replace(R.id.fragment_menu_container, fragment);
} else {
fragmentTransaction.remove(menuFragment);
}
fragmentTransaction.commit();
}
@Override public void onMenuItemSelected(int menuPosition) {
removeMenuFragment();
MenuContent.MenuItem menuItem = MenuContent.ITEMS.get(menuPosition);
Intent intent = new Intent(this, menuItem.activityClass);
startActivity(intent);
}
@Override public void onBackPressed() {
if (!removeMenuFragment())
super.onBackPressed();
}
private boolean removeMenuFragment() {
FragmentManager fragmentManager = getFragmentManager();
Fragment menuFragment = fragmentManager.findFragmentById(R.id.fragment_menu_container);
if (menuFragment != null) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(menuFragment);
fragmentTransaction.commit();
return true;
} else {
return false;
}
}}
Menu contains only one item and this item is always shown as action. This simple trick removes standard overflow menu icon from toolbar.
When we click on menu icon - we add MenuFragment to activity or remove it if it's already here.
When we click on menu item - we remove MenuFragment and start corresponding activity.
Here is sources of MenuFragment:
public class MenuFragment extends Fragment { public interface MenuListener { public void onMenuItemSelected(int menuPosition); } private MenuListener listener; public static MenuFragment newInstance() { MenuFragment fragment = new MenuFragment(); return fragment; } public MenuFragment() { // Required empty public constructor } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_menu, container, false); ListView menuListView = (ListView) view.findViewById(R.id.fragment_menu_listview); menuListView.setAdapter(new MenuAdapter(getActivity())); menuListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
listener.onMenuItemSelected(position);
}
});
return view;
}
@Override public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (MenuListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement MenuListener");
}
}
}
MenuFrament contains MenuListener, any activity should implements it and could override BaseActivity implementation do perform whatever you want. Starting new activity from menu - it's just an example.
Full sources: https://github.com/Shakenbeer/CustomOptionsMenu
No comments:
Post a Comment