Android Layer-list Badge - android

I followed this article : http://javarticles.com/2015/09/android-icon-badge-example-using-layer-list-drawable.html . My code is almost exactley the same , with a little tweaking in the positioning part.Regardless what i try it never draws above the first layer drawable height.
I am trying to use this on an ActionBarSerlock drawer toggle button(yuh i know ABS is #Deprecated) or also known as hamburger, but how am i to set the circle to draw outside the bounds of the first image?I always get something like this:
I want the circle to draw fully and not be cut by the height of the first image.

I used the LayerDrawable method setLayerInset() .Took me some time to play around , now it looks like this:
mLayerDrawable.setLayerInset(0, 0, vSeperator, hSeperator, 0);
mLayerDrawable.setLayerInset(1, 0, 0, 0, vSeperator);
Where the vSeprator/hSeparator are density independent calculated values:
float density = getResources().getDisplayMetrics().density;
int vSeperator = (int) (10 * density + 0.5f);
int hSeperator = (int) (10 * density + 0.5f);
Hint: I'm shifting both images in equal of size but opposite directions

Related

Android - calculating pixel rotation without matrix? And checking if pixel is in view

I'm hoping someone can help me out. I'm making an image manipulation app, and I found I needed a better way to load in large images.
My plan, is to iterate through "hypothetical" pixels of an image (a "for loop" that covers width/height of the base image, so each iteration represents a pixel), scale/translate/rotate that pixels position relative to the view, then use this information to determine which pixels are being displayed in the view itself, then use a combination of BitmapRegionDecoder and BitmapFactory.Options to load in only the section of image that the output actually needs rather than a full (even if scaled) image.
So far I seem to have covered scale of the image and translation properly, but I can't seem to figure out how to calculate rotation. Since it's not a real Bitmap pixel I can't use Matrix.rotate =( Here is the image translations in the onDraw of the view, imgPosX and imgPosY hold the center point of the image:
m.setTranslate(-userImage.getWidth() / 2.0f, -userImage.getHeight() / 2.0f);
m.postScale(curScale, curScale);
m.postRotate(angle);
m.postTranslate(imgPosX, imgPosY);
mCanvas.drawBitmap(userImage.get(), m, paint);
and here is the math so far of how I'm trying to determine if an images pixel is on the screen:
for(int j = 0;j < imageHeight;j++) {
for(int i = 0;i < imageWidth;i++) {
//image starts completely center in view, assume image is original size for simplicity
//this is the original starting position for each pixel
int x = Math.round(((float) viewSizeWidth / 2.0f) - ((float) newImageWidth / 2.0f) + i);
int y = Math.round(((float) viewSizeHeight / 2.0f) - ((float) newImageHeight / 2.0f) + j);
//first we scale the pixel here, easy operation
x = Math.round(x * imageScale);
y = Math.round(y * imageScale);
//now we translate, we do this by determining how many pixels
//our images x/y coordinates have differed from it's original
//starting point, imgPosX and imgPosY in the view start in center
//of view
x = x + Math.round((imgPosX - ((float) viewSizeWidth / 2.0f)));
y = y + Math.round((imgPosY - ((float) viewSizeHeight / 2.0f)));
//TODO need rotation here
}
}
so, assuming my math up until rotation is correct (probably not but it appears to be working so far), how would I then calculate the rotation from that pixels position? I've tried other similar questions like:
Link 1
Link 2
Link 3
without using rotation the pixels I expect to actually be on the screen are represented (I made text file that outputs the results in 1's and 0's so I can have a visual representation of whats on the screen), but with the formula found in those questions the information isn't what is expected. (Scenario: I've rotated an image so only the top left corner is visible in the view. Using the info from Here to rotate the pixel, I should expect to see a triangular set of 1's in the upper left corner of the output file, but that's not the case)
So, how would I calculate a a pixels position after rotation without using the Android matrix? But still get the same results.
And if I've just messed it up entirely my apologies =( Any help would be appreciated, this project has gone on for so long and I want to finally be done lol
If you need any more information I will provide as much as I possibly can =) Thank you for your time
I realize this question is particularly difficult so I will be posting a bounty as soon as SO allows.
You do not need to create your own Matrix, use the existing one.
http://developer.android.com/reference/android/graphics/Matrix.html
You can map bitmap coordinates to screen coordinates by using
float[] coords = {x, y};
m.mapPoints(coords);
float sx = coords[0];
float sy = coords[1];
If you want to map screen to bitmap coordinates, you can create the inverse matrix
Matrix inverse = new Matrix(m);
inverse.inverse();
inverse.mapPoints(...)
I think your overall approach is going to be slow, as doing the pixel manipulation on the CU from Java has a lot of overhead. When drawing bitmaps normally, the pixel manipulation is done on the GPU.

How to draw large sized text on a canvas?

