Replaced CircularImageView with ShapeableImageView
This commit is contained in:
parent
95fd195df8
commit
7aab762712
5 changed files with 10 additions and 394 deletions
|
@ -1,337 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package code.name.monkey.retromusic.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
|
||||
public class CircularImageView extends AppCompatImageView {
|
||||
|
||||
private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
|
||||
|
||||
// Default Values
|
||||
private static final float DEFAULT_BORDER_WIDTH = 4;
|
||||
private static final float DEFAULT_SHADOW_RADIUS = 8.0f;
|
||||
|
||||
// Properties
|
||||
private float borderWidth;
|
||||
private int canvasSize;
|
||||
private float shadowRadius;
|
||||
private int shadowColor = Color.BLACK;
|
||||
|
||||
// Object used to draw
|
||||
private Bitmap image;
|
||||
private Drawable drawable;
|
||||
private Paint paint;
|
||||
private Paint paintBorder;
|
||||
|
||||
// region Constructor & Init Method
|
||||
public CircularImageView(final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CircularImageView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CircularImageView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
// Init paint
|
||||
paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
paintBorder = new Paint();
|
||||
paintBorder.setAntiAlias(true);
|
||||
|
||||
// Load the styled attributes and set their properties
|
||||
TypedArray attributes =
|
||||
context.obtainStyledAttributes(attrs, R.styleable.CircularImageView, defStyleAttr, 0);
|
||||
|
||||
// Init Border
|
||||
if (attributes.getBoolean(R.styleable.CircularImageView_civ_border, true)) {
|
||||
float defaultBorderSize =
|
||||
DEFAULT_BORDER_WIDTH * getContext().getResources().getDisplayMetrics().density;
|
||||
setBorderWidth(
|
||||
attributes.getDimension(
|
||||
R.styleable.CircularImageView_civ_border_width, defaultBorderSize));
|
||||
setBorderColor(
|
||||
attributes.getColor(R.styleable.CircularImageView_civ_border_color, Color.WHITE));
|
||||
}
|
||||
|
||||
// Init Shadow
|
||||
if (attributes.getBoolean(R.styleable.CircularImageView_civ_shadow, false)) {
|
||||
shadowRadius = DEFAULT_SHADOW_RADIUS;
|
||||
drawShadow(
|
||||
attributes.getFloat(R.styleable.CircularImageView_civ_shadow_radius, shadowRadius),
|
||||
attributes.getColor(R.styleable.CircularImageView_civ_shadow_color, shadowColor));
|
||||
}
|
||||
attributes.recycle();
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Set Attr Method
|
||||
public void setBorderWidth(float borderWidth) {
|
||||
this.borderWidth = borderWidth;
|
||||
requestLayout();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setBorderColor(int borderColor) {
|
||||
if (paintBorder != null) {
|
||||
paintBorder.setColor(borderColor);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void addShadow() {
|
||||
if (shadowRadius == 0) {
|
||||
shadowRadius = DEFAULT_SHADOW_RADIUS;
|
||||
}
|
||||
drawShadow(shadowRadius, shadowColor);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setShadowRadius(float shadowRadius) {
|
||||
drawShadow(shadowRadius, shadowColor);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setShadowColor(int shadowColor) {
|
||||
drawShadow(shadowRadius, shadowColor);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScaleType getScaleType() {
|
||||
return SCALE_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScaleType(ScaleType scaleType) {
|
||||
if (scaleType != SCALE_TYPE) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"ScaleType %s not supported. ScaleType.CENTER_CROP is used by default. So you don't need to use ScaleType.",
|
||||
scaleType));
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Draw Method
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
// Load the bitmap
|
||||
loadBitmap();
|
||||
|
||||
// Check if image isn't null
|
||||
if (image == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isInEditMode()) {
|
||||
canvasSize = canvas.getWidth();
|
||||
if (canvas.getHeight() < canvasSize) {
|
||||
canvasSize = canvas.getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
// circleCenter is the x or y of the view's center
|
||||
// radius is the radius in pixels of the cirle to be drawn
|
||||
// paint contains the shader that will texture the shape
|
||||
int circleCenter = (int) (canvasSize - (borderWidth * 2)) / 2;
|
||||
// Draw Border
|
||||
canvas.drawCircle(
|
||||
circleCenter + borderWidth,
|
||||
circleCenter + borderWidth,
|
||||
circleCenter + borderWidth - (shadowRadius + shadowRadius / 2),
|
||||
paintBorder);
|
||||
// Draw CircularImageView
|
||||
canvas.drawCircle(
|
||||
circleCenter + borderWidth,
|
||||
circleCenter + borderWidth,
|
||||
circleCenter - (shadowRadius + shadowRadius / 2),
|
||||
paint);
|
||||
}
|
||||
|
||||
private void loadBitmap() {
|
||||
if (this.drawable == getDrawable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.drawable = getDrawable();
|
||||
this.image = drawableToBitmap(this.drawable);
|
||||
updateShader();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
canvasSize = w;
|
||||
if (h < canvasSize) {
|
||||
canvasSize = h;
|
||||
}
|
||||
if (image != null) {
|
||||
updateShader();
|
||||
}
|
||||
}
|
||||
|
||||
private void drawShadow(float shadowRadius, int shadowColor) {
|
||||
this.shadowRadius = shadowRadius;
|
||||
this.shadowColor = shadowColor;
|
||||
setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
|
||||
paintBorder.setShadowLayer(shadowRadius, 0.0f, shadowRadius / 2, shadowColor);
|
||||
}
|
||||
|
||||
private void updateShader() {
|
||||
if (image == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Crop Center Image
|
||||
image = cropBitmap(image);
|
||||
|
||||
// Create Shader
|
||||
BitmapShader shader = new BitmapShader(image, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
||||
|
||||
// Center Image in Shader
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setScale(
|
||||
(float) canvasSize / (float) image.getWidth(),
|
||||
(float) canvasSize / (float) image.getHeight());
|
||||
shader.setLocalMatrix(matrix);
|
||||
|
||||
// Set Shader in Paint
|
||||
paint.setShader(shader);
|
||||
}
|
||||
|
||||
private Bitmap cropBitmap(Bitmap bitmap) {
|
||||
Bitmap bmp;
|
||||
if (bitmap.getWidth() >= bitmap.getHeight()) {
|
||||
bmp =
|
||||
Bitmap.createBitmap(
|
||||
bitmap,
|
||||
bitmap.getWidth() / 2 - bitmap.getHeight() / 2,
|
||||
0,
|
||||
bitmap.getHeight(),
|
||||
bitmap.getHeight());
|
||||
} else {
|
||||
bmp =
|
||||
Bitmap.createBitmap(
|
||||
bitmap,
|
||||
0,
|
||||
bitmap.getHeight() / 2 - bitmap.getWidth() / 2,
|
||||
bitmap.getWidth(),
|
||||
bitmap.getWidth());
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
|
||||
private Bitmap drawableToBitmap(Drawable drawable) {
|
||||
if (drawable == null) {
|
||||
return null;
|
||||
} else if (drawable instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) drawable).getBitmap();
|
||||
}
|
||||
|
||||
int intrinsicWidth = drawable.getIntrinsicWidth();
|
||||
int intrinsicHeight = drawable.getIntrinsicHeight();
|
||||
|
||||
if (!(intrinsicWidth > 0 && intrinsicHeight > 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// Create Bitmap object out of the drawable
|
||||
Bitmap bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
return bitmap;
|
||||
} catch (OutOfMemoryError e) {
|
||||
// Simply return null of failed bitmap creations
|
||||
Log.e(getClass().toString(), "Encountered OutOfMemoryError while generating bitmap!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Mesure Method
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int width = measureWidth(widthMeasureSpec);
|
||||
int height = measureHeight(heightMeasureSpec);
|
||||
/*int imageSize = (width < height) ? width : height;
|
||||
setMeasuredDimension(imageSize, imageSize);*/
|
||||
setMeasuredDimension(width, height);
|
||||
}
|
||||
|
||||
private int measureWidth(int measureSpec) {
|
||||
int result;
|
||||
int specMode = MeasureSpec.getMode(measureSpec);
|
||||
int specSize = MeasureSpec.getSize(measureSpec);
|
||||
|
||||
if (specMode == MeasureSpec.EXACTLY) {
|
||||
// The parent has determined an exact size for the child.
|
||||
result = specSize;
|
||||
} else if (specMode == MeasureSpec.AT_MOST) {
|
||||
// The child can be as large as it wants up to the specified size.
|
||||
result = specSize;
|
||||
} else {
|
||||
// The parent has not imposed any constraint on the child.
|
||||
result = canvasSize;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int measureHeight(int measureSpecHeight) {
|
||||
int result;
|
||||
int specMode = MeasureSpec.getMode(measureSpecHeight);
|
||||
int specSize = MeasureSpec.getSize(measureSpecHeight);
|
||||
|
||||
if (specMode == MeasureSpec.EXACTLY) {
|
||||
// We were told how big to be
|
||||
result = specSize;
|
||||
} else if (specMode == MeasureSpec.AT_MOST) {
|
||||
// The child can be as large as it wants up to the specified size.
|
||||
result = specSize;
|
||||
} else {
|
||||
// Measure the text (beware: ascent is a negative number)
|
||||
result = canvasSize;
|
||||
}
|
||||
|
||||
return (result + 2);
|
||||
}
|
||||
// endregion
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
package code.name.monkey.retromusic.views
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.core.content.withStyledAttributes
|
||||
import code.name.monkey.retromusic.R
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
/** @author Hemanth S (h4h13).
|
||||
*/
|
||||
class NetworkImageView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) :
|
||||
CircularImageView(context, attrs, defStyleAttr) {
|
||||
|
||||
init {
|
||||
context.withStyledAttributes(attrs, R.styleable.NetworkImageView, 0, 0) {
|
||||
val url = getString(R.styleable.NetworkImageView_url_link)
|
||||
setImageUrl(context, url!!)
|
||||
}
|
||||
}
|
||||
|
||||
fun setImageUrl(imageUrl: String) {
|
||||
setImageUrl(context, imageUrl)
|
||||
}
|
||||
|
||||
private fun setImageUrl(context: Context, imageUrl: String) {
|
||||
Glide.with(context)
|
||||
.load(imageUrl)
|
||||
.error(R.drawable.ic_account)
|
||||
.placeholder(R.drawable.ic_account)
|
||||
.into(this)
|
||||
}
|
||||
}
|
|
@ -6,13 +6,12 @@
|
|||
android:layout_height="match_parent"
|
||||
android:padding="@dimen/padding_album_cover">
|
||||
|
||||
<code.name.monkey.retromusic.views.CircularImageView
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/player_image"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:civ_border="false"
|
||||
app:civ_shadow="false"
|
||||
app:shapeAppearance="@style/ShapeAppearance.Material3.Circle"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
|
@ -117,18 +117,17 @@
|
|||
app:layout_constraintTop_toBottomOf="@+id/nextSongLabel"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<code.name.monkey.retromusic.views.CircularImageView
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/artistImage"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:elevation="10dp"
|
||||
app:civ_border="false"
|
||||
app:civ_shadow="false"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:shapeAppearance="@style/ShapeAppearance.Material3.Circle"
|
||||
app:srcCompat="@drawable/default_artist_art" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
@ -139,8 +138,8 @@
|
|||
android:name="code.name.monkey.retromusic.fragments.player.CoverLyricsFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="20dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:elevation="20dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/playerToolbar"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/playerToolbar" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -254,4 +254,8 @@
|
|||
</style>
|
||||
|
||||
<style name="Theme.AppWidget" parent="Theme.RetroMusic.FollowSystem" />
|
||||
|
||||
<style name="ShapeAppearance.Material3.Circle" parent="">
|
||||
<item name="cornerSize">50%</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue