¿Cómo logró Google hacer esto? Deslice ActionBar en la aplicación de Android

Realmente quiero implementar esto (la navegación lateral) en una aplicación propia, ¿alguien sabe cómo Google logró hacer esto?

Parece que han retirado la ventana actual e insertado una navegación propia.

De hecho, hay una manera de hacer esto. Incluso sin implementar tu propia ActionBar .

¡Solo echa un vistazo a la hierachyviewer ! (Ubicado en el directorio de herramientas)

DecorView el DecorView y LinearLayout como un niño. Este LinearLayout contiene tanto la ActionBar como el otro contenido. Entonces, simplemente puede aplicar FrameLayout.LayoutParams a este LinearLayout y obtener espacio en el lado izquierdo de esta manera. Luego, puede llenar este espacio con su menú-ListView y superponer el otro contenido con un FrameLayout, que, cuando se hace clic, colapsa el menú. Entonces, aquí hay un código:

Primero, la clase para colapsar / expandir (SlideMenu.java):

 package your.cool.app; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.Window; import android.view.animation.TranslateAnimation; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; public class SlideMenu { //just a simple adapter public static class SlideMenuAdapter extends ArrayAdapter { Activity act; SlideMenu.SlideMenuAdapter.MenuDesc[] items; class MenuItem { public TextView label; public ImageView icon; } static class MenuDesc { public int icon; public String label; } public SlideMenuAdapter(Activity act, SlideMenu.SlideMenuAdapter.MenuDesc[] items) { super(act, R.id.menu_label, items); this.act = act; this.items = items; } @Override public View getView(int position, View convertView, ViewGroup parent) { View rowView = convertView; if (rowView == null) { LayoutInflater inflater = act.getLayoutInflater(); rowView = inflater.inflate(R.layout.menu_listitem, null); MenuItem viewHolder = new MenuItem(); viewHolder.label = (TextView) rowView.findViewById(R.id.menu_label); viewHolder.icon = (ImageView) rowView.findViewById(R.id.menu_icon); rowView.setTag(viewHolder); } MenuItem holder = (MenuItem) rowView.getTag(); String s = items[position].label; holder.label.setText(s); holder.icon.setImageResource(items[position].icon); return rowView; } } private static boolean menuShown = false; private static View menu; private static LinearLayout content; private static FrameLayout parent; private static int menuSize; private static int statusHeight = 0; private Activity act; SlideMenu(Activity act) { this.act = act; } //call this in your onCreate() for screen rotation public void checkEnabled() { if(menuShown) this.show(false); } public void show() { //get the height of the status bar if(statusHeight == 0) { Rect rectgle = new Rect(); Window window = act.getWindow(); window.getDecorView().getWindowVisibleDisplayFrame(rectgle); statusHeight = rectgle.top; } this.show(true); } public void show(boolean animate) { menuSize = Functions.dpToPx(250, act); content = ((LinearLayout) act.findViewById(android.R.id.content).getParent()); FrameLayout.LayoutParams parm = (FrameLayout.LayoutParams) content.getLayoutParams(); parm.setMargins(menuSize, 0, -menuSize, 0); content.setLayoutParams(parm); //animation for smooth slide-out TranslateAnimation ta = new TranslateAnimation(-menuSize, 0, 0, 0); ta.setDuration(500); if(animate) content.startAnimation(ta); parent = (FrameLayout) content.getParent(); LayoutInflater inflater = (LayoutInflater) act.getSystemService(Context.LAYOUT_INFLATER_SERVICE); menu = inflater.inflate(R.layout.menu, null); FrameLayout.LayoutParams lays = new FrameLayout.LayoutParams(-1, -1, 3); lays.setMargins(0,statusHeight, 0, 0); menu.setLayoutParams(lays); parent.addView(menu); ListView list = (ListView) act.findViewById(R.id.menu_listview); list.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { //handle your menu-click } }); if(animate) menu.startAnimation(ta); menu.findViewById(R.id.overlay).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { SlideMenu.this.hide(); } }); Functions.enableDisableViewGroup((LinearLayout) parent.findViewById(android.R.id.content).getParent(), false); ((ExtendedViewPager) act.findViewById(R.id.viewpager)).setPagingEnabled(false); ((ExtendedPagerTabStrip) act.findViewById(R.id.viewpager_tabs)).setNavEnabled(false); menuShown = true; this.fill(); } public void fill() { ListView list = (ListView) act.findViewById(R.id.menu_listview); SlideMenuAdapter.MenuDesc[] items = new SlideMenuAdapter.MenuDesc[5]; //fill the menu-items here SlideMenuAdapter adap = new SlideMenuAdapter(act, items); list.setAdapter(adap); } public void hide() { TranslateAnimation ta = new TranslateAnimation(0, -menuSize, 0, 0); ta.setDuration(500); menu.startAnimation(ta); parent.removeView(menu); TranslateAnimation tra = new TranslateAnimation(menuSize, 0, 0, 0); tra.setDuration(500); content.startAnimation(tra); FrameLayout.LayoutParams parm = (FrameLayout.LayoutParams) content.getLayoutParams(); parm.setMargins(0, 0, 0, 0); content.setLayoutParams(parm); Functions.enableDisableViewGroup((LinearLayout) parent.findViewById(android.R.id.content).getParent(), true); ((ExtendedViewPager) act.findViewById(R.id.viewpager)).setPagingEnabled(true); ((ExtendedPagerTabStrip) act.findViewById(R.id.viewpager_tabs)).setNavEnabled(true); menuShown = false; } } 

Algunos métodos de ayuda (para mí, en Funciones estáticas.java):

  public static int dpToPx(int dp, Context ctx) { Resources r = ctx.getResources(); return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()); } //originally: http://stackoverflow.com/questions/5418510/disable-the-touch-events-for-all-the-views //modified for the needs here public static void enableDisableViewGroup(ViewGroup viewGroup, boolean enabled) { int childCount = viewGroup.getChildCount(); for (int i = 0; i < childCount; i++) { View view = viewGroup.getChildAt(i); if(view.isFocusable()) view.setEnabled(enabled); if (view instanceof ViewGroup) { enableDisableViewGroup((ViewGroup) view, enabled); } else if (view instanceof ListView) { if(view.isFocusable()) view.setEnabled(enabled); ListView listView = (ListView) view; int listChildCount = listView.getChildCount(); for (int j = 0; j < listChildCount; j++) { if(view.isFocusable()) listView.getChildAt(j).setEnabled(false); } } } } 

