gpt4 book ai didi

java - 如何与抽屉导航一起滑动操作栏

转载 作者:行者123 更新时间:2023-12-02 06:06:16 25 4
gpt4 key购买 nike

我想做的是滑动ActionBar随着NavigationDrawer当抽屉打开时。我目前没有使用任何第三方库,如果可能的话,我想保持这种方式。我所需要的只是一个方法的实现,如:getActionBarView.slide(dp);
这是我目前用来创建 NavigationDrawer 的代码:

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

但它没有做我想要的,它产生了这个:

I am currently stuck with this

我想要实现的是:

current screen shot from app

最佳答案

PLEASE NOTE: This answer was originally written when Android 4.4 (KitKat) was still pretty new. Since Android 5.0 and especially because of the introduction of the ToolBar this answer cannot be considered up-to-date anymore! But from a technical perspective and for those of you who want to learn about the inner workings of Android this answer might still hold a lot of value!


NavigationDrawer专门设计用于位于 ActionBar 下方并且没有办法实现 NavigationDrawer制作 ActionBar随它移动 - 除非可能正在寻找 View这构成了 ActionBar并将其与 NavigationDrawer 一起制作动画,但我永远不会推荐这样的东西,因为它既困难又容易出错。在我看来,你只有两种选择:
  • 使用 library like the SlidingMenu
  • 实现自定义滑动菜单

  • 既然你说你不想使用实现自定义滑动菜单的库是你唯一的选择,幸运的是,一旦你知道如何去做,这真的不是那么难。

    1) 基本说明

    您可以移动 Activity 的全部内容- 我的意思是一切,包括 ActionBar - 通过在 View 上放置边距或填充这构成了 Activity .此 ViewView 的父级id android.R.id.content :
    View content = (View) activity.findViewById(android.R.id.content).getParent();

    在 Honeycomb(Android 版本 3.0 - API 级别 11)或更高版本上 - 换句话说,在 ActionBar 之后引入了 - 您需要使用边距来更改 Activities position 和在以前的版本上,您需要使用填充。为了简化这一点,我建议创建辅助方法,为每个 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 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);

    您可以显示 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(Android 2.1 - API 级别 7)以上的大多数设备(如果不是所有设备)。

    2) 动画 Activity
    创建滑动菜单的第一部分是制作 Activity错。因此,我们应该首先尝试移动 Activity像这样:
    enter image description here

    要创建它,我们只需要将上面的代码放在一起:
    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();
    }
    }
    }

    您可以使用 ActivitySlider像这样的类:
    ActivitySlider slider = new ActivitySlider(activity);

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

    3) 添加滑动菜单

    现在我们要在 Activity 时显示一个菜单像这样移开:
    enter image description here
    正如你所看到的,它也插入了 ActionBar去旁边。
    ActivitySlider class不需要修改那么多来创建滑动菜单,基本上我们只需要添加两个方法, showMenu()hideMenu() .我将坚持最佳实践并使用 Fragment作为滑动菜单。我们需要的第一件事是 View - 例如 FrameLayout - 作为我们 Fragment 的容器.我们需要添加这个 ViewView 的父级的 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);

    由于我们设置了容器的可见性 View仅当滑动菜单实际打开时才可见,我们可以使用以下方法检查菜单是打开还是关闭:
    public boolean isMenuVisible() {
    return this.menuContainer.getVisibility() == View.VISIBLE;
    }

    设置菜单 Fragment我们添加了一个执行 FragmentTransaction 的 setter 方法并添加菜单 FragmentFrameLayout :
    public void setMenuFragment(Fragment fragment) {
    FragmentManager manager = this.activity.getSupportFragmentManager();
    FragmentTransaction transaction = manager.beginTransaction();
    transaction.replace(R.id.flMenuContainer, fragment);
    transaction.commit();
    }

    我还倾向于添加第二个 setter 来实例化 Fragment来自 Class为了方便:
    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;

    我们必须在容器上放置一个上边距 View菜单 Fragment像这样:
    // 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);

    最后我们可以把所有这些放在一起:
    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();
    }
    }
    }

    我在 showMenu() 中使用了静态辅助方法将倾角转换为像素。下面是这个方法的代码:
    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;
    }

    您可以使用这个新版本的 ActivitySlider像这样的类:
    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) 结论与测试

    当您知道可以简单地在 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


    希望能帮到你,如果还有什么问题或者不清楚的地方,请随时提问!

    关于java - 如何与抽屉导航一起滑动操作栏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23783496/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com