I made a custom view using canvas's drawText method. Somehow none of the text is showing on any of the Jelly Bean devices. It works fine for ICS and below.
Does anyone know if anything has changed from API 15 to 16 for this method or any related methods?
Edit Code: (from the draw method where canvas is supplied as a parameter)
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(3);
paint.setColor(context.getResources().getColor(R.color.plot_background));
canvas.drawRect(new Rect(0,0,getWidth(),getHeight()), paint);
paint.setColor(color_text);
paint.setTextSize(getScaled(18.5f));
paint.setTextAlign(Align.CENTER);
canvas.drawText(title, (graphwidth / 2) + horstart, border/2+15, paint);
I know the line is been executed and the coordinates are correct because the same code works on the older platforms.
Thanks Eric. Figured out the error. I scale everything in the app base on canvas.getDensity(). getDensity() at the moment the draw function is ALWAYS 0 for jelly bean devices for some reason. But it does return the correct value for anything between 1.6 -> 4.0.3
I didn't post the code for that (which is my fault) is because I didn't suspect getDensity() to be the problem since it never did in the last two years while the app is in the market.
The workaround was to modify the getScaled function.
public float getScaled(Canvas canvas,float in){
return in * ( canvas.getDensity()==0 ? 1 : canvas.getDensity()/ 160.0f);
}
The documentation does say that DENSITY_NONE could be returned but I think what might have happened is that in Jelly Bean does the scaling for you since if I just multiply it by 1, it works as a charm on the two different density device that I just tested on.
(P.S. Can anyone familiar the internals of Android OS correct me if I am wrong or confirm it? )
Related
I have some problems with android.graphics.path.
I am developing a game. I have some Paths. They don't change there sizes, shapes. I draw them onto a canvas. I move them in every game's frame. So I set an offset: dx
It works fine on many devices:
Nokia X Dual Sim (Android 4.4.4 Cyanogenmode)(API level 19)
Samsung Galaxy Duos (GT-S7562)(Android 4.0.4)(API level 15)
Sony X8 (Android 2.3.7 Cyanogenmode)(API level 10)
and on some others
But it not works well on some other devices:
Samsung Galaxy Ace II (Android 4.1.2)(API level 16)
Nokia X Dual Sim (Nokia X platform 1.2)(Android Studio shows: API level 16)
and on some others
I set the offset in my draw method:
path.offset(dX, 0);
Not working means: it not moves. The system draws it to the original position.
But when it works, it works descent (moves, and it is fast)
I also tried with Matrixes:
translateMatrix = new Matrix();
translateMatrix.setTranslate(dX, 0);
path.transform(translateMatrix);
The same happened.
I see something on developer site of Android:
http://developer.android.com/reference/android/graphics/Path.html
void offset(float dx, float dy)
Offset the path by (dx,dy), returning true on success.
??? It must have changed, that's why here is an error
It may in connection with the Hardware Accelerated mode:
https://groups.google.com/forum/#!topic/android-developers/HgGVSbSghpk
I support from Api level 9, but I turned Harware Accelerated mode. (It works form API level 14)
The problem also shows up when I turn it off.
I don't see why is it sometimes work over API level 14, and sometimes why not?
It says, hardware accelerated mode supports Path:
http://android-developers.blogspot.de/2011/03/android-30-hardware-acceleration.html
Do you know what is the problem with this? What should I do?
Something that mixes it up more:
This works on every device (but works slower - it lags):
Canvas temp = new Canvas(bitmap);
path.draw(temp);
originalCanvas.drawBitmap(bitmap, 0, 0, myPaint);
So
I make a temporary canvas
I set it on a bitmap
I draw on that canvas (it will be on the bitmap)
I draw the bitmap onto the original canvas
Why does it work here?
Please help me how should I make it work.
I made it work, but not with the offset
I made my own offset function:
it saves the last position, and calculates the new one with the
offset.
Then it creates a new Path without using operator new
calculateNewPoints();
wallpath.rewind();
wallpath.moveTo(newPosX, newPosY);
.
.
.
wallpath.close();
It performs well :)
On API level 16 on manifest add the following attribute to the application
<application android:hardwareAccelerated="false" ...>
Indeed there seems to be a bug related to Path.offset() on older Android devices.
I have purchased an 8 years old "Samsung Galaxy S3" phone with Android 4.1.2 to test my word game and was surprised to discover, that Paths in the app are not filled:
My workaround is shown below, for older Android devices I do not use Path.offset() anymore, but instead Canvas.translate() and then draw the not offsetted Path object:
public class LetterTile extends RectF {
// consider anything older then Android 6.0 Marshmallow as an old device
public static final boolean TOO_OLD =
(Build.VERSION.SDK_INT < Build.VERSION_CODES.M);
private final Path mPathFill = new Path();
// mPathFillMoved is same as mPathFill, but offset by left, top
// it is used on newer devices to paint gradient over all tiles
private final Path mPathFillMoved = new Path();
public void draw(Canvas canvas, Paint gradientPaint) {
if (!TOO_OLD) {
// the mPathFillMoved only works on newer Android devices
canvas.drawPath(mPathFillMoved, gradientPaint);
}
canvas.save();
canvas.translate(left, top);
if (TOO_OLD) {
// on older Android devices just draw mPathFilled in plain yellow
canvas.drawPath(mPathFill, gradientPaint);
}
canvas.restore();
I've got custom widget - blinking circle in custom frequency. In my onDraw method i paint a circle.
#Override
protected void onDraw(Canvas c) {
super.onDraw(c);
if (data[mBitIndex] == '1'){
c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, mPaint);
}
}
This works perfect on android 4.2.1, but if i try this on android 2.3, no circle shown. I've done some experiments to determine the problem
#Override
protected void onDraw(Canvas c) {
super.onDraw(c);
if (data[mBitIndex] == '1'){
c.drawColor(mPaint.getColor());
}
}
This works as i expected on both versions of android. But, it's a square, not circle, which i want.
How is it even possible to draw square, but not circle? I've searching on the internet, but noone has this problem before or it's only my problem. I don't get this, can anyone explain me where could be mistake?
On Honeycomb (API 13) or earlier, the hardware acceleration is buggy and cannot draw your circle.
Disable hardware acceleration for your view:
setLayerType(View.LAYER_TYPE_SOFTWARE,null);
This worked for me.
Or set targetSDKVersion to 14 or higher (which might cause other issues).
There was a curious bug in my app where the Paint.measureText(String text) was returning a different answer when it seemed to me it should be returning the same value. Eventually I tracked it down to the ANTI_ALIAS_FLAG being being changed when I used the Paint object to draw the text, i.e.
Paint text_paint = new Paint();
text_paint.setStyle(Paint.Style.FILL);
text_paint.setTextSize(50);
text_paint.setColor(Color.BLACK);
Log.d("MyApp","AA1="+text_paint.isAntiAlias());
canvas.drawText("SomeText", 10, 10, text_paint);
Log.d("MyApp","AA2="+text_paint.isAntiAlias());
with Android version 4.2.1 produced the log output
04-29 19:30:00.702: D/MyApp(18806): AA1=false
04-29 19:30:00.707: D/MyApp(18806): AA2=true
And the result of measureText depends on this ANTI_ALIAS_FLAG. So my questions areWhat is this ANTI_ALIAS_FLAG, and what determines whether one chooses to set it true or false?It seems to me that a call like canvas.drawText(...) shouldn't alter the paint object being used, so what on earth is going on, and is this a bug?
I drew a basic Smith chart on the canvas using circles, arcs and lines. I have run the app on numerous size emulator screens and all work perfectly but once I tried it on an actual device (Android level 2.3.5) the chart does not line up as it should i.e. some objects are out of place.
While writing the code I was careful to use get.Width() and get.Height() for the parameters instead of using pixels so that the app would work correctly on all devices. Below is an example of the code i used:
canvas.drawCircle(canvas.getWidth()*1/2, canvas.getHeight()*3/8, canvas.getWidth()*475/1000, black);
canvas.drawCircle(canvas.getWidth()*5/8, canvas.getWidth()*5/8, canvas.getWidth()*349/1000, black);
canvas.drawCircle(canvas.getWidth()*6/8, canvas.getWidth()*5/8, canvas.getWidth()*228/1000, black);
canvas.drawCircle(canvas.getWidth()*7/8, canvas.getWidth()*5/8, canvas.getWidth()*103/1000, black);
arc0.set(canvas.getWidth()/2, canvas.getHeight()*-139/700, canvas.getWidth()*100/69, canvas.getHeight()*3/8);
arc1.set(canvas.getWidth()*-6/112, canvas.getHeight()*-80/100, canvas.getWidth()*195/100, canvas.getHeight()*72/192);
arc2.set(canvas.getWidth()*7/10, canvas.getHeight()*70/700, canvas.getWidth()*125/100, canvas.getHeight()*3/8);
arc3.set(canvas.getWidth()/2, canvas.getHeight()*3/8, canvas.getWidth()*100/69, canvas.getHeight()*91/96);
arc4.set(canvas.getWidth()*-8/112, canvas.getHeight()*3/8, canvas.getWidth()*195/100, canvas.getHeight()*150/100);
arc5.set(canvas.getWidth()*7/10, canvas.getHeight()*3/8, canvas.getWidth()*125/100, canvas.getHeight()*65/100);
The graph lines up fine on all the different size emulator screens i have tried, so would anybody be able to tell me why it doesn't line up on an actual device. Thanks
You have to use percentage values not numbers... moreover try to take a look on conversions of the value u r getting from getwidth(). Possibly some values are missing..
I'm working on a custom view for an android application, similar to the Analog Gauge sample code available from Mind the Robot.
Running the code from listed site, I get see this on my screen:
(Motorola Droid, 2.2.3), (Emulator, 4.0.3)
(Xoom, 4.0.3)(Other phone, 4.0.3)
The hand is missing!
The drawing calls are being made (I can see them in logcat), but the canvas elements the calls draw are invisible.
It's not API level dependent, though; if I import it the right way into a project, it will hand will show up when I run it on the Xoom.
But, when I move the files to a different project folder (same source code, same layouts) it goes back to missing the dial.
What's going on? How could the same code be producing such different outcomes on different devices?
So, the key clue in my mystery seemed to be that it worked on the emulator, but not on the hardware devices.
Hardware Rendering
I did peruse the hardware rendering page on the Android Developer's website, but apparently not closely enough.
http://developer.android.com/guide/topics/graphics/hardware-accel.html
While it does mention that the API's are available beginning version 11, it does not say that Hardware Rendering is turned on for all applications by default, starting with API Level 14 (ICS).
What does this mean for us?
Almost everything is faster; except for the few things that don't work.
I managed to violate two of these, without realizing it:
Canvas.DrawTextOnPath()
Paint.setShadowLayer()
It's not mentioned in the API reference (or anywhere else I can find, and certainly not checked by Lint), but using any of the listed operations can do weird things.
In my case, Canvas.DrawTextOnPath() seemed to work just fine.
But when Android notice that the paint that I used on the hand had shadow layer set, it silently ignored it.
How do I know if my View is hardware accelerated?
From the documentation link above:
There are two different ways to check whether the application is hardware accelerated:
View.isHardwareAccelerated() returns true if the View is attached to a hardware accelerated window.
Canvas.isHardwareAccelerated() returns true if the Canvas is hardware accelerated
If you must do this check in your drawing code, use Canvas.isHardwareAccelerated() instead >of View.isHardwareAccelerated() when possible. When a view is attached to a hardware >accelerated window, it can still be drawn using a non-hardware accelerated Canvas. This >happens, for instance, when drawing a view into a bitmap for caching purposes.
In my case, the opposite appears to have occurred.
The custom view logs that it is not Hardware-accelerated; however, the canvas reports that it is hardware-accelerated.
Work Arounds and Fixings
The simplest fix is forcing the custom view to do software rendering. Per the documentation this can be accomplished by:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Alternatively, you could remove the offending operations, and keep hardware rendering turned on.
Learn from my misfortune. Good luck, all.
I put it into init() and worked fine after that.
private void init() {
setLayerType(myView.LAYER_TYPE_SOFTWARE, null);
....
}
With myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); suggestion I can see hand. But I have still a problem: I see scale with only 0 written! As in the picture and two strage zeros out of the schema: (GALAXY NEXUS 4.2.1)
My drawScale() method is as in the example:
private void drawScale(Canvas canvas) {
canvas.drawOval(scaleRect, scalePaint);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
for (int i = 0; i < totalNicks; ++i) {
float y1 = scaleRect.top;
float y2 = y1 - 0.020f;
canvas.drawLine(0.5f, y1, 0.5f, y2, scalePaint);
if ((i % 5) == 0) {
int value = nickToDegree(i);
if ((value >= minDegrees) && (value <= maxDegrees)) {
String valueString = Integer.toString(value);
canvas.drawText(valueString, 0.5f, y2 - 0.015f, scalePaint);
}
}
canvas.rotate(degreesPerNick, 0.5f, 0.5f);
}
canvas.restore();
}
in my case i made this:
AnalogView bar = (AnalogView) findViewById(R.id.AnalogBar);
bar.setLayerType(bar.LAYER_TYPE_SOFTWARE, null);
if (value_list.size()>0) bar.SetData(Double.parseDouble(value_list.get(value_list.size()-1)));
where SetData in AnalogView is
public void SetData(double data) {
setHandTarget((float)data);
invalidate();
}
On Galaxy S4 Android 4.4.2
TYPE_TEMPERATURE is deprecated
use
TYPE_AMBIENT_TEMPERATURE
For anyone having problems with text drawing on scale in the initialisation do this:
scalePaint.setLinearText(true);