i have have design a class whos constructor produce 52 Cards object each orject has its own color, value, isTrump(boolean) and imageIcon which store image of each card. now there is another class called Que who stores all the objects of cards into a que and shuffle them. there is a method which deals the cards object among 4 players into array of object(Cards). now i used LayeredPane and Jlabels to show them and a mouse-listener also attached with each.
Now i only want a tip how can i start each trick. means i place one card then three other robots placed there cards automatically after analysing my card on the bases of some predefined rules.
please any suggestions
public class Cards {
public Cards(int a, int b){
this.cardvalue = a;
this.color = b;
this.isTrump = false;
switch(a){
case 11: this.facevalue = 1;
break;
case 12: this.facevalue = 2;
break;
case 13: this.facevalue = 3;
break;
case 14: this.facevalue = 4;
break;
default : this.facevalue = 0;
}
switch(b){
case 1: this.colorName = "Spade";
this.CardImage= new ImageIcon(getClass().getResource("/testing/Cards/Spade/Spade ("+a+").jpg"));
break;
case 2: this.colorName = "Heart";
this.CardImage= new ImageIcon(getClass().getResource("/testing/Cards/Heart/Heart ("+a+").jpg"));
break;
case 3: this.colorName = "Diamond";
this.CardImage= new ImageIcon(getClass().getResource("/testing/Cards/Diamond/Diamond ("+a+").jpg"));
break;
default : this.colorName = "Club";
this.CardImage= new ImageIcon(getClass().getResource("/testing/Cards/Club/Club ("+a+").jpg"));
}
}
public void isTrump(){
this.isTrump = true;
}
public int getCardValue(){
return this.cardvalue;
}
public int getColor(){
return this.color;
}
public int getFaceValue(){
return this.facevalue;
}
public boolean getisTrump(){
return this.isTrump;
}
public String getColorName(){
return this.colorName;
}
public ImageIcon getCardImage(){
return this.CardImage;
}
public ImageIcon getBackSide(){
return backSide;
}
private String colorName;
private int cardvalue;
private int color; // 1 For Spade 2 For Heart 3 For Diamond 4 For Club
private int facevalue;
private boolean isTrump;
private ImageIcon CardImage;
private ImageIcon backSide = new
ImageIcon(getClass().getResource("/testing/Cards/Backside.jpg"));
}
Class which deal the card is
public class CardsInHand {
public CardsInHand(){
totalCards = new Que(52);
int a =1; int b=2;
for (int j = 0;j<52;j++){
if(j==13||j==26||j==39) a++;
if(b==15) b=2;
totalCards.put(new Cards(b,a));
b++;
}
totalCards.QueShuffle();
}
public static void Deal(){
card = new CardsInHand();
setPlayers();
}
private static void setPlayers(){
for(int i =0;i<13;i++){
Hands[0][i] = totalCards.get();
Hands[1][i] = totalCards.get();
Hands[2][i] = totalCards.get();
Hands[3][i] = totalCards.get();
}
sortingarr();
}
private static void Sorting(Cards arr[]){
//here some Sorting algorithm i used
}
private static void sortingarr(){
Sorting(Hands[0]);
Sorting(Hands[1]);
Sorting(Hands[2]);
Sorting(Hands[3]);
NumberSorting(Hands[0]);
NumberSorting(Hands[1]);
NumberSorting(Hands[2]);
NumberSorting(Hands[3]);
}
private static void NumberSorting(Cards arr[]){
//some algorith i used for number sorting
}
public static Cards[] getPlayer1(){
return Hands[0];
}
public static Cards[] getPlayer2(){
return Hands[1];
}
public static Cards[] getPlayer3(){
return Hands[2];
}
public static Cards[] getPlayer4(){
return Hands[3];
}
public static Cards[][] getAllHands(){
return Hands;
}
private final static Cards[] Player1 = new Cards[13];
private final static Cards[] Player2 = new Cards[13];
private final static Cards[] Player3 = new Cards[13];
private final static Cards[] Player4 = new Cards[13];
private final static Cards[][] Hands = {Player1,Player2,Player3,Player4};
private static Que totalCards;
private static CardsInHand card;
}
the class which is showing the cards after dealing is
public class ShowCards {
private JFrame frame = new JFrame();
private static JLayeredPane lpane = new JLayeredPane();
private static JLabel[] Player1 = new JLabel[13];
private static JLabel[] Player2 = new JLabel[13];
private static JLabel[] Player3 = new JLabel[13];
private static JLabel[] Player4 = new JLabel[13];
private static JButton button = new JButton("Deal Again");
public ShowCards()
{
CardsInHand.Deal();
frame.setPreferredSize(new Dimension(800, 640));
frame.setLayout(new BorderLayout());
frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.LINE_AXIS));
frame.add(lpane, BorderLayout.CENTER);
frame.add(button);
lpane.add(button, new Integer(14), 0);
button.setBounds(200, 250, 100, 70);
button.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
buttonActionPerformed(evt);
}
});
lpane.setBounds(30, 30, 270, 270);
cardDeal();
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
static void cardDeal(){
loadJLabels1();
loadJLabels2();
loadJLabels3();
loadJLabels4();
}
static void loadJLabels1(){
int k = 30;
for(int i=0;i<13;i++){
Player1[i] = new JLabel();
Player1[i].setIcon(CardsInHand.getPlayer1()[i].getCardImage());
Player1[i].setBounds(k, 20, 170, 100);
Player1[i].setOpaque(true);
Player1[i].addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent evt)
{
int count = evt.getClickCount();
if (count == 1){
int Index = getIndex(Player1, (JLabel)evt.getSource());
Player1[Index].setIcon(CardsInHand.getPlayer1()[Index].getBackSide());
}
}
});
lpane.add(Player1[i], new Integer(i), 0);
k = k+30;
}
}
static void loadJLabels2(){
int k = 140;
for(int i=0;i<13;i++){
Player2[i] = new JLabel();
Player2[i].setIcon(CardsInHand.getPlayer2()[i].getCardImage());
Player2[i].setBounds(30, k, 170, 100);
Player2[i].setOpaque(true);
Player2[i].addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent evt)
{
int count = evt.getClickCount();
if (count == 1){
int Index = getIndex(Player2, (JLabel)evt.getSource());
Player2[Index].setIcon(CardsInHand.getPlayer2()[Index].getBackSide());
}
}
});
lpane.add(Player2[i], new Integer(i), 0);
k = k+20;
}
}
static void loadJLabels3(){
int k = 140;
for(int i=0;i<13;i++){
Player3[i] = new JLabel();
Player3[i].setIcon(CardsInHand.getPlayer3()[i].getCardImage());
Player3[i].setBounds(400, k, 170, 100);
Player3[i].setOpaque(true);
Player3[i].addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent evt)
{
int count = evt.getClickCount();
if (count == 1){
int Index = getIndex(Player3, (JLabel)evt.getSource());
Player3[Index].setIcon(CardsInHand.getPlayer2()[Index].getBackSide());
}
}
});
lpane.add(Player3[i], new Integer(i), 0);
k = k+20;
}
}
static void loadJLabels4(){
int k = 30;
for(int i=0;i<13;i++){
Player4[i] = new JLabel();
Player4[i].setIcon(CardsInHand.getPlayer4()[i].getCardImage());
Player4[i].setBounds(k, 500, 170, 100);
Player4[i].setOpaque(true);
Player4[i].addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent evt)
{
int count = evt.getClickCount();
if (count == 1){
int Index = getIndex(Player4, (JLabel)evt.getSource());
Player4[Index].setIcon(CardsInHand.getPlayer2()[Index].getBackSide());
}
}
});
lpane.add(Player4[i], new Integer(i), 0);
k = k+30;
}
}
private void buttonActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
CardsInHand.Deal();
cardDeal();
}
static int getIndex(JLabel[] arr, JLabel obj){
int index=0;
for(JLabel cmp: arr){
if(cmp == obj) break;
index++;
}
return index;
}
}
Now i also creat a player class which creat three Bots to play and one will be the human. the class is as
public class Players {
public Players(Cards[] arr){
this.scoreInHand = getScoreInHand(arr);
getCardOfEachSuit(arr, this.cardsOfEachSuit);
this.isBalanceHand = getIsBalanceHand(arr);
this.longestSuit = getLongestSuit(arr);
this.numberOfTrump = getNumberOfTrump(arr);
this.smallestSuit = getSmallestSuit(arr);
this.strongestSuit = getStrongestSuit(arr);
this.weakestSuit = getWeakestSuit(arr);
for(int i=0;i<13;i++){
this.remainingSpade[i] = true;
this.remainingHeart[i] = true;
this.remainingClub[i] = true;
this.remainingDiamond[i] = true;
}
for(int i=0;i<4;i++){
this.rightOpCutSuit[i]= false;
this.leftOpCutSuit[i] = false;
this.frontOpCutSuit[i] = false;
}
this.coatCaution = false;
this.gcCaution = false;
this.numberOfTrumpExist = 13;
this.openingBet =0;
this.respondingBet =0;
this.trickStarter = false;
}
public static void createRobot(){
CardsInHand.Deal();
for(int i=0; i<3;i++){
Robots[i] = new Players(CardsInHand.getAllHands()[i+1]);
}
}
private int getScoreInHand(Cards[] arr){
int temp = 0;
for(Cards x: arr){
temp = temp + x.getFaceValue();
}
return temp;
}
private int getStrongestSuit(Cards[] arr){
int strong=0;
int S=0;int C=0;int D=0;int H=0;
for(Cards x: arr){
if(x.getColorName()=="Spade") S = S + x.getFaceValue();
else if(x.getColorName()=="Heart") H = H + x.getFaceValue();
else if(x.getColorName()=="Club") C = C + x.getFaceValue();
else D = D + x.getFaceValue();
}
int[] temp = {S,H,D,C};
int max = temp[0];
for(int i=0;i<4;i++){
if(max<temp[i]){
max=temp[i];
strong = i;
}
}
return strong+1;
}
private int getWeakestSuit(Cards[] arr){
int weak=0;
int S=0;int C=0;int D=0;int H=0;
for(Cards x: arr){
if(x.getColorName()=="Spade") S = S + x.getFaceValue();
else if(x.getColorName()=="Heart") H = H + x.getFaceValue();
else if(x.getColorName()=="Club") C = C + x.getFaceValue();
else D = D + x.getFaceValue();
}
int[] temp = {S,H,D,C};
int min = temp[0];
for(int i=0;i<4;i++){
if(min>temp[i]){
min=temp[i];
weak = i;
}
}
return weak+1;
}
private int getLongestSuit(Cards[] arr){
int Longest = 0;
int[] temp = new int[4];
getCardOfEachSuit(arr, temp);
int max = temp[0];
for(int i=0;i<4;i++){
if(max<temp[i]){
max=temp[i];
Longest = i;
}
}
return Longest+1;
}
private int getSmallestSuit(Cards[] arr){
int Smallest = 0;
int[] temp = new int[4];
getCardOfEachSuit(arr, temp);
int max = temp[0];
for(int i=0;i<4;i++){
if(max>temp[i]){
max=temp[i];
Smallest = i;
}
}
return Smallest+1;
}
private boolean getIsBalanceHand(Cards[] arr){
int S=0;int C=0;int D=0;int H=0;
for(Cards x: arr){
if(x.getColorName()=="Spade") S++;
else if(x.getColorName()=="Heart") H++;
else if(x.getColorName()=="Club") C++;
else D++;
}
if((S<=4&&S>=3)&&(H<=4&&H>=3)&&(D<=4&&D>=3)&&(C<=4&&C>=3)) return true;
else return false;
}
private void getCardOfEachSuit(Cards[] arr, int[] array){
int S=0;int C=0;int D=0;int H=0;
for(Cards x: arr){
if(x.getColorName()=="Spade") S++;
else if(x.getColorName()=="Heart") H++;
else if(x.getColorName()=="Club") C++;
else D++;
}
array[0] = S;
array[1] = H;
array[2] = D;
array[3] = C;
}
private int getNumberOfTrump(Cards[] arr){
int temp = 0;
for(Cards x: arr){
if(x.getisTrump() == true) temp = temp+1;
}
return temp;
}
public void setTrickStarter(boolean a){
this.trickStarter = a;
}
public boolean getTrickStarter(){
return this.trickStarter;
}
private static Players[] Robots = new Players[3]; //
private int[] cardsOfEachSuit = new int[4]; // ok
private boolean[] remainingSpade = new boolean[13]; //
private boolean[] remainingHeart = new boolean[13]; //
private boolean[] remainingDiamond = new boolean[13]; //
private boolean[] remainingClub = new boolean[13]; //
private int scoreInHand; // ok
private int longestSuit; // ok
private int smallestSuit; // ok
private int strongestSuit; // ok
private int weakestSuit; // ok
private boolean isBalanceHand; // ok
private int numberOfTrump; // ok
private int numberOfTrumpExist; //
private boolean[] rightOpCutSuit = new boolean[4]; //
private boolean[] frontOpCutSuit = new boolean[4]; //
private boolean[] leftOpCutSuit = new boolean[4]; //
private int openingBet; //
private int respondingBet; //
private boolean gcCaution; //
private boolean coatCaution; //
private boolean trickStarter;
}
Now please guide me little forward what is can do to start playing.
Related
I am trying to set node animation, that depends on previous animations of that node as well as other nodes.
To demonstrate the issue I'll use a simple Pane with 4 Label children:
The main class as well as the model and view classes:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public final class Puzzle extends Application{
private Controller controller;
#Override
public void start(Stage stage) throws Exception {
controller = new Controller();
BorderPane root = new BorderPane(controller.getBoardPane());
root.setTop(controller.getControlPane());
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) { launch();}
}
class View{
private static final double size = 70;
private static Duration animationDuration = Duration.millis(600);
private int[][] cellModels;
private Node[][] cellNodes;
private CountDownLatch latch;
Button play = new Button("Play");
private Pane board, control = new HBox(play);;
View(Model model) {
cellModels = model.getCellModels();
cellNodes = new Node[cellModels.length][cellModels[0].length];
makeBoardPane();
((HBox) control).setAlignment(Pos.CENTER_RIGHT);
}
private void makeBoardPane() {
board = new Pane();
for (int row = 0; row < cellModels.length ; row ++ ) {
for (int col = 0; col < cellModels[row].length ; col ++ ) {
Label label = new Label(String.valueOf(cellModels[row][col]));
label.setPrefSize(size, size);
Point2D location = getLocationByRowCol(row, col);
label.setLayoutX(location.getX());
label.setLayoutY(location.getY());
label.setStyle("-fx-border-color:blue");
label.setAlignment(Pos.CENTER);
cellNodes[row][col] = label;
board.getChildren().add(label);
}
}
}
synchronized void updateCell(int id, int row, int column) {
if(latch !=null) {
try {
latch.await();
} catch (InterruptedException ex) { ex.printStackTrace();}
}
latch = new CountDownLatch(1);
Node node = getNodesById(id).get(0);
Point2D newLocation = getLocationByRowCol(row, column);
Point2D moveNodeTo = node.parentToLocal(newLocation );
TranslateTransition transition = new TranslateTransition(animationDuration, node);
transition.setFromX(0); transition.setFromY(0);
transition.setToX(moveNodeTo.getX());
transition.setToY(moveNodeTo.getY());
//set animated node layout to the translation co-ordinates:
//https://stackoverflow.com/a/30345420/3992939
transition.setOnFinished(ae -> {
node.setLayoutX(node.getLayoutX() + node.getTranslateX());
node.setLayoutY(node.getLayoutY() + node.getTranslateY());
node.setTranslateX(0);
node.setTranslateY(0);
latch.countDown();
});
transition.play();
}
private List<Node> getNodesById(int...ids) {
List<Node> nodes = new ArrayList<>();
for(Node node : board.getChildren()) {
if(!(node instanceof Label)) { continue; }
for(int id : ids) {
if(((Label)node).getText().equals(String.valueOf(id))) {
nodes.add(node);
break;
}
}
}
return nodes ;
}
private Point2D getLocationByRowCol(int row, int col) {
return new Point2D(size * col, size * row);
}
Pane getBoardPane() { return board; }
Pane getControlPane() { return control;}
Button getPlayBtn() {return play ;}
}
class Model{
private int[][] cellModels = new int[][] { {0,1}, {2,3} };
private SimpleObjectProperty<int[][]> cellModelsProperty =
cellModelsProperty = new SimpleObjectProperty<>(cellModels);
void addChangeListener(ChangeListener<int[][]> listener) {
cellModelsProperty.addListener(listener);
}
int[][] getCellModels() {
return (cellModelsProperty == null) ? null : cellModelsProperty.get();
}
void setCellModels(int[][] cellModels) {
cellModelsProperty.set(cellModels);
}
}
and the controller class:
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.Pane;
class Controller {
private View view ;
private Model model;
Controller() {
model = new Model();
model.addChangeListener(getModelCangeListener());//observe model changes
view = new View(model);
view.getPlayBtn().setOnAction( a -> shuffle()); //animation works fine
//view.getPlayBtn().setOnAction( a -> IntStream.
// range(0,4).forEach( (i)-> shuffle())); //messes the animation
}
private ChangeListener<int[][]> getModelCangeListener() {
return (ObservableValue<? extends int[][]> observable,
int[][] oldValue, int[][] newValue)-> {
for (int row = 0; row < newValue.length ; row++) {
for (int col = 0; col < newValue[row].length ; col++) {
if(newValue[row][col] != oldValue[row][col]) {
final int fRow = row, fCol = col;
new Thread( () -> view.updateCell(
newValue[fRow][fCol], fRow, fCol)).start();
}
}
}
};
}
void shuffle() {
int[][] modelData = model.getCellModels();
int rows = modelData.length, columns = modelData[0].length;
int[][] newModelData = new int[rows][columns];
for (int row = 0; row < rows ; row ++) {
for (int col = 0; col < columns ; col ++) {
int colIndex = ((col+1) < columns ) ? col + 1 : 0;
int rowIndex = ((col+1) < columns ) ? row : ((row + 1) < rows) ? row +1 : 0;
newModelData[row][col] = modelData[rowIndex][colIndex];
}
}
model.setCellModels(newModelData);
}
Pane getBoardPane() { return view.getBoardPane(); }
Pane getControlPane() { return view.getControlPane(); }
}
Play button handler changes the model data (see shuffle()), the change triggers ChangeListener. The ChangeListener animates each changed label by invoking view.updateCell(..) on a separate thread. This all works as expected.
The problem starts when I try to run a few consecutive model updates (shuffle()). To simulate it I change
view.getPlayBtn().setOnAction( a -> shuffle());
with
view.getPlayBtn().setOnAction( a -> IntStream.range(0,4).forEach( (i)-> shuffle()));
which messes the animation (it plays in wrong order and ends up in wrong positions).
This does not come as a surprise: to work properly animations have to be played in a certain order: a label should be re-animated only after all four labels finished their previous animation.
The code posted runs each update on a thread, so execution order is not guaranteed.
My question is what is the right way to implement the needed sequence of multiple nodes and multiple animations ?
I looked at SequentialTransition but I couldn't figure out how it can be used to overcome the issue in question.
I did come up with a solution which I will post as an answer, because of the length of this post, and because I don't think the solution is good.
Your code does not adhere to the seperation of concerns principle. (Or at least you don't do it well.)
The animation should be done by the view and the view alone instead of collaborating between the controller and the view. Put all the animations for scheduling the animations in the View class.
SequentialTransition could wrap multiple animations in a single animation that plays them sequentially, but it shouldn't be the controller's concern to do this.
public class View {
private static final double SIZE = 70;
private static final Duration ANIMATION_DURATION = Duration.millis(600);
private final Map<Integer, Label> labelsById = new HashMap<>(); // stores labels by id
private final Button play = new Button("Play");
private Pane board;
private final HBox control;
private final Timeline animation;
private final LinkedList<ElementPosition> pendingAnimations = new LinkedList<>(); // stores parameter combination for update calls
private Node animatedNode;
public View(int[][] cellModels) { // we don't really need the whole model here
makeBoardPane(cellModels);
this.control = new HBox(play);
control.setAlignment(Pos.CENTER_RIGHT);
final DoubleProperty interpolatorValue = new SimpleDoubleProperty();
animation = new Timeline(
new KeyFrame(Duration.ZERO, evt -> {
ElementPosition ePos = pendingAnimations.removeFirst();
animatedNode = labelsById.get(ePos.id);
Point2D newLocation = getLocationByRowCol(ePos.row, ePos.column);
// create binding for layout pos
animatedNode.layoutXProperty().bind(
interpolatorValue.multiply(newLocation.getX() - animatedNode.getLayoutX())
.add(animatedNode.getLayoutX()));
animatedNode.layoutYProperty().bind(
interpolatorValue.multiply(newLocation.getY() - animatedNode.getLayoutY())
.add(animatedNode.getLayoutY()));
}, new KeyValue(interpolatorValue, 0d)),
new KeyFrame(ANIMATION_DURATION, evt -> {
interpolatorValue.set(1);
// remove bindings
animatedNode.layoutXProperty().unbind();
animatedNode.layoutYProperty().unbind();
animatedNode = null;
if (pendingAnimations.isEmpty()) {
// abort, if no more animations are pending
View.this.animation.stop();
}
}, new KeyValue(interpolatorValue, 1d)));
animation.setCycleCount(Animation.INDEFINITE);
}
private void makeBoardPane(int[][] cellModels) {
board = new Pane();
for (int row = 0; row < cellModels.length; row++) {
for (int col = 0; col < cellModels[row].length; col++) {
Point2D location = getLocationByRowCol(row, col);
int id = cellModels[row][col];
Label label = new Label(Integer.toString(id));
label.setPrefSize(SIZE, SIZE);
label.setLayoutX(location.getX());
label.setLayoutY(location.getY());
label.setStyle("-fx-border-color:blue");
label.setAlignment(Pos.CENTER);
labelsById.put(id, label);
board.getChildren().add(label);
}
}
}
private static class ElementPosition {
private final int id;
private final int row;
private final int column;
public ElementPosition(int id, int row, int column) {
this.id = id;
this.row = row;
this.column = column;
}
}
public void updateCell(int id, int row, int column) {
pendingAnimations.add(new ElementPosition(id, row, column));
animation.play();
}
private static Point2D getLocationByRowCol(int row, int col) {
return new Point2D(SIZE * col, SIZE * row);
}
public Pane getBoard() {
return board;
}
public Pane getControlPane() {
return control;
}
public Button getPlayBtn() {
return play;
}
}
Usage example with reduced complexity:
private int[][] oldValue;
#Override
public void start(Stage primaryStage) {
List<Integer> values = new ArrayList<>(Arrays.asList(0, 1, 2, 3));
oldValue = new int[][]{{0, 1}, {2, 3}};
View view = new View(oldValue);
Button btn = new Button("Shuffle");
btn.setOnAction((ActionEvent event) -> {
Collections.shuffle(values);
int[][] newValue = new int[2][2];
for (int i = 0; i < 2 * 2; i++) {
newValue[i / 2][i % 2] = values.get(i);
}
System.out.println(values);
for (int row = 0; row < newValue.length; row++) {
for (int col = 0; col < newValue[row].length; col++) {
if (newValue[row][col] != oldValue[row][col]) {
view.updateCell(newValue[row][col], row, col);
}
}
}
oldValue = newValue;
});
Scene scene = new Scene(new BorderPane(view.getBoard(), null, view.getControlPane(), btn, null));
primaryStage.setScene(scene);
primaryStage.show();
}
The solution I came up with involved controlling the execution order of the updating threads.
Todo it I changed the controller class by implementing an inner class based on this
so answer. See UpdateView in the following code.
I also modified getModelCangeListener() to use UpdateView:
import java.util.stream.IntStream;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.Pane;
class Controller {
private View view ;
private Model model;
private static int threadNumber = 0, threadAllowedToRun = 0;
private static final Object myLock = new Object();
Controller() {
model = new Model();
model.addChangeListener(getModelCangeListener());//observe model changes
view = new View(model);
view.getPlayBtn().setOnAction( a -> IntStream.
range(0,4).forEach( (i)-> shuffle()));
}
private ChangeListener<int[][]> getModelCangeListener() {
return (ObservableValue<? extends int[][]> observable,
int[][] oldValue, int[][] newValue)-> {
for (int row = 0; row < newValue.length ; row++) {
for (int col = 0; col < newValue[row].length ; col++) {
if(newValue[row][col] != oldValue[row][col]) {
final int fRow = row, fCol = col;
new Thread( new UpdateView(
newValue[fRow][fCol], fRow, fCol)).start();
}
}
}
};
}
private void shuffle() {
int[][] modelData = model.getCellModels();
int rows = modelData.length, columns = modelData[0].length;
int[][] newModelData = new int[rows][columns];
for (int row = 0; row < rows ; row ++) {
for (int col = 0; col < columns ; col ++) {
int colIndex = ((col+1) < columns ) ? col + 1 : 0;
int rowIndex = ((col+1) < columns ) ? row : ((row + 1) < rows) ? row +1 : 0;
newModelData[row][col] = modelData[rowIndex][colIndex];
}
}
model.setCellModels(newModelData);
}
Pane getBoardPane() { return view.getBoardPane(); }
Pane getControlPane() { return view.getControlPane(); }
//https://stackoverflow.com/a/23097860/3992939
class UpdateView implements Runnable {
private int id, row, col, threadID;
UpdateView(int id, int row, int col) {
this.id = id; this.row = row; this.col = col;
threadID = threadNumber++;
}
#Override
public void run() {
synchronized (myLock) {
while (threadID != threadAllowedToRun) {
try {
myLock.wait();
} catch (InterruptedException e) {}
}
view.updateCell(id, row, col);
threadAllowedToRun++;
myLock.notifyAll();
}
}
}
}
While it as a possible solution I think a solution based on JavaFx own tools is preferable.
The following answer is based this answer. I refactored it, breaking it into smaller, more verbose "pieces", which makes it (for me) easier to understand.
I am posting it in hope it will help others as well.
import java.util.LinkedList;
import java.util.stream.IntStream;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public final class Puzzle extends Application{
private Controller controller;
#Override
public void start(Stage stage) throws Exception {
controller = new Controller();
BorderPane root = new BorderPane(controller.getBoardPane());
root.setTop(controller.getControlPane());
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) { launch();}
}
class Controller {
private View view ;
private Model model;
Controller() {
model = new Model();
model.addChangeListener(getModelCangeListener());//observe model changes
view = new View(model);
view.getPlayBtn().setOnAction( a -> IntStream.
range(0,4).forEach( (i)-> shuffle()));
}
private ChangeListener<int[][]> getModelCangeListener() {
return (ObservableValue<? extends int[][]> observable,
int[][] oldValue, int[][] newValue)-> {
for (int row = 0; row < newValue.length ; row++) {
for (int col = 0; col < newValue[row].length ; col++) {
if(newValue[row][col] != oldValue[row][col]) {
view.updateCell(newValue[row][col], row, col);
}
}
}
};
}
private void shuffle() {
int[][] modelData = model.getCellModels();
int rows = modelData.length, columns = modelData[0].length;
int[][] newModelData = new int[rows][columns];
for (int row = 0; row < rows ; row ++) {
for (int col = 0; col < columns ; col ++) {
int colIndex = ((col+1) < columns ) ? col + 1 : 0;
int rowIndex = ((col+1) < columns ) ? row : ((row + 1) < rows) ? row +1 : 0;
newModelData[row][col] = modelData[rowIndex][colIndex];
}
}
model.setCellModels(newModelData);
}
Pane getBoardPane() { return view.getBoardPane(); }
Pane getControlPane() { return view.getControlPane(); }
}
class View{
private final Timeline timeLineAnimation;// private final Timeline timeLineAnimation;
private final LinkedList<ElementPosition> pendingAnimations = new LinkedList<>(); // stores parameter combination for update calls
private static final Duration ANIMATION_DURATION = Duration.millis(600);
private static final double SIZE = 70;
private int[][] cellModels;
private final Button play = new Button("Play");
private Pane board, control = new HBox(play);
Node animatedNode;
View(Model model) {
cellModels = model.getCellModels();
makeBoardPane();
((HBox) control).setAlignment(Pos.CENTER_RIGHT);
timeLineAnimation = new TimeLineAnimation().get();
}
private void makeBoardPane() {
board = new Pane();
for (int row = 0; row < cellModels.length ; row ++ ) {
for (int col = 0; col < cellModels[row].length ; col ++ ) {
int id = cellModels[row][col];
Label label = new Label(String.valueOf(id));
label.setPrefSize(SIZE, SIZE);
Point2D location = getLocationByRowCol(row, col);
label.setLayoutX(location.getX());
label.setLayoutY(location.getY());
label.setStyle("-fx-border-color:blue");
label.setAlignment(Pos.CENTER);
label.setId(String.valueOf(id));
board.getChildren().add(label);
}
}
}
public void updateCell(int id, int row, int column) {
pendingAnimations.add(new ElementPosition(id, row, column));
timeLineAnimation.play();
}
private Node getNodeById(int id) {
return board.getChildren().filtered(n ->
Integer.valueOf(n.getId()) == id).get(0);
}
private Point2D getLocationByRowCol(int row, int col) {
return new Point2D(SIZE * col, SIZE * row);
}
Pane getBoardPane() { return board; }
Pane getControlPane() { return control;}
Button getPlayBtn() {return play ;}
private static class ElementPosition {
private final int id, row, column;
public ElementPosition(int id, int row, int column) {
this.id = id;
this.row = row;
this.column = column;
}
}
class TimeLineAnimation {
private final Timeline timeLineAnimation;
//value to be interpolated by time line
final DoubleProperty interpolatorValue = new SimpleDoubleProperty();
private Node animatedNode;
TimeLineAnimation() {
timeLineAnimation = new Timeline(getSartKeyFrame(), getEndKeyFrame());
timeLineAnimation.setCycleCount(Animation.INDEFINITE);
}
// a 0 duration event, used to do the needed setup for next key frame
private KeyFrame getSartKeyFrame() {
//executed when key frame ends
EventHandler<ActionEvent> onFinished = evt -> {
//protects against "playing" when no pending animations
if(pendingAnimations.isEmpty()) {
timeLineAnimation.stop();
return;
};
ElementPosition ePos = pendingAnimations.removeFirst();
animatedNode = getNodeById(ePos.id);
Point2D newLocation = getLocationByRowCol(ePos.row, ePos.column);
// bind x,y layout properties interpolated property interpolation effects
//both x and y
animatedNode.layoutXProperty().bind(
interpolatorValue.multiply(newLocation.getX() - animatedNode.getLayoutX())
.add(animatedNode.getLayoutX()));
animatedNode.layoutYProperty().bind(
interpolatorValue.multiply(newLocation.getY() - animatedNode.getLayoutY())
.add(animatedNode.getLayoutY()));
};
KeyValue startKeyValue = new KeyValue(interpolatorValue, 0d);
return new KeyFrame(Duration.ZERO, onFinished, startKeyValue);
}
private KeyFrame getEndKeyFrame() {
//executed when key frame ends
EventHandler<ActionEvent> onFinished =evt -> {
// remove bindings
animatedNode.layoutXProperty().unbind();
animatedNode.layoutYProperty().unbind();
};
KeyValue endKeyValue = new KeyValue(interpolatorValue, 1d);
return new KeyFrame(ANIMATION_DURATION, onFinished, endKeyValue);
}
Timeline get() { return timeLineAnimation; }
}
}
class Model{
private int[][] cellModels = new int[][] { {0,1}, {2,3} };
private SimpleObjectProperty<int[][]> cellModelsProperty =
cellModelsProperty = new SimpleObjectProperty<>(cellModels);
void addChangeListener(ChangeListener<int[][]> listener) {
cellModelsProperty.addListener(listener);
}
int[][] getCellModels() {
return (cellModelsProperty == null) ? null : cellModelsProperty.get();
}
void setCellModels(int[][] cellModels) { cellModelsProperty.set(cellModels);}
}
(to run copy paste the entire code into Puzzle.java)
I'm having a problem in my code where the animation from another class does not animate and is stuck in one frame. The image moves across the screen in the right manner though. I know that the problem is somehow related to the UPDATE method of the animation. I have tried every possible solutions in my knowledge to find what causes the error and the solutions I found online were not quite helpful. Any help will be appreciated.
Here's my Code:
LevelOneScreen.java
public class LevelOneScreen implements Screen {
private final MyGame app;
WalkAnimate walkAnimate;
private Stage stage;
private Image levelOneImage;
private Image holdStartImage;
public Image walkRightImage;
public Image walkLeftImage;
public float deltaTime = Gdx.graphics.getDeltaTime();
public LevelOneScreen(final ThumbChase app){
this.app = app;
this.stage = new Stage(new StretchViewport(app.screenWidth,app.screenHeight , app.camera));
}
#Override
public void show() {
Gdx.input.setInputProcessor(stage);
walkAnimate = new WalkAnimate();
walkAnimate.update(deltaTime);
levelOneBackground();
holdStart();
ninjaWalk();
}
public void holdStart(){
Texture holdStartTexture = new Texture("HoldStart.png");
holdStartImage = new Image(holdStartTexture);
float holdStartImageW = holdStartImage.getWidth();
float holdStartImageH = holdStartImage.getHeight();
float holdStartImgWidth = app.screenWidth*0.8f;
float holdStartImgHeight = holdStartImgWidth *(holdStartImageH/holdStartImageW);
holdStartImage.isTouchable();
holdStartImage.setSize(holdStartImgWidth,holdStartImgHeight);
holdStartImage.setPosition(stage.getWidth()/2-holdStartImgWidth/2,stage.getHeight()/2-holdStartImgHeight/2);
stage.addActor(holdStartImage);
holdStartImage.addListener(new ActorGestureListener(){
/* public void touchDown (InputEvent event, float x, float y, int pointer, int button){
holdStartImage.setVisible(false);
};*/
public void fling(InputEvent event, float velocityX, float velocityY, int button) {
holdStartImage.setVisible(false);
}
public void touchDown (InputEvent event, float x, float y, int pointer, int button){
holdStartImage.setVisible(false);
};
public void touchDrag (InputEvent event, float x, float y, int pointer, int button){
holdStartImage.setVisible(false);
};
});
}
public void levelOneBackground(){
Texture levelOneTexture = new Texture("BGBlue Resize.png");
levelOneImage = new Image(levelOneTexture);
levelOneImage.setSize(app.screenWidth,app.screenHeight);
levelOneImage.setPosition(0,0);
stage.addActor(levelOneImage);
/*levelOneImage.addListener(new ActorGestureListener(){
public void touchDown (InputEvent event, float x, float y, int pointer, int button){
holdStartImage.setVisible(false);
};
});*/
}
public void ninjaWalk(){
TextureRegion ninjaWalkRight = new TextureRegion(walkAnimate.getCurrentFrameRight());
TextureRegion ninjaWalkLeft = new TextureRegion(walkAnimate.getCurrentFrameLeft());
//Texture ninjaWalkRight = new Texture("badlogic.jpg");
//Texture ninjaWalkLeft = new Texture("badlogic.jpg");
walkRightImage = new Image(ninjaWalkRight);
walkLeftImage = new Image(ninjaWalkLeft);
float walkImageW = walkRightImage.getWidth();
float walkImageH = walkRightImage.getHeight();
float walkImageWidth = app.screenWidth*0.25f;
float walkImageHeight = walkImageWidth*(walkImageH/walkImageW);
walkRightImage.isTouchable();
walkLeftImage.isTouchable();
walkRightImage.setSize(walkImageWidth,walkImageHeight);
walkLeftImage.setSize(walkImageWidth,walkImageHeight);
walkRightImage.setPosition(stage.getWidth()/2-walkImageWidth/2,0);
walkLeftImage.setPosition(stage.getWidth()/2-walkImageWidth/2,0);
walkRightImage.addAction(moveBy(app.screenWidth*0.2f,0,1f));
stage.addActor(walkRightImage);
walkRightImage.addListener(new ActorGestureListener(){
public void pan(InputEvent event, float x, float y, float deltaX, float deltaY) {
holdStartImage.setVisible(true);
}
});
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//walkAnimate.update(deltaTime);
update(delta);
}
public void update(float deltaTime){
stage.act(deltaTime);
stage.draw();
app.batch.begin();
app.batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
stage.dispose();
}
}
WalkAnimate.java
public class WalkAnimate {
public MyGame app;
public Stage stage;
private Animation walkAnimationRight;
private Animation walkAnimationLeft;
private Texture walkSheetRight;
private Texture walkSheetLeft;
private TextureRegion[] walkFramesRight;
private TextureRegion[] walkFramesLeft;
private TextureRegion currentFrameRight;
private TextureRegion currentFrameLeft;
private float stateTime;
private Rectangle bound; //used for positioning and collision detection
private static final int FRAME_COLS_WALK = 3;
private static final int FRAME_ROWS_WALK= 2;
private float screenWidth = Gdx.graphics.getWidth();
private float screenHeight = Gdx.graphics.getHeight();
public float currentFrameWidth = (float)(screenHeight*0.15);
public float currentFrameHeight = (float)(screenHeight*0.15);
public float walkSheetWidth;
public float walkSheetHeight;
public WalkAnimate () {
walkSheetRight = new Texture("ninjaWalkRight.png");
walkSheetWidth = walkSheetRight.getWidth();
walkSheetHeight = walkSheetRight.getWidth();
TextureRegion[][] tmp = TextureRegion.split(walkSheetRight, (int) walkSheetRight.getWidth() / FRAME_COLS_WALK, (int) walkSheetRight.getHeight() / FRAME_ROWS_WALK);
walkFramesRight = new TextureRegion[FRAME_COLS_WALK * FRAME_ROWS_WALK];
int index = 0 ;
for (int i = 0; i < FRAME_ROWS_WALK; i++) {
for (int j = 0; j < FRAME_COLS_WALK; j++) {
walkFramesRight[index++] = tmp[i][j];
}
}
walkAnimationRight = new Animation(0.044f, walkFramesRight);
stateTime = 0f;
walkSheetLeft = new Texture("ninjaWalkLeft.png");
walkSheetWidth = walkSheetLeft.getWidth();
walkSheetHeight = walkSheetLeft.getWidth();
TextureRegion[][] tmp1 = TextureRegion.split(walkSheetLeft, (int) walkSheetRight.getWidth() / FRAME_COLS_WALK, (int)walkSheetLeft.getHeight() / FRAME_ROWS_WALK);
walkFramesLeft = new TextureRegion[FRAME_COLS_WALK * FRAME_ROWS_WALK];
int index1 = 0;
for (int i = 0; i < FRAME_ROWS_WALK; i++) {
for (int j = 0; j < FRAME_COLS_WALK; j++) {
walkFramesLeft[index1++] = tmp1 [i][j];
}
}
walkAnimationLeft = new Animation(0.044f, walkFramesLeft);
stateTime = 0f;
currentFrameRight = walkAnimationRight.getKeyFrame(stateTime, true);
currentFrameLeft = walkAnimationLeft.getKeyFrame(stateTime, true);
}
public Rectangle getBound(){
return bound;
}
public void update(float delta){
stateTime += delta;
}
public TextureRegion getCurrentFrameRight(){
return currentFrameRight;
}
public TextureRegion getCurrentFrameLeft(){
return currentFrameLeft;
}
}
Like the earlier comment by Eames. Just by glancing at your code, I can say that you should begin by moving
walkAnimate.update(delta);
To the render section. The show section only runs once (when the class is started). Your walkAnimate.update(delta) should be running constantly to change the frame when is time. Otherwise it would only update once.
You can also do it the way I do it.
The class where you are going to use it. The 16 represents the number of images. The 1F means it is done in one second.
public void show() {
texture = new Texture("item/coin_animation.png");
animation = new Animation(new TextureRegion(texture), 16, 1f);
}
public void render(float delta) {
animation.update(delta);
}
Animation Class
public Array<TextureRegion> frames;
private float maxFrameTime;
private float currentFrameTime;
private int frameCount;
private int frame;
private static TextureRegion textureRegion;
public Animation(TextureRegion region, int frameCount, float cycleTime){
textureRegion = region;
frames = new Array<TextureRegion>();
int frameWidth = textureRegion.getRegionWidth() / frameCount;
for (int i = 0; i < frameCount; i++){
frames.add(new TextureRegion(textureRegion, i * frameWidth, 0, frameWidth, textureRegion.getRegionHeight()));
}
this.frameCount = frameCount;
maxFrameTime = cycleTime / frameCount;
frame = 0;
}
public void update(float delta){
currentFrameTime += delta;
if (currentFrameTime > maxFrameTime){
frame++;
currentFrameTime = 0;
}
if (frame >= frameCount)
frame = 0;
}
public TextureRegion getFrame(){
return frames.get(frame);
}
public void restart(){
frame = 0;
currentFrameTime = 0;
}
I will answer my question, I've tried using a separate stage for the animation and it works. I also used inputmultiplexor to set two stages both as inputprocessors. I know that this is not the best solution and I am open for more proper solutions. This is open for editing.
I have been following this tutorial to learn how to implement Pinch Zoom and Pan capabilities into an app I am developing.
https://www.youtube.com/watch?v=BY8hLhu50po&list=PL9jCwTXYWjDJjDE_JxRozYGKGt8gbUXg7
Basically, he loads an image from the gallery and displays it in an image view that supports Zoom/Pan functionality.
I would like to preload an image into the image view rather than select one from the gallery. Ideally, I'd like to load an image from drawable.
The tutorial's app had a lot of extra features that I am trying to weed out.
First it opens a gallery. Then you select an image to display, and it displays it as a thumbnail inside of mImageView.
On Long Click, it hides mImageView and displays a maximized image inside of a second Image View. (MPinchZoomImageView)
At this point, the image supports zoom and pan functionality.
If I could, I'd like to skip the gallery and the first Image View and only use the PinchZoom Image View. I'm not sure how I'd do that.
Code:
ImageViewMainActivity
public class ImageViewMainActivity extends AppCompatActivity {
ImageView mImageView;
PinchZoomImageView mPinchZoomImageView;
private Uri mImageUri;
private static final int REQUEST_OPEN_RESULT_CODE = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_view_main);
mImageView = (ImageView) findViewById(R.id.imageView);
mPinchZoomImageView = (PinchZoomImageView) findViewById(R.id.pinchZoomImageView);
mImageView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
pinchZoomPan();
return true;
}
});
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_OPEN_RESULT_CODE); // pass it a context of 0
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
View decorView = getWindow().getDecorView();
if(hasFocus) {
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
);
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if(requestCode == REQUEST_OPEN_RESULT_CODE && resultCode == RESULT_OK) {
if(resultData != null) {
mImageUri = resultData.getData();
Glide.with(this)
.load(mImageUri)
.into(mImageView);
}
}
}
private void pinchZoomPan() {
mPinchZoomImageView.setImageUri(mImageUri);
mImageView.setAlpha(0.f); // set mImageView invisible.
mPinchZoomImageView.setVisibility(View.VISIBLE);
}
}
PinchZoomImageView
public class PinchZoomImageView extends ImageView {
private Bitmap mBitmap;
private int mImageWidth;
private int mImageHeight;
private final static float mMinZoom = 1.f;
private final static float mMaxZoom = 4.f;
private float mScaleFactor = 1.f;
private ScaleGestureDetector mScaleGestureDetector;
private final static int NONE = 0;
private final static int PAN = 1;
private final static int ZOOM = 2;
private int mEventState;
private float mStartX = 0;
private float mStartY = 0;
private float mTranslateX = 0;
private float mTranslateY = 0;
private float mPreviousTranslateX = 0;
private float mPreviousTranslateY = 0;
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(mMinZoom, Math.min(mMaxZoom, mScaleFactor));
// invalidate();
// requestLayout();
return super.onScale(detector);
}
}
public PinchZoomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
mScaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mEventState = PAN;
mStartX = event.getX() - mPreviousTranslateX;
mStartY = event.getY() - mPreviousTranslateY;
break;
case MotionEvent.ACTION_UP:
mEventState = NONE;
mPreviousTranslateX = mTranslateX;
mPreviousTranslateY = mTranslateY;
break;
case MotionEvent.ACTION_MOVE:
mTranslateX = event.getX() - mStartX;
mTranslateY = event.getY() - mStartY;
break;
case MotionEvent.ACTION_POINTER_DOWN:
mEventState = ZOOM;
break;
}
mScaleGestureDetector.onTouchEvent(event);
if((mEventState == PAN && mScaleFactor != mMinZoom) || mEventState == ZOOM) { // called under the condition that window is zoomed i
invalidate();
requestLayout();
}
return true;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int imageWidth = MeasureSpec.getSize(widthMeasureSpec);
int imageHeight = MeasureSpec.getSize(heightMeasureSpec);
int scaledWidth = Math.round(mImageWidth * mScaleFactor);
int scaledHeight = Math.round(mImageHeight * mScaleFactor);
setMeasuredDimension(
Math.min(imageWidth, scaledWidth),
Math.min(imageHeight, scaledHeight)
);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.scale(mScaleFactor, mScaleFactor);
// canvas.scale(mScaleFactor, mScaleFactor, mScaleGestureDetector.getFocusX(), mScaleGestureDetector.getFocusY());
if((mTranslateX * -1) < 0) {
mTranslateX = 0;
} else if ((mTranslateX * -1) > mImageWidth * mScaleFactor - getWidth()) {
mTranslateX = (mImageWidth * mScaleFactor - getWidth()) * -1;
}
if((mTranslateY * -1) < 0) {
mTranslateY = 0;
} else if ((mTranslateY * -1) > mImageHeight * mScaleFactor - getHeight()) {
mTranslateY = (mImageHeight * mScaleFactor - getHeight()) * -1;
}
canvas.translate(mTranslateX/mScaleFactor, mTranslateY/mScaleFactor);
canvas.drawBitmap(mBitmap, 0, 0, null);
canvas.restore();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
public void setImageUri(Uri uri) {
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContext().getContentResolver(), uri);
float aspecRatio = (float) bitmap.getHeight() / (float) bitmap.getWidth();
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
mImageWidth = displayMetrics.widthPixels;
mImageHeight = Math.round(mImageWidth * aspecRatio);
mBitmap = Bitmap.createScaledBitmap(bitmap, mImageWidth, mImageHeight, false);
invalidate();
requestLayout();
} catch (IOException e) {
e.printStackTrace();
}
}
}
XML
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="mobapptut.com.imageviewer.ImageViewMainActivity">
<ImageView
android:layout_width="200dp"
android:layout_height="150dp"
android:id="#+id/imageView"
android:layout_centerInParent="true" />
<mobapptut.com.imageviewer.PinchZoomImageView
android:visibility="invisible"
android:id="#+id/pinchZoomImageView"
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Thank You
i use VLCJ 3.0.1, Plattform 3.5.2 and JNA 3.5.2 under Ubuntu 14.10.
I would like to create a simple Vlcj Mediaplayer with equalizer.
The problem is, i can play the *.mp3, but when i enable the equalizer and change the gain from any band, i can hear that gain from the band up to max high. I couldn't change it to lower gain. No effect from the Slider. Only when i change the gain to max or min, the effekt is off.
Here is the Code only for testing:
FesterTest.java
public static void main(String[] args) {
Canvas m_surface;
EmbeddedMediaPlayer m_mediaPlayer;
Equalizer m_equalizer;
Native.loadLibrary(RuntimeUtil.getLibVlcLibraryName(), LibVlc.class);
m_surface = new Canvas();
m_surface.setBackground(Color.black);
m_surface.setBounds(0, 0, 1024, 600);
MediaPlayerFactory mediaPlayerFactory = new MediaPlayerFactory();
m_mediaPlayer = mediaPlayerFactory.newEmbeddedMediaPlayer();
m_mediaPlayer.setVideoSurface(mediaPlayerFactory.newVideoSurface(m_surface));
System.out.println(mediaPlayerFactory.isEqualizerAvailable());
m_equalizer = mediaPlayerFactory.newEqualizer();
System.out.println(LibVlcConst.MAX_GAIN);
System.out.println(LibVlcConst.MIN_GAIN);
System.out.println(LibVlcConst.MIN_VOLUME);
System.out.println(LibVlcConst.MAX_VOLUME);
JFrame f = new JFrame();
JPanel p = new JPanel();
f.add(p);
p.add(m_surface);
f.setVisible(true);
m_mediaPlayer.playMedia("/home/patrick/Dev/content/068-becky_g_-_shower.mp3");
EqualizerFrame frame = new EqualizerFrame(mediaPlayerFactory.getEqualizerBandFrequencies(), mediaPlayerFactory.getEqualizerPresetNames(), mediaPlayerFactory, m_mediaPlayer, m_equalizer);
frame.pack();
frame.setVisible(true);
}
and EqualizerFrame:
public class EqualizerFrame extends JFrame implements ChangeListener, ActionListener, ItemListener {
private static final String BAND_INDEX_PROPERTY = "equalizerBandIndex";
private final String dbFormat = "%.2fdB";
private final MediaPlayerFactory mediaPlayerFactory;
private final MediaPlayer mediaPlayer;
private final Equalizer equalizer;
private final SliderControl preampControl;
private final SliderControl[] bandControls;
private final JToggleButton enableButton;
private final JComboBox presetComboBox;
#SuppressWarnings({ "unchecked", "rawtypes" })
public EqualizerFrame(List<Float> list, List<String> presets, MediaPlayerFactory mediaPlayerFactory, MediaPlayer mediaPlayer, Equalizer equalizer) {
super("Equalizer");
this.mediaPlayerFactory = mediaPlayerFactory;
this.mediaPlayer = mediaPlayer;
this.equalizer = equalizer;
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
JPanel contentPane = new JPanel();
contentPane.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
contentPane.setLayout(new BorderLayout(0, 4));
JPanel bandsPane = new JPanel();
bandsPane.setLayout(new GridLayout(1, 1 + list.size(), 2, 0));
preampControl = new SliderControl("Preamp", (int)LibVlcConst.MIN_GAIN, (int)LibVlcConst.MAX_GAIN, 0, dbFormat);
preampControl.getSlider().addChangeListener(this);
bandsPane.add(preampControl);
bandControls = new SliderControl[list.size()];
for(int i = 0; i < list.size(); i++) {
bandControls[i] = new SliderControl(formatFrequency(list.get(i)), (int)LibVlcConst.MIN_GAIN, (int)LibVlcConst.MAX_GAIN, 0, dbFormat);
bandControls[i].getSlider().putClientProperty(BAND_INDEX_PROPERTY, i);
bandControls[i].getSlider().addChangeListener(this);
bandsPane.add(bandControls[i]);
}
contentPane.add(bandsPane, BorderLayout.CENTER);
JPanel controlsPane = new JPanel();
controlsPane.setLayout(new BoxLayout(controlsPane, BoxLayout.X_AXIS));
enableButton = new JToggleButton("Enable");
enableButton.setMnemonic('e');
controlsPane.add(enableButton);
controlsPane.add(Box.createHorizontalGlue());
JLabel presetLabel = new JLabel("Preset:");
presetLabel.setDisplayedMnemonic('p');
controlsPane.add(presetLabel);
presetComboBox = new JComboBox();
presetLabel.setLabelFor(presetComboBox);
DefaultComboBoxModel presetModel = (DefaultComboBoxModel)presetComboBox.getModel();
presetModel.addElement(null);
for(String presetName : presets) {
presetModel.addElement(presetName);
}
presetComboBox.setRenderer(new DefaultListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if(value != null) {
label.setText(String.valueOf(value));
}
else {
label.setText("--Select--");
}
return label;
}
});
controlsPane.add(presetComboBox);
contentPane.add(controlsPane, BorderLayout.SOUTH);
setContentPane(contentPane);
enableButton.addActionListener(this);
presetComboBox.addItemListener(this);
}
private String formatFrequency(float hz) {
if(hz < 1000.0f) {
return String.format("%.0f Hz", hz);
}
else {
return String.format("%.0f kHz", hz / 1000f);
}
}
#Override
public final void actionPerformed(ActionEvent e) {
boolean enable = enableButton.isSelected();
if(!enable) {
presetComboBox.setSelectedItem(null);
}
mediaPlayer.setEqualizer(enable ? equalizer : null);
}
#Override
public void stateChanged(ChangeEvent e) {
if(e.getSource() instanceof JSlider) {
JSlider slider = (JSlider)e.getSource();
Integer index = (Integer)slider.getClientProperty(BAND_INDEX_PROPERTY);
int value = slider.getValue();
// Band...
if(index != null) {
equalizer.setAmp(index, (value / 100f));
}
// Preamp...
else {
equalizer.setPreamp(value / 100f);
}
if(!applyingPreset) {
presetComboBox.setSelectedItem(null);
}
}
}
boolean applyingPreset;
#Override
public final void itemStateChanged(ItemEvent e) {
String presetName = (String)presetComboBox.getSelectedItem();
if(e.getStateChange() == ItemEvent.SELECTED) {
if(presetName != null) {
Equalizer presetEqualizer = mediaPlayerFactory.newEqualizer(presetName);
if(presetEqualizer != null) {
applyingPreset = true;
preampControl.getSlider().setValue((int)(presetEqualizer.getPreamp() * 100f)); // FIXME
float[] amps = presetEqualizer.getAmps();
for(int i = 0; i < amps.length; i++) {
bandControls[i].getSlider().setValue((int)(amps[i] * 100f));
}
applyingPreset = false;
}
}
}
}
}
I don't know what can i do an hope anybody can help me!!!!
Thanks
Patrick
searchlist = new FileList(list_vector,ind,j);//a custom list field
searchlist.setRowHeight(40);
searchListManager = new VerticalFieldManager(
Manager.VERTICAL_SCROLL |Manager.VERTICAL_SCROLLBAR)
searchListManager.add(searchlist);
objManager.add(searchListManager);
HomeScreen1.this.add(header_manager);
HomeScreen1.this.add(objManager);
//after few lines of code
button_manager.add(Previous);
button_manager.add(Next);
objManager.add(button_manager);
now my problem is when i scroll over the list field then next n previous are not visible
but when i press key up n down then they get only visible
what to do???????????
Try to implement searchListManager as a scrollable VerticalManager with fixed size (Display height - header manager height - button manager height)
UPDATE Code to try:
class Scr extends MainScreen implements ListFieldCallback {
int DISPLAY_WIDTH = Display.getWidth();
int DISPLAY_HEIGHT = Display.getHeight();
Vector mItems = new Vector();
ListField mListField = new ListField();
SizedVFM mListManager = new SizedVFM(DISPLAY_WIDTH, DISPLAY_HEIGHT - 50);
ButtonField mPrevButtonField = new ButtonField("Previous",
ButtonField.CONSUME_CLICK);
ButtonField mNextButtonField = new ButtonField("Next",
ButtonField.CONSUME_CLICK);
HorizontalFieldManager mButtonsManager = new HorizontalFieldManager(
FIELD_HCENTER);
public Scr() {
for (int i = 1; i < 31; i++) {
mItems.addElement("item " + String.valueOf(i));
}
mListField.setCallback(this);
mListField.setSize(30);
add(mListManager);
mListManager.add(mListField);
mPrevButtonField.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
Dialog.inform("Previouse pressed");
}
});
mNextButtonField.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
Dialog.inform("Next pressed");
}
});
mButtonsManager.add(mPrevButtonField);
mButtonsManager.add(mNextButtonField);
add(mButtonsManager);
}
public void drawListRow(ListField field, Graphics g, int i, int y, int w) {
// Draw the text.
String text = (String) get(field, i);
g.drawText(text, 0, y, 0, w);
}
public Object get(ListField listField, int index) {
return mItems.elementAt(index);
}
public int getPreferredWidth(ListField listField) {
return DISPLAY_WIDTH;
}
public int indexOfList(ListField listField, String prefix, int start) {
return 0;
}
}
class SizedVFM extends VerticalFieldManager {
int mWidth;
int mHeight;
public SizedVFM(int width, int height) {
super(VERTICAL_SCROLL | VERTICAL_SCROLLBAR);
mWidth = width;
mHeight = height;
}
public int getPreferredHeight() {
return mHeight;
}
public int getPreferredWidth() {
return mWidth;
}
public void setHeight(int height) {
mHeight = height;
}
protected void sublayout(int maxWidth, int maxHeight) {
super.sublayout(maxWidth, maxHeight);
setExtent(getPreferredWidth(), getPreferredHeight());
}
}
And the result should be like that:
alt text http://img215.imageshack.us/img215/1402/9530list.jpg