Is it necessary to create seperate .xml files rather than using DisplayMetrics? - android

I am preparing a chess game and I have to declare many ImageViews Buttons dynamically.
However, my friends said that it will not support other screen sizes, but I heard about DisplayMetrics code; we can get the screen width and height through code only.
Well I am using different drawables for different screen sizes. Can I release my application in google playstore like this?

You can do it however you feel better, but using xml files would result in less code to write, to maintain and to execute at runtime.
So, i'd go the standard way (xml files).
You can still use the programmatic way, but then you have to mess with getWidth, getHeight and multiply by the density scale factor, ... you have to do some math!!

This could be done without XML, but i would use a canvas for chess.
Maybe something like this:
private Paint createChessrBoard(int size)
{
Bitmap bitmap = Bitmap.createBitmap(size* 2, size* 2, Bitmap.Config.ARGB_8888);
Paint fill = new Paint(Paint.ANTI_ALIAS_FLAG);
fill.setStyle(Paint.Style.FILL);
fill.setColor(0x22000000);
Canvas canvas = new Canvas(bitmap);
Rect rect = new Rect(0, 0, size, size);
canvas.drawRect(rect, fill);
rect.offset(size, size);
canvas.drawRect(rect, fill);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(new BitmapShader(bitmap, BitmapShader.TileMode.REPEAT, BitmapShader.TileMode.REPEAT));
return paint;
}
You can use this to get the devices DPI
int dpi = getResources().getDisplayMetrics().density;

Related

Android canvas positioning, and customization

