Have you ever rotate an ImageView before? It’s quite simple to rotate an ImageView, as that could simply be done by even in the layout xml code with the android:rotation attribute.
A simple example of code as below
<CustomImageView
android:id="@+id/image_on"
android:layout_width="60dp"
android:layout_height="60dp"
android:rotation="-6"
android:scaleType="centerCrop"
android:src="@drawable/simpleimage"/>
The image generated is rotated to the left as expected. It’s all good until it got inspected by sharp eyes (of a good designer), will bring to light that it is not good, especially on lower resolution devices. (Shhhh … this would make a good “interview test material” for designer :P)
An enlarged image of the rotated ImageView is as below …
Jagged all over the edges
You would notice the rough jagged edges.
Applying anti-alias should help the smoother the edges between different color within the image. But unfortunately for the edges of the image will not be anti-alias, as Android doesn’t anti-alias across it’s view.
Trying to be smart, we could add padding, margin, or even use background image to , hopefully Android would be smart to blend them to smooth out the rough jagged edges. Sorry, they don’t help.
There are several stackoverflow that recommend turning off Hardware Acceleration for the particular view, but it doesn’t help in my case as well.
What I did was to experiment adding some transparent border on all the sides of my image and save as PNG, then use that image to rotate. Bingo! … the edge got anti-aliased, and as smooth as I like. Check out below, smooth smooth edges!
Jagged cleared in all edges
But wait… we don’t always have the image readily that we could manually change by adding transparent border to it. Even if we do, it’s so much trouble to do so. This is not feasible!
Given that we are sure that transparent border does help, perhaps we could do it programmatically.
So, I try to expand a Canvas by 1 px on all sides but it turn out that this is not as simple as I thought, as stated in this stackoverflow,.
So given that we can’t add 1 px, so now I cheat…. I shrink the image by 1 px for all sides, by painting the bitmap through a shader. That will leave the side of the image as a transparent padding of 1px each.
The code as below (it is added into the void onDraw(Canvas canvas) function of my CustomImageView that inherit from ImageView.
if (getDrawable() instanceof BitmapDrawable) {
Paint paint = new Paint();
paint.setAntiAlias(true);
Bitmap bitmap = ((BitmapDrawable) getDrawable()).getBitmap();
BitmapShader shader =
new BitmapShader(bitmap,
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Matrix matrix = new Matrix();
float scale;
/* Note: this piece of code handling like Centre-Crop scaling */
if (bitmap.getWidth() > bitmap.getHeight()) {
scale = (float)canvas.getHeight()
/ (float) bitmap.getHeight();
matrix.setScale(scale, scale);
matrix.postTranslate(
(canvas.getWidth() - bitmap.getWidth() * scale) * 0.5f, 0);
} else {
scale = (float)canvas.getWidth()
/ (float) bitmap.getWidth();
matrix.setScale(scale, scale);
matrix.postTranslate(0,
(canvas.getHeight() - bitmap.getHeight() * scale) * 0.5f);
}
shader.setLocalMatrix(matrix);
paint.setShader(shader);
/* this is where I shrink the image by 1px each side,
move it to the center */
canvas.translate(1, 1);
canvas.drawRect(
0.0f, 0.0f, canvas.getWidth()-2, canvas.getHeight()-2, paint);
}
With the above code, you’ll get the exact jagged-clear image that I show above. The actual image although shrink by 1 px on each side, but due to the anti-aliasing effect, it makes it seems expanded 1 px as well, hence canceling the shrink look of 1 px.
My hope is Android would automatically by default handle anti-aliasing for all the View’s edges when it is rotated. To me the above is still a ‘hacky’ way of achieving it. If you have a better ‘hack’ to it, do share.