I want to draw on canvas month's text vertical along screen height.
Paint init:
this.paint = new Paint();
this.paint.setAntiAlias(true);
this.paint.setDither(true);
this.paint.setSubpixelText(true);
this.paint.setColor(color_text_dark);
this.paint.setTextAlign(Align.RIGHT);
Drawing:
// Set the scale to the widest month
float scale = getHeight() / this.max_month_width;
String month_string = FULL_MONTH_NAME_FORMATTER.
format(active_month_calendar.getTime());
canvas.save();
canvas.translate(getWidth(), 0);
canvas.rotate(-90);
canvas.scale(scale, scale);
canvas.drawText(month_string, 0, 0, this.paint);
canvas.restore();
Result looks good on hdpi screen, but very ugly and pixelated on xhdpi one.
I did more test on various devices, and understood what result depends on Android version, not screen density and resolution.
Code works fine on 2.x platform, but doesn't work on 4.0.3+. Suppose, Android draw implementation was changed here.
Full code you can see here.
hdpi version 2.3.5 (also tested 2.2)
xhdpi version 4.2 (also tested 4.1, 4.0.3)
Trying different variations for paint antialias, subpixel text has no effect. How can I fix this issue?
The problem is that you're drawing text at one size and scaling the result up. Once you've determined how wide you want the text to be, you should use calls to Paint.measureText(), adjusting the size via Paint.setTextSize() accordingly. Once it measures correctly, then you do your call to Canvas.drawText().
An alternative would be to not measure the text at all and just immediately call:
paint.setTextSize(paint.getSize() * scale)
There's no guarantee the text will fit in this case, though.
None of your other transform calls should result in interpolation, so it should give you very sharp lines.
Edit
Here is a code sample and comparison screenshot:
canvas.save();
canvas.scale(10, 10);
canvas.drawText("Hello", 0, 10, mTextPaint);
canvas.restore();
float textSize = mTextPaint.getTextSize();
mTextPaint.setTextSize(textSize * 10);
canvas.drawText("Hello", 0, 300, mTextPaint);
mTextPaint.setTextSize(textSize);
I don't have enough reputation to comment on Krylez's excellent answer, but I'd like to reply to mcfly soft's comment/question about paths.
The idea is the same for paths as text. Instead of scaling and translating the canvas a path is drawn on, put the same scaling and translation into a matrix and pass that to Path.transform:
// instead of this:
canvas.scale(sX, sY);
canvas.translate(trX, trY);
canvas.drawPath(path);
// do this:
matrix.postScale(sX, sY);
matrix.postTranslate(trX, trY);
path.transform(matrix);
canvas.drawPath(path);

how to get rotated image region?

For example I got a picture position in 10,10 with width of 100 and height of 120. I centre rotated it clock-wise 20 degree, the position will be smaller and size will be bigger since the edge will be coming out, but how would I calculate the value for them?
No sure I made the question clear enough, will change it if I find a better way to explain it.
Trying to do this in Android
I assume that you want to know how the bounding box of an object is transformed if you rotate it around its center.
If the original box has width w and height h, the new one, after rotation by r, has dimensions:
wr = abs(sin(r)) * h + abs(cos(r)) * w
hr = abs(sin(r)) * w + abs(cos(r)) * h

Android Drawable looks ugly due to scaling

I am writing a View that should show a drawable that seems to "never end".
It should be twice or third the displaysize and move slow through the display.
Therefore I studied some samplecode by Google and found the important Lines
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
canvasWidth = width;
canvasHeight = height;
float sf = backgroundImage.getWidth() / canvasWidth;
backgroundImage = Bitmap.createScaledBitmap(backgroundImage,
(int) (canvasWidth * sf), canvasHeight, true);
}
To rescale the image and than
// decrement the far background
backgroundXPos = backgroundXPos - DELTAMOVE;
// calculate the wrap factor for matching image draw
int newFarX = backgroundImage.getWidth() - (-backgroundXPos);
// if we have scrolled all the way, reset to start
if (newFarX <= 0) {
backgroundXPos = 0;
// only need one draw
canvas.drawBitmap(backgroundImage, backgroundXPos, 0, null);
} else {
// need to draw original and wrap
canvas.drawBitmap(backgroundImage, backgroundXPos, 0, null);
canvas.drawBitmap(backgroundImage, newFarX, 0, null);
}
To draw the moving image. The images is already moving, it's fine.
But, and this is the point of my question, the image looks very ugly. Its original is 960*190 pixels by 240ppi. It should be drawn inside a view with 80dip of height and "fill_parent" width.
It should look same (and good) on all devices. I have tried a lot but I don't know how to make the picture look nice.
Thanks for your help.
Best regards,
Till
Since you're saying that it's a never ending drawable, probably you're writing a game of some sort. If your image is a pixel-art type, then you don't want any scaling; pixel-art-type images cannot be scaled and keep its crisp look (you can try using nearest neighbor interpolation and scaling to an integer multiple of the original, which sometimes might work, but sometimes you will still need manual tweaks). This is the rare case where you actually would need to have different image resource for different screen resolutions.
Otherwise you might want to use a vector image, but if -- as you said -- your original is a high resolution image, then vector image probably won't help much here.
btw, you probably want to show some screenshot. "Looks ugly" is just as helpful as saying my code does not work.
Just a guess, but instead of passing a null paint to your drawBitmap() calls, try making a paint with bitmap filtering disabled:
Paint p = new Paint();
p.setFilterBitmap(false);
canvas.drawBitmap(backgroundImage, backgroundXPos, 0, p);
Hope that helps.

