Come far scorrere l'ActionBar insieme a NavigationDrawer

Quello che voglio fare è far scorrere l' ActionBar insieme al NavigationDrawer quando il cassetto viene aperto. Attualmente non utilizzo alcuna biblioteca di terze parti e, se ansible, voglio mantenerlo in questo modo. Tutto quello che serve è un'implementazione di metodo come: getActionBarView.slide(dp);

Questo è il codice attualmente utilizzato per creare il NavigationDrawer :

  • java.lang.ClassNotFoundException: android.view.fragment
  • File di dictionary Android. Quale è più veloce, database o file di lettura direttamente?
  • Android DownloadManager e SSL (https)
  • Come posso serializzare la mia class utente per il database Firebase e correggere questo errore?
  • il confronto di due stringhe non funziona in android
  • Esempio di regista, stage, gruppo, attore, Box2D e GestureDector LibGdx
  •  mDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) { public void onDrawerClosed(View view) { invalidateOptionsMenu(); // calling onPrepareOptionsMenu() to hide action bar icons } @Override public void onDrawerSlide(View drawerView, float slideOffset) { if (getDeviceType(getApplicationContext()) == DEVICE_TYPE_PHONE) { drawerLayout.setScrimColor(Color.parseColor("#00FFFFFF")); float moveFactor = (listView.getWidth() * slideOffset); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { all_menu_container_parent.setTranslationX(moveFactor); } else { TranslateAnimation anim = new TranslateAnimation(lastTranslate, moveFactor, 0.0f, 0.0f); anim.setDuration(0); anim.setFillAfter(true); all_menu_container_parent.startAnimation(anim); lastTranslate = moveFactor; } } } public void onDrawerOpened(View drawerView) { // calling onPrepareOptionsMenu() to hide action bar icons } }; drawerLayout.setDrawerListener(mDrawerToggle); 

    Ma non fa quello che voglio, produce questo:

    Attualmente sono bloccato con questo

    Quello che voglio realizzare è questo:

    schermo corrente scattato da app

    One Solution collect form web for “Come far scorrere l'ActionBar insieme a NavigationDrawer”

    NOTA BENE: questa risposta è stata scritta originariamente quando Android 4.4 (KitKat) era ancora piuttosto nuovo. Dal momento che Android 5.0 e soprattutto a causa dell'introduzione della ToolBar questa risposta non può essere considerata aggiornata più! Ma da una prospettiva tecnica e per quelli di voi che vogliono conoscere il funzionamento interno di Android questa risposta potrebbe ancora avere un grande valore!

    Il NavigationDrawer stato specificamente progettato per essere situato sotto l' ActionBar e non è ansible implementare il NavigationDrawer per far sì che l' ActionBar sposti con esso – a less che non sia forse ansible cercare la View che ActionBar l' ActionBar e lo anima accanto a NavigationDrawer , ma non vorrei mai raccomandi qualcosa di simile in quanto sarebbe difficile e errato. A mio parere si hanno solo due opzioni:

    1. Utilizzo di una libreria come il SlidingMenu
    2. Implementazione di un menu di scorrimento personalizzato

    Poiché hai detto che non vuoi usare una libreria che implementa un menu di scorrimento personalizzato è la tua unica opzione, fortunatamente questo non è veramente difficile una volta che sai come farlo.


    1) Spiegazione di base

    Puoi spostare tutto il contenuto Activity – intendo tutto, incluso l' ActionBar – inserendo un margine o un riempimento sulla View che costituisce l' Activity . Questa View è il padre della View con il id android.R.id.content :

     View content = (View) activity.findViewById(android.R.id.content).getParent(); 

    Su Honeycomb (Android versione 3.0 – Livello API 11) o superiore – in altre parole dopo l'introduzione di ActionBar – è necessario utilizzare margini per modificare la posizione Activities e sulle versioni precedenti è necessario utilizzare un'imbottitura. Per semplificare questo scopo, suggerisco di creare methods helper che eseguano l'azione corretta per each livello API. Vediamo innanzitutto come impostare la posizione Activity :

     public void setActivityPosition(int x, int y) { // With this if statement we can check if the devices API level is above Honeycomb or below if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // On Honeycomb or abvoe we set a margin FrameLayout.LayoutParams contentParams = (FrameLayout.LayoutParams) this.content.getLayoutParams(); contentParams.setMargins(x, y, -x, -y); this.content.setLayoutParams(contentParams); } else { // And on devices below Honeycomb we set a padding this.content.setPadding(x, y, -x, -y); } } 

    Si noti che in entrambi i casi c'è un margine negativo o un riempimento negativo sui lati opposti. Ciò significa essenzialmente aumentare la dimensione Activity oltre i suoi limiti normali. Ciò impedisce la modifica della dimensione effettiva Activity quando lo scorriamo da qualche parte.

    Inoltre abbiamo bisogno di due methods per get l'attuale posizione Activity . Uno per la posizione x, uno per la posizione y:

     public int getActivityPositionX() { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // On Honeycomb or above we return the left margin FrameLayout.LayoutParams contentParams = (FrameLayout.LayoutParams) this.content.getLayoutParams(); return contentParams.leftMargin; } else { // On devices below Honeycomb we return the left padding return this.content.getPaddingLeft(); } } public int getActivityPositionY() { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // On Honeycomb or above we return the top margin FrameLayout.LayoutParams contentParams = (FrameLayout.LayoutParams) this.content.getLayoutParams(); return contentParams.topMargin; } else { // On devices below Honeycomb we return the top padding return this.content.getPaddingTop(); } } 

    È anche molto semplice aggiungere animazioni. L'unica cosa importnte qui è un po 'di math per animarla dalla sua posizione precedente alla sua nuova posizione

     // We get the current position of the Activity final int currentX = getActivityPositionX(); final int currentY = getActivityPositionY(); // The new position is set setActivityPosition(x, y); // We animate the Activity to slide from its previous position to its new position TranslateAnimation animation = new TranslateAnimation(currentX - x, 0, currentY - y, 0); animation.setDuration(500); this.content.startAnimation(animation); 

    È ansible visualizzare una View nella posizione che viene rivelata scorporando l' Activity aggiungendola al genitore della View :

     final int currentX = getActivityPositionX(); FrameLayout menuContainer = new FrameLayout(context); // The width of the menu is equal to the x position of the `Activity` FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(currentX, ViewGroup.LayoutParams.MATCH_PARENT); menuContainer.setLayoutParams(params); ViewGroup parent = (ViewGroup) content.getParent(); parent.addView(menuContainer); 

    E questo è praticamente tutto quello di cui hai bisogno per creare un menu di scorrimento fondamentale che funziona per la maggior parte se non tutti i dispositivi sopra Eclair (Android 2.1 – API livello 7).


    2) Animazione Activity

    La prima parte della creazione di un menu scorrevole sta rendendo l' Activity spostata fuori strada. In questo modo dobbiamo prima cercare di spostare l' Activity in questo modo:
    immettere qui la descrizione dell'immagine

    Per creare questo, dobbiamo solo mettere insieme il codice:

     import android.os.Build; import android.support.v4.app.FragmentActivity; import android.view.View; import android.view.animation.TranslateAnimation; import android.widget.FrameLayout; public class ActivitySlider { private final FragmentActivity activity; private final View content; public ActivitySlider(FragmentActivity activity) { this.activity = activity; // Here we get the content View from the Activity. this.content = (View) activity.findViewById(android.R.id.content).getParent(); } public void slideTo(int x, int y) { // We get the current position of the Activity final int currentX = getActivityPositionX(); final int currentY = getActivityPositionY(); // The new position is set setActivityPosition(x, y); // We animate the Activity to slide from its previous position to its new position TranslateAnimation animation = new TranslateAnimation(currentX - x, 0, currentY - y, 0); animation.setDuration(500); this.content.startAnimation(animation); } public void setActivityPosition(int x, int y) { // With this if statement we can check if the devices API level is above Honeycomb or below if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // On Honeycomb or above we set a margin FrameLayout.LayoutParams contentParams = (FrameLayout.LayoutParams) this.content.getLayoutParams(); contentParams.setMargins(x, y, -x, -y); this.content.setLayoutParams(contentParams); } else { // And on devices below Honeycomb we set a padding this.content.setPadding(x, y, -x, -y); } } public int getActivityPositionX() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // On Honeycomb or above we return the left margin FrameLayout.LayoutParams contentParams = (FrameLayout.LayoutParams) this.content.getLayoutParams(); return contentParams.leftMargin; } else { // On devices below Honeycomb we return the left padding return this.content.getPaddingLeft(); } } public int getActivityPositionY() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // On Honeycomb or above we return the top margin FrameLayout.LayoutParams contentParams = (FrameLayout.LayoutParams) this.content.getLayoutParams(); return contentParams.topMargin; } else { // On devices below Honeycomb we return the top padding return this.content.getPaddingTop(); } } } 

    È ansible utilizzare la class ActivitySlider come questa:

     ActivitySlider slider = new ActivitySlider(activity); // This would move the Activity 400 pixel to the right and 100 pixel down slider.slideTo(400, 100); 

    3) Aggiunta del menu di scorrimento

    Ora vogliamo rivelare un menu quando l' Activity si allontana dal modo seguente: immettere qui la descrizione dell'immagine
    Come potete vedere, spinge anche l' ActionBar al fianco.

    La class ActivitySlider non deve essere modificata tanto per creare un menu di scorrimento, fondamentalmente aggiungiamo solo due methods, showMenu() e hideMenu() . Sosterrò le migliori pratiche e utilizzerò un Fragment come menu scorrevole. La prima cosa che abbiamo bisogno di bisogno è una View – ad esempio un FrameLayout – come un contenitore per il nostro Fragment . Dobbiamo aggiungere questa View al genitore della View Activity :

     // We get the View of the Activity View content = (View) activity.findViewById(android.R.id.content).getParent(); // And its parent ViewGroup parent = (ViewGroup) content.getParent(); // The container for the menu Fragment is a FrameLayout // We set an id so we can perform FragmentTransactions later on FrameLayout menuContainer = new FrameLayout(this.activity); menuContainer.setId(R.id.flMenuContainer); // The visibility is set to GONE because the menu is initially hidden menuContainer.setVisibility(View.GONE); // The container for the menu Fragment is added to the parent parent.addView(menuContainer); 

    Poiché abbiamo impostato la visibilità del contenitore in VISIBLE solo quando il menu di scorrimento è effettivamente aperto, è ansible utilizzare il seguente metodo per verificare se il menu è aperto o chiuso:

     public boolean isMenuVisible() { return this.menuContainer.getVisibility() == View.VISIBLE; } 

    Per impostare il Fragment del menu aggiungiamo un metodo setter che esegue una FrameLayout FragmentTransaction e aggiunge il Fragment menu a FrameLayout :

     public void setMenuFragment(Fragment fragment) { FragmentManager manager = this.activity.getSupportFragmentManager(); FragmentTransaction transaction = manager.beginTransaction(); transaction.replace(R.id.flMenuContainer, fragment); transaction.commit(); } 

    Ho anche l'intenzione di aggiungere un secondo setter che istanzia il Fragment di una Class per comodità:

     public <T extends Fragment> void setMenuFragment(Class<T> cls) { Fragment fragment = Fragment.instantiate(this.activity, cls.getName()); setMenuFragment(fragment); } 

    C'è un'altra cosa importnte da considerare quando si tratta del menu Fragment . Stiamo operando molto più in alto nella gerarchia di View quanto normalmente. Come tale, dobbiamo prendere in considerazione le cose come l'altezza della barra di stato. Se non abbiamo fatto conto di questo, la parte superiore del menu Fragment saremmo nascosti dietro la barra di stato. È ansible get l'altezza della barra di stato come questo:

     Rect rectangle = new Rect(); Window window = this.activity.getWindow(); window.getDecorView().getWindowVisibleDisplayFrame(rectangle); final int statusBarHeight = rectangle.top; 

    Dobbiamo mettere un margine superiore sul contenitore View del menu Fragment come questo:

     // These are the LayoutParams for the menu Fragment FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, ViewGroup.LayoutParams.MATCH_PARENT); // We put a top margin on the menu Fragment container which is equal to the status bar height params.setMargins(0, statusBarHeight, 0, 0); menuContainer.setLayoutParams(fragmentParams); 

    Infine possiamo mettere tutto insieme:

     import android.graphics.Rect; import android.os.Build; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import android.widget.FrameLayout; import at.test.app.R; import at.test.app.helper.LayoutHelper; public class ActivitySlider { private final FragmentActivity activity; private final View content; private final FrameLayout menuContainer; public ActivitySlider(FragmentActivity activity) { this.activity = activity; // We get the View of the Activity this.content = (View) activity.findViewById(android.R.id.content).getParent(); // And its parent ViewGroup parent = (ViewGroup) this.content.getParent(); // The container for the menu Fragment is added to the parent. We set an id so we can perform FragmentTransactions later on this.menuContainer = new FrameLayout(this.activity); this.menuContainer.setId(R.id.flMenuContainer); // We set visibility to GONE because the menu is initially hidden this.menuContainer.setVisibility(View.GONE); parent.addView(this.menuContainer); } public <T extends Fragment> void setMenuFragment(Class<T> cls) { Fragment fragment = Fragment.instantiate(this.activity, cls.getName()); setMenuFragment(fragment); } public void setMenuFragment(Fragment fragment) { FragmentManager manager = this.activity.getSupportFragmentManager(); FragmentTransaction transaction = manager.beginTransaction(); transaction.replace(R.id.flMenuContainer, fragment); transaction.commit(); } public boolean isMenuVisible() { return this.menuContainer.getVisibility() == View.VISIBLE; } // We pass the width of the menu in dip to showMenu() public void showMenu(int dpWidth) { // We convert the width from dip into pixels final int menuWidth = LayoutHelper.dpToPixel(this.activity, dpWidth); // We move the Activity out of the way slideTo(menuWidth, 0); // We have to take the height of the status bar at the top into account! Rect rectangle = new Rect(); Window window = this.activity.getWindow(); window.getDecorView().getWindowVisibleDisplayFrame(rectangle); final int statusBarHeight = rectangle.top; // These are the LayoutParams for the menu Fragment FrameLayout.LayoutParams fragmentParams = new FrameLayout.LayoutParams(menuWidth, ViewGroup.LayoutParams.MATCH_PARENT); // We put a top margin on the menu Fragment container which is equal to the status bar height fragmentParams.setMargins(0, statusBarHeight, 0, 0); this.menuContainer.setLayoutParams(fragmentParams); // Perform the animation only if the menu is not visible if(!isMenuVisible()) { // Visibility of the menu container View is set to VISIBLE this.menuContainer.setVisibility(View.VISIBLE); // The menu slides in from the right TranslateAnimation animation = new TranslateAnimation(-menuWidth, 0, 0, 0); animation.setDuration(500); this.menuContainer.startAnimation(animation); } } public void hideMenu() { // We can only hide the menu if it is visible if(isMenuVisible()) { // We slide the Activity back to its original position slideTo(0, 0); // We need the width of the menu to properly animate it final int menuWidth = this.menuContainer.getWidth(); // Now we need an extra animation for the menu fragment container TranslateAnimation menuAnimation = new TranslateAnimation(0, -menuWidth, 0, 0); menuAnimation.setDuration(500); menuAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { // As soon as the hide animation is finished we set the visibility of the fragment container back to GONE menuContainer.setVisibility(View.GONE); } @Override public void onAnimationRepeat(Animation animation) { } }); this.menuContainer.startAnimation(menuAnimation); } } public void slideTo(int x, int y) { // We get the current position of the Activity final int currentX = getActivityPositionX(); final int currentY = getActivityPositionY(); // The new position is set setActivityPosition(x, y); // We animate the Activity to slide from its previous position to its new position TranslateAnimation animation = new TranslateAnimation(currentX - x, 0, currentY - y, 0); animation.setDuration(500); this.content.startAnimation(animation); } public void setActivityPosition(int x, int y) { // With this if statement we can check if the devices API level is above Honeycomb or below if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // On Honeycomb or above we set a margin FrameLayout.LayoutParams contentParams = (FrameLayout.LayoutParams) this.content.getLayoutParams(); contentParams.setMargins(x, y, -x, -y); this.content.setLayoutParams(contentParams); } else { // And on devices below Honeycomb we set a padding this.content.setPadding(x, y, -x, -y); } } public int getActivityPositionX() { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // On Honeycomb or above we return the left margin FrameLayout.LayoutParams contentParams = (FrameLayout.LayoutParams) this.content.getLayoutParams(); return contentParams.leftMargin; } else { // On devices below Honeycomb we return the left padding return this.content.getPaddingLeft(); } } public int getActivityPositionY() { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // On Honeycomb or above we return the top margin FrameLayout.LayoutParams contentParams = (FrameLayout.LayoutParams) this.content.getLayoutParams(); return contentParams.topMargin; } else { // On devices below Honeycomb we return the top padding return this.content.getPaddingTop(); } } } 

    Utilizzo un metodo statico di helper in showMenu() per convertire i dip di pixel. Ecco il codice di questo metodo:

     public static int dpToPixel(Context context, int dp) { float scale = getDisplayDensityFactor(context); return (int) (dp * scale + 0.5f); } private static float getDisplayDensityFactor(Context context) { if (context != null) { Resources res = context.getResources(); if (res != null) { DisplayMetrics metrics = res.getDisplayMetrics(); if(metrics != null) { return metrics.density; } } } return 1.0f; } 

    È ansible utilizzare questa nuova versione della class ActivitySlider come questa:

     ActivitySlider slider = new ActivitySlider(activity); slider.setMenuFragment(MenuFragment.class); // The menu is shown with a width of 200 dip slider.showMenu(200); ... // Hide the menu again slider.hideMenu(); 

    4) Conclusioni e prove

    Fare qualcosa di simile è sorprendentemente facile quando sai che puoi semplicemente mettere un margine o un'imbottitura sulla View Activity . Ma la difficoltà è farla funzionare su un sacco di dispositivi diversi. Le implementazioni possono cambiare molto su livelli API diversi e che possono influenzare notevolmente come funziona. Detto che qualsiasi codice che ho inviato qui dovrebbe funzionare per la maggior parte se non tutti i dispositivi sopra Eclair (Android 2.1 – API livello 7) senza alcun problema.
    Naturalmente la soluzione che ho inviato qui non è completa, potrebbe utilizzare un po 'di lucidatura extra e pulizia, quindi non esitate a migliorare il codice in base alle proprie esigenze!

    Ho provato tutto sui seguenti dispositivi:

    HTC

    • Un M8 (Android 4.4.2 – KitKat): Lavorare
    • Sensation (Android 4.0.3 – Ice Cream Sandwich): Lavorare
    • Desiderio (Android 2.3.3 – Gingerbread): Lavorare
    • One (Android 4.4.2 – KitKat): Lavorare

    Samsung

    • Galaxy S3 Mini (Android 4.1.2 – Jelly Bean): Lavorare
    • Galaxy S4 Mini (Android 4.2.2 – Jelly Bean): Lavorare
    • Galaxy S4 (Android 4.4.2 – KitKat): Lavorare
    • Galaxy S5 (Android 4.4.2 – KitKat): Lavorare
    • Galaxy S Plus (Android 2.3.3 – Pan di zenzero): Lavorare
    • Galaxy Ace (Android 2.3.6 – Pan di zenzero): Lavorare
    • Galaxy S2 (Android 4.1.2 – Jelly Bean): Lavorare
    • Galaxy S3 (Android 4.3 – Jelly Bean): Lavorare
    • Galaxy Note 2 (Android 4.3 – Jelly Bean): Lavorare
    • Galaxy Nexus (Android 4.2.1 – Jelly Bean): Lavorare

    Motorola

    • Moto G (Android 4.4.2 – KitKat): Lavorare

    LG

    • Nexus 5 (Android 4.4.2 – KitKat): Lavorare

    ZTE

    • Lama (Android 2.1 – Eclair): Lavorare

    Spero che possa aiutarti e se avete altre domande o qualunque altra cosa è poco chiara, non esitate a chiedere!

    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.