Hi, I will share my experience while working with sliding menu without using Navigation Drawer and Action Bar.
Develop Android project with sliding menu with motion events and menu with expandable listview.
I created 5 parent groups and 3 children's for every even parent group. Instead of normal Strings, I tried to use Data Transfer Objects.
When parent group clicked, then child groups need to open if present.
When child group clicked , then parent group selection color needs to change.
we need to create two more layout files, one for menu drawer and another for main application. Then create new android xml file and named it as
I have kept a sliding button on top left of the layout to toggle the menu and create new android layout file and named it as
package com.shyam.smelsample.common;
import android.app.Activity;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
public class SimpleGestureFilter extends SimpleOnGestureListener{
public final static int SWIPE_UP = 1;
public final static int SWIPE_DOWN = 2;
public final static int SWIPE_LEFT = 3;
public final static int SWIPE_RIGHT = 4;
public final static int MODE_TRANSPARENT = 0;
public final static int MODE_SOLID = 1;
public final static int MODE_DYNAMIC = 2;
private final static int ACTION_FAKE = -13; //just an unlikely number
private int swipe_Min_Distance = 180;
private int swipe_Max_Distance = 300;
private int swipe_Min_Velocity = 100;
private int mode = MODE_DYNAMIC;
private boolean running = true;
private boolean tapIndicator = false;
private Activity context;
private GestureDetector detector;
private SimpleGestureListener listener;
public SimpleGestureFilter(Activity context,SimpleGestureListener sgl) {
this.context = context;
this.detector = new GestureDetector(context, this);
this.listener = sgl;
}
public void onTouchEvent(MotionEvent event){
if(!this.running)
return;
boolean result = this.detector.onTouchEvent(event);
if(this.mode == MODE_SOLID)
event.setAction(MotionEvent.ACTION_CANCEL);
else if (this.mode == MODE_DYNAMIC) {
if(event.getAction() == ACTION_FAKE)
event.setAction(MotionEvent.ACTION_UP);
else if (result)
event.setAction(MotionEvent.ACTION_CANCEL);
else if(this.tapIndicator){
event.setAction(MotionEvent.ACTION_DOWN);
this.tapIndicator = false;
}
}
//else just do nothing, it's Transparent
}
public void setMode(int m){
this.mode = m;
}
public int getMode(){
return this.mode;
}
public void setEnabled(boolean status){
this.running = status;
}
public void setSwipeMaxDistance(int distance){
this.swipe_Max_Distance = distance;
}
public void setSwipeMinDistance(int distance){
this.swipe_Min_Distance = distance;
}
public void setSwipeMinVelocity(int distance){
this.swipe_Min_Velocity = distance;
}
public int getSwipeMaxDistance(){
return this.swipe_Max_Distance;
}
public int getSwipeMinDistance(){
return this.swipe_Min_Distance;
}
public int getSwipeMinVelocity(){
return this.swipe_Min_Velocity;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
final float xDistance = Math.abs(e1.getX() - e2.getX());
final float yDistance = Math.abs(e1.getY() - e2.getY());
if(xDistance > this.swipe_Max_Distance || yDistance > this.swipe_Max_Distance)
return false;
velocityX = Math.abs(velocityX);
velocityY = Math.abs(velocityY);
boolean result = false;
if(velocityX > this.swipe_Min_Velocity && xDistance > this.swipe_Min_Distance){
if(e1.getX() > e2.getX()) // right to left
this.listener.onSwipe(SWIPE_LEFT);
else
this.listener.onSwipe(SWIPE_RIGHT);
result = true;
}
else if(velocityY > this.swipe_Min_Velocity && yDistance > this.swipe_Min_Distance){
if(e1.getY() > e2.getY()) // bottom to up
this.listener.onSwipe(SWIPE_UP);
else
this.listener.onSwipe(SWIPE_DOWN);
result = true;
}
return result;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
this.tapIndicator = true;
return false;
}
@Override
public boolean onDoubleTap(MotionEvent arg) {
this.listener.onDoubleTap();;
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent arg) {
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent arg) {
if(this.mode == MODE_DYNAMIC){ // we owe an ACTION_UP, so we fake an
arg.setAction(ACTION_FAKE); //action which will be converted to an ACTION_UP later.
this.context.dispatchTouchEvent(arg);
}
return false;
}
public static interface SimpleGestureListener{
void onSwipe(int direction);
void onDoubleTap();
}
}
package com.shyam.smelsample.gui;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.shyam.slidingmenuwithexpandablelistexample.R;
import com.shyam.smelsample.common.SimpleGestureFilter;
import com.shyam.smelsample.common.SimpleGestureFilter.SimpleGestureListener;
import com.shyam.smelsample.dto.CategoriesDTO;
import com.slidingview.MyHorizontalScrollView;
import com.slidingview.MyHorizontalScrollView.SizeCallback;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupClickListener;
import android.widget.ExpandableListView.OnGroupCollapseListener;
import android.widget.ExpandableListView.OnGroupExpandListener;
import android.widget.RelativeLayout.LayoutParams;
public class MainActivity extends Activity implements SimpleGestureListener
{
MyHorizontalScrollView scrollView;
View menu,app,btnSlide;
ExpandableListView categoryMenulistView;
private boolean menuOut = false;
HashMap<String, List<CategoriesDTO>> expListDataChild;
ArrayList<Integer> selectedData;
ArrayList<CategoriesDTO> categoryObjectsParentList;
ExpandableListAdapter listAdapter;
private SimpleGestureFilter detector;
int idCounter = 10;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = LayoutInflater.from(this);
scrollView = (MyHorizontalScrollView) inflater.inflate(R.layout.activity_main, null);
setContentView(scrollView);
// Detect touched area
detector = new SimpleGestureFilter(this,this);
menu = inflater.inflate(R.layout.menu_drawer, null);
app = inflater.inflate(R.layout.main_application, null);
btnSlide = app.findViewById(R.id.slideBtn);
btnSlide.setOnClickListener(new ClickListenerForScrolling(scrollView, menu));
categoryMenulistView = (ExpandableListView) menu.findViewById(R.id.categorylist);
selectedData = new ArrayList<Integer>();
prepareExpandableListData();
listAdapter = new ExpandableListAdapter(this, categoryObjectsParentList, expListDataChild);
categoryMenulistView.setAdapter(listAdapter);
// Listview Group click listener
categoryMenulistView.setOnGroupClickListener(new OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {
ImageView groupImageView = (ImageView)v.findViewById(R.id.groupImageView);
int clickedId = categoryObjectsParentList.get(groupPosition).getId();
//check here is any of this childs are in selected data, if selected remove them and its parent
//if not selected, add this id to selected data.
ArrayList<Integer> childIds =categoryObjectsParentList.get(groupPosition).getChildrenIds();
boolean childrenSelected = checkChildrenSelected(childIds);
boolean flagClicked = iterateSelectedData(clickedId); // if already selected, ID will be removed from list.
if(flagClicked)
{
groupImageView.setBackgroundResource(R.color.thickgray);
}
else
{
if(childrenSelected)
{
groupImageView.setBackgroundResource(R.color.thickgray);
}
else
{
groupImageView.setBackgroundResource(R.color.sky_blue);
selectedData.add(clickedId);
}
}
return false;
}
});
// Listview Group expanded listener
categoryMenulistView.setOnGroupExpandListener(new OnGroupExpandListener() {
@Override
public void onGroupExpand(int groupPosition) {
}
});
// Listview Group collasped listener
categoryMenulistView.setOnGroupCollapseListener(new OnGroupCollapseListener() {
@Override
public void onGroupCollapse(int groupPosition) {
}
});
// Listview on child click listener
categoryMenulistView.setOnChildClickListener(new OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
View groupView = parent.getExpandableListAdapter().getGroupView(groupPosition, true, null, (ViewGroup) v.getParent());
ImageView groupCatIcon = (ImageView) groupView.findViewById(R.id.groupImageView);
ImageView childImageView = (ImageView)v.findViewById(R.id.childImageView);
int childClickId = expListDataChild.get(categoryObjectsParentList.get(groupPosition).getTitle()).get(childPosition).getId();
int parentId = expListDataChild.get(categoryObjectsParentList.get(groupPosition).getTitle()).get(childPosition).getParentId();
ArrayList<Integer> childIds =categoryObjectsParentList.get(groupPosition).getChildrenIds();
boolean flagClicked = iterateSelectedData(childClickId);
if(flagClicked)
{
childImageView.setBackgroundResource(R.color.lightgray);
boolean counterFlag = checkOtherSiblingsClicked(childIds);
if(counterFlag == true)//remove parent id from selected data
{
removeParentId(parentId);
groupCatIcon.setBackgroundResource(R.color.thickgray);
}
else // add parent id in selected data
{
selectedData.add(parentId);
groupCatIcon.setBackgroundResource(R.color.sky_blue);
}
}
else
{
childImageView.setBackgroundResource(R.color.sky_blue);
selectedData.add(childClickId);
removeParentId(parentId);
groupCatIcon.setBackgroundResource(R.color.thickgray);
}
listAdapter.notifyDataSetChanged();
return false;
}
});
final View[] children = new View[] { menu, app };
// Scroll to app (view[1]) when layout finished.
int scrollToViewIdx = 1;
scrollView.initViews(children, scrollToViewIdx, new SizeCallbackForMenu(btnSlide));
}
public void removeParentId(int parentId)
{
for(int i=0;i<selectedData.size();i++)
{
if (selectedData.get(i) == parentId)
{
selectedData.remove(i);
break;
}
else
{
}
}
}
private boolean iterateSelectedData(int clickedId)
{
Boolean flagClicked = false;
for(int i=0;i<selectedData.size();i++)
{
if (selectedData.get(i) == clickedId)
{
flagClicked = true;
selectedData.remove(i);
break;
}
else
{
flagClicked = false;
}
}
return flagClicked;
}
protected Boolean checkChildrenSelected(ArrayList<Integer> childIds) {
Boolean flagClicked = false;
int selectedDataSize = selectedData.size();
for(int i=0;i<selectedDataSize;i++)
{
int SelectedId = 0;
if(selectedData.size()>0)
{
SelectedId = selectedData.get(0);
}
int childsDataSize = childIds.size();
for(int j=0;j<childsDataSize;j++)
{
if (childIds.get(j) == SelectedId)
{
flagClicked = true;
selectedData.remove(0);
break;
}
}
}
return flagClicked;
}
private boolean checkOtherSiblingsClicked(ArrayList<Integer> childIds) {
boolean flag = false;
for(int i=0;(i<childIds.size() && !flag);i++)
{
int siblingId = childIds.get(i);
for(int j=0;j<selectedData.size();j++)
{
if(siblingId == selectedData.get(j))//remove parent id from selected data
{
flag = true;
break;
}
else
{
flag = false;
}
}
}
return flag;
}
private void prepareExpandableListData() {
expListDataChild = new HashMap<String,List<CategoriesDTO>>();
categoryObjectsParentList = new ArrayList<CategoriesDTO>();
//creating 5 parents
for(int i=1;i<=5;i++)
{
CategoriesDTO dto = new CategoriesDTO();
dto.setId(i);
dto.setTitle("Parent No-"+i);
dto.setParentId(-1);
if(i % 2 == 0)
{
//creating 3 children to each even number parent
ArrayList<CategoriesDTO> childrenList = new ArrayList<CategoriesDTO>();
ArrayList<Integer> childIds = new ArrayList<Integer>();
for(int j=1;j<=3;j++)
{
CategoriesDTO childdto = new CategoriesDTO();
childdto.setId(idCounter);
childdto.setTitle("children No-"+j);
childdto.setParentId(i);
childrenList.add(childdto);
childIds.add(idCounter);
idCounter++;
}
dto.setChildrenIds(childIds);
expListDataChild.put(dto.getTitle(), childrenList);
}
else
{
dto.setChildrenIds(new ArrayList<Integer>());
expListDataChild.put(dto.getTitle(),new ArrayList<CategoriesDTO>());
}
categoryObjectsParentList.add(dto); //adding parent to ArrayList
}
}
private void openDrawerMenu()
{
// Scroll to 0 to reveal menu
int left = 0;
scrollView.smoothScrollTo(left, 0);
menuOut = true;
}
public void closeDrawerFunction(View v)
{
int menuWidth = menu.getMeasuredWidth();
// Scroll to menuWidth so menu isn't on screen.
menuOut = false;
int left = menuWidth;
scrollView.smoothScrollTo(left, 0);
}
/**
* Helper for examples with a HSV that should be scrolled by a menu View's width.
*/
class ClickListenerForScrolling implements OnClickListener {
HorizontalScrollView scrollView;
View menu;
RelativeLayout categoryRelView;
/**
* Menu must NOT be out/shown to start with.
*/
public ClickListenerForScrolling(HorizontalScrollView scrollView, View menu) {
super();
this.scrollView = scrollView;
this.menu = menu;
categoryRelView = (RelativeLayout)menu.findViewById(R.id.category_relView);
}
@Override
public void onClick(View v) {
int menuWidth = menu.getMeasuredWidth();
// Ensure menu is visible
menu.setVisibility(View.VISIBLE);
if (!menuOut) {
// Scroll to 0 to reveal menu
int left = 0;
scrollView.smoothScrollTo(left, 0);
} else {
// Scroll to menuWidth so menu isn't on screen.
int left = menuWidth;
scrollView.smoothScrollTo(left, 0);
}
menuOut = !menuOut;
}
}
/**
* Helper that remembers the width of the 'slide' button, so that the 'slide' button remains in view, even when the menu is
* showing.
*/
class SizeCallbackForMenu implements SizeCallback {
int btn1width;
View btn1;
LayoutParams lp ;
public SizeCallbackForMenu(View btnSlide) {
super();
this.btn1 = btnSlide;
}
@Override
public void onGlobalLayout() {
lp = (LayoutParams) btn1.getLayoutParams();
btn1width = btn1.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
}
@Override
public void getViewSize(int idx, int w, int h, int[] dims) {
dims[0] = w;
dims[1] = h;
final int menuIdx = 0;
if (idx == menuIdx) {
dims[0] = w - btn1width;
}
}
}
public class ExpandableListAdapter extends BaseExpandableListAdapter {
private Context _context;
private ArrayList<CategoriesDTO> catObjectsList;
private HashMap<String,List<CategoriesDTO>> catChildObjectsData;
public ExpandableListAdapter(Context context, List<String> listDataHeader,
HashMap<String, List<String>> listChildData) {
this._context = context;
}
public ExpandableListAdapter(Context context,ArrayList<CategoriesDTO> categoryObjectsList,
HashMap<String, List<CategoriesDTO>> expListDataChild2) {
this._context = context;
this.catObjectsList = categoryObjectsList;
this.catChildObjectsData = expListDataChild2;
}
@Override
public Object getChild(int groupPosition, int childPosititon) {
return this.catChildObjectsData.get(this.catObjectsList.get(groupPosition).getTitle()).get(childPosititon).getTitle();
}
public Object getChildObject(int groupPosition, int childPosititon) {
return this.catChildObjectsData.get(this.catObjectsList.get(groupPosition).getTitle()).get(childPosititon);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public View getChildView(int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
CategoriesDTO dto = (CategoriesDTO) getChildObject(groupPosition, childPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) this._context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.custom_expandable_menu_child, null);
}
ImageView catChildIcon = (ImageView)convertView.findViewById(R.id.childImageView);
boolean flagClicked = iterateSelectedData(dto.getId());
if(flagClicked)
{
catChildIcon.setBackgroundResource(R.color.sky_blue);
}
else
{
catChildIcon.setBackgroundResource(R.color.mediumgray);
}
TextView txtListChild = (TextView) convertView.findViewById(R.id.lblListItem);
String childTitle = Html.fromHtml(dto.getTitle()).toString();
txtListChild.setText(childTitle);
return convertView;
}
private boolean iterateSelectedData(int clickedId)
{
Boolean flagClicked = false;
for(int i=0;i<selectedData.size();i++)
{
if (selectedData.get(i) == clickedId)
{
flagClicked = true;
break;
}
else
{
flagClicked = false;
}
}
return flagClicked;
}
@Override
public int getChildrenCount(int groupPosition) {
return this.catChildObjectsData.get(this.catObjectsList.get(groupPosition).getTitle()).size();
}
@Override
public Object getGroup(int groupPosition) {
return this.catObjectsList.get(groupPosition).getTitle();
}
@Override
public int getGroupCount() {
int count = 0;
for(int i=0;i<catObjectsList.size();i++)
{
CategoriesDTO dto = catObjectsList.get(i);
if(dto.getParentId()==-1)
{
count++;
}
}
return count;
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
String headerTitle = (String) getGroup(groupPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) this._context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.custom_expandable_menu_group, null);
}
TextView lblListHeader = (TextView) convertView.findViewById(R.id.lblListHeader);
ImageView catIcon = (ImageView)convertView.findViewById(R.id.groupImageView);
boolean flagClicked = iterateSelectedData(catObjectsList.get(groupPosition).getId());
if(flagClicked)
{
catIcon.setBackgroundResource(R.color.sky_blue);
}
else
{
catIcon.setBackgroundResource(R.color.thickgray);
}
String parentTitle = Html.fromHtml(headerTitle).toString();
lblListHeader.setText(parentTitle);
return convertView;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent me){
// Call onTouchEvent of SimpleGestureFilter class
this.detector.onTouchEvent(me);
return super.dispatchTouchEvent(me);
}
@Override
public void onSwipe(int direction) {
switch (direction) {
case SimpleGestureFilter.SWIPE_RIGHT :
if(!menuOut)//if menu drawer is not opened
{
openDrawerMenu();
}
break;
case SimpleGestureFilter.SWIPE_LEFT :
if(menuOut)//if menu drawer is opened
{
closeDrawerFunction(null);
}
break;
case SimpleGestureFilter.SWIPE_DOWN :
break;
case SimpleGestureFilter.SWIPE_UP :
break;
}
}
@Override
public void onDoubleTap() {
}
}
Thats it! Now run the program and see the application.