Draw Dynamic Shapes - java

I need some help to make an activity.
I have stored coordinates from shapes and name of shapes in a database. These are grouped into rooms.
In the activity I need the top of the screen 8 TextViews (with colors and textst) in two rows. If I click on a textView, accordingly the shapes for the room need to be drawn on the screen under the textViews.
If I click on a shape, I need to get back the information, on which shape I made a click.

I guess you could create a custom view:
public class CustomView extends View { ... }
and then you simply override appropriate methods like:
#Override
public boolean onTouchEvent(MotionEvent ev) { /* check for hitting shapes here */ }
#Override
protected void onDraw(Canvas canvas) { /* draw anything you want here */ }
Use the search engine of your choice to find out more. This looks like what you need

Related

Designing a simple event-driven GUI

I am creating a simple event-driven GUI for a video game I am making with LibGDX. It only needs to support buttons (rectangular) with a single act() function called when they are clicked. I would appreciate some advice on structuring because the solution I've thought of so far seems to be far from ideal.
My current implementation involves all buttons extending a Button class. Each button has a Rectangle with its bounds and an abstract act() method.
Each game screen (e.g. main menu, character select, pause menu, the in-game screen) has a HashMap of Buttons. When clicked, the game screen iterates through everything in the HashMap, and calls act() on any button that was clicked.
The problem I'm having is that Buttons have to have their act() overridden from their superclass in order to perform their action, and that the Buttons aren't a member of the Screen class which contains all the game code. I am subclassing Button for each button in the game. My main menu alone has a ButtonPlay, ButtonMapDesigner, ButtonMute, ButtonQuit, etc. This is going to get messy fast, but I can't think of any better way to do it while keeping a separate act() method for each button.
Since my mute button isn't a part of the main menu screen and can't access game logic, it's act() is nothing more than mainMenuScreen.mute();. So effectively, for every button in my game, I have to create a class class that does nothing more than <currentGameScreen>.doThisAction();, since the code to actually do stuff must be in the game screen class.
I considered having a big if/then to check the coordinates of each click and call the appropriate action if necessary. For example,
if (clickWithinTheseCoords)
beginGame();
else if(clickWithinTheseOtherCoords)
muteGame();
...
However, I need to be able to add/remove buttons on the fly. When a unit is clicked from the game screen, a button to move it needs to appear, and then disappear when the unit is actually moved. With a HashMap, I can just map.add("buttonMove", new ButtonMove()) and map.remove("buttonMove") in the code called when a unit is clicked or moved. With the if/else method, I won't need a separate class for every button, but I would need to keep track of whether each clickable area tested is visible and clickable by the user at this point in the game, which seems like an even bigger headache that what I have right now.
I would provide a runnable to all the buttons which u will run in the act method. To give u a simple example.
private final Map<String, Button> buttons = new HashMap<>();
public void initialiseSomeExampleButtons() {
buttons.put("changeScreenBytton", new Button(new Runnable() {
#Override
public void run() {
//Put a change screen action here.
}
}));
buttons.put("muteButton", new Button(new Runnable() {
#Override
public void run() {
//Do a mute Action here
}
}));
}
public class Button {
//Your other stuff like rectangle
private final Runnable runnable;
protected Button(Runnable runnable) {
this.runnable = runnable;
}
public void act() {
runnable.run();
}
}
You keep track of your buttons via the map and just need to pass a runnable action to every button in the constructor. I intentionally skipped some code so that you can try yourself. If you have any questions, let me know.
Sneh's response reminded me of a fairly major oversight - instead of having to create a separate class for every button, I could use anonymous inner classes whenever I created a button, specifying its coordinates and act() method every time. I explored lambda syntax as a possible shorter method to do this, but ran into limitations with it. I ended up with a flexible solution, but ended up reducing it a bit further to fit my needs. Both ways are presented below.
Each game screen in my game is subclassed from a MyScreen class, which extends LibGDX's Screen but adds universal features like updating the viewport on resize, having a HashMap of Buttons, etc. I added to the MyScreen class a buttonPressed() method, which takes in as its one parameter an enum. I have ButtonValues enum which contains all the possible buttons (such as MAINMENU_PLAY, MAINMENU_MAPDESIGNER, etc.). In each game screen, buttonPressed() is overriden and a switch is used to perform the correct action:
public void buttonPressed(ButtonValues b) {
switch(b) {
case MAINMENU_PLAY:
beginGame();
case MAINMENU_MAPDESIGNER:
switchToMapDesigner();
}
}
The other solution has the button store a lambda expression so that it can perform actions on its own, instead of requiring buttonPressed() to act as an intermediary that performs the correct action based on what button was pressed.
To add a button, it is created with its coordinates and type (enum), and added to the HashMap of buttons:
Button b = new Button(this,
new Rectangle(300 - t.getRegionWidth() / 2, 1.9f * 60, t.getRegionWidth(), t.getRegionHeight()),
tex, ButtonValues.MAINMENU_PLAY);
buttons.put("buttonPlay", b);
To remove it, just buttons.remove("buttonPlay"). and it'll disappear from the screen and be forgotten by the game.
The arguments are the game screen which owns it (so the button can call buttonPressed() on the game screen), a Rectangle with its coordinates, its texture (used to draw it), and its enum value.
And here's the Button class:
public class Button {
public Rectangle r;
public TextureRegion image;
private MyScreen screen;
private ButtonValues b;
public Button(MyScreen screen, Rectangle r, TextureRegion image, ButtonValues b) {
this.screen = screen;
this.r = r;
this.image = image;
this.b = b;
}
public void act() {
screen.buttonPressed(b);
}
public boolean isClicked(float x, float y) {
return x > r.x && y > r.y && x < r.x + r.width && y < r.y + r.height;
}
}
isClicked() just takes in an (x, y) and checks whether that point is contained within the button. On mouse click, I iterate through all the buttons and call act() if a button isClicked.
The second way I did it was similar, but with a lambda expression instead of the ButtonValues enum. The Button class is similar, but with these changes (it's a lot simpler than it sounds):
The field ButtonValues b is replaced with Runnable r, and this is removed from the constructor. Added is a setAction() method which takes in a Runnable and sets r to the Runnable passed to it. The act() method is just r.run(). Example:
public class Button {
[Rectangle, Texture, Screen]
Runnable r;
public Button(screen, rectangle, texture) {...}
public void setAction(Runnable r) { this.r = r; }
public void act() { r.run(); }
}
To create a button, I do the following:
Button b = new Button(this,
new Rectangle(300 - t.getRegionWidth() / 2, 1.9f * 60, t.getRegionWidth(), t.getRegionHeight()),
tex);
b.setAction(() -> b.screen.doSomething());
buttons.put("buttonPlay", b);
First, a button is created with its containing game screen class, its bounding box, and its texture. Then, in the second command, I set its action - in this case, b.screen.doSomething();. This can't be passed to the constructor, because b and b.screen don't exist at that point. setAction() takes a Runnable and sets it as that Button's Runnable that is called when act() is called. However, Runnables can be created with lambda syntax, so you don't need to create an anonymous Runnable class and can just pass in the function it performs.
This method allows much more flexibility, but with one caveat. The screen field in Button holds a MyScreen, the base screen class from which all of my game screens are extended. The Button's function can only use methods that are part of the MyScreen class (which is why I made buttonPressed() in MyScreen and then realized I could just scrap the lambda expressions completely). The obvious solution is to cast the screen field, but for me it wasn't worth the extra code when I could just use the buttonPressed() method.
If I had a beginGame() method in my MainMenuScreen class (which extends MyScreen), the lambda expression passed to the button would need to involve a cast to MainMenuScreen:
b.setAction(() -> ((MainMenuScreen) b.screen).beginGame());
Unfortunately, even wildcard syntax doesn't help here.
And finally, for completeness, the code in the game loop to operate the buttons:
public abstract class MyScreen implements Screen {
protected HashMap<String, Button> buttons; // initialize this in the constructor
// this is called in every game screen's game loop
protected void handleInput() {
if (Gdx.input.justTouched()) {
Vector2 touchCoords = new Vector2(Gdx.input.getX(), Gdx.input.getY());
g.viewport.unproject(touchCoords);
for (HashMap.Entry<String, Button> b : buttons.entrySet()) {
if (b.getValue().isClicked(touchCoords.x, touchCoords.y))
b.getValue().act();
}
}
}
}
And to draw them, located in a helper class:
public void drawButtons(HashMap<String, Button> buttons) {
for (HashMap.Entry<String, Button> b : buttons.entrySet()) {
sb.draw(b.getValue().image, b.getValue().r.x, b.getValue().r.y);
}
}

Java/libGDX - issues with Actions.fadeIn() and Actions.fadeOut()

it's my first time posting and I'm self taught so be please gentle!
I've been building a bomberman replica game in libGDX using Game and Screen classes:
public class Main extends Game {
...
#Override
public void create() {
levelScreen = new LevelScreen(playerCount, new int[playerCount]);
levelScreen.level.addAction(Actions.sequence(Actions.alpha(0), Actions.fadeIn(2f)));
this.setScreen(levelScreen);
}
However when the game launches there is no fade effect.
public class LevelScreen implements Screen {
...
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 0.1f, 0.5f, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
level.act();
level.draw();
batch.end();
}
I want this levelScreen to fade in from black but it just doesn't!
When the round is over I want to fadeOut of this levelScreen to black, then fadeIn to a trophyScreen from black:
(From Main Class)
#Override
public void render() {
super.render();
if (endRoundTimer <= 0) {
trophyScreen = new TrophyScreen(playerCount, levelScreen.getScore());
levelScreen.level.addAction(Actions.sequence(Actions.fadeOut(1), Actions.run(new Runnable() {
#Override
public void run() {
setScreen(trophyScreen);
}
})));
}
}
And I've tried using the show() method in the TrophyScreen:
public class TrophyScreen implements Screen {
...
#Override
public void show() {
stage.addAction(Actions.sequence(Actions.alpha(0), Actions.fadeIn(1)));
}
I've done loads of searching and tried various things but no joy. I'm sure I'm missing something somewhere in a draw() or render() method that is preventing the fade Action from taking place.
UPDATE1
#Override public void draw() {
super.draw();
if (roundOver) {
this.getBatch().begin(); String s = String.format("%s", message);
font_text.draw(this.getBatch(), s, (90 + (2 * 30)), (this.getHeight() / 2));
this.getBatch().end();
}
For fading to work on actors, they must properly apply their own color's alpha in the draw method. And for an entire hierarchy of objects to fade at once, they must all also apply the parentAlpha parameter from the draw method signature.
So your draw method in any custom Actor subclass should look like this:
public void draw (Batch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
//..do drawing
}
If you are using a Sprite in your Actor instead of a TextureRegion (which I don't recommend due to redundancies) you must apply the color to the Sprite instead of Batch.
Note that this method of fading the whole game is not a "clean" fade. Any actors that are overlapping other actors will show through each other when the parent alpha is less than 1 during the fade. An alternative that would provide a clean-looking fade would be to draw a copy of your background (or black) over your entire scene and fade that instead.
I assume that level is an object of class that extends Stage and you are creating a control inside the stage, which is weird. You are not appling color to your font_text which I assume it is a BitmapFont
Solution, the weird way
If you want to do it in this way you will need something like that:
#Override public void draw() {
super.draw();
if (roundOver) {
getBatch().begin();
String s = String.format("%s", message);
font_text.setColor(getRoot().getColor())
font_text.draw(this.getBatch(), s, (90 + (2 * 30)), (this.getHeight() / 2));
getBatch().end();
}
}
getRoot() gets Group from Stage, we do it, because every action applied to Stage is actually applied to this Group root element. We get color (which has alpha channel) and we copy the color to the bitmapFont.
This solution is weird, because you are actually creating an Label inside Stage. It is pointless, actors plays on stage, not inside.
Solution, the good way
You want to draw text, right? So just use Label which is an actor, who shows a text. Actors do jobs for you:
stage = new Stage();
Label.LabelStyle labelStyle = new Label.LabelStyle(bitmapFont, Color.WHITE);
Label label = new Label("Hi, I am a label!", labelStyle);
stage.addActor(label);
Then you can apply actions and they will work fine (and every actor can have own actions applied).
stage.addAction(Actions.sequence(Actions.alpha(0), Actions.fadeIn(5)));
label.addAction(Actions.moveBy(0, 300, 15));
There is a lot of different actors like TextButton, Image, ScrollPane. They are customizable, easy to manage and they can be integrated in groups and tables.
Output:
A better way would be to just start by drawing a black image over everything, so you don't have to mess with every scene object's alpha. Use layering to do that. This post may be helpful.
Then you can control it's alpha channel, change it's rendering to 0 right before unpausing the game action to get it's drawing cycles back. Reactivate it on stage ending for your fade out effect.
Thank you cray, it's way better like this.

Determining which shape is being hovered over - Java

So I have 3 rectangles drawn on my JPanel that are acting as buttons, and what I would like to do is when the mouse is hovered over one of them, the Jpanel will repaint, and change the color of only that rectangle to red.
The only way I can think to do it is to have 3 separate variables that determines if the mouse is over each component. Then there would be code in the repaint method that, if rect1hover is true, then draw using g.setColor to red.
#Override
public void mouseMoved(MouseEvent e) {
if(rect1.contains(e.getX(), e.getY())){
rect1hover = true;
}
}
But this seems really inefficient. Is there a better way to do this?
Sure. Put your drawn rectangles in a List. Then you can write this code.
#Override
public void mouseMoved(MouseEvent e) {
for (DrawnRectangle r : rectangles) {
if (r.contains(e.getX(), e.getY())){
r.setHoverBackground();
}
}
}
You'll have to create a DrawnRectangle class with a setHoverBackground method.
You can use real components, then just add a MouseListener to each component and you don't need to do any lookup or do custom painting.
See Playing With Shapes for more information.

Android horizontal text scroll - automatic and by gesture

I'm relatively new to Android programming, and I need a control that holds text and scrolls automatically. Now, I know about the "marquee" in the TextView control and it works fine for what it's intended, but that approach has two problems.
1) I need the text to scroll regardless of its length, i.e. if the text is only "Hello", and the control is set to match parents width, it needs to scroll.
2) The control needs to respond to user scroll - by flicking/dragging it left/right, the text should also scroll.
And naturally, when the text is "gone" to the left side, it should reappear on the right side and continue scrolling. For now, it should be a single line text.
Does anything like that exist, and if not, what would be the best approach guidelines to implementing it?
I ended up extending the default TextView, and pulling out Marquee class from TextView source. From there it's easy to modify the Marquee class so that it starts/stops when needed, and no longer requires the TextView to be selected (if that requirement is necessary).
To implement slide by gesture, the base class implements OnGestureListener and in onScroll(...) I update the offset in Marquee class, so when the View is drawn the next time it applies the new scroll offset.
And finally, to actually scroll by the amount needed, in the constructor I set the custom scroller, and in onDraw apply the scroll.
The important parts of code:
public class MarqueeTextView extends TextView implements OnGestureListener {
private GestureDetector gestureDetector;
private Marquee marquee;
private Scroller scroller;
// constructor
public MarqueeTextView(Context context, AttributeSet attrs) {
this.marquee = new Marquee(this);
this.scroller = new Scroller(context);
this.setScroller(scroller);
gestureDetector = new GestureDetector(getContext(), this);
// when enabled, longpress disables further movement tracking
gestureDetector.setIsLongpressEnabled(false);
}
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
marquee.addToScroll(distanceX);
return false;
}
// onDraw
protected void onDraw(Canvas canvas) {
scroller.setFinalX((int) marquee.mScroll);
super.onDraw(canvas);
}
// Marquee handler
private static final class Marquee extends Handler {
// mostly the same as original
// ...
float mScroll;
public void addToScroll(float amount) {
mScroll += amount;
// detect if needs to start over
}
}
}

