Android 6.0 incorrectly handles drawCircle method - android

In my app I need to draw circles using bitmap and the drawCircle() method.
Everything was working fine and exactly as it should up until Android 6.0.
It still draws circles on all the previous versions, but draws rectangles when I use the app on 6.0. But if I change it to be filled, it draws a circle both in api 22 and api 23.
Anyone has the same problem or any idea why this happens?
Here is the source code and a screenshot (app running on API 23 on the left, and API 22 on the right). same app on different api's
public final class Circle1View extends View {
private float xCenter, yCenter;
private Bitmap grid = null;
public Circle1View (Context context) {
super(context);
init();
}
private void init() {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
#Override
protected void onDraw(Canvas canvas) {
int w = getWidth();
int h = getHeight();
xCenter = w / 2;
yCenter = h / 2;
drawBitmaps(w, h);
canvas.translate(xCenter, yCenter);
canvas.scale(xCenter, yCenter);
canvas.drawBitmap(grid, null, new RectF(-1, -1, 1, 1), null);
}
private void drawBitmaps(int w, int h) {
grid = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas();
canvas.translate(xCenter, yCenter);
canvas.scale(xCenter, yCenter);
Paint gridPaint = new Paint();
gridPaint.setStrokeWidth(0.01f);
// Works with FILL
// gridPaint.setStyle(Paint.Style.FILL);
gridPaint.setStyle(Paint.Style.STROKE);
canvas.setBitmap(grid);
canvas.drawCircle(0, 0, 0.5f, gridPaint);
}
}

I think it has something to do with the scaling and translation you do. Imagine the circle that is drawn is so small, it only takes 4 pixels. When enlarging this back to the full size, you are left with 4 straight lines between these pixels.
When I change the stroke width to 0.04f, the issue is gone. I would suggest you simplify your code by drawing on the supplied Canvas directly:
#Override
protected void onDraw(Canvas canvas) {
int w = getWidth();
int h = getHeight();
xCenter = w / 2;
yCenter = h / 2;
Paint gridPaint = new Paint();
gridPaint.setStrokeWidth(1f);
gridPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(xCenter, yCenter, w/4, gridPaint);
}
As for your question about the difference between API levels: Marshmallow introduced changes for drawBitmap(). You can have a look at the respective source code for Lollipop and Marshmallow.

Related

Can I draw a simple paint pie chart like this?

I'm not looking for anything fancy. I have tried to follow tutorials about pie and bar graphs, but a lot of the tutorials don't follow the guidelines of my app. In my app, in one of my fragment layouts, I would like to simply draw a pie chart of two values.
as simple as -
Draw Circle
(%value) of the circle is red.
(other%value) of circle is blue.
Can this not be done this way?
No need to do the leg work when someone else has made it available.
I highly recommend HoloGraphLibrary. I use it anytime I need to make a pie graph.
https://bitbucket.org/danielnadeau/holographlibrary/wiki/Home
It is very easy to use and it looks great.
If all you want is the chart you describe, then I think adding any library is major overkill. This very simple example should give you some ideas. The setPercentage() method sets the size of the red area; the remainder will be blue. Please note that there is no exception handling implemented, and the percentage should be between 0 and 100, inclusive.
public class MainActivity extends Activity
{
SimplePieChart pie;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
pie = new SimplePieChart(this);
pie.setPercentage(65);
setContentView(pie);
}
public class SimplePieChart extends View
{
private RectF rect = new RectF();
private Paint paint = new Paint();
private int percentage;
public SimplePieChart(Context context)
{
this(context, null);
}
public SimplePieChart(Context context, AttributeSet attrs)
{
super(context, attrs);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
}
public void setPercentage(int percentage)
{
this.percentage = percentage;
invalidate();
}
public int getPercentage()
{
return percentage;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
if (w > h)
{
rect.set(w / 2 - h / 2, 0, w / 2 + h / 2, h);
}
else
{
rect.set(0, h / 2 - w / 2, w, h / 2 + w / 2);
}
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
paint.setColor(Color.RED);
canvas.drawArc(rect, 0, 360 * percentage / 100, true, paint);
paint.setColor(Color.BLUE);
canvas.drawArc(rect, 360 * percentage / 100, 360 - 360 * percentage / 100, true, paint);
}
}
}

