In Brief: This article will help you on building sliding menu and expandable description window in android xamarin with simple steps using Animation and Touch detection.
In my previous post shared my thought about How to draw route between two geo-location in a google map xamrin iOS, Best Practice and issues with ListView in Android Xamarin,
How to use Google Place API with Autocomplete in Xamarin Android, Integrating Login by Google Account in Xamarin.Android,How to avoid ImageBitmap OutOfMemoryException and Rounded corner Image in android Xamarin,Drawing path between two location in xamarin android.
In Detail:
Sliding menu is now became the one of the most common feature in a mobile application. This menu is hidden and can be shown by swiping the screen from left to right or tapping the icon on action bar,and in the same way hides on swipe from right to left or tap any where on the screen.
In steps:
1.Edit the layout file as follow :
Listview is used to bind the menu items and Textview to show expandable description window in the bottom of the screen.
Here Listview is placed below in the layout file for the higher the z-index value.
How to use Google Place API with Autocomplete in Xamarin Android, Integrating Login by Google Account in Xamarin.Android,How to avoid ImageBitmap OutOfMemoryException and Rounded corner Image in android Xamarin,Drawing path between two location in xamarin android.
Sliding menu is now became the one of the most common feature in a mobile application. This menu is hidden and can be shown by swiping the screen from left to right or tapping the icon on action bar,and in the same way hides on swipe from right to left or tap any where on the screen.
Here i have implemented this based on swiping gestures and animation. As we know touch gestures are most user friendly interaction in mobile application and animation are key to designing a pleasant user experience. Sliding menu can also be achieved by using "Navigation Drawer",but in this post i didn't made use of that concept.This below mentioned procedure will be helpful to those who are looking for a sliding menu implementation with quicker and lesser coding :) ;)
In steps:
1.Edit the layout file as follow :
Listview is used to bind the menu items and Textview to show expandable description window in the bottom of the screen.
Here Listview is placed below in the layout file for the higher the z-index value.
Main.axml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:minWidth="25px" android:minHeight="25px"> <RelativeLayout android:id="@+id/titleBarLinearLayout" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="55dp" android:background="#ff46a1e1"> <ImageView android:id="@+id/menuIconImgView" android:src="@drawable/menu_icon" android:scaleType="fitXY" android:layout_height="fill_parent" android:layout_width="50dp" android:padding="0dp" android:requiresFadingEdge="none" android:fadingEdge="none" android:layout_marginTop="2dp" android:layout_marginBottom="2dp" /> <TextView android:id="@+id/txtActionBarText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:text="Home" android:layout_gravity="center" android:clickable="true" android:layout_centerVertical="true" android:textSize="18dp" android:layout_centerHorizontal="true" /> </RelativeLayout> <TextView android:id="@+id/txtPage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:text="Home" android:layout_gravity="center" android:clickable="true" android:layout_centerVertical="true" android:textSize="25dp" android:layout_centerHorizontal="true" android:textStyle="bold" /> <TextView android:id="@+id/txtDescription" android:layout_width="match_parent" android:layout_height="100dp" android:textColor="#000000" android:text="Desription goes here" android:layout_gravity="center" android:clickable="true" android:textSize="18dp" android:layout_above="@+id/btnImgExpander" android:background="#FFFFFF" android:gravity="center" android:visibility="gone" /> <ImageView android:id="@+id/btnImgExpander" android:layout_alignParentBottom="true" android:layout_height="30dp" android:layout_width="match_parent" android:src="@drawable/up_arrow" android:background="#fff2f2f2" /> <ListView android:id="@+id/menuListView" android:layout_below="@+id/titleBarLinearLayout" android:background="#ff64bbf8" android:divider="#CFEBFF" android:dividerHeight="1dp" android:layout_marginLeft="0dp" android:layout_height="match_parent" android:layout_width="match_parent" android:visibility="gone" /> </RelativeLayout>
2: create custom layout for Menu item Listview
MenuCustomLayout.axml
MenuCustomLayout.axml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ff64bbf8"> <ImageView android:id="@+id/ivMenuImg" android:src="@drawable/Icon" android:layout_width="30dp" android:layout_height="30dp" android:padding="1dp" android:layout_marginLeft="20dp" android:layout_marginTop="15dp" android:layout_marginBottom="9.2dp" android:layout_gravity="center_vertical" /> <TextView android:id="@+id/txtMnuText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="18dp" android:layout_marginLeft="65dp" android:text="suchith" android:textSize="16dp" android:typeface="sans" android:textColor="#FFFFFF" /> </RelativeLayout>
3. Write a customListview adapter class
String array of menu text and menu icon passed as argument
String array of menu text and menu icon passed as argument
public class MenuListAdapterClass : BaseAdapter<string> { Activity _context; string[] _mnuText; int[] _mnuUrl; //action event to pass selected menu item to main activity internal event Action<string> actionMenuSelected; public MenuListAdapterClass(Activity context,string[] strMnu,int[] intImage) { _context = context; _mnuText = strMnu; _mnuUrl = intImage; } public override string this[int position] { get { return this._mnuText[position]; } } public override int Count { get { return this._mnuText.Count(); } } public override long GetItemId(int position) { return position; } public override View GetView(int position, View convertView, ViewGroup parent) { MenuListViewHolderClass objMenuListViewHolderClass; View view; view = convertView; if ( view == null ) { view= _context.LayoutInflater.Inflate ( Resource.Layout.MenuCustomLayout , parent , false ); objMenuListViewHolderClass = new MenuListViewHolderClass (); objMenuListViewHolderClass.txtMnuText = view.FindViewById<TextView> ( Resource.Id.txtMnuText ); objMenuListViewHolderClass.ivMenuImg=view.FindViewById<ImageView> ( Resource.Id.ivMenuImg ); objMenuListViewHolderClass.initialize ( view ); view.Tag = objMenuListViewHolderClass; } else { objMenuListViewHolderClass = ( MenuListViewHolderClass ) view.Tag; } objMenuListViewHolderClass.viewClicked = () => { if ( actionMenuSelected != null ) { actionMenuSelected ( _mnuText[position] ); } }; objMenuListViewHolderClass.txtMnuText.Text = _mnuText [position]; objMenuListViewHolderClass.ivMenuImg.SetImageResource ( _mnuUrl [position] ); return view; } } //Viewholder class internal class MenuListViewHolderClass:Java.Lang.Object { internal Action viewClicked{ get; set;} internal TextView txtMnuText; internal ImageView ivMenuImg; public void initialize(View view) { view.Click += delegate { viewClicked(); }; } }
4. Write the class for gesture listening.
Here Left to right swipe area is restricted to 100 unit from the left[if(e1.GetX()<100) ].
Here Left to right swipe area is restricted to 100 unit from the left[if(e1.GetX()<100) ].
//GestureListener.cs using System; using Android.Views; namespace AndroidGesture { class GestureListener: Java.Lang.Object, GestureDetector.IOnGestureListener { public event Action LeftEvent; public event Action RightEvent; public event Action SingleTapEvent; static int SWIPE_MAX_OFF_PATH = 250; static int SWIPE_MIN_DISTANCE = 100; static int SWIPE_THRESHOLD_VELOCITY = 200; public GestureListener() { } public bool OnFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { try { if ( Math.Abs ( e1.GetY () - e2.GetY () ) > SWIPE_MAX_OFF_PATH ) return false; // right to left swipe if ( e1.GetX () - e2.GetX () > SWIPE_MIN_DISTANCE && Math.Abs ( velocityX ) > SWIPE_THRESHOLD_VELOCITY && LeftEvent != null ) { RightEvent (); } else if ( e2.GetX () - e1.GetX () > SWIPE_MIN_DISTANCE && Math.Abs ( velocityX ) > SWIPE_THRESHOLD_VELOCITY && RightEvent != null ) { //left to right swipe if(e1.GetX()<100) LeftEvent (); } } catch ( Exception e ) { Console.WriteLine ( "Failed to work" +e.Message); } return false; } public bool OnDown(MotionEvent e) { return true; } public void OnLongPress(MotionEvent e) {} public bool OnScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return true; } public void OnShowPress(MotionEvent e) { } public bool OnSingleTapUp(MotionEvent e) { SingleTapEvent (); Console.WriteLine ( "Single tap up" ); return true; } } }
5. Declare and Initialize the GestureListener and UI controls.
Here also Sliding menu width is restricted to 3/4 of the full screen width.
Here boolean flag "isSingleTapFired" is declared to avoid the conflict between gesture event "SingleTap()" and menu click event "menuIconImageView.Click()" i.e. Conflict is when you click on title bar menuIcon to expand the menu, in the other side gesture event SingleTap() also get fires.
8. At last but important one is sliding the menu.
Animate the menu from left to right and right to left using TranslateAnimation function.
For the movement along x-axis specify start,end point and keep y-axis start,end point as zero.
Explore the sample code at github: https://github.com/suchithm/GestureSwipeXamarin.Android
Screen Recording:
Final touch:
Even though it takes too much to scroll down[;(] it is simple concept. need to more focus on the animation function and the gesture listener and rest is regular coding.
Thanks for your time here,Visit again. I will come back with new and exciting topic on mobile application development.
Happiee coding :)
Reference:
http://developer.xamarin.com/api/type/Android.Views.Animations.TranslateAnimation/
https://developer.xamarin.com/guides/cross-platform/application_fundamentals/touch/part_3_touch_in_android/
http://forums.xamarin.com/discussion/comment/40052/
Here also Sliding menu width is restricted to 3/4 of the full screen width.
using System; using Android.App; using Android.Views; using Android.Widget; using Android.OS; using Android.Views.Animations; using System.Linq; using Android.Graphics; namespace AndroidGesture { [Activity ( Label = "SlidingMenu" , MainLauncher = true , Icon = "@drawable/icon" )] public class MainActivity : Activity { GestureDetector gestureDetector; GestureListener gestureListener; ListView menuListView; MenuListAdapterClass objAdapterMenu; ImageView menuIconImageView; int intDisplayWidth; bool isSingleTapFired=false; TextView txtActionBarText; TextView txtPageName; TextView txtDescription; ImageView btnDescExpander; protected override void OnCreate ( Bundle bundle ) { base.OnCreate ( bundle ); Window.RequestFeature ( WindowFeatures.NoTitle ); SetContentView ( Resource.Layout.Main ); FnInitialization (); TapEvent (); FnBindMenu (); //find definition in below steps } void TapEvent() { //title bar menu icon menuIconImageView.Click += delegate(object sender , EventArgs e ) { if ( !isSingleTapFired ) { FnToggleMenu (); //find definition in below steps isSingleTapFired = false; } }; //bottom expandable description window btnDescExpander.Click += delegate(object sender , EventArgs e ) { FnDescriptionWindowToggle(); }; } void FnInitialization() { //gesture initialization gestureListener = new GestureListener (); gestureListener.LeftEvent += GestureLeft; //find definition in below steps gestureListener.RightEvent += GestureRight; gestureListener.SingleTapEvent += SingleTap; gestureDetector = new GestureDetector (this,gestureListener); menuListView = FindViewById<ListView> ( Resource.Id.menuListView ); menuIconImageView = FindViewById<ImageView> ( Resource.Id.menuIconImgView ); txtActionBarText = FindViewById<TextView> ( Resource.Id.txtActionBarText ); txtPageName=FindViewById<TextView> ( Resource.Id.txtPage ); txtDescription=FindViewById<TextView> ( Resource.Id.txtDescription ); btnDescExpander =FindViewById<ImageView> ( Resource.Id.btnImgExpander ); //changed sliding menu width to 3/4 of screen width Display display = this.WindowManager.DefaultDisplay; var point = new Point (); display.GetSize (point); intDisplayWidth = point.X; intDisplayWidth=intDisplayWidth - (intDisplayWidth/3); using ( var layoutParams = ( RelativeLayout.LayoutParams ) menuListView.LayoutParameters ) { layoutParams.Width = intDisplayWidth; layoutParams.Height = ViewGroup.LayoutParams.MatchParent; menuListView.LayoutParameters = layoutParams; } }6. Bind the menu item to listview and display the selected item in title bar
void FnBindMenu() { string[] strMnuText={ GetString(Resource.String.Home),GetString(Resource.String.AboutUs),GetString(Resource.String.Products),GetString(Resource.String.Events),GetString(Resource.String.Serivce),GetString(Resource.String.Clients),GetString(Resource.String.Help),GetString(Resource.String.Solution),GetString(Resource.String.ContactUs)}; int [] strMnuUrl={Resource.Drawable.icon_home,Resource.Drawable.icon_aboutus,Resource.Drawable.icon_product,Resource.Drawable.icon_event,Resource.Drawable.icon_service,Resource.Drawable.icon_client,Resource.Drawable.icon_help,Resource.Drawable.icon_solution,Resource.Drawable.icon_contactus}; if ( objAdapterMenu != null ) { objAdapterMenu.actionMenuSelected -= FnMenuSelected; objAdapterMenu = null; } objAdapterMenu = new MenuListAdapterClass (this,strMnuText,strMnuUrl); objAdapterMenu.actionMenuSelected += FnMenuSelected; menuListView.Adapter = objAdapterMenu; } void FnMenuSelected(string strMenuText) { txtActionBarText.Text = strMenuText; txtPageName.Text = strMenuText; //selected action goes here }7. Write down the action for above defined gesture events.
Here boolean flag "isSingleTapFired" is declared to avoid the conflict between gesture event "SingleTap()" and menu click event "menuIconImageView.Click()" i.e. Conflict is when you click on title bar menuIcon to expand the menu, in the other side gesture event SingleTap() also get fires.
void GestureLeft() { if(!menuListView.IsShown) FnToggleMenu (); isSingleTapFired = false; } void GestureRight() { if(menuListView.IsShown) FnToggleMenu (); isSingleTapFired = false; } void SingleTap() { if ( menuListView.IsShown ) { FnToggleMenu (); isSingleTapFired = true; } else { isSingleTapFired = false; } } public override bool DispatchTouchEvent (MotionEvent ev) { gestureDetector.OnTouchEvent ( ev ); return base.DispatchTouchEvent (ev); }
8. At last but important one is sliding the menu.
Animate the menu from left to right and right to left using TranslateAnimation function.
For the movement along x-axis specify start,end point and keep y-axis start,end point as zero.
//toggling the left menu void FnToggleMenu() { Console.WriteLine ( menuListView.IsShown ); if(menuListView.IsShown) { menuListView.Animation = new TranslateAnimation ( 0f , -menuListView.MeasuredWidth , 0f , 0f ); menuListView.Animation.Duration = 300; menuListView.Visibility = ViewStates.Gone; } else { menuListView.Visibility = ViewStates.Visible; menuListView.RequestFocus (); menuListView.Animation = new TranslateAnimation ( -menuListView.MeasuredWidth, 0f , 0f , 0f );//starting edge of layout menuListView.Animation.Duration = 300; } } //bottom desription window sliding void FnDescriptionWindowToggle() { if(txtDescription.IsShown) { txtDescription.Visibility = ViewStates.Gone; txtDescription.Animation = new TranslateAnimation ( 0f ,0f,0f,txtDescription.MeasuredHeight ); txtDescription.Animation.Duration = 300; btnDescExpander.SetImageResource ( Resource.Drawable.up_arrow ); } else { txtDescription.Visibility = ViewStates.Visible; txtDescription.RequestFocus (); txtDescription.Animation = new TranslateAnimation ( 0f , 0f ,txtDescription.MeasuredHeight,0f); txtDescription.Animation.Duration = 300; btnDescExpander.SetImageResource ( Resource.Drawable.down_arrow ); }
Explore the sample code at github: https://github.com/suchithm/GestureSwipeXamarin.Android
Screen Recording:
Final touch:
Even though it takes too much to scroll down[;(] it is simple concept. need to more focus on the animation function and the gesture listener and rest is regular coding.
Thanks for your time here,Visit again. I will come back with new and exciting topic on mobile application development.
Happiee coding :)
Reference:
http://developer.xamarin.com/api/type/Android.Views.Animations.TranslateAnimation/
https://developer.xamarin.com/guides/cross-platform/application_fundamentals/touch/part_3_touch_in_android/
http://forums.xamarin.com/discussion/comment/40052/
How can we shift menu to right and swipe from right to left?
ReplyDeleteHi Suchith Madavu, Great it works well. But one issue I am facing. I have implemented this for my filter layer. On starting of application my filter layer will be hidden. On click of filter button in tablet this layer has to show with animation on close button click filter layer will hide with animation. Very first time when I click the filter button filter layer shows up without animation. Next time onward animation works fine. Do you have any idea why this happen?
ReplyDeleteHello Ranjith, you can check this http://www.appliedcodelog.com/2016/01/navigation-drawer-using-material-design.html navigation drawer using android material design.
DeletePlease replace visibility from viewstate.gone to viewstate.invisible
DeleteThis will solve the non animation for the very first time movement.
Excellent Suchith..It's working Properly. thanx a lot
ReplyDeleteThanks arvind. Glad that it could helped you.
DeleteSuchith can you tell me how to add theme in this example? like in your output coming in my output black screen coming(with proper output) there is no theme but in your screen one theme is there.
ReplyDeleteHere didn't used any theme. Changes in the output screen is because of the device internal theme.
DeleteHere didn't used any theme. Changes in the output screen is because of the device internal theme.
DeleteHello,
ReplyDeleteAnd great work,
But I want to ask how to open or call a new page when any item pressed..
which event do that please?
Hello,
ReplyDeleteAnd great work,
But I want to ask how to open or call a new page when any item pressed..
which event do that please?
Hello Yasmeen,
DeleteFnMenuSelected() inside an activity class gets fired according to the action event "actionMenuSelected" from adapter class. where you can think of adding new screen.
This example gives more emphasis on custom gesture swipe recognition. To call new page you can think of adding FrameLayout in Main.axml and you can load Fragments according to the menu selection. check this: http://www.appliedcodelog.com/2016/01/navigation-drawer-using-material-design.html
Deletehello,
ReplyDeletehow to add scroll to screen have contain this menu?
i add scroll to screen but side one tab appear from side menu and other disappear
Sorry I didn't get you. Menu has been added to list view,so how you get scrolling issue there.
Delete