栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如何将ActionBar和NavigationDrawer一起滑动

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何将ActionBar和NavigationDrawer一起滑动

请注意:该答案最初是在Android 4.4(KitKat)仍然很新时编写的。从Android5.0开始,尤其是由于引入了

ToolBarthis
,此答案不再被认为是
最新的!但是从技术角度来看,对于那些想了解Android内部运作原理的人来说,这个答案可能仍然具有很大的价值!

NavigationDrawer
专门设计为位于其下方的
ActionBar
,有没有办法实现
NavigationDrawer
,使
ActionBar
移动与它-除非也许在寻找View使了
ActionBar
和动画它旁边的
NavigationDrawer
,但我绝不会推荐这样的东西,因为它会困难且容易出错。我认为您只有两种选择:

使用类似SlidingMenu的库实施自定义滑动菜单既然您说过不想使用实现自定义滑动菜单的库是唯一的选择,幸运的是,一旦知道如何做,这实际上就不那么困难了。

1)基本说明

您可以移动整个内容Activity-我的意思是包括一切ActionBar-通过在组成的内容上加上边距或填充View来移动Activity。这View是的父View与该ID android.R.id.content:

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

Honeycomb
(Android版本3.0-API级别11)或更高版本上(换句话说,在ActionBar引入之后),您需要使用边距来更改
Activities
位置,而在以前的版本上则需要使用填充。为了简化此操作,我建议创建辅助方法,以对每个API级别执行正确的操作。我们首先来看一下如何设置的位置
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);    }}

请注意,在两种情况下,相对的边都为负边距或为负填充。这实际上是要

Activity
超出其正常范围的大小。
Activity
当我们将其滑动到某处时,这可以防止更改其实际大小。

我们还需要两种方法来获取的当前位置

Activity
。一个用于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();    }}

添加动画也非常简单。唯一重要的是要进行一点数学运算,以使其从以前的位置移动到新的位置

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

您可以显示View在其通过滑动掉显露的位置

Activity
将其添加到的父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);

这几乎就是创建基本的滑动菜单所需的全部功能,该菜单可在Eclair(Android2.1-API级别7)以上的大多数(如果不是所有)设备上运行。

2)动画 Activity

创建滑动菜单的第一部分是将其Activity移开。因此,我们应该首先尝试这样移动Activity:

为此,我们只需要将上面的代码放在一起:

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();        }    }}

You can use the

ActivitySlider
class like this:

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

3)添加滑动菜单

现在,我们要在菜单Activity移开时显示一个菜单,如下所示:在此处输入图片说明如您所见,菜单也将菜单推ActionBar到了侧面。

ActivitySlider
无需为创建滑动菜单而对类进行大量修改,基本上我们只需添加两个方法,showMenu()和hideMenu()。我将遵循最佳做法,并使用
a Fragment
作为滑动菜单。我们需要的第一件事是View-例如
frameLayout-作为我们的容器Fragment。我们需要把它添加View到的父
View
Activity

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

Since we set the visibility of the container

View
to VISIBLE only when the
sliding menu is actually open we can use the following method to check if the
menu is open or closed:

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

To set the menu

Fragment
we add a setter method that performs a
FragmentTransaction
and adds the menu
Fragment
to the
frameLayout
:

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

I also tend to add a second setter which instantiates the

Fragment
from a
Class
for convenience:

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

关于菜单,还有一件事要考虑Fragment。我们在View层次结构中的操作比正常情况要远得多。因此,我们必须考虑到状态栏的高度。如果我们不考虑这一点,菜单的顶部Fragment将隐藏在状态栏的后面。您可以像这样获取状态栏的高度:

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

We have to put a top margin on the container

View
of the menu
Fragment

like this:

// These are the LayoutParams for the menu FragmentframeLayout.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 heightparams.setMargins(0, statusBarHeight, 0, 0);menuContainer.setLayoutParams(fragmentParams);

Finally we can put all this together:

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();        }    }}

I use a static helper method in

showMenu()
to convert dip to pixels. Here is
the pre of this method:

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;}

You can use this new version of the

ActivitySlider
class like this:

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

4) Conclusion & Testing

做这样的事情是非常容易的,当你知道你可以简单地把保证金或填充上View的Activity。但是困难在于使它可以在许多不同的设备上运行。实施在多个API级别上可能会发生很大变化,并且可能对其行为产生重大影响。话虽如此,我在这里发布的任何代码都可以在Eclair(Android 2.1-API级别7)以上的大多数设备上使用,即使不是所有设备也可以正常使用

当然,我在这里发布的解决方案是不完整的,它可能需要额外的抛光和清理,因此可以随时根据您的需要改进代码!

我已经在以下设备上测试了所有内容:

HTC

  • One M8 (Android 4.4.2 - KitKat): Working
  • Sensation (Android 4.0.3 - Ice Cream Sandwich): Working
  • Desire (Android 2.3.3 - Gingerbread): Working
  • One (Android 4.4.2 - KitKat): Working

Samsung

  • Galaxy S3 Mini (Android 4.1.2 - Jelly Bean): Working
  • Galaxy S4 Mini (Android 4.2.2 - Jelly Bean): Working
  • Galaxy S4 (Android 4.4.2 - KitKat): Working
  • Galaxy S5 (Android 4.4.2 - KitKat): Working
  • Galaxy S Plus (Android 2.3.3 - Gingerbread): Working
  • Galaxy Ace (Android 2.3.6 - Gingerbread): Working
  • Galaxy S2 (Android 4.1.2 - Jelly Bean): Working
  • Galaxy S3 (Android 4.3 - Jelly Bean): Working
  • Galaxy Note 2 (Android 4.3 - Jelly Bean): Working
  • Galaxy Nexus (Android 4.2.1 - Jelly Bean): Working

Motorola

  • Moto G (Android 4.4.2 - KitKat): Working

LG

  • Nexus 5 (Android 4.4.2 - KitKat): Working

ZTE

  • Blade (Android 2.1 - Eclair): Working

I hope I could help you and if you have any further questions or anything else
is unclear please feel free to ask!



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/486502.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号