Hey I'd like to ask if there is any way that I can use the Kotlins forEachIndexed() function for lists in Java? I searched everywhere but I couldn't find anything. I want to iterate over a list of ImageButtons and to each list item I want to set an Image from another list of bitmap image. So in Kotlin it would be:
buttons.forEachIndexed{index, button ->
button.setOnclickListener{
button.setImageResource(images[index])
}
}
The way I am iterating over buttons is
for(ImageButton button: buttons)
but I don't have any way to iterate over the indices. Can I somehow turn the Kotlin code above to Java?
Here is my code so far:pastebin.com/Rh9vcNbz
If you have the Kotlin stdlib in the project but are working on a Java file, you can use Kotlin's forEachIndexed from Java. It's not particularly pretty but it's doable:
import kotlin.collections.CollectionsKt;
...
CollectionsKt.forEachIndexed(list, (index, element) -> {
doSomething();
return Unit.INSTANCE;
});
I work with Java and Kotlin for some years now and sadly I think that there is nothing in Java (atleast which is standard) that can deliver you something like Kotlin does with its forEachIndexed.
I mean if you look at the implementation of the "forEachIndexed"...
It is nothing more then this:
var index = 0
for (item in this) action(checkIndexOverflow(index++), item)
it basically creates an int variable for you, and increments it, while iterating over the List and invoking your Lambda.
If you still want something similar, which is still "hacky" - I wrote something for you, not that I want to say that it is perfect but does the job - and it is still generic.
New Classes:
public interface IndexedAction<T> {
void action(T item, int index);
}
public class ExtendedArrayList<T> extends ArrayList<T> {
public void forEachIndexed(IndexedAction<T> indexedAction) {
int i = 0;
for (T element : this) {
indexedAction.action(element, i);
i++;
}
}
}
Usage:
#Test // Robolectric Unit Test
public void myTest() {
ExtendedArrayList<Bitmap> bitmapArray = new ExtendedArrayList<>();
ExtendedArrayList<ImageButton> buttons = new ExtendedArrayList<>();
Application context = RuntimeEnvironment.application;
int width = 200;
int height = 100;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmapArray.add(bitmap);
bitmapArray.add(bitmap);
bitmapArray.add(bitmap);
bitmapArray.add(bitmap);
Collections.shuffle(bitmapArray);
ImageButton button1 = new ImageButton(context);
ImageButton button2 = new ImageButton(context);
ImageButton button3 = new ImageButton(context);
ImageButton button4 = new ImageButton(context);
buttons.add(button1);
buttons.add(button2);
buttons.add(button3);
buttons.add(button4);
buttons.forEachIndexed((button, index) ->
button.setImageBitmap(bitmapArray.get(index)));
}
Related
Situation
I have an application which contains a GridPane thats composed of multiple Panes. Inside of every Pane is an ImageView that allows me to display an Image inside that Pane. My application follows the MVC pattern and I would like my setPaneImage( ) function to dynamically change one Pane's ImageView image.
Problem
I don't know how to access the Pane's ImageView ? I know you can use the ImageView's setImage( ) to change its image. To access the ImageView contained inside the Pane, I suppose you have to use the .getChildren( ) method but I can't figure out how I can implement this...
Here's the simplified code :
public class ViewController extends Application implements EventHandler
{
final Image paneVide = new Image( "sample/img/paneVide.png" );
#Override
public void start(Stage primaryStage) throws Exception
{
// Initializing attributes
BorderPane borderPane = new BorderPane( );
gridPane = new GridPane( );
Pane p = new Pane( );
// Adding ImageView to the Pane
ImageView imv = new ImageView( );
imv.setPreserveRatio( true );
imv.setFitHeight( p.getHeight() );
imv.setFitWidth( p.getWidth() );
p.getChildren().add( imv );
// Setting handler
p.setOnMouseEntered( this );
// Adding to GridPane
gridPane.add( p, i, j );
// [...] Rest of the display code
}
#Override
public void handle(Event event)
{
Pane p;
if( event.getSource() instanceof Pane ) {
p = (Pane) event.getSource();
// MOUSE HOVER
if( event.getEventType().toString() == "MOUSE_ENTERED" )
setPaneImage( p, "VIDE" );
}
}
// Here's the function where I don't really know what to do...
// How do I access the Pane's ImageView so that I can dynamically
// change its image ??
public void setPaneImage( Pane p, String img )
{
switch( img )
{
case "VIDE":
p.getChildren().add( new ImageView(paneVide) ); // Setting pane's image
break;
}
}
}
Thank you very much !
The simplest way would be to store a reference to the ImageView in a field:
private ImageView imv;
public void start(Stage primaryStage) throws Exception {
...
this.imv = new ImageView();
...
}
public void setPaneImage(Pane p, String img) {
...
imv.setImage(paneVide);
...
}
Assuming the ImageView is the only child of the Pane, you could also retrieve the ImageView from the child list:
public void setPaneImage(Pane p, String img) {
ImageView imv = (ImageView) p.getChildren().get(0);
...
}
Additional notes:
Some parts of your code could be considered bad practice:
if(event.getEventType().toString() == "MOUSE_ENTERED")
You're comparing Strings using == instead of equals
Instead of comparing the type converted to String you should compare to the event type: event.getEventType() == MouseEvent.MOUSE_ENTERED
You don't need that check. It's the only event the handler listens to. Handling different types of events should be done using a different handler. You could create such a handler using anonymus classes or lambda expressions easily. You could avoid the source check using the same strategy.
public void setPaneImage(Pane p, String img) {
switch( img )
{
case "VIDE":
Using magic strings is a bad idea. It's hard to find the error if you've got a typo in the string. You could simply just pass the Image itself (paneVide). This way you don't need the switch at all and any typos would become obvious on compilation.
imv.setFitHeight(p.getHeight());
imv.setFitWidth(p.getWidth());
You do this before the first layout pass. At that time the size of p is still 0. You should use bindings instead to accommodate for later updates of the properties:
imv.fitHeightProperty.bind(p.heightProperty());
imv.fitWidthProperty.bind(p.widthProperty());
imv.setManaged(false); // make sure p can shrink
It would also be beneficial to make sure the GridPane resizes p.
public class ViewController extends Application implements EventHandler
The Application class is the entry point to the program. Using it as controller and event handler violates the seperation of concerns principle. Also it's preferable to specify the type parameter for EventHandler.
I'm looking for a bit of help with a problem I'm having. I am creating a GridLayout on my GUI and in each Grid there will be a JLabel. Along side this I have an ArrayList which contains images which will be displayed in each Grid.
What I am trying to do is when I click a specific grid, it will add an image from the ArrayList and place it in the grid position. What I would like is have a left click to add the item in the ArrayList and a right click to remove the item in the list.
The ArrayList and GUI code are in different classes and the ArrayList is implemented in the main method. I have tried to no avail, I cannot seem to get the grids to represent the list.
Basically I need a GridLayout to give a visual representation of an ArrayList, that can be manipulated with mouse interaction
Can anyone point me in the right direction?
Code for the Grids:
for (int i = 0; i < 12; i++)
{
JLabel assetLabel = new JLabel("Test"+(i+1));
System.out.println("assetLabel"+(i));
assetLabel.addMouseListener(new ParcelInfo(i));
assetLabel.setBackground(Color.WHITE);
assetLabel.setOpaque(true);
assetGrid.add(assetLabel);
}
Code for the items I need in the JLabel:
public class test
{
private ImageIcon img;
test(ImageIcon i)
{
this.img=i;
}
}
This is how you define a Listener for it.
public List<Image> arrayList = new ArrayList<>();
public OtherClassWithArray foo = new OtherClassWithArray();
jLabel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1) // left click
jLabel.setIcon(arrayList.get(xyz)); // access arraylist here
// jLabel.setIcon(foo.getArrayList.get(xyz); // access arrayList from other class
else // right click
// do smth here
}
});
this feels like I am cheating or doing something wrong. I am a Java student working on a simple JavaFX project.
As I loop through and create buttons in a flowPane, I was having trouble using the loop counter i inside an inner class. It's the part where I assign event handlers. I have dealt with this issue before, I get the difference between "final" and "effectively final" so I don't believe I am asking that.
It's just that creating this copy of i by using "int thisI = i" just feels wrong, design-wise. Is there not a better way to do this? I looked into lambdas and they also have the "final or effectively final" requirement.
Here's my code, any level or criticism or suggestion for improvement is welcome, thanks!
private FlowPane addFlowPaneCenter() {
FlowPane flow = new FlowPane();
flow.setPadding(new Insets(0, 0, 0, 0));
flow.setVgap(0);
flow.setHgap(0);
flow.setPrefWrapLength(WIDTH_OF_CENTER); // width of function buttons
Button centerButtons[] = new Button[NUM_BUTTONS];
ImageView centerImages[] = new ImageView[NUM_BUTTONS];
for (int i=0; i < NUM_BUTTONS; i++) {
centerImages[i] = new ImageView(
new Image(Calculator.class.getResourceAsStream(
"images/button-"+(i)+".png")));
centerButtons[i] = new Button();
centerButtons[i].setGraphic(centerImages[i]);
centerButtons[i].setPadding(Insets.EMPTY);
centerButtons[i].setId("button-"+(i));
flow.getChildren().add(centerButtons[i]);
// add a drop shadow on mouseenter
DropShadow shadow = new DropShadow();
// ***** here's the workaround is this really a good approach
// to use this in the inner class instead of i? thanks *****
int thisI = i;
// set event handlers for click, mousein, mouseout
centerButtons[i].setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
// change graphic of button to down graphic
ImageView downImage = new ImageView(new
Image(Calculator.class.getResourceAsStream(
"images/button-"+(thisI)+"D.png")));
// call function to effect button press
System.out.println("Button click");
// change graphic back
centerButtons[thisI].setGraphic(centerImages[thisI]);
}});
centerButtons[i].addEventHandler(MouseEvent.MOUSE_ENTERED,
new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent e) {
centerButtons[thisI].setEffect(shadow);
}
});
centerButtons[i].addEventHandler(MouseEvent.MOUSE_EXITED,
new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent e) {
centerButtons[thisI].setEffect(null);
}
});
}
return flow;
}
You can remove the arrays centerButtons and centerImages completely. Instead create local variables for the image and the button within the loop and use those, e.g.
final ImageView image = new ImageView(...);
final Button button = new Button();
button.setGraphic(centerImages[i]);
...
You can use the local variables in your eventhandlers, e.g.
button.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
...
// change graphic back
button.setGraphic(image);
}});
Two minor improvements I noticed:
Try to avoid creating an Image more than once, because every time you create an Image, the actual data will be loaded again. Your handler will create a new Image for each click. I usually create Images in static final fields.
Event handlers are a nice opportunity to practice lambda expressions. :)
I just have a question on the following. I've got a 2D array of buttons that require me to run another method when I click on them. My current method relies on the following input, a String (which I can get from the user easily) and two integer values. These are dictated by the buttons position in the array.
I have a button listener attached to these buttons but I am not too sure how I can work out what the button's position actually is. I've made my own button class (because I wanted some specific settings and I thought it would be easier) and when making it I implemented a method called getXPos and getYPos which basically hold the values for the actual button in the array when it was created. Thing is I don't know how to retrieve these values now as the listener doesn't actually know what button is being pressed does it?
I can use the getSource() method but I then don't know how to invoke that source's methods. For example I tried to do the following.
int x = event.getSource().getXPos(); but I am unable to do this. Is there any way of telling what button I have pressed so I can access it's internal methods or something similar? Thanks!
To call a method on the source, you have to cast it first. If you're never adding your ActionListener to anything but an instance of your special MyButton with its x and y variables, you can do this:
MyButton button = (MyButton) event.getSource();
int x = button.getXPos();
int y = button.getYPos();
Then MyButton contains the x and y:
public class MyButton extends JButton {
private int xPos;
private int yPos;
// ...
public int getXPos() {
return xPos;
}
public int getYPos() {
return yPos;
}
}
And make sure you're always adding your listener to instances of MyButton:
MyButton myButton = new MyButton();
// ...
myButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MyButton button = (MyButton) e.getSource();
int xPos = button.getXPos();
int yPos = button.getYPos();
// Do something with x and y
}
});
I'm rather new to programming Blackberries and was wondering if anybody knows of a tutorial or a snippet about how to load an image into the screen and set an onClick listener to it?
edit, got this far:
ButtonField btf1 = new ButtonField("Fine!");
ButtonField btf2 = new ButtonField("Great!");
RichTextField rtf = new RichTextField("HELLO, HOW ARE YOU?");
Bitmap LOGO = Bitmap.getBitmapResource("1.png");
BitmapField LogoBmpField = new BitmapField(LOGO);
HelloWorldScreen()
{
setTitle("My First App");
add(rtf);
add(btf1);
add(btf2);
add(LogoBmpField);
}
Thanks!
edit: by the way, how should interfaces be made for blackberry? simply by
ButtonField btf1 = new ButtonField("Fine!");
add(btf1);
Or is there some more visible way, such as in XML for android?
One more thing, how to I change or set properties of some object. Say I want to change the title of my button- btf1.(expecting list of available properties to appear ) doesn't give anything.
Place your image in your res folder and try this;
Bitmap bmpLogo = Bitmap.getBitmapResource("yourImage.jpg");
BitmapField logo = new BitmapField(bmpLogo){
protected boolean trackwheelClick(int status, int time)
{
// Your onclick code here
return true;
}
};
add(logo);
1) You need to make your BitmapField focusable.
I just tried this:
BitmapField LogoBmpField = new BitmapField(LOGO, BitmapField.FOCUSABLE) {
protected boolean trackwheelClick(int status, int time) {
System.out.println(" -- You clicked me! ");
return true;
}
};
and it seems to work.
2) And to change the text on your button use
setLabel()
btf1.setLabel("new button text");
public class MyScreen extends MainScreen {
public LanguageSelector() {
Bitmap logoBitmap = Bitmap.getBitmapResource("normalarabflag.png");
BitmapField LogoBmpField = new BitmapField(logoBitmap, BitmapField.FOCUSABLE | Field.FIELD_HCENTER) {
protected boolean navigationClick(int status, int time) {
System.out.println(" -- You clicked me! ");
UiApplication.getUiApplication().pushScreen(new SecoundScreen());
Dialog.alert("Load Complete");
return true;
}
};
LabelField labelfield = new LabelField("Arabic ",Field.FIELD_HCENTER|LabelField.FOCUSABLE);
VerticalFieldManager vrt=new VerticalFieldManager(USE_ALL_WIDTH) {
protected void sublayout(int maxWidth, int maxHeight) {
super.sublayout(Display.getWidth(),Display.getHeight());
setExtent(Display.getWidth(),Display.getHeight());
}
};
Font f=labelfield.getFont();
int hight1=f.getAdvance(labelfield.getText());
int k=labelfield.getPreferredHeight();
int number=hight1/Display.getWidth()+1;
int hight2=logoBitmap.getHeight();
int padding=(Display.getHeight()-((number*k)+hight2))/2;
if(padding>0) {
LogoBmpField.setPadding(padding,0,0,0);
}
vrt.add(LogoBmpField);
vrt.add(labelfield);
add(vrt);
}
}