-
-
Save homj/f456dede83cb34a9e997 to your computer and use it in GitHub Desktop.
| /* | |
| * Copyright 2014 Johannes Homeier | |
| * | |
| * 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 de.twoid.drawable; | |
| import android.graphics.Canvas; | |
| import android.graphics.ColorFilter; | |
| import android.graphics.Paint; | |
| import android.graphics.Rect; | |
| import android.graphics.RectF; | |
| import android.graphics.drawable.Drawable; | |
| import android.view.Gravity; | |
| public class DrawerIconDrawable extends Drawable { | |
| public static final int STATE_DRAWER = 0; | |
| public static final int STATE_ARROW = 1; | |
| private static final float BASE_DRAWABLE_SIZE = 48f; | |
| private static final float BASE_ICON_SIZE = 18f; | |
| private static final float BASE_BAR_WIDTH = BASE_ICON_SIZE; | |
| private static final float BASE_BAR_HEIGHT = 2f; | |
| private static final float BASE_BAR_SPACING = 5f; | |
| private static final float BASE_BAR_SHRINKAGE = BASE_BAR_WIDTH/6f; | |
| private static final float FULL_ROTATION = 720f; | |
| private static final float TOPRECT_FIRST_ROTATION = 450f; | |
| private static final float TOPRECT_SECOND_ROTATION = FULL_ROTATION-TOPRECT_FIRST_ROTATION; | |
| private static final float MIDRECT_FIRST_ROTATION = 360f; | |
| private static final float MIDRECT_SECOND_ROTATION = FULL_ROTATION-MIDRECT_FIRST_ROTATION; | |
| private static final float BOTRECT_FIRST_ROTATION = 270f; | |
| private static final float BOTRECT_SECOND_ROTATION = FULL_ROTATION-BOTRECT_FIRST_ROTATION; | |
| private static final float LEVEL_BREAKPOINT = 0.5f; | |
| // level of the animation | |
| private float level; | |
| // Dimensions | |
| private int width; | |
| private int height; | |
| private int offsetX; | |
| private int offsetY; | |
| private float barWidth; | |
| private float barHeight; | |
| private float barSpacing; | |
| private float barShrinkage; | |
| // Drawing-Objects | |
| private Paint mPaint; | |
| private Rect iconRect; | |
| private RectF topRect; | |
| private RectF middleRect; | |
| private RectF bottomRect; | |
| private int gravity = Gravity.CENTER; | |
| private boolean breakpointReached = false; | |
| /** | |
| * Create a new DrawerIconDrawableV1 with size in pixel | |
| * | |
| * @param size the size (both width and height) this drawable should have in pixel | |
| */ | |
| public DrawerIconDrawable(int size) { | |
| this(size, size); | |
| } | |
| /** | |
| * Create a new DrawerIconDrawableV1 with a specfied width and height in pixel | |
| * @param width the width this drawable should have in pixel | |
| * @param height the height this drawable should have in pixel | |
| */ | |
| public DrawerIconDrawable(int width, int height) { | |
| this(width, height, 0, 0); | |
| } | |
| /** | |
| * Create a new DrawerIconDrawableV1 with specified width and height in pixel and also apply a {@link Gravity} to align the icon | |
| * @param width the width this drawable should have in pixel | |
| * @param height the height this drawable should have in pixel | |
| * @param gravity the gravity to align the icon in this drawable | |
| */ | |
| public DrawerIconDrawable(int width, int height, int gravity) { | |
| this(width, height, 0, 0, gravity); | |
| } | |
| /** | |
| * Create a new DrawerIconDrawableV1 with specified width and height in pixel and also apply a offset to the icon | |
| * @param width the width this drawable should have in pixel | |
| * @param height the height this drawable should have in pixel | |
| * @param offsetX the offset the icon should have from its center in x-direction | |
| * @param offsetY the offset the icon should have from its center in y-direction | |
| */ | |
| public DrawerIconDrawable(int width, int height, int offsetX, int offsetY) { | |
| this(width, height, 0, 0, Gravity.CENTER); | |
| } | |
| /** | |
| * Create a new DrawerIconDrawableV1 with specified width and height in pixel and also apply a offset to the icon plus a {@link Gravity} to align the icon | |
| * @param width the width this drawable should have in pixel | |
| * @param height the height this drawable should have in pixel | |
| * @param offsetX the offset the icon should have from its center in x-direction | |
| * @param offsetY the offset the icon should have from its center in y-direction | |
| * @param gravity the gravity to align the icon in this drawable | |
| */ | |
| public DrawerIconDrawable(int width, int height, int offsetX, int offsetY, int gravity) { | |
| this.width = width; | |
| this.height = height; | |
| this.offsetX = offsetX; | |
| this.offsetY = offsetY; | |
| this.gravity = gravity; | |
| setBounds(new Rect(0, 0, width, height)); | |
| mPaint = new Paint(); | |
| mPaint.setAntiAlias(true); | |
| mPaint.setColor(0xffffffff); | |
| iconRect = new Rect(); | |
| topRect = new RectF(); | |
| middleRect = new RectF(); | |
| bottomRect = new RectF(); | |
| setDefaultIconSize(); | |
| setLevel(0); | |
| } | |
| @Override | |
| public void draw(Canvas canvas) { | |
| canvas.translate(iconRect.left + offsetX, iconRect.top + offsetY); | |
| float scaleFactor = level < LEVEL_BREAKPOINT ? level * 2 : (level - 0.5f) * 2; | |
| drawTopRect(canvas, scaleFactor); | |
| drawMiddleRect(canvas, scaleFactor); | |
| drawBottomRect(canvas, scaleFactor); | |
| } | |
| private void drawTopRect(Canvas canvas, float scaleFactor) { | |
| canvas.save(); | |
| offsetTopRect(barShrinkage * scaleFactor, 0, -barShrinkage * scaleFactor, 0); | |
| canvas.rotate( | |
| level < LEVEL_BREAKPOINT | |
| ? level * TOPRECT_FIRST_ROTATION | |
| : LEVEL_BREAKPOINT*TOPRECT_FIRST_ROTATION + (1 - level) * TOPRECT_SECOND_ROTATION | |
| , iconRect.width()/2 | |
| , iconRect.height()/2); | |
| canvas.drawRect(topRect, mPaint); | |
| canvas.restore(); | |
| } | |
| private void drawMiddleRect(Canvas canvas, float scaleFactor) { | |
| canvas.save(); | |
| offsetMiddleRect(0, 0, -barShrinkage*2f/3f * scaleFactor, 0); | |
| canvas.rotate( | |
| level < LEVEL_BREAKPOINT | |
| ? level * MIDRECT_FIRST_ROTATION | |
| : LEVEL_BREAKPOINT*MIDRECT_FIRST_ROTATION + (1 - level) * MIDRECT_SECOND_ROTATION | |
| , iconRect.width()/2 | |
| , iconRect.height()/2); | |
| canvas.drawRect(middleRect, mPaint); | |
| canvas.restore(); | |
| } | |
| private void drawBottomRect(Canvas canvas, float scaleFactor) { | |
| canvas.save(); | |
| offsetBottomRect(barShrinkage * scaleFactor, 0, -barShrinkage * scaleFactor, | |
| 0); | |
| canvas.rotate( | |
| level < LEVEL_BREAKPOINT | |
| ? level * BOTRECT_FIRST_ROTATION | |
| : LEVEL_BREAKPOINT*BOTRECT_FIRST_ROTATION + (1 - level) * BOTRECT_SECOND_ROTATION | |
| , iconRect.width()/2 | |
| , iconRect.height()/2); | |
| canvas.drawRect(bottomRect, mPaint); | |
| canvas.restore(); | |
| } | |
| private void offsetTopRect(float offsetLeft, float offsetTop, | |
| float offsetRight, float offsetBottom) { | |
| topRect.set( | |
| iconRect.width()/2 - barWidth/2 + offsetLeft | |
| , iconRect.height()/2 - barSpacing - barHeight/2 + offsetTop | |
| , iconRect.width()/2 + barWidth/2 + offsetRight | |
| , iconRect.height()/2 - barSpacing + barHeight/2 + offsetBottom); | |
| } | |
| private void offsetMiddleRect(float offsetLeft, float offsetTop, | |
| float offsetRight, float offsetBottom) { | |
| middleRect.set( | |
| iconRect.width()/2 - barWidth/2 + offsetLeft | |
| , iconRect.height()/2 - barHeight/2 + offsetTop | |
| , iconRect.width()/2 + barWidth/2 + offsetRight | |
| , iconRect.height()/2 + barHeight/2 + offsetBottom); | |
| } | |
| private void offsetBottomRect(float offsetLeft, float offsetTop, | |
| float offsetRight, float offsetBottom) { | |
| bottomRect.set( | |
| iconRect.width()/2 - barWidth/2 + offsetLeft | |
| , iconRect.height()/2 + barSpacing - barHeight/2 + offsetTop | |
| , iconRect.width()/2 + barWidth/2 + offsetRight | |
| , iconRect.height()/2 + barSpacing + barHeight/2 + offsetBottom); | |
| } | |
| @Override | |
| public void setAlpha(int alpha) { | |
| mPaint.setAlpha(alpha); | |
| invalidateSelf(); | |
| } | |
| @Override | |
| public void setColorFilter(ColorFilter cf) { | |
| mPaint.setColorFilter(cf); | |
| invalidateSelf(); | |
| } | |
| @Override | |
| public int getOpacity() { | |
| return 0; | |
| } | |
| /** | |
| * set the color of the Drawable; | |
| * @param color | |
| */ | |
| public void setColor(int color) { | |
| mPaint.setColor(color); | |
| invalidateSelf(); | |
| } | |
| /** | |
| * set the size of the icon; this size should be smaller than the size of this drawable itself to fully show the transformation! | |
| * @param size the size of the icon in pixel | |
| */ | |
| public void setIconSize(int size){ | |
| if(size > Math.min(width, height)) size = Math.min(width, height); | |
| iconRect.set(0,0,size,size); | |
| Gravity.apply(gravity, iconRect.width(), iconRect.height(), getBounds(), iconRect); | |
| float ratio = size / BASE_ICON_SIZE; | |
| barWidth = BASE_BAR_WIDTH * ratio; | |
| barHeight = BASE_BAR_HEIGHT * ratio; | |
| barSpacing = BASE_BAR_SPACING * ratio; | |
| barShrinkage = BASE_BAR_SHRINKAGE * ratio; | |
| invalidateSelf(); | |
| } | |
| /** | |
| * Apply a {@link Gravity} to align the icon in this drawable | |
| * @param gravity the gravity to align the icon in this drawable | |
| */ | |
| public void setGravity(int gravity){ | |
| Gravity.apply(gravity, iconRect.width(), iconRect.height(), getBounds(), iconRect); | |
| invalidateSelf(); | |
| } | |
| /** | |
| * resets the icon size to its default size (as specified in the Material-Design-guidelines | |
| * this means, the icon will be 1/3 of the minimal size of this drawable | |
| */ | |
| public void setDefaultIconSize(){ | |
| setIconSize((int) (Math.min(width, height) * BASE_ICON_SIZE/BASE_DRAWABLE_SIZE)); | |
| } | |
| /** | |
| * offset the icon from its center | |
| * @param offsetX the offset the icon should have from its center in x-direction | |
| * @param offsetY the offset the icon should have from its center in y-direction | |
| */ | |
| public void offsetIcon(int offsetX, int offsetY){ | |
| this.offsetX = offsetX; | |
| this.offsetY = offsetY; | |
| invalidateSelf(); | |
| } | |
| /** | |
| * set the state of the Drawable; | |
| * | |
| * @param level | |
| */ | |
| public void setState(int state) { | |
| switch (state) { | |
| case STATE_DRAWER: | |
| setLevel((float) STATE_DRAWER); | |
| break; | |
| case STATE_ARROW: | |
| setLevel((float) STATE_ARROW); | |
| break; | |
| } | |
| } | |
| /** | |
| * set the level of the animation; drawer indicator is fully displayed at 0; | |
| * arrow is fully displayed at 1 | |
| * | |
| * @param level | |
| */ | |
| public void setLevel(float level) { | |
| if (level == 1) | |
| breakpointReached = true; | |
| if (level == 0) | |
| breakpointReached = false; | |
| this.level = (breakpointReached ? LEVEL_BREAKPOINT : 0) + level / 2; | |
| invalidateSelf(); | |
| } | |
| @Override | |
| public int getIntrinsicWidth() { | |
| return width; | |
| } | |
| @Override | |
| public int getIntrinsicHeight() { | |
| return height; | |
| } | |
| } |
| package de.twoid.drawericondrawabletest; | |
| import android.app.Activity; | |
| import android.os.Bundle; | |
| import android.view.Gravity; | |
| public class MainActivity extends Activity { | |
| DrawerIconDrawable drawerIconDrawable; | |
| @Override | |
| protected void onCreate(Bundle savedInstanceState) { | |
| super.onCreate(savedInstanceState); | |
| setContentView(R.layout.activity_main); | |
| float DIPS = getResources().getDisplayMetrics().density; | |
| drawerIconDrawable = new DrawerIconDrawable( | |
| (int) (72*DIPS) //width of the drawable | |
| , (int) (48*DIPS) //height of the drawable | |
| , (int) (18*DIPS) //x-offset of the icon | |
| , 0 //y-offset of the icon | |
| , Gravity.CENTER_VERTICAL | Gravity.LEFT //let the icon center vertically and align to the left of the drawable | |
| ); | |
| getActionBar().setDisplayShowHomeEnabled(false); | |
| getActionBar().setDisplayHomeAsUpEnabled(true); | |
| getActionBar().setHomeAsUpIndicator(drawerIconDrawable); | |
| } | |
| } |
Do I just copy and paste this into the code of my app?
Yes, just copy it ;) I'm going to post a updated version soon though.. so you might just wait for that
I must be missing something completely obvious to get this working .. :(
mDrawerIcon = new DrawerIconDrawable(50, 50, 16);
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setHomeAsUpIndicator(mDrawerIcon);
Is that all that's needed? When doing this, I only see my application icon in the action bar, not the drawable...
UPDATE:
Ok, I got it working! In case anyone has similar issues, the fix for me was to use:
getSupportActionBar().setLogo(mDrawerIcon);
And, then to achieve the spinning effect, in the ActionBarDrawerToggle::onDrawerSlide() set:
mDrawerIcon.setLevel(slideOffset);
And yes, homj, it sounds like you're aware of the scaling issues. It appears ~2x too small, and the incoming size isn't used.
Looking forward to your next update! And, thank you for providing a wonderfully self-contained "library"!!
I just updated the code! You can now set the icon-size besides the drawable-size itself and apply a offset and a gravity to the icon to arrange it.
Feel free to test it and provide some feedback if you notice any bugs or issues!
This should do it:
getActionBar().setHomeAsUpIndiactor(drawerIconDrawable);