/* * Copyright 2014 Google Inc. All rights reserved. * * 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. * * The original is from Google you can find here: * https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget/BezelImageView.java * * Modified and improved with additional functionality by Mike Penz */ package com.pan.materialdrawer.view; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewOutlineProvider; import android.widget.ImageView; import com.pan.materialdrawer.R; import com.pan.materialdrawer.util.DrawerImageLoader; /** * An {@link android.widget.ImageView} that draws its contents inside a mask and draws a border * drawable on top. This is useful for applying a beveled look to image contents, but is also * flexible enough for use with other desired aesthetics. */ public class BezelImageView extends ImageView { private Paint mBlackPaint; private Paint mMaskedPaint; private Rect mBounds; private RectF mBoundsF; private Drawable mMaskDrawable; private boolean mDrawCircularShadow = true; private ColorMatrixColorFilter mDesaturateColorFilter; private int mSelectorAlpha = 150; private int mSelectorColor; private ColorFilter mSelectorFilter; private boolean mCacheValid = false; private Bitmap mCacheBitmap; private int mCachedWidth; private int mCachedHeight; private boolean isPressed = false; private boolean isSelected; public BezelImageView(Context context) { this(context, null); } public BezelImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BezelImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // Attribute initialization final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BezelImageView, defStyle, R.style.BezelImageView); mMaskDrawable = a.getDrawable(R.styleable.BezelImageView_biv_maskDrawable); if (mMaskDrawable != null) { mMaskDrawable.setCallback(this); } mDrawCircularShadow = a.getBoolean(R.styleable.BezelImageView_biv_drawCircularShadow, true); mSelectorColor = a.getColor(R.styleable.BezelImageView_biv_selectorOnPress, 0); a.recycle(); // Other initialization mBlackPaint = new Paint(); mBlackPaint.setColor(0xff000000); mMaskedPaint = new Paint(); mMaskedPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); // Always want a cache allocated. mCacheBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Create a desaturate color filter for pressed state. ColorMatrix cm = new ColorMatrix(); cm.setSaturation(0); mDesaturateColorFilter = new ColorMatrixColorFilter(cm); //create a selectorFilter if we already have a color if (mSelectorColor != 0) { this.mSelectorFilter = new PorterDuffColorFilter(Color.argb(mSelectorAlpha, Color.red(mSelectorColor), Color.green(mSelectorColor), Color.blue(mSelectorColor)), PorterDuff.Mode.SRC_ATOP); } } @Override protected void onSizeChanged(int w, int h, int old_w, int old_h) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (mDrawCircularShadow) { setOutlineProvider(new CustomOutline(w, h)); } } } @TargetApi(21) private class CustomOutline extends ViewOutlineProvider { int width; int height; CustomOutline(int width, int height) { this.width = width; this.height = height; } @Override public void getOutline(View view, Outline outline) { outline.setOval(0, 0, width, height); } } @Override protected boolean setFrame(int l, int t, int r, int b) { final boolean changed = super.setFrame(l, t, r, b); mBounds = new Rect(0, 0, r - l, b - t); mBoundsF = new RectF(mBounds); if (mMaskDrawable != null) { mMaskDrawable.setBounds(mBounds); } if (changed) { mCacheValid = false; } return changed; } @Override protected void onDraw(Canvas canvas) { if (mBounds == null) { return; } int width = mBounds.width(); int height = mBounds.height(); if (width == 0 || height == 0) { return; } if (!mCacheValid || width != mCachedWidth || height != mCachedHeight || isSelected != isPressed) { // Need to redraw the cache if (width == mCachedWidth && height == mCachedHeight) { // Have a correct-sized bitmap cache already allocated. Just erase it. mCacheBitmap.eraseColor(0); } else { // Allocate a new bitmap with the correct dimensions. mCacheBitmap.recycle(); //noinspection AndroidLintDrawAllocation mCacheBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); mCachedWidth = width; mCachedHeight = height; } Canvas cacheCanvas = new Canvas(mCacheBitmap); if (mMaskDrawable != null) { int sc = cacheCanvas.save(); mMaskDrawable.draw(cacheCanvas); if (isSelected) { if (mSelectorFilter != null) { mMaskedPaint.setColorFilter(mSelectorFilter); } else { mMaskedPaint.setColorFilter(mDesaturateColorFilter); } } else { mMaskedPaint.setColorFilter(null); } cacheCanvas.saveLayer(mBoundsF, mMaskedPaint, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG); super.onDraw(cacheCanvas); cacheCanvas.restoreToCount(sc); } else if (isSelected) { int sc = cacheCanvas.save(); cacheCanvas.drawRect(0, 0, mCachedWidth, mCachedHeight, mBlackPaint); if (mSelectorFilter != null) { mMaskedPaint.setColorFilter(mSelectorFilter); } else { mMaskedPaint.setColorFilter(mDesaturateColorFilter); } cacheCanvas.saveLayer(mBoundsF, mMaskedPaint, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG); super.onDraw(cacheCanvas); cacheCanvas.restoreToCount(sc); } else { super.onDraw(cacheCanvas); } } // Draw from cache canvas.drawBitmap(mCacheBitmap, mBounds.left, mBounds.top, null); //remember the previous press state isPressed = isPressed(); } @Override public boolean dispatchTouchEvent(MotionEvent event) { // Check for clickable state and do nothing if disabled if (!this.isClickable()) { this.isSelected = false; return super.onTouchEvent(event); } // Set selected state based on Motion Event switch (event.getAction()) { case MotionEvent.ACTION_DOWN: this.isSelected = true; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_SCROLL: case MotionEvent.ACTION_OUTSIDE: case MotionEvent.ACTION_CANCEL: this.isSelected = false; break; } // Redraw image and return super type this.invalidate(); return super.dispatchTouchEvent(event); } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mMaskDrawable != null && mMaskDrawable.isStateful()) { mMaskDrawable.setState(getDrawableState()); } if (isDuplicateParentStateEnabled()) { ViewCompat.postInvalidateOnAnimation(this); } } @Override public void invalidateDrawable(Drawable who) { if (who == mMaskDrawable) { invalidate(); } else { super.invalidateDrawable(who); } } @Override protected boolean verifyDrawable(Drawable who) { return who == mMaskDrawable || super.verifyDrawable(who); } /** * Sets the color of the selector to be draw over the * CircularImageView. Be sure to provide some opacity. * * @param selectorColor The color (including alpha) to set for the selector overlay. */ public void setSelectorColor(int selectorColor) { this.mSelectorColor = selectorColor; this.mSelectorFilter = new PorterDuffColorFilter(Color.argb(mSelectorAlpha, Color.red(mSelectorColor), Color.green(mSelectorColor), Color.blue(mSelectorColor)), PorterDuff.Mode.SRC_ATOP); this.invalidate(); } @Override public void setImageDrawable(Drawable drawable) { super.setImageDrawable(drawable); } @Override public void setImageResource(int resId) { super.setImageResource(resId); } @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); } @Override public void setImageURI(Uri uri) { if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) { DrawerImageLoader.getInstance().setImage(this, uri, null); } else { super.setImageURI(uri); } } private ColorMatrixColorFilter mTempDesaturateColorFilter; private ColorFilter mTempSelectorFilter; public void disableTouchFeedback(boolean disable) { if (disable) { mTempDesaturateColorFilter = this.mDesaturateColorFilter; mTempSelectorFilter = this.mSelectorFilter; this.mSelectorFilter = null; this.mDesaturateColorFilter = null; } else { if (mTempDesaturateColorFilter != null) { this.mDesaturateColorFilter = mTempDesaturateColorFilter; } if (mTempSelectorFilter != null) { this.mSelectorFilter = mTempSelectorFilter; } } } }