Draw text fitting in some specified Rectangle

Using a canvas, I want to draw some short label text (1-2 characters) which fits into some specified rectangle.
For some other reasons, the scaling I use is such that the dimensions of this recangle are small, i.e. about 1.
The problem I'm facing is to calculate the optimal (as large as possible so that the text still fits) text size to use with Paint.setTextSize prior to drawing the text (which I do using Canva.drawText()).
For that I can either use the Paint.Fontmetrics object to get some general font dimensions as floats or getTextBounds(String text, int start, int end, Rect bounds) to get the bounding box of the text as an integer rectangle. Due to the scaling I use, the integer bounding box from the latter is to imprecise to calculate the optimal text size for my purpose.
What I would need is some method to get the bounding box of the text with higher precision (i.e. like getStringBounds(String str, Graphics context) in java.awt.FontMetrics), but I found no suitable method.
I now was also busy with this problem.
Unfortunately I didn't manage to install the native source yet, but I'm pretty sure that text measurements (both getTextBounds() and measureText()) differ heftily from the real text output, especially for small font sizes (as to be seen in Moritz' screenshot).
I suppose that the measurement methods use a float width for a char, and the real text output uses an int (presumably due to performance). This will of course fail, if you need an absolutely exact size (e.g for autosize).
I experimented a bit with a monospace font. This Image shows some of these experiments:
.
The text scale in these experiments was set by auto scaling it.
In the top row, I created boxes with an integer increment. These match the android text output. You see, the last cipher doesn't match the screen!
In the 2nd row, the boxes were created with a float increment. The boxes match the screen better, but they don't match the android text output!
In the last row, the boxes were incremented by a float increment and the text was output char by char by the same increment. Both boxes and chars fit perfect.
In my conclusion, it is currently not possible to set an Android text output to an exact width. The problem seems not to be the measurement methods, which are exact enough. The problem seems to be, that you can not set an exact text width by setTextSize().
Try the following to reproduce this:
float width = 100; // define a width which should be achieved
m_textPaint.setTextSize( 100 ); // set a text size surely big enough
Rect r = new Rect();
String s = new String("This is a test text");
m_textPaint.getTextBounds( s, 0, s.length(), r ); // measure the text with a random size
float fac = width / r.width(); // compute the factor, which will scale the text to our target width
Log.i( "MyTestOutput", "current text width:" + r.width() );
Log.i( "MyTestOutput", "wanted text width:" + width );
Log.i( "MyTestOutput", "factor:" + fac );
Log.i( "MyTestOutput", "expected result:" + (float) r.width() * fac );
m_textPaint.setTextSize( m_textPaint.getTextSize() * fac );
m_textPaint.getTextBounds( s, 0, s.length(), r ); // now final measurement: whats the real width?
Log.i( "MyTestOutput", "real result:" + r.width() );
I get an output of:
05-26 12:18:18.420: I/MyTestOutput(23607): current text width:1125
05-26 12:18:18.425: I/MyTestOutput(23607): wanted text width:100.0
05-26 12:18:18.425: I/MyTestOutput(23607): factor:0.08888889
05-26 12:18:18.425: I/MyTestOutput(23607): expected result:100.0
05-26 12:18:18.430: I/MyTestOutput(23607): real result:94
So in my opinion, the only way to achieve very exactly autoscaled text, is to
a) autoscale it by measuring and setting text size
b) output it char by char by a self computed increment.
Unfortunately, this works only for monospaced fonts. For other fonts, it will be more complicated, but also possible.
I did that just some days ago: See my blog.
You would use a StaticLayout:
// bmp1 is a source bitmap (in that case 44x44px big).
// density is the screen density, you will need to retrieve it yourself as well.
canvas.drawBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.overlay_44x44), new Matrix(), null);
// Initialize using a simple Paint object.
final TextPaint tp = new TextPaint(WHITE);
// Save the canvas state, it might be re-used later.
canvas.save();
// Create a padding to the left & top.
canvas.translate(4*density, 4*density);
// Clip the bitmap now.
canvas.clipRect(new Rect(0, 0, bmp1.getWidth(),(int) (bmp1.getHeight() - (6*density))));
// Basic StaticLayout with mostly default values
StaticLayout sl = new StaticLayout(message, tp, bmp1.getWidth(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
sl.draw(canvas);
// Restore canvas.
canvas.restore();

Categories

Resources