So this is my scenario:
I have an svg image that contains all the music notes on the staff (sort of sprite sheet)
I have two svg image that contains the key (maybe i can merge them together anyway)
All of them are converted to android Vector Drawable.
What i want to do is to select the note from the first svg, select the cleff, and then show them next to each other (the two images should be aligned).
So what i managed to achieve is to select the portion of svg for the note (i need to refine the rect size). But what i'm still having problem is to show both of them on the same line.
public MusicScore(Context context) {
super(context);
paint = new Paint();
paint.setColor(Color.BLACK);
Resources res = context.getResources();
musicClef = (VectorDrawable) res.getDrawable(R.drawable.ic_bassclef, null);
musicNotes = (VectorDrawable) res.getDrawable(R.drawable.ic_musicnotes, null);
int width = getWidth();
int height = getHeight();
Log.i("MUSIC", "H: " + musicClef.getMinimumHeight() + " W: " + musicClef.getMinimumWidth());
}
#Override
protected void onDraw(Canvas canvas){
Log.i("MUSIC", "Called");
int left = getWidth()/2;
int top = getHeight()/2;
musicClef.setBounds(0,0,musicClef.getIntrinsicWidth(), musicClef.getIntrinsicHeight() );
Bitmap source = Bitmap.createBitmap(musicNotes.getIntrinsicWidth(), musicNotes.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Bitmap clefSource = Bitmap.createBitmap(musicClef.getIntrinsicWidth(), musicClef.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas newcanvas = new Canvas(source);
Canvas clefCanvas = new Canvas(clefSource);
int notesLeft = musicClef.getIntrinsicWidth();
int notesTop = musicClef.getIntrinsicHeight();
musicNotes.setBounds(0, 0, musicNotes.getIntrinsicWidth(), musicNotes.getIntrinsicHeight());
musicNotes.draw(newcanvas);
musicClef.draw(clefCanvas);
Rect rect = new Rect(1150,0,1700, musicNotes.getIntrinsicHeight());
Rect rect2 = new Rect(notesLeft ,0, notesLeft + 450, musicNotes.getIntrinsicHeight());
Rect clefRect = new Rect(0, 0, musicClef.getIntrinsicWidth(), musicClef.getIntrinsicHeight());
canvas.drawBitmap(clefSource, null, clefRect, null);
canvas.drawBitmap(source, rect, rect2, null);
}
So with that code i can show a portion of the notes drawable, after converting it to a Bitmap. And i can also draw both of them, and the horizontal position is aligned. The problem is that the vertical position is not aligned, what i'm getting is:
So i know that my code is not correct (i'm pretty new to canvas in android and trying to figure out what to do), and what i learned so far is:
that in order to select a portion of the image i need to convert it to the Bitmap (i haven't found any way to do it directly with the VectorDrawable)
While drawing a Bitmap on the canvas with DrawBitmap, the first Rect represents the portion area i want to show, and the second one is the size and position of the area displayed.
In order to have the bitmap displayed on the canvas, i need to create a canvas from the Bitmap and draw it in it, in order to have it displayed. Is that correct?
So i have several questions:
I would like to understand how to vertically align the images, so they have to start from the same y (top).
I'm not sure if the vector image is the best idea, maybe is better to convert it into display dependant pngs? Or anyway the canvas size is relative to the screen? Or maybe i need to make my code screen-independent (i suppose that getIntrinsicWidth/Height are returning the real size of the image, so maybe i need to scale it?)
Why I need to explicitly setBounds of both images to have them displayed?
UPDATE #1
So i understood why probably my images are not aligned on the same line. It looks like that when creating a vector asset, for some reason the sizes are "adapted", not sure if is the android systems doing that or android studio. But anyway what i found after loading two images with the same height in pixels, is that for one i have an height, and for the other i have a different height (nearly double), and that explain why the image is shifted on the bottom.
So what i tried is to make a single image with both the notes, and the clefs, and it sort of works in the emulator. But when testing on a real device i get the error:
W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (11732x1168, max=8192x8192)
Bitmap too large to be uploaded into a texture (11732x1168, max=8192x8192)
i can understand what the error means, but anyway the image size in pixel is: 2933x292 pixels. Why the getIntrinsicWidth and getIntrinsicHeight are returning that dimensions? what is their metrics?
I'm wondering that maybe the vector drawable is not the best choice? Maybe is better to cnvert it into screen-dependant pngs? and use them?

Image Blend Modes in Android Studio (Like photoshop, paint.net)?

I am trying to blend two images the same way photoshop or paint.net blend them, with different blend modes like Difference, Multiply, Additive, Color Burn, Glow, etc.
I have found that PorterDuff.Mode works quite well, but it lacks blending effects ( it only has Add, Multiply, screen) is there a way to get a bigger range of blend modes? Is there a way to edit the android.graphics.PorterDuff; library to get more blend modes?
Any ideas?
For example:
THIS
turns into this:
Result
This is the porterduff code im using btw:
private static Bitmap Result(Bitmap bottomImage, Bitmap topImage){
int sizex = bottomImage.getWidth();
int sizey = bottomImage.getHeight();
Paint paint = new Paint();
Bitmap imageBitmap = Bitmap.createBitmap(sizex, sizey , Bitmap.Config.ARGB_8888);
Canvas comboImage = new Canvas(imageBitmap);
comboImage.drawBitmap(bottomImage, 0f, 0f, paint);;
PorterDuff.Mode mode = PorterDuff.Mode.MULTIPLY;//Porterduff MODE
paint.setXfermode(new PorterDuffXfermode(mode));
Bitmap ScaledtopImage = Bitmap.createScaledBitmap(topImage, sizex, sizey, false);
comboImage.drawBitmap(ScaledtopImage, 0f, 0f, paint);
return imageBitmap;
}
I'm not really an expert on image processing but PorterDuff has some blending modes, namely darken, lighten, multiply, screen and overlay. You can then use the in, atop, xor, etc to get the selection you want of the blended sections.
http://ssp.impulsetrain.com/porterduff.html
https://developer.android.com/reference/android/graphics/PorterDuff.Mode

StaticLayout height measurements off

I am trying to draw multiline text to a bitmap with the font Latto-Reg, and StaticLayout seems to have problems with it.
paint.setTextSize(label.fontSize);
paint.setTypeface(face);
StaticLayout textLayout = new StaticLayout(label.text, paint, (int)StaticLayout.getDesiredWidth(label.text, paint), Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
Bitmap bitmapAux = Bitmap.createBitmap(textLayout.getEllipsizedWidth(), textLayout.getHeight(), Bitmap.Config.ALPHA_8);
canvas.setBitmap(bitmapAux);
canvas.save();
canvas.translate(0, textLayout.height());
textLayout.draw(canvas);
canvas.restore();
The texture has padding on top and bottom depending on the font and size, while the text fits perfectly in the bitmap it is a lot of wasted memory space and makes laying it out to be off by a random amount.
I tested using single-line drawing and the bitmap was perfectly fitting the text
paint.getTextBounds(label.text, 0, label.text.length(), rect);
Bitmap bitmapAux = Bitmap.createBitmap(rect.width(), rect.height(), Bitmap.Config.ALPHA_8);
canvas.drawText(label.text, -rect.left, -rect.bottom, paint);
I have tried getting all kinds of metrics from StaticLayout and all of them seem to be off from the text: line 0 bounds, line 0 top, last line bottom...leading to the same padding problems.
EDIT:
I solved the problem by using offset-based single line drawing. Still the StaticLayout class was drawing incorrectly with several different non-standard fonts and I want to know why.
Looking at the android developper page, it looks like it's designed to handle both the multi-line case and being used next to another Layout well, and hence there is space on top of the line of text so that if you place it directly below another Layout it will be correctly spaced. In essence, it's just not designed for what you are trying to achieve.
Overall, it may be easier to get the Text bounds from Paint.getTextBounds() to know what the extent of the text will be within the Layout.
I've created a minimal working example of what I think you're trying to accomplish: creating a bitmap precisely large enough to contain the text rendered through a StaticLayout.
It seems that there are a few things wrong with your code:
You're needlessly translating vertically inside the bitmap;
There doesn't appear to be a height() method for StaticLayout.
Here's my result:
I added a green background to illustrate the size of the bitmap, but otherwise, my code differs very little from yours:
public void createTexture() {
int width = textLayout.getEllipsizedWidth();
int height = textLayout.getHeight();
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas2 = new Canvas(bitmap);
Paint p2 = new Paint();
p2.setStyle(Style.FILL);
p2.setColor(Color.GREEN);
canvas2.drawRect(0, 0, width, height, p2);
textLayout.draw(canvas2);
}
I created a very simple custom component to draw the bitmap:
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, paint);
}
It seems that perhaps you're translating to draw multiple textures after one another. I'd recommend that you do so in your draw method instead, translating vertically in the height of the previous texture after drawing it.

Android: What's the opposite of Bitmap.extractAlpha()

I need to do something which exactly the opposite of Bitmap.extractAlpha: Apply an alpha map (which loaded from a file) onto a RGB bitmap (which also loaded from a file).
Yes! Just like "CGImageCreateWithMask" in iOS!
The Square blog had a tutorial about this just last week: http://corner.squareup.com/2013/01/transparent-jpegs.html :).
I dont know what exactly CGImageCreateWithMask does, but if you want another picture to serve as the alpha channel for your Bitmap, you can create one as described in this question, which combines four images. I haven't tried it now, but I think for two colors it would look something like this:
Paint colorPaint = new Paint();
redPaint.setShader(new BitmapShader(redChanImg, TileMode.CLAMP, TileMode.CLAMP));
Paint alphaPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
alphaPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
c.setBitmap(resultImage);
c.drawRect(0, 0, width, height, colorPaint);
c.drawBitmap(alphaImg, 0, 0, alphaPaint);
// save result somewhere
You can also always just work on the raw pixel data of a bitmap using Bitmap.getPixels

Vignette in Android

So this is what I have for a vignette style effect in Android (image is a Bitmap):
public void vignette() {
float radius = (float) (image.getWidth()/1.5);
RadialGradient gradient = new RadialGradient(image.getWidth()/2, image.getHeight()/2, radius, Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP);
Canvas canvas = new Canvas(image);
canvas.drawARGB(1, 0, 0, 0);
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setShader(gradient);
final Rect rect = new Rect(0, 0, image.getWidth(), image.getHeight());
final RectF rectf = new RectF(rect);
canvas.drawRect(rectf, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(image, rect, rect, paint);
}
This "works" but there are a couple of problems. First of all, this is not really a vignette, it's just a gradient so you can see bits of the black going nearly all the way to the center rather than feathering out closer to the edges.
The RadialGradient used also only allows for setting the radius of a circle rather than an ellipse. An ellipse would be able to more effectively match the dimensions of a non-square image than a circle.
The quality of the gradient is also not superb.
I'm trying to replicate the vignetteImage method from ImageMagick (I'm referring specifically to the php version). I have this code in PHP that produces the style of image that I want:
$im = new IMagick('city.png');
$im->vignetteImage($width/1.5, 350, 20, 20);
I've tried building ImageMagick with the NDK but have been unsuccessful in properly linking the various image libraries (I've only successfully built with gif support but no png, jpeg or tiff).
I've also attached an image comparing the two methods shown above. The image on the left was generated with ImageMagick through php and the image on the right was generated using the method shown above for Android.
If you look carefully at the image on left, tf uses exponential increase in Alpha (transparency) vs. image on right which is very linear.
Clearly Shader.TitleMode.CLAMP is a linear function. What you should do instead is use RadialGradient(float x, float y, float radius, int[] colors, float[] positions, Shader.TileMode tile) to define 10 or more points on the image with exponentially decreasing color values (black to transparent).
Alternatively, you can refer gallery 3d source for ICS gallery http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/4.0.1_r1/com/android/gallery3d/photoeditor/filters/VignetteFilter.java?av=h
I know this is an old discussion but it may help someone.
You can use AccelerateInterpolator to generate the points that Taranfx had mentioned and it will lock awesome.

Categories

Resources