From my understanding, the ImageButton within LibGDX is a frame containing an image. Is it possible to set the background of the frame?
For example, I would like to use a Button background, and apply an Icon on top of that image.
Current Code
Skin with the background:
"com.badlogic.gdx.scenes.scene2d.ui.ImageButton$ImageButtonStyle": {
"default": {
"imageDown": "button-down",
"imageUp": "button-up"
}
}
Creating the ImageButton:
// Getting imageButtonStyle with "default" style, as it just has the background.
ImageButton.ImageButtonStyle imageButtonStyle = skin.get( "default", ImageButton.ImageButtonStyle.class );
ImageButton button = new ImageButton( imageButtonStyle );
// Set the image on the button to something.
Background image.
Background image with icon overlayed.
Thank you for your help.
The trick is using the 2 different Styles available. ImageButtonStyle to set the icon attributes, but since ImageButtonStyle extends ButtonStyle, the background attributes are set in ButtonStyle. Something like:
ImageButtonStyle style = new ImageButtonStyle();
style.up = background;
style.down = background;
style.checked = background;
style.imageUp = icon_up;
style.imageDown = icon_down;
style.imageChecked = icon_checked;
style.unpressedOffsetY = -20; // to "not" center the icon
style.unpressedOffsetX = -30; // to "not" center the icon
ImageButton heartButton = new ImageButton(style);
ImageButton envelopeButton = new ImageButton(envelopeStyle);
Something like that (for me, backround, icon_* are Drawable). But that's the basics and worked like a charm for me.
You could implement your own button class that extends ImageButton.
Then, if you overload the constructor, you can pass either Drawables or Textures for imageUp, imageDown and background to it:
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.scenes.scene2d.utils.SpriteDrawable;
public class myButton extends ImageButton
{
public myButton(Texture texture_up, Texture texture_down, Texture background)
{
super(new SpriteDrawable(new Sprite(texture_up)),
new SpriteDrawable(new Sprite(texture_down)));
this.setBackground(new SpriteDrawable(new Sprite(background));
}
}
Now, you can use your own Button class and instantiate it as follows:
Texture textureUp = new Texture(Gdx.files.internal("data/image_up.png"));
Texture textureDown = new Texture(Gdx.files.internal("data/image_down.png"));
Texture background = new Texture(Gdx.files.internal("data/background.png"));
MyButton myButton = new MyButton(textureUp, textureDown, background);
Maybe play around with it a little and you'll find out what else you can do with it. Just make sure that you have the images in the correct resolution. Background doesn't have to be an image.
A button by definition will have three states:
imageDown - When the mouse is clicked on the button
imageUp - When the mouse is released on the button
imageChecked - When the mouse is hovering on the button
In the scene2d api there isn't a way yet of manually setting the imageButton's background image but if you do want something like that its best to have an array of images and an index as to what image you want, and render using a sprite batch e.g:
Texture[] images;
int currentImage;
Related
I want to add two images to a button that has two lines of text, like so:
Note: the above image is photoshopped to look like what I want.
I am not sure how to achieve this. I can programmatically add one image to the button on the left like so:
Button button = new Button(this);
int imgResource = R.drawable.titans;
String matchUpStr = "Titans\nPatriots";
button.setGravity(Gravity.START);
button.setCompoundDrawablesWithIntrinsicBounds(imgResource, 0, 0, 0);
button.setText(matchUpStr);
but it just ends up like this:
Does anyone know how I could add the two images to the left of the text in the button? I am thinking maybe you can go to the next line on the imgResource variable and add another drawable. Not sure.
note that I will be dynamically creating buttons and it is not always going to be this matchup. So I can't just add an image with these two teams on it.
I'm starting to think that maybe instead of adding two images along with two lines of text, why not just make two images that contain the text inside the images and add the images top and bottom.
Although I agree with Nero that a custom button is probably a cleaner solution.
I was able to come up with a solution using a LayerListDrawable.
I resized both bitmap drawables to be the size of the button's text and then inset them by that size in the layer list drawable. See the code example.
package cj.com.testinglayerlist;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ScaleDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
// We will need to know the size of the text in order to correctly resize our bitmaps. I
// am getting it from the button here, but it can also be retrieved from resources.
int buttonTextSize = (int) button.getTextSize();
// Get the drawables as BitmapDrawables.
BitmapDrawable patriotsDrawable = (BitmapDrawable) getDrawable(R.drawable.patriots);
BitmapDrawable titansDrawable = (BitmapDrawable) getDrawable(R.drawable.titans);
// Resize the drawables to be the size of our button text.
patriotsDrawable = resizeDrawable(buttonTextSize, patriotsDrawable);
titansDrawable = resizeDrawable(buttonTextSize, titansDrawable);
// Create an array of drawables that contains our two logos.
Drawable[] drawables = new Drawable[2];
drawables[0] = patriotsDrawable;
drawables[1] = titansDrawable;
// Create a layer drawable.
LayerDrawable layerDrawable = new LayerDrawable(drawables);
// Notice here how the top of this layer is the button text size. This is opposite of the
// other drawable whom's bottom will be the button text size.
layerDrawable.setLayerInset(0,0, buttonTextSize,0,0);
layerDrawable.setLayerInset(1,0, 0,0,buttonTextSize);
// Now I set my buttons drawable.
button.setCompoundDrawablesWithIntrinsicBounds(layerDrawable, null,null,null);
}
/**
* Takes in the desired size of the bitmap drawable and resizes the bitmap as such.
*
* #param sizeOfDrawable The desired size in pixels.
* #param bitmapDrawable The bitmap drawable to resize.
*
* #return Bitmap drawable that has been resized.
*/
private BitmapDrawable resizeDrawable(int sizeOfDrawable, BitmapDrawable bitmapDrawable) {
Bitmap bitmap = bitmapDrawable.getBitmap();
BitmapDrawable d = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(bitmap,
sizeOfDrawable,
sizeOfDrawable, true));
return d;
}
}
I was able to conjure the following using my above code snippet.
It is not 100%, but maybe a path that you can take to accomplish your goal.
Layer List Drawable Screenshot
The only solution that comes to mind is place a Button inside a relative layout (or any layout you prefer) match parent then organize the view over it (Just make sure that on api 21+ the button has elevation set it to 0 or something).
This link inspired me: Android Custom button with imageview and textview inside?
I wrote the following code:
int color = Color.argb8888(255, rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
stage.getBatch().setColor(color);
With this code I want to change the color of a sprite randomly. Unfortunately, the color of all sprites on the stage get changed but I want to declare which sprite I want to tint. How can I improve my code?
To change tint of a single sprite, use below code
Sprite mysprite;
Texture mytexture;
mytexture = new Texture("texture.png");
mysprite = new Sprite(mytexture);
mysprite.setColor(Color.WHITE.cpy().lerp(Color.BLACK, .5f));
this code changes white tint of sprite to black tint
Color color = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat(), 1f);
'rand.nextFloat()' is gives you a float between 0 and 1, so you can create random color like this.
I prefer to use in this way :
final Image image=new Image(new Texture("badlogic.jpg"));
final Color colors[]=new Color[]{Color.YELLOW,Color.RED,Color.CYAN};
image.addAction(Actions.forever(Actions.sequence(Actions.delay(.2f),Actions.run(new Runnable() {
#Override
public void run() {
image.setColor(colors[MathUtils.random(colors.length-1)]);
}
}))));
stage.addActor(image);
In my experience, you could just set your color attribute that used as vertex attributes in shader back to normal color masking like this:
stage.getBatch().setColor(your_random_color);
stage.getBatch().draw(your_texture);
stage.getBatch().setColor(1.0f,1.0f,1.0f,1.0f);
Please be noticed that I tried this way for drawing TextureRegion, but I think this would not be different with drawing Sprite.
I want to layout a set off press-able piano keys in my libGDX app, something like the following:
When a key is pressed, I need to change it from the up to down position (this is easy using an image button).
What I'm struggling with is how to layout all of the buttons such that they for something that resembles a piano keyboard. The white piano key images are rectangular, but a black piano key needs to be placed on top of it. Below shows what one of my white piano key images looks like, there is dead space in the top corner where I intend to put a black note.
Using a Table won't work, as tables layout everything side-by-side, which would leave me with gaps. I've seen you can use a Stack, but that just lays every child directly on top of the last, so that doesn't seem to help either.
If it helps, my code is something like this:
Skin skin;
Stage stage;
SpriteBatch batch;
private TextButton button1;
private TextButton button2;
#Override
public void create () {
batch = new SpriteBatch();
stage = new Stage();
Gdx.input.setInputProcessor(stage);
createSkin();
Table table = new Table();
table.setFillParent(true);
stage.addActor(table);
// Create a button with the "default" TextButtonStyle. A 3rd parameter can be used to specify a name other than "default".
button1 = new TextButton("First note", skin);
button1.getLabel().setFontScale(5);
button2 = new TextButton("Second note", skin);
button2.getLabel().setFontScale(5);
table.add(button1).width(500).height(500);
table.add(button2).width(500).height(200);
}
#Override
public void render () {
super.render();
Gdx.gl.glClearColor(0.2f, 0.2f, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
#Override
public void resize (int width, int height) {
stage.getViewport().update(width, height, true);
}
#Override
public void dispose () {
stage.dispose();
skin.dispose();
}
private void createSkin() {
skin = new Skin();
Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.WHITE);
pixmap.fill();
skin.add("white", new Texture(pixmap));
skin.add("default", new BitmapFont());
TextButton.TextButtonStyle textButtonStyle = new TextButton.TextButtonStyle();
textButtonStyle.up = skin.newDrawable("white", Color.DARK_GRAY);
textButtonStyle.down = skin.newDrawable("white", Color.BLUE);
textButtonStyle.checked = skin.newDrawable("white", Color.DARK_GRAY);
textButtonStyle.over = skin.newDrawable("white", Color.DARK_GRAY);
textButtonStyle.font = skin.getFont("default");
skin.add("default", textButtonStyle);
}
Which renders this:
There's no way to overlap things as far as I can tell using table, but I can't find another way of doing it. Is doing this possible using scene2d?
The simpliest solution seems to be to create second Stage with same viewport (stages needs to behave in the same way in case of resizing) and place it over your current one. You need to create same table but fullfill it only with black notes (your current stage would contains only white ones of course).
Notice that you will need to use InputMultiplexer to get event from both stages - you can read here about how to use it.
Add all the Buttons that represent keys to the stage directly without putting them in a Table. Set their positions manually. Should be pretty easy to do manually since piano keys have a clear pattern. Add the black keys after adding the white keys. Then you don't have to worry about the white keys being non-rectangular because the black keys will get prioritized for receiving input.
My class Triangle extends from View and its method onDraw consists of:
... onDraw(){
...
drawCircle(anyx, anyy, anyradius, anypaint);
}
And the Activity in which I want to create the Button and the Circle, both created dynamically.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.newexample);
RelativeLayout viewGroup = (RelativeLayout)findViewById(R.id.visiont);
Button b1 = new Button(this);
viewGroup.addView(b1);
b1.setX(140); // Position of the button
b1.setY(140);
ViewGroup.LayoutParams params = b1.getLayoutParams();
params.height=80; // Size of the button
params.width=80;
b1.setLayoutParams(params); // Deleting this does not change the behaviour of what I am getting (only changes the height and width)
Circle c = new Circle(this);
c.setColor(Color.DKGRAY);
c.setCircle(150,130,80);
viewGroup.addView(c);
}
An example of what is the desired result, the rectangle is the button.
What I am getting now is the opposite, the button is over the circle.
I tried to switch Button to ImageButton since I wanted to have a custom image in the button (Actually I wanted a clickable component)
It appears that, with only switching Button to ImageButton, works as desired, so, for me it's a suitable solution.
I have a number of shapes, and I have one view. I need to dynamically (i.e. programmatically) select a shape to set as the background of my view based on user inputs. So my question: who do I programmatically turn a shape into a ShapeDrawable or such?
I already look at How to change shape color dynamically?. Those posts assume the shape is already attached to a view. But me all my shapes are free agents.
It seems that is does not work with ShapeDrawable, but take a look at my GradientDrawable example:
you can create gradient drawable dynamically.. use below class
import android.graphics.drawable.GradientDrawable;
public class SomeDrawable extends GradientDrawable {
public SomeDrawable(int pStartColor, int pCenterColor, int pEndColor, int pStrokeWidth, int pStrokeColor, float cornerRadius) {
super(Orientation.BOTTOM_TOP,new int[]{pStartColor,pCenterColor,pEndColor});
setStroke(pStrokeWidth,pStrokeColor);
setShape(GradientDrawable.RECTANGLE);
setCornerRadius(cornerRadius);
}
}
and use this class as below
SomeDrawable drawable = new SomeDrawable(Color.parseColor("Start Color Code"),Color.parseColor("Center Color Code"),Color.parseColor("End Color Code"),1,Color.BLACK,00);
yourLayout.setBackgroundDrawable(drawable);
I just found that I can do
myview.setBackgroundResource(R.drawable.my_shape)