Dynamically change size of GridView

I'm developing an application for android, and i have a big old GridView that's centered vertically in the screen, with all the cells visible.
What i want, is a function by which all cells are zoomed in by a factor of 3, letting the user scroll around in the gridview instead.
Sort of how Wordfeud does it if you're familiar with the game.
I've searched the webs and haven't found a satisfactory solution, i managed to find a way to alter the layout params in my adapter for the grid tiles, but it only stretches them vertically and not horizontally, also the whole app becomes slow and unresponsive until you've scrolled through the list and let the Grid reload all the views or whatever.
Any help at all is appreciated.
In the end, i used a custom view with a Canvas instead - much easier than trying to bend GridView into something it's not supposed to be.
I know it's been a while since you asked, but I had a similar problem that it took me a long time to solve, so I'm hoping someone else may stumble on this. I ended up extending the GridLayout class and hooking up an instance of
private class ScaleGestureListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener
in the constructor:
scaleDetector = new ScaleGestureDetector(context, new ScaleGestureListener());
since it's a view group, override the dispatch… instead of the on… methods:
#Override
public boolean dispatchTouchEvent(MotionEvent ev){
scaleDetector.onTouchEvent(ev);
super.dispatchTouchEvent(ev);
return true;
}
#Override
public void dispatchDraw(Canvas canvas) {
String disp = String.valueOf(mScaleFactor);
Log.v(TAG, "dispatch draw " + disp);
canvas.save();
// i have some other stuff in here specific to my app
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}

Categories