Entonces, los diseños:

Diseño del menú (res / layout / menu.xml)

        

Diseño de los elementos de lista (res / layout / menu_listitem.xml):

     

Cómo usarlo:

En su onCreate() :

 private SlideMenu slidemenu; @Override public void onCreate(Bundle savedInstanceState) { //your onCreate code slidemenu = new SlideMenu(this); slidemenu.checkEnabled(); } 

En el controlador de tu botón de inicio de ActionBar:

 slidemenu.show(); 

¡Eso es!

Y ahora, una pequeña captura de pantalla en acción:

SlideMenu

Por lo que yo sé, está funcionando. Si tiene algún problema o mis explicaciones no son claras, contácteme!

EDITAR: ExtendedViewPager y ExtendedPagerStrip :

ExtendedViewPager:

 package your.cool.app; //source: http://blog.svpino.com/2011/08/disabling-pagingswiping-on-android.html import android.content.Context; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; public class ExtendedViewPager extends ViewPager { private boolean enabled; public ExtendedViewPager(Context context, AttributeSet attrs) { super(context, attrs); this.enabled = true; } @Override public boolean onTouchEvent(MotionEvent event) { if (this.enabled) { return super.onTouchEvent(event); } return false; } @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (this.enabled) { return super.onInterceptTouchEvent(event); } return false; } public void setPagingEnabled(boolean enabled) { this.enabled = enabled; } } 

ExtendedPagerTabStrip:

 package your.cool.app; //source: http://blog.svpino.com/2011/08/disabling-pagingswiping-on-android.html import android.content.Context; import android.support.v4.view.PagerTabStrip; import android.util.AttributeSet; import android.view.MotionEvent; public class ExtendedPagerTabStrip extends PagerTabStrip { private boolean enabled; public ExtendedPagerTabStrip(Context context, AttributeSet attrs) { super(context, attrs); this.enabled = true; } @Override public boolean onTouchEvent(MotionEvent event) { if (this.enabled) { return super.onTouchEvent(event); } return false; } @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (this.enabled) { return super.onInterceptTouchEvent(event); } return false; } public void setNavEnabled(boolean enabled) { this.enabled = enabled; } } 

Uso este SlideMenu para una actividad con un ViewPager con PagerTabStrip para tabs como Talk, Market, etc. No se pueden deshabilitar estas vistas de una manera sencilla, por lo que las dos clases anteriores solo las extienden para detener el evento onTouch cuando están deshabilitadas.

Hay varios bashs para hacer esto, sin embargo, todavía tengo que encontrar un código fuente o lib sobre cómo implementarlo con éxito con la barra de acciones en todos los niveles de la API. Una lib prometedora está aquí

https://github.com/jfeinstein10/SlidingMenu

aquí hay un video de la aplicación de ejemplo .

aquí está el enlace de la aplicación Google Play .

Esto funciona con ActionbarSherlock. Deberá construir la biblioteca SlidingMenu con ABS para que funcione. Funciona y se ve genial!

Realicé un resumen de la implementación original y el análisis XML agregado, así como la autodetection de una barra de acciones posiblemente presente, por lo que funciona con la barra de acción nativa y de soporte, como ActionBarSherlock .

Todo es ahora un proyecto de biblioteca junto con una aplicación de ejemplo y se describe en el menú deslizante para Android. ¡ Gracias a scirocco por la idea y el código iniciales!

Captura de pantalla de LibSlideMenu

Si está utilizando un nivel de API mayor que 11, puede utilizar un enfoque mucho más simple inspirado en la respuesta dada por @Scirocco

 // get content parent that is basically the whole // app screen (viewed from hierarchy viewer) final LinearLayout content = (LinearLayout) findViewById(android.R.id.content).getParent(); // make new value animator with range from 0 to 1 final ValueAnimator animator = ValueAnimator.ofFloat(0, 1); // set custom duration animator.setDuration(500); // on update is called for every value in the // given range in time frame defined by the duration animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { // get the current value float value = ((Float) (animation.getAnimatedValue())).floatValue(); // translate by that value, minus means translate left content.setTranslationX(-250 * value); } }); // start the animator animator.start(); // make or inflate custom view for test purposes Button textView = new Button(this); textView.setText("TestButton"); // add it to the frame layout that is the parent of the content on position 0 FrameLayout parent = (FrameLayout) content.getParent(); parent.addView(textView, 0); 

La idea aquí es utilizar ValueAnimator que transforma y no solo anima el diseño principal con la barra de acción, por lo que puede interactuar con la vista inflada que desea usar como panel deslizante. Debe reemplazar los valores codificados con algo que sea útil para su aplicación.

Espero que esto ayude 🙂

Bueno, actualmente estoy trabajando en un proyecto y me encontré con el menú deslizante, busqué en Google, pero me decepciona ver que nadie me ha dado una pieza de código o alguna pista sobre cómo comenzar a crear un menú deslizante, pero cada uno ha dado un enlace a algunos los proyectos / bibliotecas de Github para usar, decidí hacerlo yo mismo y finalmente tengo mi propio menú deslizante listo …

He pasado dos días en él

1. en hacer animaciones de deslizamiento

2. en hacer que funcione con todas las resoluciones de pantalla

Es realmente fácil y simple una vez que tienes una idea acerca de las animaciones , he leído en alguna parte, no es sensato reinventar la rueda (personas que se refieren al código fuente github del menú deslizante), pero creo que deberías al menos una vez trata de hacer tu propia para que te hagas una idea de cómo funciona y funciona: P

así que esta es una imagen de cómo mi menú deslizante va a funcionar

1.Find.xml //later in the code it will be refer as findLayout

                   //here i included the filter.xml, which is on top of find.xml layout and is initially invisible  

enter image description here

2.Filter.xml //later in code refer as FilterLayout

                              

enter image description here

En find.xml he incluido filter.xml inicialmente que es invisible

Ahora FilterAnimation.java

 package matchat.helpers; import com.s3.matchat.R; import android.content.Context; import android.util.DisplayMetrics; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationUtils; import android.widget.RelativeLayout; public class FilterAnimation implements AnimationListener { Context context; RelativeLayout filterLayout, otherLayout; private Animation filterSlideIn, filterSlideOut, otherSlideIn, otherSlideOut; private static int otherLayoutWidth, otherLayoutHeight; private boolean isOtherSlideOut = false; private int deviceWidth; private int margin; public FilterAnimation(Context context) { this.context = context; DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); deviceWidth = displayMetrics.widthPixels; // as my animation is x-axis related so i gets the device width and will use that width,so that this sliding menu will work fine in all screen resolutions } public void initializeFilterAnimations(RelativeLayout filterLayout) { this.filterLayout = filterLayout; filterSlideIn = AnimationUtils.loadAnimation(context, R.anim.filter_slide_in); filterSlideOut = AnimationUtils.loadAnimation(context, R.anim.filter_slide_out); } public void initializeOtherAnimations(RelativeLayout otherLayout) { this.otherLayout = otherLayout; otherLayoutWidth = otherLayout.getWidth(); otherLayoutHeight = otherLayout.getHeight(); otherSlideIn = AnimationUtils.loadAnimation(context, R.anim.other_slide_in); otherSlideIn.setAnimationListener(this); otherSlideOut = AnimationUtils.loadAnimation(context, R.anim.other_slide_out); otherSlideOut.setAnimationListener(this); } public void toggleSliding() { if(isOtherSlideOut) //check if findLayout is already slided out so get so animate it back to initial position { filterLayout.startAnimation(filterSlideOut); filterLayout.setVisibility(View.INVISIBLE); otherLayout.startAnimation(otherSlideIn); } else //slide findLayout Out and filterLayout In { otherLayout.startAnimation(otherSlideOut); filterLayout.setVisibility(View.VISIBLE); filterLayout.startAnimation(filterSlideIn); } } @Override public void onAnimationEnd(Animation animation) { if(isOtherSlideOut) //Now here we will actually move our view to the new position,because animations just move the pixels not the view { RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(otherLayoutWidth, otherLayoutHeight); otherLayout.setLayoutParams(params); isOtherSlideOut = false; } else { margin = (deviceWidth * 80) / 100; //here im coverting device percentage width into pixels, in my other_slide_in.xml or other_slide_out.xml you can see that i have set the android:toXDelta="80%",so it means the layout will move to 80% of the device screen,to work across all screens i have converted percentage width into pixels and then used it RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(otherLayoutWidth, otherLayoutHeight); params.leftMargin = margin; params.rightMargin = -margin; //same margin from right side (negavite) so that our layout won't get shrink otherLayout.setLayoutParams(params); isOtherSlideOut = true; dimOtherLayout(); } } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationStart(Animation animation) { } private void dimOtherLayout() { AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.5f); alphaAnimation.setFillAfter(true); otherLayout.startAnimation(alphaAnimation); } } 

Ahora Find.java

 package main.matchat.activities; import matchat.helpers.FilterAnimation; import com.s3.matchat.R; import android.app.Activity; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.View; import android.view.ViewTreeObserver; import android.view.View.OnClickListener; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.Button; import android.widget.RelativeLayout; public class Find extends Activity implements OnClickListener { RelativeLayout filterLayout, findLayout; Button btFilter; FilterAnimation filterAnimation; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.find); filterLayout = (RelativeLayout)findViewById(R.id.filter_layout); findLayout = (RelativeLayout)findViewById(R.id.find_layout); btFilter = (Button)findViewById(R.id.filter); btFilter.setOnClickListener(this); filterAnimation = new FilterAnimation(this); initializeAnimations(); } private void initializeAnimations() { //Setting GlobolLayoutListener,when layout is completely set this function will get called and we can have our layout onbject with correct width & height,else if you simply try to get width/height of your layout in onCreate it will return 0 final ViewTreeObserver filterObserver = filterLayout.getViewTreeObserver(); filterObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { filterLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this); DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); int deviceWidth = displayMetrics.widthPixels; int filterLayoutWidth = (deviceWidth * 80) / 100; //here im coverting device percentage width into pixels, in my other_slide_in.xml or other_slide_out.xml you can see that i have set the android:toXDelta="80%",so it means the layout will move to 80% of the device screen,to work across all screens i have converted percentage width into pixels and then used it RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(filterLayoutWidth, RelativeLayout.LayoutParams.MATCH_PARENT); filterLayout.setLayoutParams(params);//here im setting the layout params for my filter.xml because its has width 260 dp,so work it across all screen i first make layout adjustments so that it work across all screens resolution filterAnimation.initializeFilterAnimations(filterLayout); } }); final ViewTreeObserver findObserver = findLayout.getViewTreeObserver(); findObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { findLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this); filterAnimation.initializeOtherAnimations(findLayout); } }); } @Override public void onClick(View v) { int id = v.getId(); switch(id) { case R.id.filter: filterAnimation.toggleSliding(); break; } } } 

Aquí están las animaciones res / anim

1.filter_slide_in.xml

     

2.filter_slide_out.xml

     

3.other_slide_in.xml

     

4.other_slide_out.xml

     

Allí tienes un menú deslizante funcional y funcional completo, y puedes personalizarlo para cumplir tus requisitos, si alguien todavía tiene problemas para configurarlo, no dudes en preguntar, me complace ayudarte 🙂

Creé mi propia solución para desplazar la vista y revelar un menú debajo, ya que muchas otras soluciones parecían no funcionar en versiones anteriores de Android o carecían de instrucciones adecuadas sobre cómo hacer que funcione.

Mi solución tiene las siguientes características:

  • Ofrece soporte para deslizar una vista hacia afuera para revelar un menú que se encuentra debajo de ella
  • Tanto el menú como la vista de arriba pueden ser cualquier vista personalizada
  • Compatible con versiones anteriores de Android (probadas para funcionar al menos en Android 2.2)
  • Trabaja con proyectos de PhoneGap / Cordova

La solución utiliza un diseño personalizado, llamado SlidingMenuLayout, al que se espera que agregue 2 vistas. La primera vista que agrega es el menú, la segunda es la vista principal.

La forma más sencilla de agregar el diseño a su proyecto existente es anular el método setContentView() su actividad:

 @Override public void setContentView(View view) { SlidingMenuLayout layout = new SlidingMenuLayout(this); layout.setLayoutParams(new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0.0F)); layout.addView(new MenuView(this)); layout.addView(view); super.setContentView(layout); } 

En este ejemplo, MenuView es la vista que realmente mostrará el menú. Depende de usted implementar esta vista.
Finalmente, puede agregar un botón (normalmente en la esquina superior izquierda de su vista principal), que llama a openMenu() o closeMenu() en el diseño, según corresponda.
El código para SlidingMenuLayout se encuentra en la página del proyecto GitHub.

Para aquellos de ustedes que usan la biblioteca SlidingMenu ( https://github.com/jfeinstein10/SlidingMenu ) hay una manera de conectarlo y parece funcionar. Con la ayuda de @Scirocco ponga esto en su onCreate para la actividad:

 ViewGroup decorView = (ViewGroup) getWindow().getDecorView(); mSlidingMenu = new SlidingMenu(this); ViewGroup mainContent = (ViewGroup) decorView.getChildAt(0); decorView.removeView(mainContent); mSlidingMenu.setContent(mainContent); decorView.addView(mSlidingMenu); mMenu = (LinearLayout) View.inflate(this, R.layout.menuview, null); mSlidingMenu.setMenu(mMenu); mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN); mSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset); 

