/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.v4.app; import android.app.Activity; import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; import android.support.annotation.DrawableRes; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v4.content.ContextCompat; import android.support.v4.view.GravityCompat; import android.support.v4.view.ViewCompat; import android.support.v4.widget.DrawerLayout; import android.view.MenuItem; import android.view.View; /** * @deprecated Please use ActionBarDrawerToggle in support-v7-appcompat. * *
* This class provides a handy way to tie together the functionality of
* {@link DrawerLayout} and the framework ActionBar
to implement the recommended
* design for navigation drawers.
*
*
To use ActionBarDrawerToggle
, create one in your Activity and call through
* to the following methods corresponding to your Activity callbacks:
Call {@link #syncState()} from your Activity
's
* {@link Activity#onPostCreate(android.os.Bundle) onPostCreate} to synchronize the indicator
* with the state of the linked DrawerLayout after onRestoreInstanceState
* has occurred.
ActionBarDrawerToggle
can be used directly as a
* {@link DrawerLayout.DrawerListener}, or if you are already providing your own listener,
* call through to each of the listener methods from your own.
The given {@link Activity} will be linked to the specified {@link DrawerLayout}. * The provided drawer indicator drawable will animate slightly off-screen as the drawer * is opened, indicating that in the open state the drawer will move off-screen when pressed * and in the closed state the drawer will move on-screen when pressed.
* *String resources must be provided to describe the open/close drawer actions for * accessibility services.
* * @param activity The Activity hosting the drawer * @param drawerLayout The DrawerLayout to link to the given Activity's ActionBar * @param drawerImageRes A Drawable resource to use as the drawer indicator * @param openDrawerContentDescRes A String resource to describe the "open drawer" action * for accessibility * @param closeDrawerContentDescRes A String resource to describe the "close drawer" action * for accessibility */ public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, @DrawableRes int drawerImageRes, @StringRes int openDrawerContentDescRes, @StringRes int closeDrawerContentDescRes) { this(activity, drawerLayout, !assumeMaterial(activity), drawerImageRes, openDrawerContentDescRes, closeDrawerContentDescRes); } private static boolean assumeMaterial(Context context) { return context.getApplicationInfo().targetSdkVersion >= 21 && (Build.VERSION.SDK_INT >= 21); } /** * Construct a new ActionBarDrawerToggle. * *The given {@link Activity} will be linked to the specified {@link DrawerLayout}. * The provided drawer indicator drawable will animate slightly off-screen as the drawer * is opened, indicating that in the open state the drawer will move off-screen when pressed * and in the closed state the drawer will move on-screen when pressed.
* *String resources must be provided to describe the open/close drawer actions for * accessibility services.
* * @param activity The Activity hosting the drawer * @param drawerLayout The DrawerLayout to link to the given Activity's ActionBar * @param animate True to animate the drawer indicator along with the drawer's position. * Material apps should set this to false. * @param drawerImageRes A Drawable resource to use as the drawer indicator * @param openDrawerContentDescRes A String resource to describe the "open drawer" action * for accessibility * @param closeDrawerContentDescRes A String resource to describe the "close drawer" action * for accessibility */ public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, boolean animate, @DrawableRes int drawerImageRes, @StringRes int openDrawerContentDescRes, @StringRes int closeDrawerContentDescRes) { mActivity = activity; // Allow the Activity to provide an impl if (activity instanceof DelegateProvider) { mActivityImpl = ((DelegateProvider) activity).getDrawerToggleDelegate(); } else { mActivityImpl = null; } mDrawerLayout = drawerLayout; mDrawerImageResource = drawerImageRes; mOpenDrawerContentDescRes = openDrawerContentDescRes; mCloseDrawerContentDescRes = closeDrawerContentDescRes; mHomeAsUpIndicator = getThemeUpIndicator(); mDrawerImage = ContextCompat.getDrawable(activity, drawerImageRes); mSlider = new SlideDrawable(mDrawerImage); mSlider.setOffset(animate ? TOGGLE_DRAWABLE_OFFSET : 0); } /** * Synchronize the state of the drawer indicator/affordance with the linked DrawerLayout. * *This should be called from your Activity
's
* {@link Activity#onPostCreate(android.os.Bundle) onPostCreate} method to synchronize after
* the DrawerLayout's instance state has been restored, and any other time when the state
* may have diverged in such a way that the ActionBarDrawerToggle was not notified.
* (For example, if you stop forwarding appropriate drawer events for a period of time.)
* If you pass null
to this method, the default drawable from
* the theme will be used.
*
* @param indicator A drawable to use for the up indicator, or null to use
* the theme's default
* @see #setDrawerIndicatorEnabled(boolean)
*/
public void setHomeAsUpIndicator(Drawable indicator) {
if (indicator == null) {
mHomeAsUpIndicator = getThemeUpIndicator();
mHasCustomUpIndicator = false;
} else {
mHomeAsUpIndicator = indicator;
mHasCustomUpIndicator = true;
}
if (!mDrawerIndicatorEnabled) {
setActionBarUpIndicator(mHomeAsUpIndicator, 0);
}
}
/**
* Set the up indicator to display when the drawer indicator is not
* enabled.
*
* If you pass 0 to this method, the default drawable from the theme will * be used. * * @param resId Resource ID of a drawable to use for the up indicator, or 0 * to use the theme's default * @see #setDrawerIndicatorEnabled(boolean) */ public void setHomeAsUpIndicator(int resId) { Drawable indicator = null; if (resId != 0) { indicator = ContextCompat.getDrawable(mActivity, resId); } setHomeAsUpIndicator(indicator); } /** * Enable or disable the drawer indicator. The indicator defaults to enabled. * *
When the indicator is disabled, the ActionBar
will revert to displaying
* the home-as-up indicator provided by the Activity
's theme in the
* android.R.attr.homeAsUpIndicator
attribute instead of the animated
* drawer glyph.
Activity
's
* {@link Activity#onConfigurationChanged(android.content.res.Configuration) onConfigurationChanged}
* method.
*
* @param newConfig The new configuration
*/
public void onConfigurationChanged(Configuration newConfig) {
// Reload drawables that can change with configuration
if (!mHasCustomUpIndicator) {
mHomeAsUpIndicator = getThemeUpIndicator();
}
mDrawerImage = ContextCompat.getDrawable(mActivity, mDrawerImageResource);
syncState();
}
/**
* This method should be called by your Activity
's
* {@link Activity#onOptionsItemSelected(android.view.MenuItem) onOptionsItemSelected} method.
* If it returns true, your onOptionsItemSelected
method should return true and
* skip further processing.
*
* @param item the MenuItem instance representing the selected menu item
* @return true if the event was handled and further processing should not occur
*/
public boolean onOptionsItemSelected(MenuItem item) {
if (item != null && item.getItemId() == ID_HOME && mDrawerIndicatorEnabled) {
if (mDrawerLayout.isDrawerVisible(GravityCompat.START)) {
mDrawerLayout.closeDrawer(GravityCompat.START);
} else {
mDrawerLayout.openDrawer(GravityCompat.START);
}
return true;
}
return false;
}
/**
* {@link DrawerLayout.DrawerListener} callback method. If you do not use your
* ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
* through to this method from your own listener object.
*
* @param drawerView The child view that was moved
* @param slideOffset The new offset of this drawer within its range, from 0-1
*/
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
float glyphOffset = mSlider.getPosition();
if (slideOffset > 0.5f) {
glyphOffset = Math.max(glyphOffset, Math.max(0.f, slideOffset - 0.5f) * 2);
} else {
glyphOffset = Math.min(glyphOffset, slideOffset * 2);
}
mSlider.setPosition(glyphOffset);
}
/**
* {@link DrawerLayout.DrawerListener} callback method. If you do not use your
* ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
* through to this method from your own listener object.
*
* @param drawerView Drawer view that is now open
*/
@Override
public void onDrawerOpened(View drawerView) {
mSlider.setPosition(1);
if (mDrawerIndicatorEnabled) {
setActionBarDescription(mCloseDrawerContentDescRes);
}
}
/**
* {@link DrawerLayout.DrawerListener} callback method. If you do not use your
* ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
* through to this method from your own listener object.
*
* @param drawerView Drawer view that is now closed
*/
@Override
public void onDrawerClosed(View drawerView) {
mSlider.setPosition(0);
if (mDrawerIndicatorEnabled) {
setActionBarDescription(mOpenDrawerContentDescRes);
}
}
/**
* {@link DrawerLayout.DrawerListener} callback method. If you do not use your
* ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
* through to this method from your own listener object.
*
* @param newState The new drawer motion state
*/
@Override
public void onDrawerStateChanged(int newState) {
}
Drawable getThemeUpIndicator() {
if (mActivityImpl != null) {
return mActivityImpl.getThemeUpIndicator();
}
return IMPL.getThemeUpIndicator(mActivity);
}
void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
if (mActivityImpl != null) {
mActivityImpl.setActionBarUpIndicator(upDrawable, contentDescRes);
return;
}
mSetIndicatorInfo = IMPL
.setActionBarUpIndicator(mSetIndicatorInfo, mActivity, upDrawable, contentDescRes);
}
void setActionBarDescription(int contentDescRes) {
if (mActivityImpl != null) {
mActivityImpl.setActionBarDescription(contentDescRes);
return;
}
mSetIndicatorInfo = IMPL
.setActionBarDescription(mSetIndicatorInfo, mActivity, contentDescRes);
}
private class SlideDrawable extends InsetDrawable implements Drawable.Callback {
private final boolean mHasMirroring = Build.VERSION.SDK_INT > 18;
private final Rect mTmpRect = new Rect();
private float mPosition;
private float mOffset;
SlideDrawable(Drawable wrapped) {
super(wrapped, 0);
}
/**
* Sets the current position along the offset.
*
* @param position a value between 0 and 1
*/
public void setPosition(float position) {
mPosition = position;
invalidateSelf();
}
public float getPosition() {
return mPosition;
}
/**
* Specifies the maximum offset when the position is at 1.
*
* @param offset maximum offset as a fraction of the drawable width,
* positive to shift left or negative to shift right.
* @see #setPosition(float)
*/
public void setOffset(float offset) {
mOffset = offset;
invalidateSelf();
}
@Override
public void draw(Canvas canvas) {
copyBounds(mTmpRect);
canvas.save();
// Layout direction must be obtained from the activity.
final boolean isLayoutRTL = ViewCompat.getLayoutDirection(
mActivity.getWindow().getDecorView()) == ViewCompat.LAYOUT_DIRECTION_RTL;
final int flipRtl = isLayoutRTL ? -1 : 1;
final int width = mTmpRect.width();
canvas.translate(-mOffset * width * mPosition * flipRtl, 0);
// Force auto-mirroring if it's not supported by the platform.
if (isLayoutRTL && !mHasMirroring) {
canvas.translate(width, 0);
canvas.scale(-1, 1);
}
super.draw(canvas);
canvas.restore();
}
}
}