Scaling in android game development, how to do it properly?

So, recently me and my two teammates started on our exam project. Which is a basic 2D skijumping game.
We've done programming on android before but never in relation to games and we have the basic techniques down for creating animations using a thread and all that. But we seem to have a scaling issue.
Currently we have a background image in the drawable folder in the resolution 1920x1080, we are using BitmapFactory to scale the background image to the users screen size, which seems to be working fine it fits perfectly on all our 3 different smartphones.
Were creating an animation of the player sprite skiing down the hill and jumping off the edge, which looks very funny and works perfectly. For this I simply divided the screen width and height by 100 and used that percentage of either 1% screen width to move him at certain speeds on the x-axis and likewise on the y-axis with the 1% of the screen height.
It works on most phones but, for some reason it won't work on all of them? I don't understand why not? I mean the math should always work shouldn't it? On some phones he skies outside of the hill in the air and not on the proper trajectory. I don't get what I'm missing but I'm suspecting it has something to do with the screen format or something?
Can anyone enlighten me on this? Please keep in mind this is our first android game so were new at game dev.
And thanks for all your input if you have any in advance.
try this:
class V extends View implements Callback {
private Bitmap mBitmap;
private Matrix mMatrix;
private Handler mHandler;
private float mX;
private Paint mPaint;
public V(Context context) {
super(context);
Resources res = getResources();
mBitmap = BitmapFactory.decodeResource(res, R.drawable.layer0);
mMatrix = new Matrix();
mHandler = new Handler(this);
mHandler.obtainMessage(0).sendToTarget();
mPaint = new Paint();
mPaint.setColor(0xff00ff00);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
RectF src = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
RectF dst = new RectF(0, 0, w, h);
mMatrix.setRectToRect(src, dst, ScaleToFit.CENTER);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.concat(mMatrix);
canvas.drawBitmap(mBitmap, 0, 0, null);
canvas.drawRect(mX, 20, mX + 20, 40, mPaint);
}
#Override
public boolean handleMessage(Message msg) {
msg = mHandler.obtainMessage(0);
if (mX < mBitmap.getWidth()) {
mX += 1.5f;
mHandler.sendMessageDelayed(msg, 50);
} else {
Log.d(TAG, "handleMessage stop");
}
invalidate();
return true;
}
}

Border of bitmap appears different on different device in Android

