I know it's possible to create a rotated version of a Drawable (or Bitmap), as such (written about it here) :
@JvmStatic
fun getRotateDrawable(d: Drawable, angle: Int): Drawable {
if (angle % 360 == 0)
return d
return object : LayerDrawable(arrayOf(d)) {
override fun draw(canvas: Canvas) {
canvas.save()
canvas.rotate(angle.toFloat(), (d.bounds.width() / 2).toFloat(), (d.bounds.height() / 2).toFloat())
super.draw(canvas)
canvas.restore()
}
}
}
I wanted to have autoMirrored
being set to some drawable (VectorDrawable in my case), which would flip (mirror so that left is right, and right is left, but not affect top and bottom) it in case the locale of the device is RTL.
As an example (and it's only an example!), if you take a drawable that shows a left-arrow, after flipping it will be a right-arrow.
Sadly, this is available only from API 19.
That's why I decided to make a new Drawable out of it, to be a flipped version of the original one
I've found a nice article of doing the same thing to a View, here, using a Matrix. So I've tried this:
@JvmStatic
fun getMirroredDrawable(d: Drawable): Drawable {
return object : LayerDrawable(arrayOf(d)) {
override fun draw(canvas: Canvas) {
canvas.save()
val matrix = Matrix()
// use this for the other flipping: matrix.preScale(1.0f, -1.0f)
matrix.preScale(-1.0f, 1.0f);
canvas.matrix = matrix
super.draw(canvas)
canvas.restore()
}
}
}
Sadly, for some reason, this made the drawable not being shown at all. Maybe it does work, yet tries to get shown out of its bounds of whatever View that's showing it.
How can I make a flipped version of a given Drawable, similar to what I did for rotating a Drawable?
Solution:
Based on the answer suggested below (here), here's a nice way to do it:
fun Drawable.getMirroredDrawable(): Drawable {
return object : LayerDrawable(arrayOf(this)) {
val drawingRect = Rect()
val matrix = Matrix()
override fun draw(canvas: Canvas) {
matrix.reset()
matrix.preScale(-1.0f, 1.0f, canvas.width / 2.0f, canvas.height / 2.0f)
canvas.matrix = matrix
drawingRect.left = (canvas.width - intrinsicWidth) / 2
drawingRect.top = (canvas.height - intrinsicHeight) / 2
drawingRect.right = drawingRect.left + intrinsicWidth
drawingRect.bottom = drawingRect.top + intrinsicHeight
if (bounds != drawingRect)
bounds = drawingRect
super.draw(canvas)
}
}
}
Layer list. A LayerDrawable is a drawable object that manages an array of other drawables. Each drawable in the list is drawn in the order of the list—the last drawable in the list is drawn on top.
A StateListDrawable is a Drawable object that uses a different image to represent the same object, depending on what state the object is in. For example, a Button can exist in one of several states (pressed, focused on, hovered over, or none of these).
Right Click on Drawable folder -> New -> Drawable Resource File , will create a XML file inside the Drawable folder.
A Bitmap is a representation of a bitmap image (something like java. awt. Image). A Drawable is an abstraction of "something that can be drawn".
Specify the center for the flip operation.
matrix.preScale(-1.0f, 1.0f, canvas.getWidth() / 2, canvas.getHeight() / 2);
Here is a custom Drawable
class that you can use to mirror drawables:
public class MirroredDrawable extends Drawable {
final Drawable mDrawable;
final Matrix matrix = new Matrix();
MirroredDrawable(Drawable drawable) {
mDrawable = drawable;
}
@Override
public void draw(@NonNull Canvas canvas) {
matrix.reset();
matrix.preScale(-1.0f, 1.0f, canvas.getWidth() / 2, canvas.getHeight() / 2);
canvas.setMatrix(matrix);
Rect drawingRect = new Rect();
drawingRect.left = (canvas.getWidth() - mDrawable.getIntrinsicWidth()) / 2;
drawingRect.top = (canvas.getHeight() - mDrawable.getIntrinsicHeight()) / 2;
drawingRect.right = drawingRect.left + mDrawable.getIntrinsicWidth();
drawingRect.bottom = drawingRect.top + mDrawable.getIntrinsicHeight();
mDrawable.setBounds(drawingRect);
mDrawable.draw(canvas);
}
// Other methods required to extend Drawable but aren't used here.
@Override
public void setAlpha(int alpha) { }
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) { }
@Override
public int getOpacity() { return PixelFormat.OPAQUE; }
}
Here is how it can be applied:
Drawable drawable = getResources().getDrawable(R.drawable.your_drawable);
getSupportActionBar().setHomeAsUpIndicator(new MirroredDrawable(drawable));
You can use android:scaleX="-1"
in the view's xml to show the drawable's mirror image. To make it work automatically depending on the layout's direction, you could use an integer resource value:
<ImageView
android:scaleX="@integer/rtl_flip_factor"
android:src="@android:drawable/ic_media_play"/>
To finish it off, you'd need a plain values/integers.xml
for the default (LTR) case
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="rtl_flip_factor">1</integer>
</resources>
and another values-ldrtl/integers.xml
for the RTL case:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="rtl_flip_factor">-1</integer>
</resources>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With