Básicamente, lo que hace es reemplazar la linearlayout en la vista de decoración con el slidingmenu en slidingmenu lugar.

Aviso: solo lo he probado a la ligera, pero parece funcionar.

  public class ImprovedSlidingPaneLayout extends SlidingPaneLayout { Context context; FrameLayout left; FrameLayout right; Boolean canOpen = true; public ImprovedSlidingPaneLayout(Context context) { super(context); this.context = context; this.left = new FrameLayout(context); this.right = new FrameLayout(context); this.addView(left); this.addView(right); } public ImprovedSlidingPaneLayout(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (canOpen) return super.onInterceptTouchEvent(ev); else return false; } public ImprovedSlidingPaneLayout canOpen(Boolean canOpen) { this.canOpen = canOpen; return this; } public ImprovedSlidingPaneLayout makeActionBarSlide(Window window){ ViewGroup decorView = (ViewGroup) window.getDecorView(); ViewGroup mainContent = (ViewGroup) decorView.getChildAt(0); decorView.removeView(mainContent); setContentView(mainContent); decorView.addView(this); return this; } public ImprovedSlidingPaneLayout setMenuView(View view){ if((left.getChildCount()== 1)){ left.removeView(left.getChildAt(0)); } left.addView(view); return this; } public ImprovedSlidingPaneLayout setContentView(View view){ if((right.getChildCount()== 1)){ right.removeView(right.getChildAt(0)); } right.addView(view); return this; } public ImprovedSlidingPaneLayout setMenuWidth(int width){ left.setLayoutParams(new SlidingPaneLayout.LayoutParams(width, ViewGroup.LayoutParams.MATCH_PARENT)); return this; } } 

esta es mi clase extiende SlidingPaneLayout . Puede deslizarse con actio