I'm looking for help developing (or a library) which can allow me to merge together multiple images into one imageview.
My application is grouping together interactions between users, instead of displaying them singularly, and therefore I would like to merge all of their avatars, so that one adapter cell visualizes a "group".
A fantastic example of this is done on facebook.com's chat as such:
My question is, how can I provide this functionality in Android / Java? Presumably, it could a number of images between 1 and 4. Please let me know of any advice you can give :)
You can use MultiImageView.
Add dependency in app.gradle:
compile 'com.github.stfalcon:multiimageview:0.1'
Add MultiImageView to layout xml file
<com.stfalcon.multiimageview.MultiImageView
android:id="#+id/iv"
android:layout_width="100dp"
android:layout_height="100dp"/>
In java class find view by id:
final MultiImageView multiImageView = (MultiImageView) findViewById(R.id.iv);
For adding image to MultiImageView use method addImage(Bitmap bitmap). For exapple:
multiImageView.addImage(BitmapFactory.decodeResource(getResources(), R.drawable.avatar1));
For setting shape of MultiImageView use method setShape(MultiImageView.Shape shape).
multiImageView.setShape(MultiImageView.Shape.RECTANGLE);//Rectangle with round corners
multiImageView.setShape(MultiImageView.Shape.CIRCLE);//Circle
multiImageView.setShape(MultiImageView.Shape.NONE);//Without shape
Check github link for more information:
I think it is what you needed
You should overlap bitmaps onto another bitmap. A first approach are this:
Merge bitmaps
You can play with matrix and orders and sizes for a complex UI.
i know it's an old question but maybe it will help somebody else.
private Bitmap mergeThemAll(List<Bitmap> orderImagesList) {
Bitmap result = null;
if (orderImagesList != null && orderImagesList.size() > 0) {
// if two images > increase the width only
if (orderImagesList.size() == 2)
result = Bitmap.createBitmap(orderImagesList.get(0).getWidth() * 2, orderImagesList.get(0).getHeight(), Bitmap.Config.ARGB_8888);
// increase the width and height
else if (orderImagesList.size() > 2)
result = Bitmap.createBitmap(orderImagesList.get(0).getWidth() * 2, orderImagesList.get(0).getHeight() * 2, Bitmap.Config.ARGB_8888);
else // don't increase anything
result = Bitmap.createBitmap(orderImagesList.get(0).getWidth(), orderImagesList.get(0).getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
for (int i = 0; i < orderImagesList.size(); i++) {
canvas.drawBitmap(orderImagesList.get(i), orderImagesList.get(i).getWidth() * (i % 2), orderImagesList.get(i).getHeight() * (i / 2), paint);
}
} else {
Log.e("MergeError", "Couldn't merge bitmaps");
}
return result;
}
Addition to the answer by Anton Bevza
Their library has some disadvantages that are fixed in the fork
Add dependency in app.gradle:
implementation 'com.github.martipello:MultiImageView:1.0.8.2'
Add MultiImageView to layout xml file
<com.sealstudios.multiimageview.MultiImageView
android:id="#+id/iv"
android:layout_width="100dp"
android:layout_height="100dp"
app:shape="circle" />
For setting shape of MultiImageView use attributes in layout xml file
app:shape="circle" //Circle
app:shape="rectangle" //Rectangle with round corners
app:shape="none" //Without shape
Also you can change shape by using method:
multiImageView.setShape(MultiImageView.Shape.RECTANGLE);//Rectangle with round corners
multiImageView.setShape(MultiImageView.Shape.CIRCLE);//Circle
multiImageView.setShape(MultiImageView.Shape.NONE);//Without shape
In java class find view by id:
final MultiImageView multiImageView = findViewById(R.id.iv);
For adding image to MultiImageView use method addImage(Bitmap bitmap). For example:
multiImageView.addImage(BitmapFactory.decodeResource(getResources(), R.drawable.avatar));
//or
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), ImageUri);
multiImageView.addImage(bitmap);
Related
I've spent several frustrating hours trying to implement (what I thought would be) a simple FontActor class.
The idea is to just draw text at a specific position using a provided BitmapFont. That much, I've managed to accomplish. However, I'm struggling to compute my actor's width/height based on the rendered text.
(Using a FitViewport for testing)
open class FontActor<T : BitmapFont>(val font: T, var text: CharSequence = "") : GameActor() {
val layout = Pools.obtain(GlyphLayout::class.java)!!
companion object {
val identity4 = Matrix4().idt()
val distanceFieldShader: ShaderProgram = DistanceFieldFont.createDistanceFieldShader()
}
override fun draw(batch: Batch?, parentAlpha: Float) {
if (batch == null) return
batch.end()
// grab ui camera and backup current projection
val uiCamera = Game.context.inject<OrthographicCamera>()
val prevTransform = batch.transformMatrix
val prevProjection = batch.projectionMatrix
batch.transformMatrix = identity4
batch.projectionMatrix = uiCamera.combined
if (font is DistanceFieldFont) batch.shader = distanceFieldShader
// the actor has pos = x,y in local coords, but we need UI coords
// start by getting group -> stage coords (world)
val coords = Vector3(localToStageCoordinates(Vector2(0f, 0f)), 0f)
// world coordinate destination -> screen coords
stage.viewport.project(coords)
// screen coords -> font camera world coords
uiCamera.unproject(coords,
stage.viewport.screenX.toFloat(),
stage.viewport.screenY.toFloat(),
stage.viewport.screenWidth.toFloat(),
stage.viewport.screenHeight.toFloat())
// adjust position by cap height so that bottom left of text aligns with x, y
coords.y = uiCamera.viewportHeight - coords.y + font.capHeight
/// TODO: use BitmapFontCache to prevent this call on every frame and allow for offline bounds calculation
batch.begin()
layout.setText(font, text)
font.draw(batch, layout, coords.x, coords.y)
batch.end()
// viewport screen coordinates -> world coordinates
setSize((layout.width / stage.viewport.screenWidth) * stage.width,
(layout.height / stage.viewport.screenHeight) * stage.height)
// restore camera
if (font is DistanceFieldFont) batch.shader = null
batch.projectionMatrix = prevProjection
batch.transformMatrix = prevTransform
batch.begin()
}
}
And in my parent Screen class implementation, I rescale my fonts on every window resize so that they don't become "smooshed" or stretched:
override fun resize(width: Int, height: Int) {
stage.viewport.update(width, height)
context.inject<OrthographicCamera>().setToOrtho(false, width.toFloat(), height.toFloat())
// rescale fonts
scaleX = width.toFloat() / Config.screenWidth
scaleY = height.toFloat() / Config.screenHeight
val scale = minOf(scaleX, scaleY)
gdxArrayOf<BitmapFont>().apply {
Game.assets.getAll(BitmapFont::class.java, this)
forEach { it.data.setScale(scale) }
}
gdxArrayOf<DistanceFieldFont>().apply {
Game.assets.getAll(DistanceFieldFont::class.java, this)
forEach { it.data.setScale(scale) }
}
}
This works and looks great until you resize your window.
After a resize, the fonts look fine and automatically adjust with the relative size of the window, but the FontActor has the wrong size, because my call to setSize is wrong.
Initial window:
After making window horizontally larger:
For example, if I then scale my window horizontally (which has no effect on the world size, because I'm using a FitViewport), the font looks correct, just as intended. However, the layout.width value coming back from the draw() changes, even though the text size hasn't changed on-screen. After investigation, I realized this is due to my use of setScale, but simply dividing the width by the x-scaling factor doesn't correct the error. And again, if I remove my setScale calls, the numbers make sense, but the font is now squished!
Another strategy I tried was converting the width/height into screen coordinates, then using the relevant project/unproject methods to get the width and height in world coordinates. This suffers from the same issue shown in the images.
How can I fix my math?
Or, is there a smarter/easier way to implement all of this? (No, I don't want Label, I just want a text actor.)
One problem was my scaling code.
The fix was to change the camera update as follows:
context.inject<OrthographicCamera>().setToOrtho(false, stage.viewport.screenWidth.toFloat(), stage.viewport.screenHeight.toFloat())
Which causes my text camera to match the world viewport camera. I was using the entire screen for my calculations, hence the stretching.
My scaleX/Y calculations were wrong for the same reason. After correcting both of those miscalculations, I have a nicely scaling FontActor with correct bounds in world coordinates.
In my game (created using LibGDX) I have a gameworld filled with a lot of circles changing their size continiously. Because there are so many circles I want to maximize their rendering-performance: I've heard of the ShapeRenderer, but it seems like that it is not the best in case of performance. The PixMap is also no solution because my circles should be vector-based.
Is their another faster solution too? And is the ShapeRenderer really that slow?
PS: I'm already using chunks to reduce the render time.
For the ShapeRenderer (circle in particular), if we look at the method, radius does not effect performance, segments is where the work is. And this is most likely what is hurting you, as you scale up in size, you increase the segments for detail.
I am not sure about there being opengl native vector graphics either... I think ultimately to reach the graphics card, you need to eventually become vertices and polygons (if you are filling). So actually, I think the Pixmap solution is the one you might be looking for. You compute the segments and the polygons to draw once (at the highest resolution you need).
With the Pixmap you should be able to do this in a way which is as performant as any other rendering of a Texture which you change sizes using the scaling variables (which should be as performant as not changing the scale). As you can see from the circle draw method that the ShapeRenderer uses, the circle is still really just describing a polygon (you are just computing its geometry every time).
If you want to give the Pixmap option a go, here is some code to get you bootstrapped.
Here is a kotlin function for building a PolygonSprite. You will have to do the maths for plotting the vertices of your circle, but you can probably use the circle draw method to get an idea for that. If you compute your geometry for a radius of 1, then you can just use your x/y scale to set the radius at whatever size you want.
fun polygonSprite(points: Array<Vector2>): PolygonSprite {
val pix = Pixmap(1, 1, Pixmap.Format.RGBA8888)
pix.setColor(0xFFFFFFFF.toInt())
pix.fill()
val textureSolid = Texture(pix)
val vertices = FloatArray(points.size * 2)
val triangleIndices = triangulator.computeTriangles(vertices)
for (i in 0..points.size - 1) {
val point = points.get(i)
val offset = i * 2
vertices[offset] = point.x
vertices[offset + 1] = point.y
}
val polyReg = PolygonRegion(TextureRegion(textureSolid),
vertices, triangleIndices.toArray())
val poly = PolygonSprite(polyReg)
return poly
}
And here is some rendering code. It takes into account relative positioning of the shape from the parent Body and some other stuff:
fun render(camera: OrthographicCamera) {
val parentRotation = (me().physicsRoot.rotationR() * MathUtils.radDeg)
val parentTransform = me().physicsRoot.transform
val myPosition = vec2(offsetX, offsetY)
parentTransform.mul(myPosition)
poly.color = color.get()
poly.setOrigin(0f, 0f)
poly.setPosition(myPosition.x, myPosition.y)
poly.rotation = parentRotation + rotationD
poly.setScale(scaleX, scaleY)
poly.draw(JJ.B.renderWorld.polyBatch)
recycle(myPosition)
}
Also, don't make a new one of these for every one, try and reuse them.
PS: Another option is to make a circle shader :D
This is driving me crazy. I would like to be able to resize an xml vector drawable icon programmatically in order to use it in an ImageView.
This is what I've done so far which is not working
Drawable drawable = ResourcesCompat.getDrawable(getResources(),R.drawable.ic_marker,null);
drawable.setBounds(0,0,512,512);
imageVenue.setImageDrawable(drawable);
The vector icon ic_marker is not resized. It just keeps the hardcoded width and height values every time.
Any ideas?
You can change the width and height of your imageview programmatically. Since vector drawables will preserve the original quality of the image, this will make the desired output happen.
ImageView iv = (ImageView) findViewById(R.id.imgview);
int width = 60;
int height = 60;
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width,height);
iv.setLayoutParams(params);
I'm currently facing the same problem.
I'm trying something like this, cause ViewParent has actually height set explicitly, so I use match_parent and set margins. It doesn't work all the time though, cause I simply use this view in a viewholder for RecyclerView... Also I've noticed that sometimes I see scaled up version with artifacts, sometimes full size, sometimes there are margins, and bigger margins... But it still might work for you, if you use it in a simpler scenario.
mImageViewFront.setImageDrawable(vectorDrawable);
final int paddingLR = mImageViewFront.getWidth() / 4;
final int paddingTB = mImageViewFront.getHeight() / 4;
LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.setMargins(paddingLR, paddingTB, paddingLR, paddingTB);
mImageViewFront.setLayoutParams(params);
I'm trying to draw on an ImageViewTouch, a library which enables pinch zooming. I'm able to draw over the image using Canvas, but when I zoom the image, the drawing disappears.
For this, I'm trying to convert the view to a bitmap and set theImageBitmap for this same view. Here's the code:
mImage.setDrawPath(true);
mImage.setImageBitmap(loadBitmapFromView(mImage));
public static Bitmap loadBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap( v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(0, 0, v.getWidth(), v.getHeight());
v.draw(c);
return b;
}
When I do this, I get the following log error:
07-11 21:13:41.567: E/AndroidRuntime(20056): java.lang.IllegalArgumentException: width and height must be > 0
07-11 21:13:41.567: E/AndroidRuntime(20056): at android.graphics.Bitmap.createBitmap(Bitmap.java:638)
07-11 21:13:41.567: E/AndroidRuntime(20056): at android.graphics.Bitmap.createBitmap(Bitmap.java:620)
If I remove the loadBitmapFromView call the drawing normally appears over the image. When I try to do any interaction with the image (like zooming in or out), the drawing disappears, remaing only the background image, which is a picture.
--- EDIT ---
Here's some more code placed after the loadBitmapFromView call. The case is: I have a radio group listenner and when I check some radio button, I have to load the image and draw some possibles drawings over it.. then I'm trying to convert everything (the image and the drawings) into only one bitmap.
Here's the ohter part of the code:
bitmap = BitmapUtils.decodeSampledBitmapFromResource(root + DefinesAndroid.CAMINHO_SHOPPINGS_SDCARD + nomeImagemAtual, size.x, size.y);
mImage.setImageBitmap(bitmap);
After that, I draw everything I have to draw and try to convert the view to bitmap using the loadImageBitmap method I have shown.
the decodeSampledBitmapFromResource method I got from this link on android developers http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
I finally figured out a solution for my problem.
I'm using the post method of the View, which will be executed only after the view measuring and layouting, this way getWidth() and getHeight() returns the actual width and height.
Here's the code sample:
mImage.post(new Runnable() {
#Override
public void run() {
mImage.setImageBitmap(loadBitmapFromView(mImage));
}
});
Hope it also helps someone else :)
If everything else is correct you are executing your code too early.
The view's layout has not yet been measured by Android. Try executing in OnResume just to see if this is your problem.
Cheers!
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadBitmapFromView(mImage);
}
}, 1000);
java.lang.IllegalArgumentException: width and height must be > 0 is because in your Bitmap.createBitmap() call v.getLayoutParams().width or v.getLayoutParams().height is 0. Probably the LayoutParams are wrongly set in mImage.
Update: setLayoutParams() for the mImage before using createBitmap(). Something like this:
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(30, 30);
mImage.setLayoutParams(layoutParams);
In my case, I forgot to set orientation to vertical for parent LinearLayout and the default value of horizontal has been set, so I set it and my problem gone...
android:orientation="vertical"
I'm not sure if it relates to this specific question , but I had the same error and I solved it with getting a image source , in the layout xml file , from drawable instead of from mipmap (of course I had to put the image there before).
I can't figure out how to manage checkbox images size. Of course, it is possible to create different size of image in my Texture atlas and take appropriate one, but I don't want to do that.
Here is my code:
AtlasRegion checkboxOn = AssetsHelper.textures.findRegion("checked");
AtlasRegion checkboxOff = AssetsHelper.textures.findRegion("unchecked");
CheckBoxStyle checkBoxStyle = new CheckBoxStyle();
checkBoxStyle.font = AssetsHelper.font66yellow;
checkBoxStyle.checkboxOff = checkboxOff;
checkBoxStyle.checkboxOn = checkboxOn;
CheckBox cbSound = new CheckBox(" Sound", checkBoxStyle);
cbSound object doesn't have such methods to rezise image of checkbox, but there is method getImage(), but seems it doesn't work too.
This is not working:
cbSound.getImage().width = 120;
cbSound.getImage().height = 120;
FYI: for example, when I wanted to draw image I did like that:
batch.draw(textureRegion, 0, 0, widthIwant, heightIwant);
But in CheckBox class there is overrided only this (without setting width and height):
public void draw (SpriteBatch batch, float parentAlpha) {
image.setRegion(isChecked ? style.checkboxOn : style.checkboxOff);
super.draw(batch, parentAlpha);
}
Question: how can I change width and height of checkbox image?
Thanks in advance.
The libgdx widgets are using drawables for drawing images. A drawable gets automatically scaled to fit the cell it is in. So in order to change the image size, change the cell size:
cbSound.getCells().get(0).size(widht, height);
For better results, you should use a nine patch for the drawable.
You need to set the image scaling type. Also method getImageCell is more correct than method getCells().get(0). Default is none.
CheckBox soundCB = new CheckBox("Sound", uiskin);
soundCB.getImage().setScaling(Scaling.fill);
soundCB.getImageCell().size(GameConfig.WIDTH/6);
soundCB.left().pad(PAD);
soundCB.getLabelCell().pad(PAD);
//...
content.add(soundCB).width(GameConfig.WIDTH/1.5f).row(); //add to table