I'm trying to make touchable image which can focus when I scroll it by touchable button on device this is my code for touchable image:
class TouchBitmapField extends BitmapField {
Bitmap bitmap;
boolean second;
int width;
public TouchBitmapField(Bitmap startBitmap, long style, int width, boolean second) {
super(startBitmap, style);
this.second = second;
bitmap = startBitmap;
this.width = width;
}
protected void drawFocus(Graphics g, boolean on){
g.setDrawingStyle(Graphics.DRAWSTYLE_FOCUS, true );
if(on){
drawHighlightRegion(g, Field.HIGHLIGHT_FOCUS, on, width-(bitmap.getWidth()/2), 0, bitmap.getWidth(), bitmap.getHeight());
}
paint(g);
}
protected boolean touchEvent(TouchEvent message) {
if (TouchEvent.CLICK == message.getEvent()) {
FieldChangeListener listener = getChangeListener();
if (null != listener)
listener.fieldChanged(this, 1);
}
return super.touchEvent(message);
}
}
But the problem is that only transparent part of image can focus and I'm resising this Bitmap before create TouchBitmapField and as you maybe know resising bitmap remove transparent part and replace it with black. I tried this class http://www.patchou.com/2010/10/resizing-transparent-bitmaps-with-the-blackberry-jde/ to resize image but I have 25 images on my screen and it's inefficient. Any idea how to make border on image? Any efficient way to draw border in drawFocus(Graphics g, boolean on) method?
The reason why you can only focus the transparent parts of an image is because (as far as I know) drawFocus is fired before paint, causing the bitmap to be drawn last.
I think this solution is what you're looking for. If you want something other than a solid colour as the border, you can play around with the BorderFactory.
class TouchBitmapField extends BitmapField
{
Bitmap bitmap;
Border defaultBorder;
Border focusBorder;
public TouchBitmapField(Bitmap startBitmap, long style)
{
super(startBitmap, style | FOCUSABLE);
bitmap = startBitmap;
XYEdges thickness = new XYEdges(5, 5, 5, 5);
XYEdges colours = new XYEdges(0xff0000, 0xff0000, 0xff0000, 0xff0000);
XYEdges alpha = new XYEdges(0, 0, 0, 0);
// Transparent border used by default, so that adding the focus border does not resize the view
defaultBorder = BorderFactory.createSimpleBorder(thickness, colours, alpha, Border.STYLE_SOLID);
focusBorder = BorderFactory.createSimpleBorder(thickness, colours, Border.STYLE_SOLID);
setBorder(defaultBorder);
}
protected void drawFocus(Graphics graphics, boolean on)
{
// Override default blue highlight
}
protected void onFocus(int direction)
{
super.onFocus(direction);
setBorder(focusBorder);
}
protected void onUnfocus()
{
super.onUnfocus();
setBorder(defaultBorder);
}
}
If you want your border to be overlapping your image, you can override your paint method and draw as you want.
protected void paint(Graphics graphics)
{
super.paint(graphics);
if(isFocus())
{
int tempCol = graphics.getColor();
int tempAlpha = graphics.getGlobalAlpha();
graphics.setColor(0xff0000);
graphics.setGlobalAlpha(128);
for(int i = 0; i < 5; i++)
{
graphics.drawRect(i, i, getWidth() - i * 2, getHeight() - i * 2);
}
// Reset back to original state
graphics.setColor(tempCol);
graphics.setGlobalAlpha(tempAlpha);
}
}
Related
Okay so I'm starting to make a main menu for a small flash game and to do this I want to use the mouse to click on buttons etc. I have a button class in which I create two rectangles: a rectangle for the button and a rectangle for the mouse based on its X and Y, 1 pixel by 1 pixel. I use Rectangle.Intersects to check if they are touching before seeing if left mouse button is down. Problem is, the thing the mouse position is relative to changes every time so no matter where the mouse button is on the screen, it's never the same co-ordinates as in a different build in that exact same position. I seriously just need ideas now as I'm running out. If I didn't explain it very well or you need further details to help please ask - I WOULD BE SO GRATEFUL.
Will post back if I find an answer
Update - Okay here's the button class
class OnScreenButton
{
public Texture2D texture;
Vector2 position;
Rectangle rectangle;
Color colour = new Color(255, 255, 255, 255);
public Vector2 size;
public OnScreenButton(Texture2D newtexture, GraphicsDevice graphics)
{
texture = newtexture;
// ScreenW = 500, ScreenH = 600
// Img W = 80, Img H = 20
size = new Vector2(graphics.Viewport.Width / 10, graphics.Viewport.Height / 30);
size = new Vector2(texture.Width, texture.Height);
}
bool down;
public bool isClicked;
public void Update(MouseState mouseState)
{
rectangle = new Rectangle((int)position.X, (int)position.Y, (int)size.X, (int)size.Y);
Rectangle mouseRectangle = new Rectangle(mouseState.X, mouseState.Y, 1, 1);
if (mouseRectangle.Intersects(rectangle))
{
if (colour.A == 255)
{
down = false;
}
if (colour.A == 0)
{
down = true;
}
if (down)
{
colour.A += 3;
}
else
{
colour.A -= 3;
}
if (mouseState.LeftButton == ButtonState.Pressed)
{
isClicked = true;
}
}
else if (colour.A < 255)
{
colour.A += 3;
isClicked = false;
colour.A = (255);
}
}
public void SetPosition(Vector2 newPos)
{
position = newPos;
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, rectangle, colour);
}
}
}
(Sorry for weird formatting, brand new to stack overflow and the posting is still a little confusing)
Here is some other code I think is relevent...
Game.1 initializing stuff
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
protected override void Initialize()
{
// TODO: Add your initialization logic here
Mouse.WindowHandle = Window.Handle;
base.Initialize();
}
public Main()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
KeyboardState keyboardState;
MouseState mouseState;
Main menu update routine...
private void UpdateMainMenu(GameTime gameTime)
{
// Button options
if (buttonPlay.isClicked == true)
{
CreateNewGame();
currentGameState = GameState.playing;
}
buttonPlay.Update(mouseState);
if (buttonExit.isClicked == true)
{
this.Exit();
}
buttonExit.Update(mouseState);
// Press enter to play
if (keyboardState.IsKeyDown(Keys.Enter))
{
CreateNewGame();
currentGameState = GameState.playing;
}
}
Here's thee draw routine for main menu...
public void DrawMainMenu()
{
spriteBatch.Draw(mainMenuBackground, new Vector2(0, 0), Color.White);
buttonPlay.Draw(spriteBatch);
buttonExit.Draw(spriteBatch);
spriteBatch.DrawString(playerAmmoFont, String.Format("{0}", mouseState), new Vector2(0, 0), Color.White);
}
okay that's all I can think of
UPDATE - Okay so I know a few things that aren't the problem...
The whole of my button class is fine, I made a new project and inserted all the relevant code into it and it worked absolutely perfectly so I'm starting to think its something to do with the code positioning and the graphics device stuff although I still don't have a clue how to fix it.
the window appears at the same spot every time
there is no pattern to the change in coordinates at all
this is really annoying
UPDATE - OKAY. I spent a long time writing down the coordinates that I got each time I ran the code and stuck to cursor in the top right corner of the screen. Here is what I got.
(-203, -225)
(-253, -275)
(-53, -75)
(-103, -125)
(-153, -175)
(-203, -225)
(-253, -275)
(-53, -75)
(-103, -125)
(-153, -175)
(-203, -225)
(-253, -275)
(-53, -75)
(-103, -125)
(-153, -175)
(-203, -225)
(-253, -275)
(-53, -75)
(-103, -125)
(-153, -175)
(-203, -225)
(-78, -100)
(-128, -150)
(-178, -200)
(-228, -250)
(-28, -50)
(-53, -75)
(-103, -125)
(-153, -175) < AND FROM HERE THE PATTERN LOOPS ROUND.
I just don't get how the same code can execute a different bug on different executions like this.
Also, mouse.Wheel doesn't go up or down whereas it works on the project that I made to test the relevant code where the mouse position was relevant to the top left pixel of the game window.
UPDATE - EVEN MORE DAMN COMPLICATIONS - So I just rand it a few times again and the offset values are offset... the increase is the same but I got values like (-178, -200) then (-228, -250). I have also discovered that the mouse is not relative to the game window what so ever, if I jam the mouse in the top right corner of the screen and check the coordinates, then move the game window and do the same again, the coordinates don't change. Please please please help me, or tell me if I'm being stupid, or something. Thanks.
The mouse coordinates are relative to the monitor. Here is my general button class to try and work for your situation.
public class Button
{
public event EventHandler<EventArgs> Clicked;
public Vector2 Position { get; set;}
public Texture2D Texture { get; set;}
public Color Tint { get; set; }
public float Scale { get; set; }
public float Rotation { get; set; }
public int Width
{
get
{
if (texture == null)
return 0;
else
return texture.Width;
}
}
public int Height
{
get
{
if (texture == null)
return 0;
else
return texture.Height;
}
}
private void OnClick()
{
if (Clicked != null)
Clicked(this, EventArgs.Empty);
}
public Button(Vector2 position, Texture2D texture)
: base(parent)
{
Position = position;
Texture = texture;
Tint = Color.White;
Scale = 1.0f;
Rotation = 0.0f;
}
public bool HandleClick(Vector2 vector)
{
if (vector.X >= Position.X)
{
if (vector.X <= Position.X + Width)
{
if (vector.Y >= Position.Y)
{
if (vector.Y <= Position.Y + Height)
{
OnClick();
return true;
}
}
}
}
return false;
}
public bool HandleEntered(Vector2 vector)
{
if (vector.X >= Position.X)
{
if (vector.X <= Position.X + Width)
{
if (vector.Y >= Position.Y)
{
if (vector.Y <= Position.Y + Height)
{
return true;
}
}
}
}
return false;
}
public override void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Texture, Position, null, Tint, Rotation, Vector2.Zero, Scale, SpriteEffects.None, 0.0f);
}
Declare a button:
Button btn = new Button(position where you want the button, texture for the button);
btn.Clicked += () => { /* Handle button clicked code here */ };
In your update method inside your main game:
public void Update (GameTime gameTime)
{
MouseState mouseState = Mouse.GetState();
if(mouseState.LeftButton == ButtonState.Pressed) // check if mouse is clicked
{
btn.HandleClicked(new Vector2(mouseState.X, mouseState.Y)); // If true then the button clicked event will fire
// Here you can also change the color of the button if the button is currently clicked
}
// Here you can change the color of the button if the mouse is hover over the control
// Example:
btn.Tint = btn.HandleEntered(new Vector2(mouseState.X, mouseState.Y)) ? Color.White * 0.75f : Color.White;
}
Note: You can also use a rectangle for the button to adjust its size instead of strictly using the textures dimensions. Hope this gives some insight.
So here's what was going on: I had a bullet class in my game for every bullet shot. In this class I check whether the bullets hits nothing, hits the asteroid, or destroys the asteroids. If the latter is true then I would increment playerScore by 5. PlayerScore was a Game1 attribute so I thought the easiest way to do this would be to create a new Game1 in bullet.cs to allow me to refer to the variable. Deleting the "Main mainGame = new Main():" in Bullet.cs fixed this issue and I think the issue was coming from a new graphics device being made every single time I fired a single bullet.
How do I draw a radial gradient button in BlackBerry? I found "Drawing Radial Gradients" on the BlackBerry support forums. All I am able to implement on my own is a linear gradient.
This is a little tricky. Drawing linear gradients on field backgrounds is easy. Drawing radial gradients on field backgrounds is harder. Doing it on a button is harder still.
First of all, the example you link to does indeed look really bad. The biggest problem with that code is that it uses Graphics.drawArc() to construct the gradient out of concentric circles (lines). This is not at all smooth.
The biggest improvement you need to make over that is to use Graphics.fillArc() instead, which will look much smoother (although there may be a performance impact to this ...).
Your question didn't say anything about how you wanted the button to look when focused, or whether the corners needed to be rounded. That's where some of the difficulty comes in.
If you just extend the RIM ButtonField class, you'll probably have trouble with the default drawing for focus, and edge effects. It's probably necessary to directly extend the base Field class in a new, written-from-scratch, button field. I wouldn't necessarily recommend that you do all this yourself, since buttons require focus handling, click handling, etc. You should probably start with something like the BaseButtonField from the BlackBerry AdvancedUI open source library.
I have prototyped this for you, using that class as a base. (so, you'll need to download and include that source file in your project if you use this).
I created a GradientButtonField subclass:
private class GradientButtonField extends BaseButtonField {
private int startR;
private int startG;
private int startB;
private int endR;
private int endG;
private int endB;
/** the maximum distance from the field's center, in pixels */
private double rMax = -1.0;
private int width;
private int height;
private String label;
private int fontColor;
/**
* Create a gradient button field
* #param startColor the integer Color code to use at the button center
* #param endColor the integer Color code to use at the button edges
* #param label the text to show on the button
* #param fontColor color for label text
*/
public GradientButtonField (int startColor, int endColor, String label, int fontColor) {
// record start and end color R/G/B components, to
// make intermediate math easier
startR = (startColor >> 16) & 0xFF;
startG = (startColor >> 8) & 0xFF;
startB = startColor & 0xFF;
endR = (endColor >> 16) & 0xFF;
endG = (endColor >> 8) & 0xFF;
endB = endColor & 0xFF;
this.label = label;
this.fontColor = fontColor;
}
public String getLabel() {
return label;
}
protected void layout(int w, int h) {
width = Math.min(Display.getWidth(), w);
height = Math.min(Display.getHeight(), h);
if (rMax < 0.0) {
rMax = Math.sqrt((width * width)/4.0 + (height * height)/4.0);
}
setExtent(width, height);
}
private int getColor(double scale, boolean highlighted) {
int r = (int)(scale * (endR - startR)) + startR;
int g = (int)(scale * (endG - startG)) + startG;
int b = (int)(scale * (endB - startB)) + startB;
if (highlighted) {
// just brighten the color up a bit
r = (int)Math.min(255, r * 1.5);
g = (int)Math.min(255, g * 1.5);
b = (int)Math.min(255, b * 1.5);
}
return (65536 * r + 256 * g + b);
}
protected void paint(Graphics graphics) {
int oldColor = graphics.getColor();
// we must loop from the outer edge, in, to draw
// concentric circles of decreasing radius, and
// changing color
for (int radius = (int)rMax; radius >= 0; radius--) {
double scale = ((double)radius) / rMax;
boolean focused = (getVisualState() == Field.VISUAL_STATE_FOCUS);
graphics.setColor(getColor(scale, focused));
int x = width / 2 - radius;
int y = height / 2 - radius;
graphics.fillArc(x, y, 2 * radius, 2 * radius, 0, 360);
}
String text = getLabel();
graphics.setColor(fontColor);
graphics.drawText(text,
(width - getFont().getAdvance(text)) / 2,
(height - getFont().getHeight()) / 2);
// reset graphics object
graphics.setColor(oldColor);
}
}
To use this, the Manager that contains the button will need to constrain the button's size in its sublayout() implementation. Or, you can edit my GradientButtonField class to hardcode a certain size (via getPreferredWidth(), layout(), etc.), or whatever you want.
final Field button1 = new GradientButtonField(Color.DARKGRAY, Color.BLUE,
"Click Me!", Color.WHITE);
final Field button2 = new GradientButtonField(Color.DARKGRAY, Color.BLUE,
"Click Me, Too!", Color.WHITE);
Manager mgr = new Manager(Manager.NO_VERTICAL_SCROLL) {
public int getPreferredHeight() {
return Display.getHeight();
}
public int getPreferredWidth() {
return Display.getWidth();
}
protected void sublayout(int maxWidth, int maxHeight) {
setExtent(getPreferredWidth(), getPreferredHeight());
layoutChild(button1, 160, 80);
setPositionChild(button1, 20, 50);
layoutChild(button2, 120, 60);
setPositionChild(button2, 20, 150);
}
};
button1.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
Dialog.alert("clicked!");
}
});
mgr.add(button1);
mgr.add(button2);
add(mgr);
I did not round the corners, as that's a bit of work. Depending on what kind of backgrounds you're putting these buttons on, it might be easiest to create a PNG mask image (in your favorite drawing program), which is mostly transparent, and then just has filled corners that mask off the corners of the gradient below it. Then, use Graphics.drawBitmap() in the paint() method above, after you've drawn the radial gradient.
For focus highlighting, I just put in some simple code to brighten the colors when the button is focused. Again, you didn't say what you wanted for that, so I just did something simple.
Here's the result of the code above. The bottom button is focused:
I want the background image to stretch all screen. I simply created a VerticalFieldManager and modified paint method.
verticalFieldManager = new VerticalFieldManager(VerticalFieldManager.USE_ALL_WIDTH
| VerticalFieldManager.NO_HORIZONTAL_SCROLLBAR) {
public void paint(Graphics graphics) {
graphics.drawBitmap(0, 0, this.getWidth(), this.getHeight(),
backgroundBitmap, 0, 0);
super.paint(graphics);
}
};
And when i add a ListField to the verticalFieldManager and scroll, background image is not repaint. How can i repaint background when scroll?
Create EncodedImage from your Bitmap, or even directly extract EncodedImage from resources - EncodedImage.getEncodedImageResource("res/bg.png").
Then use this to scale it:
public static EncodedImage resize(EncodedImage eImage, int toWidth,
int toHeight, boolean keepAspectRatio) {
int scaleX = Fixed32.div(
Fixed32.toFP(eImage.getWidth()),
Fixed32.toFP(toWidth)
);
int scaleY = Fixed32.div(
Fixed32.toFP(eImage.getHeight()),
Fixed32.toFP(toHeight)
);
if (keepAspectRatio) {
int scale = (scaleX > scaleY) ? scaleX : scaleY;
return eImage.scaleImage32(scale, scale);
} else {
return eImage.scaleImage32(scaleX, scaleY);
}
}
Then you may draw the EncodedImage on the Graphics object:
graphics.drawImage(
0, 0,
encodedImage.getScaledWidth(), ncodedImage.getScaledHeight(),
encodedImage, 0, 0, 0
);
Below code defines a horizontal field manager with two fields. How can I amend the code so that the background is just set on the two fields being added not on the whole manager. Note, im not attempting to add an individual background image to each of the fields, instead a shared background image that spans behind the two fields.
LabelField label = new LabelField("name");
TextField e = new TextField(Field.FOCUSABLE);
final Bitmap b = Constants.SETTINGS;
final Background bg = BackgroundFactory.createBitmapBackground(Constants.SETTINGS);
HorizontalFieldManager manager = new HorizontalFieldManager()
{
public void sublayout (int width, int height)
{
Field field;
int x = 0;
super.sublayout(b.getWidth(), height);
super.setExtent(b.getWidth(), height);
for (int i = 0; i < getFieldCount(); i++)
{
field = getField(i);
layoutChild(field, Display.getWidth()/2, height);
setPositionChild(field, x, 10);
x += Display.getWidth()/2;
}
}
};
manager.add (label);
manager.add (e);
add (manager);
Rather than putting them in a custom Manager, it may be easier to just override the Fields' layout() calls to be
protected void layout(int width, int height) {
super.layout(width, height);
setExtent(Display.getWidth()/2, this.getHeight());
}
and then you can just use a normal HorizontalFieldManager you can set a background on and a padding (hfm.setPadding(10, 10, 10, 10);). Adding a padding will reduce the available width for your Fields, so you should decrease their widths in the layout() calls.
You can offset each of their individual backgrounds with some fancy, expensive Bitmap footwork (math) to appear to "share" one image using setBackGround(), or you can override their draw methods to achieve the same effect with the ability to "move" across the bitmap according to their relative position...
That what you're after? :)
edit:
create a custom field to use your bitmap and feed it whatever content you would like, then override the paint to draw what you like where you like it...
protected void paint(Graphics g){
// conditionals, etc
g.drawBitmap(x, y, width, height, bitmap, left, top);
// color changes, etc
g.drawText(yourText);
// clean up
}
Need lil help regarding paint method in MainScreen. Using the code above, I was trying to render some lists. It works fine. But it hides all my status bar icons i added using setStatus() method. When I remove the paint method , my buttons in status bar shows up. Also I have tried using setRe g.pushRegion() , but no luck.
Here is sample code :
private void addStatusBar(){
manager = new HorizontalFieldManager();
manager.add(new BitmapField(ImageUtility.loadBitMap("ask.jpg")));
manager.add(new BitmapField(ImageUtility.loadBitMap("experts.jpg")));
manager.add(new BitmapField(ImageUtility.loadBitMap("search.jpg")));
manager.add(new BitmapField(ImageUtility.loadBitMap("my_profile.jpg")));
manager.add(new BitmapField(ImageUtility.loadBitMap("groups.jpg")));
manager.add(new BitmapField(ImageUtility.loadBitMap("analitics.jpg")));
setStatus(manager);
}
protected void paint(Graphics graphics) {
//graphics.pushRegion(new XYRect(0, 0, getPreferredWidth(), getPreferredHeight()));
Bitmap image = ImageUtility.loadBitMap("header2.jpg");
graphics.drawBitmap(0, 0, 500, image.getHeight(), image, 0, 0);
for(int i =0; i < 5; i++ ){
fieldList.drawListRow(fieldList, graphics, i, 50 + (i*50), 20);
}
}
Have you tried setting the "y" offset for the graphics.drawBitmap() call to the height of the status bar?
you need to call super.paint Otherwise the status is never painted.
protected void paint(Graphics graphics) {
Bitmap image = ImageUtility.loadBitMap("header2.jpg");
graphics.drawBitmap(0, 0, 500, image.getHeight(), image, 0, 0);
for(int i =0; i < 5; i++ ){
fieldList.drawListRow(fieldList, graphics, i, 50 + (i*50), 20);
}
super.paint(graphics); }