I am using a thread to draw some animations, so I need to repaint the label for every frame. To do this without flickering I am updating the label with my backbuffer graphics object (using the lbl.update(bufferedGraphics); method), but when I do this, the label gets repainted in the top left of the Graphics object, and not where setLocation has specified.
How do I specify the location of the label within the graphics, instead of within the panel that owns the label?
Here is an SSCCE:
import javax.swing.*;
import java.awt.*;
public class LabelSSCCE extends JApplet implements Runnable {
JPanel pnl;
JLabel lbl;
Image buffer;
Graphics bufferedGraphics;
Thread t;
public void init (){
pnl = new JPanel (null);
lbl = new JLabel ();
lbl.setText ("Text");
lbl.setOpaque(true);
add(pnl);
pnl.add (lbl);
lbl.setLocation(100, 100);
lbl.setBounds (100, 100, 200, 20);
buffer = createImage (500, 500);
bufferedGraphics = buffer.getGraphics ();
t = new Thread (this, "Label");
t.start ();
}// init method
public void paint (Graphics g){
if (g != null)
g.drawImage (buffer, 0, 0, this);
}//paint
public void update (Graphics g){
paint (g);
}//update
public void render (){
bufferedGraphics.setColor (Color.WHITE);
bufferedGraphics.fillRect (0, 0, 500, 500);
lbl.update (bufferedGraphics);
update(getGraphics());
}//render
public void run (){
while (true){
try{
render ();
t.sleep (20);
} catch (InterruptedException e){
e.printStackTrace ();
}//catch
}//while
}//run
}//LabelSSCCE
First, convert the JLabel to a BufferedImage:
public BufferedImage componentToImage(Component component)
{
BufferedImage img = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
Graphics g = img.getGraphics();
g.setColor(component.getForeground());
g.setFont(component.getFont());
component.paintAll(g);
Rectangle region = new Rectangle(0, 0, img.getWidth(), img.getHeight());
return img.getSubimage(region.x, region.y, region.width, region.height);
}
Then, change the render method to something like this:
public void render() {
bufferedGraphics.setColor(Color.WHITE);
bufferedGraphics.fillRect(0, 0, 500, 500);
BufferedImage bi = componentToImage(lbl);
bufferedGraphics.drawImage(bi, lbl.getX(), lbl.getY(), null);
update(getGraphics());
}
Related
Here is a code snippet.
itemList = new List(skin, "ariel.32.white");
String[] tmpInv = new String[b+1];
tmpInv[0] = "<Empty>";
a++;
for (Entry<String, String> entry : inventoryItems.entrySet()) {
tmpInv[a] = entry.getKey();
a++;
//String key = entry.getKey();
//Object value = entry.getValue();
// ...
}
Arrays.sort(tmpInv);
itemList.setItems(tmpInv);
inventoryPane = new ScrollPane(itemList, skin);
Here is what I get, and it works fine. I'd like to add descriptive icons in front of each item but I can't seem to get it to work. I also need some way to get the name of what is selected after they are added. Currently I use
itemlist.getSelectedIndex();
You can't use a list widget to add images or tables or anything other than text as far as I am aware. You can, however, make a table and change the background Drawable to a new Texture which can simulate the effect of a list when some events occur (like a mouseMove event). You can then add that table into a ScrollPane which can handle your scroll events.
This will take you a bit of coding, but here is a working example I whipped up for you:
package <some package>;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Event;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.FocusListener;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
public class ScrollScreenTest implements Screen{
Game game;
ScrollPane scrollpane;
Skin skin;
Stage stage;
Table container, table1, table2, table3;
Texture texture1, texture2, texture3;
public ScrollScreenTest(Game game){
this.game = game;
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1); //sets up the clear color (background color) of the screen.
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); //instructs openGL to actually clear the screen to the newly set clear color.
stage.draw();
stage.act(delta);
}
#Override
public void resize(int width, int height) {
}
#Override
public void show() {
// setup skin
skin = new Skin(Gdx.files.internal("data/uiskin.json"));
texture1 = new Texture(Gdx.files.internal("iron_axe.png"));
texture2 = new Texture(Gdx.files.internal("iron_dagger.png"));
texture3 = new Texture(Gdx.files.internal("iron_sword.png"));
// table that holds the scroll pane
container = new Table();
container.setWidth(320f);
container.setHeight(300f);
// tables that hold the data you want to display
table1 = new Table(skin);
table1.add(new Image(texture1)).expandY().fillY();
table1.add(new Label("", skin)).width(10f).expandY().fillY();// a spacer
table1.add(new Label("Look at this axe I stole!", skin)).expandY().fillY();
table2 = new Table(skin);
table2.add(new Image(texture2)).expandY().fillY();
table2.add(new Label("", skin)).width(10f).expandY().fillY();// a spacer
table2.add(new Label("So dagger, much pointy.", skin)).expandY().fillY();
table3 = new Table(skin);
table3.add(new Image(texture3)).expandY().fillY();
table3.add(new Label("", skin)).width(10f).expandY().fillY();// a spacer
table3.add(new Label("Valyrian steel..", skin)).expandY().fillY();
//inner table that is used as a makeshift list.
Table innerContainer = new Table();
innerContainer.add(table1).expand().fill();
innerContainer.row();
innerContainer.add(table2).expand().fill();
innerContainer.row();
innerContainer.add(table3).expand().fill();
// create the scrollpane
scrollpane = new ScrollPane(innerContainer);
//add the scroll pane to the container
container.add(scrollpane).fill().expand();
// setup stage
stage = new Stage();
// add container to the stage
stage.addActor(container);
// setup input processor (gets clicks and stuff)
Gdx.input.setInputProcessor(stage);
// setup a listener for the tables with out data
table1.addListener(new FocusListener(){
#Override
public boolean handle(Event event){
if (event.toString().equals("mouseMoved")){
table1.background(new TextureRegionDrawable(new TextureRegion(new Texture("gray.png"))));
return false;
}
else if(event.toString().equals("exit")){
//table1.setBackground(null);
//table1.background("");
table1.setBackground(null, false);
return false;
}
return true;
}
});
table2.addListener(new FocusListener(){
#Override
public boolean handle(Event event){
if (event.toString().equals("mouseMoved")){
table2.background(new TextureRegionDrawable(new TextureRegion(new Texture("gray.png"))));
return false;
}
else if(event.toString().equals("exit")){
//table1.setBackground(null);
//table1.background("");
table2.setBackground(null, false);
return false;
}
return true;
}
});
table3.addListener(new FocusListener(){
#Override
public boolean handle(Event event){
if (event.toString().equals("mouseMoved")){
table3.background(new TextureRegionDrawable(new TextureRegion(new Texture("gray.png"))));
return false;
}
else if(event.toString().equals("exit")){
//table1.setBackground(null);
//table1.background("");
table3.setBackground(null, false);
return false;
}
return true;
}
});
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
}
}
If you want to stay with the List you could inherit the list an override the drawItem method, like this:
public class ItemList extends List<Entry<String, String>> {
private int alignment;
public ItemList(Skin skin, String styleName) {
super(skin, styleName);
}
#Override
protected GlyphLayout drawItem (Batch batch, BitmapFont font, int index, Entry<String, String> item, float x, float y, float width) {
String string = toString(item);
TextureAtlas.AtlasRegion image = // get the image, you want to draw
batch.draw(image, x, y-image.regionHeight)
return font.draw(batch, string, x +image.regionWidth +5, y, 0, string.length(), width -image.regionWidth -5, this.alignment, false, "...");
}
#Override
public void setAlignment (int alignment) {
this.alignment = alignment;
}
}
With this method you do not have to reimplement the selection mechanism of the list.
So I have done this exact thing and although it might not be the best way its the only thing I could figure out. First use the Image actor for to set your image to add into the table. Then add both image and title to the table before calling row(). to get them to align correctly I was setting them as left align and then using negitive padding on the title to override the tables spacing although I'm sure theres another way. Also using get selected you can then cast it to the correct class and call getName() which you can set in any actor class so you could do it there or just call get text or something.
I've downloaded the latest version of libgdx 1.0.1 (I used to have 0.9.8) and the core code as generated by the version 3 setup/project creation wizard is completely different - it does not dispose of the batch or the texture, there is no camera or positioning set up and when I run it the loaded images are not displayed in the correct place, probably due to the camera not being set up.
If I put back the code from the wizard version 2, everything works.
What's going wrong? here's the code generated by old and new:
Here's the new code:
#Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(img, 0, 0);
batch.end();
}
Here is the old code
private OrthographicCamera camera;
private SpriteBatch batch;
private Texture texture;
private Sprite sprite;
#Override
public void create() {
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
camera = new OrthographicCamera(1, h/w);
batch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("badlogic.jpg"));
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
TextureRegion region = new TextureRegion(texture, 0, 0, 512, 275);
sprite = new Sprite(region);
sprite.setSize(0.9f, 0.9f * sprite.getHeight() / sprite.getWidth());
sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2);
sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2);
}
#Override
public void dispose() {
batch.dispose();
texture.dispose();
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
sprite.draw(batch);
batch.end();
}
As the title i said, i want to cut a region of image (say left bottom corner), but after i cut this area, i want this area to be white as a "Paint" tool to cut image.
I could crop the image by using the code below, but in this code i just put the image where i cut it into a new image file.
public Crop() {
super();
ImageIcon icon = new ImageIcon("C:/TEMP/chest.jpg");
image = icon.getImage();
image = createImage(new FilteredImageSource(image.getSource(),
new CropImageFilter(0, 515, 250, 250)));
ImageIcon icon2= new ImageIcon(image);
BufferedImage bufferedImage = new BufferedImage(icon2.getIconWidth(),
icon2.getIconHeight(), BufferedImage.TYPE_INT_RGB);
Graphics graphics = bufferedImage.getGraphics();
graphics.drawImage(image, 0, 0, null);
File newFile = new File("C:/TEMP/chest-new.jpg");
try {
ImageIO.write(bufferedImage, "jpg", newFile);
} catch (IOException e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
super.paint(g);
if (insets == null) {
insets = getInsets();
}
g.drawImage(image, insets.left, insets.top, this);
}
public static void main(String args[]) {
JFrame f = new Crop();
f.setSize(250, 250);
f.show();
}
}
What do i want is i want the area that is cut become to "white", and original image.
As a image link below:
An image after cut
I'm working on a Game where I need to draw a board and pawns actively when I move them. I've searched for a few hours now, but I can't find a solution.
They're located in the same folder as the classes.
Thanks in advance for any help :)
code:
import java.awt.*;
import javax.swing.*;
public class MyPanel extends JPanel{
private static final long serialVersionUID = 1L;
public void paint(Graphics g) {
try {
Image board = new ImageIcon("images/ChackerBoard.jpeg").getImage();
Image black = new ImageIcon("images\\BlackPawn.jpeg").getImage();
Image white = new ImageIcon("images\\WhitePawn.jpeg").getImage();
this.setSize(320, 320);
g.drawImage(board, 0, 0, this);
for (int i = 0; i < Game.BlackList.size(); i++) {
g.drawImage(black, (Game.BlackList.get(i).GetX() * 40) - 36, (Game.BlackList.get(i).GetY() * 40) - 36, this);
}
for (int i = 0; i < Game.WhiteList.size(); i++) {
g.drawImage(white, (Game.WhiteList.get(i).GetX() * 40) - 36, (Game.WhiteList.get(i).GetY() * 40) - 36, this);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
images:
http://i.stack.imgur.com/bhuc2.jpg
http://imageshack.us/a/img7/8673/checkerboardo.jpg
#FailX in paintComponent, imageobserver object is null. Set the image observer as "this" and it works.
#Override
protected void paintComponent( Graphics g ){
super.paintComponent( g );
g.drawImage(img, 0, 0, this); //Image is also drawn
g.drawLine( 10, 10, 100, 50 ); //Line is drawn
g.draw3DRect(20,20,50,30,true); // Rectangle is drawn
}
Is there a way to show "Loading" screen with animation in blackberry?
Options:
PME animation content
multithreading + set of images + timer/counter
standard rim api
some other way
Any of this?
Thanks!
Fermin, Anthony +1. Thanks to all, you gave me the part of answer.
My final solution:
1.Create or generate (free Ajax loading gif generator) animation and add it to project.
2.Create ResponseCallback interface (see Coderholic - Blackberry WebBitmapField) to receive thread execution result:
public interface ResponseCallback {
public void callback(String data);
}
3.Create a class to handle your background thread job. In my case it was http request:
public class HttpConnector
{
static public void HttpGetStream(final String fileToGet,
final ResponseCallback msgs) {
Thread t = new Thread(new Runnable() {
public void run() {
HttpConnection hc = null;
DataInputStream din = null;
try {
hc = (HttpConnection) Connector.open("http://" + fileToGet);
hc.setRequestMethod(HttpsConnection.GET);
din = hc.openDataInputStream();
ByteVector bv = new ByteVector();
int i = din.read();
while (-1 != i) {
bv.addElement((byte) i);
i = din.read();
}
final String response = new String(bv.toArray(), "UTF-8");
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
msgs.callback(response);
}
});
}
catch (final Exception e) {
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
msgs.callback("Exception (" + e.getClass() + "): "
+ e.getMessage());
}
});
}
finally {
try {
din.close();
din = null;
hc.close();
hc = null;
}
catch (Exception e) {
}
}
}
});
t.start();
}
}
4.Create WaitScreen (a hybrid of FullScreen and AnimatedGIFField with ResponseCallback interface):
public class WaitScreen extends FullScreen implements ResponseCallback
{
StartScreen startScreen;
private GIFEncodedImage _image;
private int _currentFrame;
private int _width, _height, _xPos, _yPos;
private AnimatorThread _animatorThread;
public WaitScreen(StartScreen startScreen) {
super(new VerticalFieldManager(), Field.NON_FOCUSABLE);
setBackground(
BackgroundFactory.createSolidTransparentBackground(
Color.WHITE, 100));
this.startScreen = startScreen;
EncodedImage encImg =
GIFEncodedImage.getEncodedImageResource("ajax-loader.gif");
GIFEncodedImage img = (GIFEncodedImage) encImg;
// Store the image and it's dimensions.
_image = img;
_width = img.getWidth();
_height = img.getHeight();
_xPos = (Display.getWidth() - _width) >> 1;
_yPos = (Display.getHeight() - _height) >> 1;
// Start the animation thread.
_animatorThread = new AnimatorThread(this);
_animatorThread.start();
UiApplication.getUiApplication().pushScreen(this);
}
protected void paint(Graphics graphics) {
super.paint(graphics);
// Draw the animation frame.
graphics
.drawImage(_xPos, _yPos, _image
.getFrameWidth(_currentFrame), _image
.getFrameHeight(_currentFrame), _image,
_currentFrame, 0, 0);
}
protected void onUndisplay() {
_animatorThread.stop();
}
private class AnimatorThread extends Thread {
private WaitScreen _theField;
private boolean _keepGoing = true;
private int _totalFrames, _loopCount, _totalLoops;
public AnimatorThread(WaitScreen _theScreen) {
_theField = _theScreen;
_totalFrames = _image.getFrameCount();
_totalLoops = _image.getIterations();
}
public synchronized void stop() {
_keepGoing = false;
}
public void run() {
while (_keepGoing) {
// Invalidate the field so that it is redrawn.
UiApplication.getUiApplication().invokeAndWait(
new Runnable() {
public void run() {
_theField.invalidate();
}
});
try {
// Sleep for the current frame delay before
// the next frame is drawn.
sleep(_image.getFrameDelay(_currentFrame) * 10);
} catch (InterruptedException iex) {
} // Couldn't sleep.
// Increment the frame.
++_currentFrame;
if (_currentFrame == _totalFrames) {
// Reset back to frame 0
// if we have reached the end.
_currentFrame = 0;
++_loopCount;
// Check if the animation should continue.
if (_loopCount == _totalLoops) {
_keepGoing = false;
}
}
}
}
}
public void callback(String data) {
startScreen.updateScreen(data);
UiApplication.getUiApplication().popScreen(this);
}
}
5.In the end, create Start screen to call HttpConnector.HttpGetStream and to show WaitScreen:
public class StartScreen extends MainScreen
{
public RichTextField text;
WaitScreen msgs;
public StartScreen() {
text = new RichTextField();
this.add(text);
}
protected void makeMenu(Menu menu, int instance) {
menu.add(runWait);
super.makeMenu(menu, instance);
}
MenuItem runWait = new MenuItem("wait", 1, 1) {
public void run() {
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
getFile();
}
});
}
};
public void getFile() {
msgs = new WaitScreen(this);
HttpConnector.HttpGetStream(
"stackoverflow.com/faq", msgs);
}
//you should implement this method to use callback data on the screen.
public void updateScreen(String data)
{
text.setText(data);
}
}
UPDATE: another solution naviina.eu: A Web2.0/Ajax-style loading popup in a native BlackBerry application
The basic pattern for this kind of thing is:
Have a thread running a loop that updates a variable (such as the frame index of the animated image) and then calls invalidate on a Field which draws the image (and then sleeps for a period of time). The invalidate will queue a repaint of the field.
In the field's paint method, read the variable and draw the appropriate frame of the image.
Pseudo code (not totally complete, but to give you the idea):
public class AnimatedImageField extends Field implements Runnable {
private int currentFrame;
private Bitmap[] animationFrames;
public void run() {
while(true) {
currentFrame = (currentFrame + 1) % animationFrames.length;
invalidate();
Thread.sleep(100);
}
}
protected void paint(Graphics g) {
g.drawBitmap(0, 0, imageWidth, imageHeight, animationFrames[currentFrame], 0, 0);
}
}
Note also here I used an array of Bitmaps, but EncodedImage lets you treat an animated gif as one object, and includes methods to get specific frames.
EDIT: For completeness: Add this to a PopupScreen (as in Fermin's answer) or create your own dialog by overriding Screen directly. The separate thread is necessary because the RIM API is not thread-safe: you need to do everything UI related on the event thread (or while holding the event lock, see BlackBerry UI Threading - The Very Basics
This is simple code for loading screen ....
HorizontalFieldManager popHF = new HorizontalFieldManager();
popHF.add(new CustomLabelField("Pls wait..."));
final PopupScreen waitScreen = new PopupScreen(popHF);
new Thread()
{
public void run()
{
synchronized (UiApplication.getEventLock())
{
UiApplication.getUiApplication().pushScreen(waitScreen);
}
//Here Some Network Call
synchronized (UiApplication.getEventLock())
{
UiApplication.getUiApplication().popScreen(waitScreen);
}
}
}.start();
If it's just an animation could you show an animated gif on a popup and close it when loading operation is complete?
Easiest way is probably to use the standard GaugeField, setting style GaugeField.PERCENT. This will give you a progress bar. Add this to a PopupScreen and it will sit on top of your content. Something like..
private GaugeField _gaugeField;
private PopupScreen _popup;
public ProgressBar() {
DialogFieldManager manager = new DialogFieldManager();
_popup = new PopupScreen(manager);
_gaugeField = new GaugeField(null, 0, 100, 0, GaugeField.PERCENT);
manager.addCustomField(_gaugeField);
}
Then have an update method which will use _gaugeField.setValue(newValue); to update the progress bar.
I normally have this called from whichever thread is doing the work (loading in your case, everytime an operation is complete the progress bar is updated.
I would suggest to take a look at this simple implementation. I liked this but never used it. May be helpful to you.
link text
ActivityIndicator is a good option if you are working with at least BB OS 6.0.
http://www.brighthub.com/mobile/blackberry-platform/articles/94258.aspx
http://docs.blackberry.com/en/developers/deliverables/17966/Screen_APIs_1245069_11.jsp