setShadowLayer Android API differences
setShadowLayer()
is only supported on text when hardware acceleration is on. Hardware acceleration is on by default when targetSdk=14
or higher. An easy workaround is to put your View in a software layer: myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
.
Android canvas setShadowLayer greatly decreases performance
While digging around to find a way to speed up my large text shadows, I stumbled on this question and answer:
setShadowLayer Android API differences
By using:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
I drastically sped up all the text shadows in my app.
Here is a sample of how I use it:
/**
* Set a backlight (shadow) on the passed TextView.
* @param textView
*/
void setBacklight(TextView textView) {
if (textView != null) {
float textSize = textView.getTextSize();
textView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
textView.setShadowLayer(textSize,0,0,getColor(R.color.color_backlight));
}
}
According to this doc:
https://developer.android.com/guide/topics/graphics/hardware-accel.html
It says that you can disable hardware acceleration for the view.
I don't know why, but somehow this magically speeds up my TextView shadow layers.
I know, I know. That method doesn't exist for the Canvas or Paint classes. So to answer the specific question (so I don't get blasted by everyone...), you could set that on the View that you intend to draw the canvas. Like this:
void inTheShadows(View view) {
float left = 0f;
float top = 0f;
float right = 10f;
float bottom = 10f;
Canvas canvas = new Canvas();
Paint paint = new Paint();
paint.setShadowLayer(8.f, 0, 0, 0xff000000);
canvas.drawRect(left, top, right, bottom, paint);
view.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
view.onDrawForeground(canvas);
}
Achieve setShadowLayer effect during drawCircle (or drawRoundRect) without turning off hardware acceleration
I had found a way to achieve such. First, we construct the ball + shadow off-screen image. Note, by using off-screen bitmap drawing technique, no GPU will be involved. The key is, don't use the Canvas
from onDraw
to perform drawCircle
.
Instead, construct our very own Canvas
, backed by an off-screen bitmap.
private Bitmap generateBallBitmap() {
final int ballShadowSize = Utils.dpToPixel(BALL_SHADOW_SIZE);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(historyChartBallColor);
paint.setShadowLayer(ballShadowSize, ballShadowSize, ballShadowSize, historyChartBallShadow);
Bitmap bitmap = Bitmap.createBitmap((int)((ballSize + ballShadowSize)*2f), (int)((ballSize + ballShadowSize)*2f), Bitmap.Config.ARGB_8888);
bitmap.eraseColor(Color.TRANSPARENT);
Canvas canvas = new Canvas(bitmap);
canvas.drawCircle(ballSize, ballSize, ballSize, paint);
return bitmap;
}
In custom view onDraw
function, just draw the off-screen bitmap directly.
if (null == this.ballBitmap) {
this.ballBitmap = generateBallBitmap();
}
canvas.drawBitmap(this.ballBitmap, px - this.ballSize, py, null);
For the entire process, I merely depend on the default value of layer type, without calling setLayerType
explicitly.
The outcome is fast, yet shadow effect is visible too.
Is there an equivalent to setShadowLayer when defining shapes in XML in Android?
Using XML, there's no real way to do this that I know of. I've seen some suggestions of making a second box of the same shape behind the item, and just filling it with black, but I don't feel that's a good solution. I've been trying to find a way to do this myself for a while.
If it helps, here's a link to a similar question I posted, along with some code. I got it working for some images, but it still seems to have trouble finding the alpha channel sometimes. Basically, I've overridden ImageView and put this in the onDraw()
method:
@Override
protected void onDraw(Canvas canvas)
{
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.omen);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShadowLayer(5.5f, 6.0f, 6.0f, Color.BLACK);
canvas.drawColor(Color.GRAY);
canvas.drawRect(50, 50, 50 + bmp.getWidth(), 50 + bmp.getHeight(), paint);
canvas.drawBitmap(bmp, 50, 50, null);
}
That was just testing, though, so obviously a lot of the parameters would have to be more generic. I haven't had a lot of time to work on it lately though, but maybe this will help you find your answer.
Android Paint setShadowLayer() ignores color of its Paint
You have made your inner bitmap too small and it custs the edges of the rectange. Try this:
Bitmap outerBm = Bitmap.createBitmap(
rect.width(), rect.height(), Bitmap.Config.ARGB_8888);
Bitmap innerBm = Bitmap.createBitmap(
rect.width() + 50, rect.height() + 50, Bitmap.Config.ARGB_8888);
Canvas outerCanvas = new Canvas(outerBm);
Canvas innerCanvas = new Canvas(innerBm);
outerCanvas.rotate(3);
rect.left += 25;
rect.top += 25;
rect.right -= 25;
rect.bottom -= 25;
Paint shadowPaint = new Paint();
shadowPaint.setColor(Color.BLUE);;
shadowPaint.setShadowLayer(12, 12, 12, 0xFF555555);
innerCanvas.drawRect(rect, shadowPaint);
outerCanvas.drawBitmap(innerBm, 0, 0, null);
Possible bug with shadow layer?
You can try this maybe by changing the radius :
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- Drop Shadow Stack -->
<item>
<shape>
<corners android:radius="12dp" />
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp" />
<solid android:color="#00CCCCCC" />
</shape>
</item>
<item>
<shape>
<corners android:radius="12dp" />
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp" />
<solid android:color="#10CCCCCC" />
</shape>
</item>
<item>
<shape>
<corners android:radius="12dp" />
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp" />
<solid android:color="#20CCCCCC" />
</shape>
</item>
<item>
<shape>
<corners android:radius="12dp" />
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp" />
<solid android:color="#30CCCCCC" />
</shape>
</item>
<item>
<shape>
<corners android:radius="12dp" />
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp" />
<solid android:color="#50CCCCCC" />
</shape>
</item>
<!-- Background -->
<item>
<shape>
<solid android:color="@android:color/white" />
<corners android:radius="12dp" />
</shape>
</item>
</layer-list>
Shadow layer work with the emulator but not on an actual device
I'm not sure, but this maybe because of hardware acceleration. setShadowLayer
doen't work if view accelerated. Try to disable acceleration for whole app and check. Read this.
Unsupported Drawing Operations
setShadowLayer(): works with text only
Use a software layer type to force a view to be rendered in software. If a view that is hardware accelerated (for instance, if your whole application is hardware acclerated), is having rendering problems, this is an easy way to work around limitations of the hardware rendering pipeline.
Use setLayerType to set layer type to individual views or turn off acceleration in your manifest for whole app.
Related Topics
Commitallowingstateloss() in Fragment Activities
How to Set a Fragment Tag by Code
Adding a Vertical Scrollbar to an Alertdialog in Android
Remove All Items from Recyclerview
Speechrecognizer Causes Anr... I Need Help with Android Speech API
How to Add Cardview in Layout Xml in Androidx
How to Remove White Underline in a Searchview Widget in Toolbar Android
Trying to Remove Fragment from View Gives Me Nullpointerexception on Mnextanim
Creating Animation on Imageview While Changing Image Resource
When to Use Fragmentmanager::Putfragment and Getfragment
Gradle Sync Failed: Failed to Find Build Tools Revision 24.0.0 Rc1
How to Set Ringtone with Ringtonemanager.Action_Ringtone_Picker
How to Access an Existing Sqlite Database in Android
No Repository Found Error in Installing Adt in Eclipse Indigo