Hello I am trying to make the bitmap to rounded shape programmatically but I see that in new phone it is less rounded then same APK I installed there I see more rounded corners in old phone. See in the screenshot
Sony Z Xperia 4.3
Samsung Y Dous 2.3
I could not understand why it so displaying different border radius somewhat more rounded and less rounded in another device. Any idea how I can resolve this to make same for all devices.
Code :
public class RoundedCornersDrawable extends BitmapDrawable {
private final BitmapShader bitmapShader;
private final Paint p;
private final RectF rect;
private final float borderRadius;
public RoundedCornersDrawable(final Resources resources, final Bitmap bitmap) {
super(resources, bitmap);
bitmapShader = new BitmapShader(getBitmap(), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
p = getPaint();
p.setAntiAlias(true);
p.setShader(bitmapShader);
final int w = (int) (resources.getDisplayMetrics().widthPixels) - (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 35, resources.getDisplayMetrics());
final int h = 200;
rect = new RectF(0, 0, w, h);
borderRadius = 15;
}
#Override
public void draw(final Canvas canvas) {
canvas.drawRoundRect(rect, borderRadius, borderRadius, p);
}
}
Thanks in advance
I guess that has to do with the different screen densities of the devices (how many pixel are in one inch of the screen). One pixel is not the same size on the screen on all devices. Here's some info about supporting multiple screens. You should also have a look at DisplayMetrics, there you can get the current screen density that you should add to you height and radius calculations.
Alternatively, you could use the display width as basis for the height and the radius calculation. Right now you are using a fix amount of 200 and 15 pixel.
Something like this:
h = w/10;
borderRadius = h / 3;

Custom View not drawing itself

I read different questions here about this topic, but I still can't find an answer. Feel free to close this question for any reason.
I have a simple Circle class that extends View.
The code for this class is:
public class ProgressCircle extends View {
Paint mCirclePaint;
float extRadius;
float viewWidth, viewHeight;
float centerX, centerY;
public ProgressCircle(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
init();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
float xpad = (float) getPaddingLeft() + getPaddingRight();
float ypad = (float) getPaddingTop() + getPaddingBottom();
float ww = (float)w - xpad; float hh = (float)h - ypad;
extRadius = Math.min(ww, hh) / 2;
viewWidth = this.getWidth();
viewHeight = this.getHeight();
centerX = viewWidth / 2; centerY = viewHeight / 2;
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(centerX, centerY, extRadius, mCirclePaint);
canvas.drawText("ciao", 0, 0, mCirclePaint);
}
private void init() {
mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint.setColor(0x666666);
mCirclePaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
I confirmed that every method in this classed is called when the main activity is created (through the use of some Log.d()s). I added a <com.mypackage.Circle> element in my main activity's LinearLayout and after that I added a sample testing button.
What I achieved is that the button is shown while my Circle isn't, but still the button (which in the LinearLayout comes after the Circle) is not the first element of the layout: that makes me think that something actually happens, but nothing gets drawn.
It was just a silly problem: the color in mCirclePaint.setColor(0x666666) was an invalid one. It worked with mCirclePaint.setColor(Color.RED) or with any other color defined in the res folder.
If you need to specify a color value, you'll have to include the transparency byte (otherwise it is not a 32bit integer that you are specifying, but a 24bit). So 0x666666 is invalid, but 0xff666666 is a valid color and will draw.
After reading the documentation(http://developer.android.com/guide/topics/graphics/2d-graphics.html):
The Android framework will only call onDraw() as necessary. Each time
that your application is prepared to be drawn, you must request your
View be invalidated by calling invalidate(). This indicates that you'd
like your View to be drawn and Android will then call your onDraw()
method (though is not guaranteed that the callback will be
instantaneous).
Also another thing worth checking is the dimensions you're drawing insure nothing is invalid like a height of 0 etc..
I notice you're not overriding View.onMeasure().
Because you're not overriding this method onsizeChanged() might be passed size 0. You can check this by putting a breakpoint in the onSizechanged() method or printing to Logcat.

Issue with Canvas.rotate() in Android .How does it exactly work?

Please find the code of my onDraw method below(.I'm trying to rotate the canvas(//Rotate call -b) by 25 degrees after drawing the arc .But i find that the arc is still drawn from 0 to 50 degrees.I was expecting it to move by another 25 degrees.
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
int px = getMeasuredWidth() / 2;
int py = getMeasuredHeight() / 2;
// radius - min
int radius = 130;
// Defining bounds for the oval for the arc to be drawn
int left = px - radius;
int top = py - radius;
int right = left + (radius * 2);
int bottom = top + (radius * 2);
RectF rectF = new RectF(left, top, right, bottom);
paint.setColor(Color.RED);
paint.setStyle(Style.FILL);
//canvas.rotate(25,px,py);//Rotate call -a
canvas.drawArc(rectF, 0, 50, true, paint);
canvas.rotate(25,px,py);//Rotate call -b
}
}
But if i place the rotate call(//Rotate call -a) before drawing the arc i see that the arc drawn is shifted by 25 degrees more .What exactly is happening here? Can someone explain to me?
Thanks
The Canvas maintains a Matrix that is responsible for all transformations on it. Even for rotation. As you can see in the documentation, the rotate method says:
Preconcat the current matrix with the specified rotation.
All transformations are done on the Canvas Matrix,hence, on the Canvas. The arc you draw isn't rotated. You first rotate the Canvas and then draw on it.
Hence, in your code, call -a works, not call -b.
EDIT:
For issues like postrotate and prerotate check the Matrix class(postRotate and preRotate methods).
Few Examples: this and this.
And few things you might want to read : this and this.

Categories

Resources