I am asking this today for the purpose of comparing the two.
What I am trying to do is make a bot that is able to click the space-bar at a certain time.
Anyway, let me show what I am trying to compare.
Robot robot = new Robot();
This will be out Robot. (java.awt.Robot;)
My question is, which is faster for the purpose of try to constantly read one (or more) pixels on the screen at once?
My current (in progress) programs that are the same with the exception of one using
Robot.getPixelColor(int, int)
and the other uses
BufferedImage image Robot.createScreenCapture(Rectangle)
imaage.getRGB
These are being run at many times a second, and I am simply trying to figure out which is more consistently faster. They both seem random at the speed at which they operate, so any advice is appreciated.
In case you wish for an example to put these into, here is the setup for it.
Here is the example using Robot.getPixelColor(int, int)
Robot robot = new Robot();
Rectangle rect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
Point prompt = new Point(913, 506, robot);
BufferedImage screenImage = robot.createScreenCapture(rect);
for(int i = 0; i < 360; i++) {
zones.add(new Point(radius * Math.cos(Math.toRadians(i)) + centerX, radius * Math.sin(Math.toRadians(i)) + centerY, robot));
}
System.out.println("Resolution: " + width + " * " + height/* + " " + robot.getPixelColor(20, 20)/* + " " + screenImage.getRGB((int) prompt.getX(), (int) prompt.getY())*/);
while(true) {
tempZone = null;
while(prompt.hasColor(promptColor)) {
System.out.println("Found prompt, trying to find zone.");
int count = 0;
for(int i = 0; i < zones.size(); i++) {
if(zones.get(i).hasColor(zoneColor)){
tempZone = zones.get(i);
break;
}
}
if(tempZone != null) {
System.out.println("Found zone at: " + tempZone.getX() + ", " + tempZone.getY() + ". Looking for ticker...");
tempZone.whenHasColor(zoneColor);
}else{
System.out.println("Unable to find zone.");
}
while(prompt.hasColor(promptColor)) {}
}
}
With its Point Class:
public class Point {
private double x;
private double y;
private final Robot robot;
public Point(double x, double y, Robot rob) {
this.x = x;
this.y = y;
this.robot = rob;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public boolean hasColor(Color color) {
return (robot.getPixelColor((int )x, (int) y).equals(color));
}
public void whenHasColor(Color color) throws IOException {
Rectangle rect = new Rectangle(889 + (int) x, 449 + (int) y, 1, 1);
while(hasColor(color)) {
//Nothing
}
BufferedImage cap = robot.createScreenCapture(rect);
if(Math.abs(Math.abs(cap.getRGB(0, 0)) - 15657182) < 350000) {
System.out.println("Missed Zone, failure.");
}else {
robot.keyPress(Main.promptKey);
System.out.println("Found ticker pressing key. ");
}
}
}
For anyone wondering where could something this specific come from, I am using a modified version of this website as my medium for making this bot.
Now for the example with using Robot.createScreenCapture(Rectangle) and BufferedImage.getRGB(int, int)
Robot robot = new Robot();
Rectangle rect = new Rectangle(889, 449, 149, 149);
Point prompt = new Point(24, 57, robot);
String format = "png";
String fileName = "fullDebug1." + format;
BufferedImage screenImage = robot.createScreenCapture(rect);
for(int i = 0; i < 360; i++) {
zones.add(new Point(radius * Math.cos(Math.toRadians(i)) + centerX, radius * Math.sin(Math.toRadians(i)) + centerY, robot));
}
System.out.println("Resolution: " + width + " * " + height/* + " " + screenImage.getRGB((int) prompt.getX(), (int) prompt.getY())*/);
while(true) {
tempZone = null;
int zoneFinal = 0;
while(prompt.hasColor(promptColor)) {
System.out.println("Found prompt, trying to find zone.");
BufferedImage zoneImage = robot.createScreenCapture(rect);
int count = 0;
for(int i = 0; i < zones.size(); i++) {
if(zones.get(i).hasColorZones(zoneColor, zoneImage)){
tempZone = zones.get(i);
break;
}
}
if(tempZone != null) {
System.out.println("Found zone at: " + tempZone.getX() + ", " + tempZone.getY() + ". Looking for ticker...");
tempZone.whenHasColor(zoneColor);
}else{
System.out.println("Unable to find zone.");
}
while(prompt.hasColor(promptColor)) {}
}
and its Point class:
public class Point {
private double x;
private double y;
private final Robot robot;
public Point(double x, double y, Robot rob) {
this.x = x;
this.y = y;
this.robot = rob;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public boolean hasColor(int color) {
Rectangle rect = new Rectangle(889, 449, 149, 149);
BufferedImage screenImage = robot.createScreenCapture(rect);
return (Math.abs(screenImage.getRGB((int) x, (int) y)) == Math.abs(color))
}
public boolean hasColorPixel(int color, Rectangle rect) throws IOException {
BufferedImage cap = robot.createScreenCapture(rect);
return Math.abs(cap.getRGB(0, 0)) == Math.abs(color);
}
public boolean hasColorZones(int color, BufferedImage screenImage) {
return Math.abs(screenImage.getRGB((int) x, (int) y)) == Math.abs(color);
}
public boolean hasColorList(int[] colors, BufferedImage screenImage) {
for(int i : colors) {
if(Math.abs(screenImage.getRGB((int) x, (int) y)) == Math.abs(i)) {
return true;
}
}
return false;
}
public void whenHasColor(int color) throws IOException {
Rectangle rect = new Rectangle(889 + (int) x, 449 + (int) y, 1, 1);
while(hasColorPixel(color, rect)) {
//Nothing
}
BufferedImage cap = robot.createScreenCapture(rect);
if(Math.abs(Math.abs(cap.getRGB(0, 0)) - 15657182) < 350000) {
System.out.println("Missed Zone, failure.");
}else {
robot.keyPress(Main.promptKey);
System.out.println("Found ticker pressing key. ");
}
}
}
So my question is, which will run faster? Sometimes the latter option (which I more thoroughly debugged) gets 10 in a row, but then will fail randomly too. At first I thought it was that my laptop could not handle it, but upon running it on my desktop I had the same results. I would rather not have the bot be inconsistent with working properly or not.
I do hope I was able to give enough information for anyone to be able to help me. If there is anything I need to add or change, please tell me! This is my first question and I would like to learn to be better with this site.
Another apologies to if 1) the pixels are not the same on your screen, therefore you would not be able to run this to simulate my situation, and 2) if this post was long. I wanted to make a good "Minimal, Complete, and Verifiable example," but I wasn't sure how to shorten it any more.
EDIT
To make the code put in more relevant, as Ben pointed out, I could have cut it. But I thought of something that makes it more useful.
What can be done to that code to optimize it to run faster, so the bot will be 100% consistently working, or at least closer to 100% than it is now (~60%)
Related
I am trying to make a Model from a set tiles with libgdx's MeshPartBuilder and I noticed that when I'm generating the Model the memory usage goes up by ~200MB and doesn't come down, but when I'm doing a Thread.sleep(), It stays at a normal level.
Here is the method I call to prepare the model.
public void updateModel() {
System.out.println("Updating model for chuck " + chunkUUID.toString());
ModelBuilder mdlBuilder = new ModelBuilder();
mdlBuilder.begin();
MeshPartBuilder meshBuilder = null;
for(int y = 0; y < size[1]; y++) {
for(int x = 0; x < size[0]; x++) {
if(tiles[x][y] == null) {
System.out.println(x+":"+y+" is null");
continue;
}
tiles[x][y].getModel(chunkUUID.toString() + "." + x + ":" + y, this, mdlBuilder, meshBuilder, modelOffset[0] + x, modelOffset[1],
modelOffset[2] + y);
}
System.out.println(y);
try {
Thread.sleep(250L);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
model = mdlBuilder.end();
iModel = new ModelInstance(model);
}
And here is the getModel method that is called in the loop.
public void getModel(String partId, Chunk chunk, ModelBuilder mdlBuilder, MeshPartBuilder mshBuilder, float offsetX, float offsetY, float offsetZ) {
int counter = 0;
mshBuilder = mdlBuilder.part(partId+"."+counter, GL20.GL_TRIANGLES, vrtxAttr, material);
mshBuilder.rect(
offsetX, offsetY+cornersHeight[0], offsetZ,
offsetX, offsetY+cornersHeight[1], offsetZ+1F,
offsetX+1F, offsetY+cornersHeight[2], offsetZ+1F,
offsetX+1F, offsetY+cornersHeight[3], offsetZ,
0,1,0);
}
Changing the material doesn't seem to help and if I reduce the sleep length to 10ms, the memory usage goes up again.
However replacing the Thread.sleep part with System.gc() helps but doesn't have the same effect (20-40MB more).
Does anyone know why this is hapenning and if there is a solution ?,
That sounds extremely philosophical doesn't it?
Anyways, I have a rather complex problem.
My main_activity class gathers all of the Zombies like so:
//Testing Runnable (used to compare the first zombie with the player)
private Runnable updateLocations = new Runnable(){
#Override
public void run(){
try {
while(true) {
image_player.getLocationInWindow(pLoc);
Zombie zoms = zombieCollection.next();
if(!zoms.equals(null)){
zoms.getZombieImage().getLocationInWindow(zLoc);
}
System.out.println("Zombie: x = " + zLoc[0] + "; y = " + zLoc[1]);
System.out.println("Player: x = " + pLoc[0] + "; y = " + pLoc[1]);
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
My zombie class gathers information like so:
public class Zombie{
float X, Y;
int Width, Height;
Direction fdirc;
ImageView zImage;
Player player;
boolean dead;
int[] zLoc;
public Zombie(ImageView zImage, Player player){
zLoc = new int[2];
zImage.getLocationOnScreen(zLoc);
this.zImage = zImage;
this.X = zLoc[0];
this.Y = zLoc[1];
this.Width = zImage.getWidth();
this.Height = zImage.getHeight();
this.fdirc = Direction.EAST;
this.player = player;
this.dead = false;
Thread thread = new Thread(this.startZombieChase);
thread.start();
}
public ImageView getZombieImage(){
return zImage;
}
private Runnable startZombieChase = new Runnable() {
#Override
public void run() {
try {
while(!dead) {
moveTowardsPlayer();
Thread.sleep(10);
updateZombie.sendEmptyMessage(0);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
private Handler updateZombie = new Handler(Looper.getMainLooper()) {
public void handleMessage(android.os.Message msg) {
/** Because the zombie should always be on top! **/
zImage.getLocationOnScreen(zLoc);
zImage.bringToFront();
zImage.setX(X);
zImage.setY(Y);
}
};
private void moveTowardsPlayer(){
int player_x = player.getPosition()[0];
int player_y = player.getPosition()[1];
l("Where is it in zombie class : player - " + player_x + " " + player_y + "zombie - " + X + " " + Y);
float compareX = player_x - (int)X;
float compareY = player_y - (int)Y;
// Y is closer, so we're moving horizontally.
if(Math.abs(compareX) < Math.abs(compareY)){
//Moving North
if(player_y > Y){
Y+=1;
}
//Moving South
else if(player_y < Y){
Y-=1;
}
}
// X is closer, so we're moving vertically.
else{
//Moving East
if(player_x > X){
X+=1;
}
//Moving West
else if(player_x < X){
X-=1;
}
}
}
public void l(Object string){
System.out.println("Log - " + string);
}
}
The problem I'm having is that it will move relative to a number (So, it does move relative to something) but, not the correct thing.
logcat tells me this:
Where is it in zombie class: player - 750 451 zombie - 750 451
Where it is in main_activity: player - 750 451 zombie - 792 619
Could anyone help me understand what I'm doing wrong?
The entire project is located here.
Zombies that moves away from the Brainz, must be an ill Zombie. We can't have that, now can we?
To have a function that moves the Zombie towards the non-zombie, you use a function, but that function makes use of variables that are not arguments, and there fore hard to find out where they come from. I'd go with something like this: (this is a bit verbose, but clearly shows what is happening)
/*
* Function to update the position of the Zombie, aka walk to the Player.
* #player_pos - Where's the Brainz at?
* #zombie_pos - Where am I?
* Might want to build it overloaded with an option for the speed.
*
* #return - We return the new Zombie pos.
*/
private double [] moveTowardsPlayer(double [] player_pos, double [] zombie_pos) {
// To make sure we don't override the old position, we copy values. (Java stuff)
double [] player_pos_old = player_pos.clone();
double [] zombie_pos_old = zombie_pos.clone();
// Let's get the new X pos for the Zombie
double left_or_right = player_pos_old[0] - zombie_pos_old[0]; // pos number is right, neg is left
double zombie_pos_new_x;
if (left_or_right > 0) { // Right
zombie_pos_new_x = player_pos_old[0] + zombie_speed;
} else { // Left - this way we make sure we are always getting nearer to the Brainz.
zombie_pos_new_x = player_pos_old[0] - zombie_speed;
}
// TODO: do the same for the Y pos.
// Bring it together
double [] zombie_pos_new = {zombie_pos_new_x, zombie_pos_new_y};
// One step closer to the Brainz!
return zombie_pos_new;
}
And use like:
double [] zombie_pos = moveTowardsPlayer([2, 2], [5, 4]);
this.X = zombie_pos[0]; // I'd advice to keep these together as one var.
this.Y = zombie_pos[1]; // But it's your game.
And then figure out when the Zombie gets the Brainz (or the Bullet)
this here is not correct:
if(!zoms.equals(null)){
if you are trying to check that zoms is not pointing to a null reference then do
if(zoms != null ){
This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 8 years ago.
I am trying to create a program where you can change a fans speed, color, radius, and whether you can turn the fan on or off, I have the fan class working fine, except for the toString() method, for some reason when I set some values in the test program, it just defaults to the regular values, any help is accepted.
Thank you.
public class Fan {
final int SLOW = 1;
final int MEDIUM = 2;
final int FAST = 3;
public int speed = SLOW;
public boolean on = false;
public double radius = 5;
public String color = new String("blue");
//fan on
boolean fanOn() {
on = true;
return on;
}
//fan off
boolean fanOff() {
on = false;
return on;
}
//sets fan speed
String setSpeed(String speed) {
if (speed == "SLOW"){
speed = String.valueOf(SLOW);
} else if (speed == "MEDIUM") {
speed = String.valueOf(MEDIUM);
} else if (speed == "FAST") {
speed = String.valueOf(FAST);
} else {
speed = "Please enter 'SLOW', 'MEDIUM' or 'FAST' to change the speed of the fan.";
}
return speed;
}
//sets custom radius
double setRadius(double rad) {
rad = radius;
return rad;
}
//sets custom color
String setColor(String colorType) {
return colorType;
}
//toString() method
public String toString() {
return ("Speed: " + speed + "\nRadius: " + radius + "\nColor: " + "\nOn: " + on);
}
}
//test program
public class TestFan {
public static void main(String[] args) {
Fan fan1 = new Fan();
fan1.setColor("green");
fan1.setSpeed("FAST");
fan1.setRadius(3.5);
fan1.fanOff();
System.out.println(fan1.toString());
}
}
This just outputs:
Speed: 1
Radius: 5.0
Color:
On: false
public class Fan {
final int SLOW = 1;
final int MEDIUM = 2;
final int FAST = 3;
public int speed = SLOW;
public boolean on = false;
public double radius = 5;
public String color = new String("blue");
//fan on
void fanOn() {
on = true;
return on;
}
//fan off
void fanOff() {
on = false;
return on;
}
//sets fan speed
void setSpeed(String speed) {
this.speed=speed;
}
//sets custom radius
double setRadius(double rad) {
rad = radius;
return rad;
}
//sets custom color
void setColor(String colorType) {
color = colorType;
}
//toString() method
public String toString() {
return ("Speed: " + speed + "\nRadius: " + radius + "\nColor: " + "\nOn: " + on);
}
}
//test program
public class TestFan {
public static void main(String[] args) {
Fan fan1 = new Fan();
fan1.setColor("green");
fan1.setSpeed("FAST");
fan1.setRadius(3.5);
fan1.fanOff();
System.out.println(fan1.toString());
}
}
The problem asks for an acm graphics program that reads a txt file like this:
R
FUN
SALES
RECEIPT
MERE#FARM
DOVE###RAIL
MORE#####DRAW
HARD###TIED
LION#SAND
EVENING
EVADE
ARE
D
and makes a crossword puzzle, with blank squares on letters, black squares on '#', and nothing on empty spaces. The problem also asks that "if the square is at the beginning of a word running across, down, or both, the square should contain a number that is assigned sequentially through the puzzle."
I have the square drawing working, but I'm stuck on drawing the numbers correctly. There is something wrong with how I'm detecting null space and black squares. Can someone tell me what I'm doing wrong, please?
Here is the code:
import acm.program.*;
import java.io.*;
import java.util.*;
import acm.graphics.*;
import java.awt.*;
public class Crossword extends GraphicsProgram {
public void run() {
String fileName = "crosswordfile.txt";
makeCrosswordPuzzle(fileName);
}
private static final int sqCon = 15; // constant for square x and y dimensions
private int y = 0;
public void makeCrosswordPuzzle(String fileName) {
BufferedReader rd;
int y = 0; // y value for the square being added during that loop. increments by sqCon after every line
int wordNumber = 1; // variable for numbers added to certain boxes. increments every time the program adds a number
try {
rd = new BufferedReader(new FileReader(fileName));
String line = rd.readLine(); //reads one line of the text document at a time and makes it a string
while (line != null) {
int x = 0;
for (int i = 0; i < line.length(); i++) {
char lineChar = line.charAt(i);// the character being examined for each loop
GRect whiteSq = new GRect(sqCon,sqCon); //GRect for blank squares
GRect blackSq = new GRect(sqCon,sqCon);//GRect for black squares
blackSq.setFilled(true);
blackSq.setFillColor(Color.BLACK);
if (lineChar == '#'){
add (blackSq,x,y);
}
if (Character.isLetter(lineChar)) {
add (whiteSq, x, y);
// if the element above or to the left of the current focus is null or blackSq, place the number and then increment wordNumber
GObject above = getElementAt(x+sqCon/2,y-sqCon/2);
GObject left = getElementAt(x-sqCon/2, y+sqCon/2);
GLabel wordNumberLabel = new GLabel(Integer.toString(wordNumber));
if (above == null || left == null || above == blackSq || left == blackSq) {
add(wordNumberLabel,x,y+sqCon);
wordNumber++;
}
}
x += sqCon;
}
line = rd.readLine();
y += sqCon;
}
rd.close();
}
catch (IOException e) {
throw new ErrorException(e);
}
}
}
Edited to add:
I copied your code over to my Eclipse and ran it. Here's the result.
You did fine on the upper half, but you missed the down numbers on the lower half.
Here's the same code, reformatted so it's easier to read.
import java.awt.Color;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import acm.graphics.GLabel;
import acm.graphics.GObject;
import acm.graphics.GRect;
import acm.program.GraphicsProgram;
import acm.util.ErrorException;
public class Crossword extends GraphicsProgram {
private static final long serialVersionUID = -7971434624427958742L;
public void run() {
// String fileName = "crosswordfile.txt";
String fileName = "C:/Eclipse/eclipse-4.2-work/com.ggl.testing/crosswordfile.txt";
makeCrosswordPuzzle(fileName);
}
private static final int sqCon = 15; // constant for square x and y
// dimensions
private int y = 0;
public void makeCrosswordPuzzle(String fileName) {
BufferedReader rd;
int y = 0; // y value for the square being added during that loop.
// increments by sqCon after every line
int wordNumber = 1; // variable for numbers added to certain boxes.
// increments every time the program adds a number
try {
rd = new BufferedReader(new FileReader(fileName));
String line = rd.readLine(); // reads one line of the text document
// at a time and makes it a string
while (line != null) {
int x = 0;
for (int i = 0; i < line.length(); i++) {
char lineChar = line.charAt(i);// the character being
// examined for each loop
GRect whiteSq = new GRect(sqCon, sqCon); // GRect for blank
// squares
GRect blackSq = new GRect(sqCon, sqCon);// GRect for black
// squares
blackSq.setFilled(true);
blackSq.setFillColor(Color.BLACK);
if (lineChar == '#') {
add(blackSq, x, y);
}
if (Character.isLetter(lineChar)) {
add(whiteSq, x, y);
// if the element above or to the left of the current
// focus is null or blackSq, place the number and then
// increment wordNumber
GObject above = getElementAt(x + sqCon / 2, y - sqCon
/ 2);
GObject left = getElementAt(x - sqCon / 2, y + sqCon
/ 2);
GLabel wordNumberLabel = new GLabel(
Integer.toString(wordNumber));
if (above == null || left == null || above == blackSq
|| left == blackSq) {
add(wordNumberLabel, x, y + sqCon);
wordNumber++;
}
}
x += sqCon;
}
line = rd.readLine();
y += sqCon;
}
rd.close();
} catch (IOException e) {
throw new ErrorException(e);
}
}
}
I followed the advice of my own comment. I created the crossword puzzle answer, numbered the crossword puzzle answer, and finally drew the crossword puzzle answer.
Here's the applet result:
I kept a List of crossword puzzle cells. That way, I could determine the length and the width of the puzzle by the number of characters on a row and the number of rows of the input text file. I didn't have to hard code the dimensions.
For each crossword cell, I kept track of whether or not it was a letter, and whether or not it was a dark space.
When determining where to put the numbers, I followed 2 rules.
An across number is placed where the cell left of the cell is empty or dark, and there are three or more letters across.
A down number is placed where the cell above the cell is empty or dark, there are three or more letters down, and there is no across number.
You can see in the code that I had to do some debug printing to get the crossword puzzle clue numbering correct. I broke the process into many methods to keep each method as simple as possible.
Finally, I drew the crossword puzzle answer from the information in the List.
Here's the code:
import java.awt.Color;
import java.awt.Point;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import acm.graphics.GLabel;
import acm.graphics.GRect;
import acm.program.GraphicsProgram;
import acm.util.ErrorException;
public class Crossword extends GraphicsProgram {
private static final boolean DEBUG = false;
private static final long serialVersionUID = -7971434624427958742L;
private List<CrosswordCell> crosswordCellList;
#Override
public void run() {
this.crosswordCellList = new ArrayList<CrosswordCell>();
// String fileName = "crosswordfile.txt";
String fileName = "C:/Eclipse/eclipse-4.2-work/" +
"com.ggl.testing/crosswordfile.txt";
try {
readCrosswordAnswer(fileName);
if (DEBUG) printCrosswordAnswer();
numberCrosswordCells();
if (DEBUG) printCrosswordAnswer();
drawCrosswordAnswer();
} catch (FileNotFoundException e) {
throw new ErrorException(e);
} catch (IOException e) {
throw new ErrorException(e);
}
}
private void readCrosswordAnswer(String fileName)
throws FileNotFoundException, IOException {
BufferedReader reader =
new BufferedReader(new FileReader(fileName));
String line = "";
int row = 0;
while ((line = reader.readLine()) != null) {
for (int column = 0; column < line.length(); column++) {
CrosswordCell cell = new CrosswordCell(column, row);
char lineChar = line.charAt(column);
if (lineChar == '#') {
cell.setDarkCell(true);
} else if (Character.isLetter(lineChar)) {
cell.setLetter(true);
}
crosswordCellList.add(cell);
}
row++;
}
reader.close();
}
public void printCrosswordAnswer() {
for (CrosswordCell cell : crosswordCellList) {
System.out.println(cell);
}
}
private void numberCrosswordCells() {
int clueNumber = 1;
for (CrosswordCell cell : crosswordCellList) {
if (cell.isLetter()) {
clueNumber = testCell(cell, clueNumber);
}
}
}
private int testCell(CrosswordCell cell, int clueNumber) {
Point p = cell.getLocation();
CrosswordCell leftCell = getLeftCell(p.x, p.y);
List<CrosswordCell> acrossList = getRightCells(p.x, p.y);
if (DEBUG) {
System.out.print(p);
System.out.println(", " + leftCell + " " +
acrossList.size());
}
if ((leftCell == null) && (acrossList.size() >= 3)) {
cell.setClueNumber(clueNumber++);
} else {
CrosswordCell aboveCell = getAboveCell(p.x, p.y);
List<CrosswordCell> downList = getBelowCells(p.x, p.y);
if (DEBUG) {
System.out.print(p);
System.out.println(", " + aboveCell + " " +
downList.size());
}
if ((aboveCell == null) && (downList.size() >= 3)) {
cell.setClueNumber(clueNumber++);
}
}
return clueNumber;
}
private CrosswordCell getAboveCell(int x, int y) {
int yy = y - 1;
return getCell(x, yy);
}
private CrosswordCell getLeftCell(int x, int y) {
int xx = x - 1;
return getCell(xx, y);
}
private List<CrosswordCell> getBelowCells(int x, int y) {
List<CrosswordCell> list = new ArrayList<CrosswordCell>();
for (int i = y; i < (y + 3); i++) {
CrosswordCell cell = getCell(x, i);
if (cell != null) {
list.add(cell);
}
}
return list;
}
private List<CrosswordCell> getRightCells(int x, int y) {
List<CrosswordCell> list = new ArrayList<CrosswordCell>();
for (int i = x; i < (x + 3); i++) {
CrosswordCell cell = getCell(i, y);
if (cell != null) {
list.add(cell);
}
}
return list;
}
private CrosswordCell getCell(int x, int y) {
for (CrosswordCell cell : crosswordCellList) {
Point p = cell.getLocation();
if ((p.x == x) && (p.y == y)) {
if (cell.isDarkCell()) {
return null;
} else if (cell.isLetter()){
return cell;
} else {
return null;
}
}
}
return null;
}
private void drawCrosswordAnswer() {
int sqCon = 32;
for (CrosswordCell cell : crosswordCellList) {
Point p = cell.getLocation();
if (cell.isDarkCell()) {
drawDarkCell(p, sqCon);
} else if (cell.isLetter()) {
drawLetterCell(cell, p, sqCon);
}
}
}
private void drawDarkCell(Point p, int sqCon) {
GRect blackSq = new GRect(sqCon, sqCon);
blackSq.setFilled(true);
blackSq.setFillColor(Color.BLACK);
add(blackSq, p.x * sqCon, p.y * sqCon);
}
private void drawLetterCell(CrosswordCell cell, Point p, int sqCon) {
GRect whiteSq = new GRect(sqCon, sqCon);
add(whiteSq, p.x * sqCon, p.y * sqCon);
if (cell.getClueNumber() > 0) {
String label = Integer.toString(cell.getClueNumber());
GLabel wordNumberLabel = new GLabel(label);
add(wordNumberLabel, p.x * sqCon + 2, p.y * sqCon + 14);
}
}
class CrosswordCell {
private boolean darkCell;
private boolean isLetter;
private int clueNumber;
private Point location;
public CrosswordCell(int x, int y) {
this.location = new Point(x, y);
this.clueNumber = 0;
this.darkCell = false;
this.isLetter = false;
}
public boolean isDarkCell() {
return darkCell;
}
public void setDarkCell(boolean darkCell) {
this.darkCell = darkCell;
}
public boolean isLetter() {
return isLetter;
}
public void setLetter(boolean isLetter) {
this.isLetter = isLetter;
}
public int getClueNumber() {
return clueNumber;
}
public void setClueNumber(int clueNumber) {
this.clueNumber = clueNumber;
}
public Point getLocation() {
return location;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("CrosswordCell [location=");
builder.append(location);
builder.append(", clueNumber=");
builder.append(clueNumber);
builder.append(", darkCell=");
builder.append(darkCell);
builder.append(", isLetter=");
builder.append(isLetter);
builder.append("]");
return builder.toString();
}
}
}
I need to justify some text (RTL), which is a string (S1) from the server. But a TextView can't justify text, so I have to use a WebView, now I have to create a HTML file in which
will display S1. And then I store the address of that html file in the database and then I display that html file. I've seen this question asked before on SO and many have recommended to use a 3rd party library, I've tried all of those approaches to no avail (they work in 90% of scenarios but are no fully reliable).
I feel that this approach seems convoluted, I was wondering if there is a better approach?
I use the following code that answer with very people that need this subject and i create formula that support in every display.
public class TextJustify {
final static String SYSTEM_NEWLINE = "\n";
final static float COMPLEXITY = 5.12f; // Reducing this will increase
// efficiency but will decrease
// effectiveness
final static Paint p = new Paint();
/* #author Mathew Kurian */
public static void run(final TextView tv, float origWidth, int paddingLeft, int paddingRight, int marginLeft, int marginRight) {
origWidth-= paddingRight+marginRight+paddingLeft+marginLeft;
String s = tv.getText().toString();
p.setTypeface(tv.getTypeface());
String[] splits = s.split(SYSTEM_NEWLINE);
float width = origWidth - 5;
for (int x = 0; x < splits.length; x++)
if (p.measureText(splits[x]) > width) {
splits[x] = wrap(splits[x], width, p);
String[] microSplits = splits[x].split(SYSTEM_NEWLINE);
for (int y = 0; y < microSplits.length - 1; y++)
microSplits[y] = justify(removeLast(microSplits[y], " "),
width, p);
StringBuilder smb_internal = new StringBuilder();
for (int z = 0; z < microSplits.length; z++)
smb_internal.append(microSplits[z]
+ ((z + 1 < microSplits.length) ? SYSTEM_NEWLINE
: ""));
splits[x] = smb_internal.toString();
}
final StringBuilder smb = new StringBuilder();
for (String cleaned : splits)
smb.append(cleaned + SYSTEM_NEWLINE);
tv.setGravity(Gravity.RIGHT);
tv.setText(smb);
}
private static String wrap(String s, float width, Paint p) {
String[] str = s.split("\\s"); // regex
StringBuilder smb = new StringBuilder(); // save memory
smb.append(SYSTEM_NEWLINE);
for (int x = 0; x < str.length; x++) {
float length = p.measureText(str[x]);
String[] pieces = smb.toString().split(SYSTEM_NEWLINE);
try {
if (p.measureText(pieces[pieces.length - 1]) + length > width)
smb.append(SYSTEM_NEWLINE);
} catch (Exception e) {
}
smb.append(str[x] + " ");
}
return smb.toString().replaceFirst(SYSTEM_NEWLINE, "");
}
private static String removeLast(String s, String g) {
if (s.contains(g)) {
int index = s.lastIndexOf(g);
int indexEnd = index + g.length();
if (index == 0)
return s.substring(1);
else if (index == s.length() - 1)
return s.substring(0, index);
else
return s.substring(0, index) + s.substring(indexEnd);
}
return s;
}
private static String justifyOperation(String s, float width, Paint p) {
float holder = (float) (COMPLEXITY * Math.random());
while (s.contains(Float.toString(holder)))
holder = (float) (COMPLEXITY * Math.random());
String holder_string = Float.toString(holder);
float lessThan = width;
int timeOut = 100;
int current = 0;
while (p.measureText(s) < lessThan && current < timeOut) {
s = s.replaceFirst(" ([^" + holder_string + "])", " "
+ holder_string + "$1");
lessThan = p.measureText(holder_string) + lessThan
- p.measureText(" ");
current++;
}
String cleaned = s.replaceAll(holder_string, " ");
return cleaned;
}
private static String justify(String s, float width, Paint p) {
while (p.measureText(s) < width) {
s = justifyOperation(s, width, p);
}
return s;
}
}
and for calling this you mus use following code, I tested for Persian language and in every display and device worked fine.
public static final int FinallwidthDp = 320 ;
public static final int widthJustify = 223 ;
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int widthPixels = metrics.widthPixels;
float scaleFactor = metrics.density;
float widthDp = widthPixels / scaleFactor;
TextView tv = (TextView) findViewById(R.id.textView1);
ViewGroup.MarginLayoutParams lp1 = (ViewGroup.MarginLayoutParams) tv.getLayoutParams();
tv.setText(text);
TextJustify.run(tv,widthDp / FinallwidthDp * widthJustify , tv.getPaddingLeft(),tv.getPaddingRight() , lp1.leftMargin, lp1.rightMargin);
this algorithm tested on various device and worked fine in normal activity (not dialog) and wrap-content width for TextView, and worked with every padding and margin.if not good for you, you can change widthJustify until look good to you, I hope this useful.
for newly update see This
LIBRARY: https://github.com/bluejamesbond/TextJustify-Android
SUPPORTS: Android 2.0 to 5.X; String/Spannables; RTL language support! NO WEBVIEW :)
SCREENSHOT
Try this:
Add a TextViewJustify.java file in src folder.
TextViewJustify.java wil be like this
import android.graphics.Paint;
import android.view.Gravity;
import android.widget.TextView;
public class TextViewJustify {
/*
* PLEASE DO NOT REMOVE Coded by Mathew Kurian I wrote this code for a
* Google Interview for Internship. Unfortunately, I got too nervous during
* the interview that I messed, but anyhow that doesn't matter. I have
* resent my work in hopes that I might still get a position there. Thank
* you :DD
*/
final static String SYSTEM_NEWLINE = "\n";
final static float COMPLEXITY = 5.12f; // Reducing this will increase
// efficiency but will decrease
// effectiveness
final static Paint p = new Paint();
public static void justifyText(final TextView tv, final float origWidth) {
String s = tv.getText().toString();
p.setTypeface(tv.getTypeface());
String[] splits = s.split(SYSTEM_NEWLINE);
float width = origWidth - 5;
for (int x = 0; x < splits.length; x++)
if (p.measureText(splits[x]) > width) {
splits[x] = wrap(splits[x], width, p);
String[] microSplits = splits[x].split(SYSTEM_NEWLINE);
for (int y = 0; y < microSplits.length - 1; y++)
microSplits[y] = justify(removeLast(microSplits[y], " "),
width, p);
StringBuilder smb_internal = new StringBuilder();
for (int z = 0; z < microSplits.length; z++)
smb_internal.append(microSplits[z]
+ ((z + 1 < microSplits.length) ? SYSTEM_NEWLINE
: ""));
splits[x] = smb_internal.toString();
}
final StringBuilder smb = new StringBuilder();
for (String cleaned : splits)
smb.append(cleaned + SYSTEM_NEWLINE);
tv.setGravity(Gravity.LEFT);
tv.setText(smb);
}
private static String wrap(String s, float width, Paint p) {
String[] str = s.split("\\s"); // regex
StringBuilder smb = new StringBuilder(); // save memory
smb.append(SYSTEM_NEWLINE);
for (int x = 0; x < str.length; x++) {
float length = p.measureText(str[x]);
String[] pieces = smb.toString().split(SYSTEM_NEWLINE);
try {
if (p.measureText(pieces[pieces.length - 1]) + length > width)
smb.append(SYSTEM_NEWLINE);
} catch (Exception e) {
}
smb.append(str[x] + " ");
}
return smb.toString().replaceFirst(SYSTEM_NEWLINE, "");
}
private static String removeLast(String s, String g) {
if (s.contains(g)) {
int index = s.lastIndexOf(g);
int indexEnd = index + g.length();
if (index == 0)
return s.substring(1);
else if (index == s.length() - 1)
return s.substring(0, index);
else
return s.substring(0, index) + s.substring(indexEnd);
}
return s;
}
private static String justifyOperation(String s, float width, Paint p) {
float holder = (float) (COMPLEXITY * Math.random());
while (s.contains(Float.toString(holder)))
holder = (float) (COMPLEXITY * Math.random());
String holder_string = Float.toString(holder);
float lessThan = width;
int timeOut = 100;
int current = 0;
while (p.measureText(s) < lessThan && current < timeOut) {
s = s.replaceFirst(" ([^" + holder_string + "])", " "
+ holder_string + "$1");
lessThan = p.measureText(holder_string) + lessThan
- p.measureText(" ");
current++;
}
String cleaned = s.replaceAll(holder_string, " ");
return cleaned;
}
private static String justify(String s, float width, Paint p) {
while (p.measureText(s) < width) {
s = justifyOperation(s, width, p);
}
return s;
}
}
And use this class like this:
TextViewJustify.justifyText(your_text_view, 225f);
In my case it was 225f. change it according to your need.
You can justify Text using WebView Simply
LinearLayout lv=(LinearLayout)dialog.findViewById(R.id.**yourId**);
String text1 = "<html><body>"
+ "<p align=\"justify\">"
+**your text**
+ "</p> "
+ "</body></html>";
WebView wv=new WebView(getApplicationContext());
wv.loadData(text1,"text/html","utf-8");
lv.removeAllViews();
lv.addView(wv);
i made simple class.
this can be used just like TextView
import android.content.Context;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.widget.TextView;
/**
* text justifying
* you can just use like TextView
* #author hyunsikkim
*
*/
public class JustifiedTextView extends TextView {
public JustifiedTextView(Context context) {
super(context);
}
public JustifiedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
private void setBreakText(String text) {
if(text == null) return;
String breakText = breakText(getPaint(), text,
getWidth()-this.getPaddingLeft()-this.getPaddingRight());
if(breakText.equals(getText()) == false) {
setText(breakText);
}
}
public String breakText(Paint textPaint, String strText, int breakWidth) {
StringBuilder sb = new StringBuilder();
int endValue = 0;
final String NEW_LINE = "\n";
do{
endValue = textPaint.breakText(strText, true, breakWidth, null);
if(endValue > 0) {
/**
* handle if text contains NEW_LINE
*/
final int index = strText.indexOf(NEW_LINE);
if(0<=index && index <= endValue) {
endValue = index + NEW_LINE.length();
}
final String sub = strText.substring(0, endValue);
sb.append(sub);
/**
* handle breaked text endWidth NEW_LINE
*/
if(sub.endsWith(NEW_LINE) == false) {
if(strText.length() != endValue) {
sb.append(NEW_LINE);
}
}
strText = strText.substring(endValue);
}
} while(endValue > 0);
return sb.toString();
}
public String breakText(Paint textPaint, int id, int breakWidth) {
String strText = getResources().getString(id);
return breakText(textPaint, strText, breakWidth);
}
#Override
protected void onTextChanged(CharSequence text, int start,
int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
/**
* this control changes from setText(Charsequence text)
*/
if(getWidth() != 0) {
setBreakText(text.toString());
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
/**
* this help to break initial text.
*/
if(w != oldw) {
setBreakText(getText().toString());
}
}
}
Use web view
WebView tv = (WebView) findViewById(R.id.aboutme);
String youtContentStr = String.valueOf(Html
.fromHtml("<![CDATA[<body style=\"text-align:justify;background-color:#00222222;\">"
+ text
+ "</body>]]>"));
tv.setBackgroundColor(Color.TRANSPARENT);
tv.loadData(youtContentStr, "text/html", "utf-8");`