Bem, acredito que está pronto, depois vou ter que te explicar sobre as Configurações para você poder reconfigurar caso precise. Eu centralizei todas as configurações no método “iniciarConfiguracoes()”, então você já sabe o ponto de todo o código que precisará alterar, e não ficará perdida.
Você vai ter que baixar o JNativeHook e importar o JAR dele para o seu Projeto, eu fiz no Eclipse seguindo o que está escrito aqui, veja como é no NetBeans. Eu usei a versão 2.0.3, acho que já deve ter novas versões, mas não sei se são compatíveis ou se você terá que mudar o código.
Assim fica a estrutura do Projeto:
Classe Main:
package main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import main.listener.ListenerDeEventosNativos;
import main.listener.ProcessadorDeEventosNativos;
public class Main {
private static BufferedImage checkBoxImage;
private static JFrame tela;
private static Robot robo;
private static BuscadorDaCheckboxDaLinhaSelecionada buscadorDaCheckboxDaLinhaSelecionada;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
tela = new Tela();
} catch (Exception e) {
e.printStackTrace();
saidaComErro(e);
}
}
});
try {
ListenerDeEventosNativos.iniciar();
iniciarConfiguracoes();
Clicador clicador = new Clicador(robo, buscadorDaCheckboxDaLinhaSelecionada);
ListenerDeEventosNativos
.addNativeKeyListener(new ProcessadorDeEventosNativos(clicador::clicarNaCheckbox, () -> {
SwingUtilities.invokeLater(() -> tela.repaint());
}));
} catch (Exception e1) {
e1.printStackTrace();
saidaComErro(e1);
}
}
/** Altere as configurações para corresponder as suas necessidades. */
private static void iniciarConfiguracoes() throws Exception {
robo = new Robot();
checkBoxImage = ImageIO.read(new File("src/images/partedecheckbox.png"));
BuscadorDeLinhaSelecionada buscadorDeLinhaSelecionada = new BuscadorDeLinhaSelecionada(new Color(255, 150, 50),
5, 5);
Rectangle areaDaSelecao = new Rectangle(674, 318, 103, 492);
buscadorDaCheckboxDaLinhaSelecionada = new BuscadorDaCheckboxDaLinhaSelecionada(checkBoxImage,
buscadorDeLinhaSelecionada, areaDaSelecao, 365, 400);
}
private static void saidaComErro(Exception e) throws HeadlessException {
JOptionPane.showMessageDialog(null, e);
System.exit(1);
}
@SuppressWarnings("serial")
private static class Tela extends JFrame {
Tela() {
super("Clicador em Checkbox de linha Selecionada");
System.out.println("creating instance");
setSize(230, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new Painel());
setAlwaysOnTop(true);
setVisible(true);
}
}
@SuppressWarnings("serial")
private static class Painel extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
BufferedImage areaDaSelecao = buscadorDaCheckboxDaLinhaSelecionada.areaDaSelecaoBuscada.get();
if (areaDaSelecao != null) {
g.drawImage(areaDaSelecao, 20, 20, null);
BufferedImage areaDaCheckbox = buscadorDaCheckboxDaLinhaSelecionada.areaComCheckboxBuscada.get();
if (areaDaCheckbox != null) {
g.drawImage(areaDaCheckbox, areaDaSelecao.getWidth() + 30, 20, null);
g.drawImage(checkBoxImage, areaDaSelecao.getWidth() + 30, areaDaCheckbox.getHeight() + 30, null);
}
}
}
}
}
Clicador:
package main;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
public class Clicador {
private Robot robo;
private BuscadorDaCheckboxDaLinhaSelecionada buscadorDaCheckboxDaLinhaSelecionada;
public Clicador(Robot robo, BuscadorDaCheckboxDaLinhaSelecionada buscadorDaCheckboxDaLinhaSelecionada) {
this.robo = robo;
this.buscadorDaCheckboxDaLinhaSelecionada = buscadorDaCheckboxDaLinhaSelecionada;
}
/**
* Retorna true apenas se encontrou e clicou na Checkbox, false caso contrário.
*/
public boolean clicarNaCheckbox() {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle boundsDaCheckboxEncontrada = buscadorDaCheckboxDaLinhaSelecionada.buscarCheckboxDaLinhaSelecionada(
robo.createScreenCapture(new Rectangle(0, 0, screenSize.width, screenSize.height)));
System.out.println(boundsDaCheckboxEncontrada);
if (boundsDaCheckboxEncontrada != null) {
int centroDaCheckBox_X = boundsDaCheckboxEncontrada.x + (boundsDaCheckboxEncontrada.width / 2);
int centroDaCheckBox_Y = boundsDaCheckboxEncontrada.y + (boundsDaCheckboxEncontrada.height / 2);
clicar(centroDaCheckBox_X, centroDaCheckBox_Y);
System.out.println("Clicou na Checkbox - x: " + centroDaCheckBox_X + ", y: " + centroDaCheckBox_Y);
return true;
}
return false;
}
/** Clica na posição x e y passadas. */
private void clicar(int x, int y) {
robo.mouseMove(x, y);
robo.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robo.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
}
}
Buscador da Linha Selecionada pelo Ctrl+F:
package main;
import java.awt.Color;
import java.awt.image.BufferedImage;
public class BuscadorDeLinhaSelecionada {
private Color corDaSelecao;
private int margemSuperiorAdicionada; // valor em pixels
private int margemInferiorAdicionada; // valor em pixels
public BuscadorDeLinhaSelecionada(Color corDaSelecao, int margemSuperiorAdicionada, int margemInferiorAdicionada) {
this.corDaSelecao = corDaSelecao;
this.margemSuperiorAdicionada = margemSuperiorAdicionada;
this.margemInferiorAdicionada = margemInferiorAdicionada;
}
public LinhaDaSelecao encontrarInicioEFimDaLinhaSelecionada(BufferedImage areaDaSelecaoScreenshot, int margemY) {
LinhaDaSelecao linha = encontrarInicioEFim(areaDaSelecaoScreenshot, corDaSelecao, margemY);
if (linha == null)
return null;
System.out.println("inicioY: " + linha.inicioY + " / linha.fimY: " + linha.fimY);
linha.inicioY = linha.inicioY - margemSuperiorAdicionada;
linha.fimY = linha.fimY + margemInferiorAdicionada;
System.out.println("inicioY: " + linha.inicioY + " / linha.fimY: " + linha.fimY);
return linha;
}
private LinhaDaSelecao encontrarInicioEFim(BufferedImage img, Color cor, int margemY) {
boolean encontrou = false;
LinhaDaSelecao linhaDaSelecao = new LinhaDaSelecao();
linhaDaSelecao.inicioY = Integer.MAX_VALUE;
linhaDaSelecao.fimY = Integer.MIN_VALUE;
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
if (new Color(img.getRGB(x, y)).equals(corDaSelecao)) {
int posicaoYRealDoPixel = y + margemY;
if (linhaDaSelecao.inicioY > posicaoYRealDoPixel) {
encontrou = true;
linhaDaSelecao.inicioY = posicaoYRealDoPixel;
}
if (linhaDaSelecao.fimY < posicaoYRealDoPixel) {
encontrou = true;
linhaDaSelecao.fimY = posicaoYRealDoPixel;
}
}
}
}
return (encontrou) ? linhaDaSelecao : null;
}
public static class LinhaDaSelecao {
public int inicioY;
public int fimY;
}
}
Buscador de uma Imagem que está também dentro de outra Imagem (talvez a classe mais reutilizável!):
package main;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
public class BuscadorDeImagemDentroDeImagem {
/**
* Busca a 'imagemFilho' dentro da 'imagemPai' retornando um {@link Rectangle}
* com a informação (x, y, width e height) de onde ela foi encontrada na
* 'imagemPai'; ou retorna null caso não a encontre.
*/
public static Rectangle buscarFilho(BufferedImage imagemPai, BufferedImage imagemFilho) {
for (int x = 0; x < imagemPai.getWidth(); x++) {
for (int y = 0; y < imagemPai.getHeight(); y++) {
if (contemFilho(imagemPai, imagemFilho, x, y)) {
return new Rectangle(x, y, imagemFilho.getWidth(), imagemFilho.getHeight());
}
}
}
return null;
}
/**
* Retorna true apenas se a 'imagemFilho' for encontrada na 'imagemPai' na
* posição 'offsetX' e 'offsetY' passadas; retorna false caso contrário.
*/
public static boolean contemFilho(BufferedImage imagemPai, BufferedImage imagemFilho, int offsetX, int offsetY) {
if (!filhoCabeDentroDoPai(imagemPai, imagemFilho, offsetX, offsetY)) {
return false; // não contém o filho se ele não cabe dentro do pai
}
for (int x = 0; x < imagemFilho.getWidth(); x++) {
for (int y = 0; y < imagemFilho.getHeight(); y++) {
if (imagemPai.getRGB(offsetX + x, offsetY + y) != imagemFilho.getRGB(x, y)) {
return false;
}
}
}
return true;
}
private static boolean filhoCabeDentroDoPai(BufferedImage imagemPai, BufferedImage imagemFilho, int offsetX,
int offsetY) {
if ((offsetX + imagemFilho.getWidth()) > imagemPai.getWidth()) {
return false;
}
if ((offsetY + imagemFilho.getHeight()) > imagemPai.getWidth()) {
return false;
}
return true;
}
}
Buscador da Checkbox que será clicada:
package main;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import main.BuscadorDeLinhaSelecionada.LinhaDaSelecao;
public class BuscadorDaCheckboxDaLinhaSelecionada {
private BuscadorDeLinhaSelecionada buscadorDeLinhaSelecionada;
private Rectangle areaDaSelecao;
private int bordaEsquerdaDaAreaDeCheckbox;
private int bordaDireitaDaAreaDeCheckbox;
private BufferedImage imagemDaCheckbox;
/** Não altere o valor desta property. */
public final ObjectProperty<BufferedImage> areaComCheckboxBuscada = new SimpleObjectProperty<>();
/** Não altere o valor desta property. */
public final ObjectProperty<BufferedImage> areaDaSelecaoBuscada = new SimpleObjectProperty<>();
public BuscadorDaCheckboxDaLinhaSelecionada(BufferedImage imagemDaCheckbox,
BuscadorDeLinhaSelecionada buscadorDeLinhaSelecionada, Rectangle areaDaSelecao,
int bordaEsquerdaDaAreaDeCheckbox, int bordaDireitaDaAreaDeCheckbox) {
this.imagemDaCheckbox = imagemDaCheckbox;
this.buscadorDeLinhaSelecionada = buscadorDeLinhaSelecionada;
this.areaDaSelecao = areaDaSelecao;
this.bordaEsquerdaDaAreaDeCheckbox = bordaEsquerdaDaAreaDeCheckbox;
this.bordaDireitaDaAreaDeCheckbox = bordaDireitaDaAreaDeCheckbox;
}
/**
* Recebe um screenshot de toda a tela, e retorna um Rectangle com as bouds do
* checkbox a ser clicado, ou retorna null caso não tenha encontrado ele ou a
* linha Selecionada.
*/
public Rectangle buscarCheckboxDaLinhaSelecionada(BufferedImage screenshot) {
areaDaSelecaoBuscada.set(
screenshot.getSubimage(areaDaSelecao.x, areaDaSelecao.y, areaDaSelecao.width, areaDaSelecao.height));
LinhaDaSelecao linhaSelecionada = buscadorDeLinhaSelecionada
.encontrarInicioEFimDaLinhaSelecionada(areaDaSelecaoBuscada.get(), areaDaSelecao.y);
if (linhaSelecionada != null) {
int width = bordaDireitaDaAreaDeCheckbox - bordaEsquerdaDaAreaDeCheckbox;
int height = linhaSelecionada.fimY - linhaSelecionada.inicioY;
Rectangle boundsDaAreaComCheckboxBuscada = new Rectangle(bordaEsquerdaDaAreaDeCheckbox,
linhaSelecionada.inicioY, width, height);
System.out.println("boundsDaAreaComCheckboxBuscada: " + boundsDaAreaComCheckboxBuscada);
areaComCheckboxBuscada
.set(screenshot.getSubimage(boundsDaAreaComCheckboxBuscada.x, boundsDaAreaComCheckboxBuscada.y,
boundsDaAreaComCheckboxBuscada.width, boundsDaAreaComCheckboxBuscada.height));
Rectangle boundsDaCheckbox = BuscadorDeImagemDentroDeImagem.buscarFilho(areaComCheckboxBuscada.get(),
imagemDaCheckbox);
if (boundsDaCheckbox != null) {
boundsDaCheckbox.x += boundsDaAreaComCheckboxBuscada.x;
boundsDaCheckbox.y += boundsDaAreaComCheckboxBuscada.y;
}
System.out.println("boundsDaCheckbox: " + boundsDaCheckbox);
return boundsDaCheckbox;
}
return null;
}
}
Classe que consome o JNativeHook:
package main.listener;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.keyboard.NativeKeyListener;
public class ListenerDeEventosNativos {
public static void iniciar() throws NativeHookException {
removerLogDoJNativeHook();
registrarONativeHook();
}
private static void registrarONativeHook() throws NativeHookException {
try {
GlobalScreen.registerNativeHook();
} catch (NativeHookException ex) {
System.err.println("There was a problem registering the native hook.");
throw ex;
}
}
private static void removerLogDoJNativeHook() {
// Clear previous logging configurations.
LogManager.getLogManager().reset();
// Get the logger for "org.jnativehook" and set the level to off.
Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
logger.setLevel(Level.OFF);
}
public static void addNativeKeyListener(NativeKeyListener listener) {
// Construct the example object and initialze native hook.
GlobalScreen.addNativeKeyListener(listener);
}
}
Classe que processa quando “Enter” é teclado:
package main.listener;
import org.jnativehook.keyboard.NativeKeyEvent;
import org.jnativehook.keyboard.NativeKeyListener;
public class ProcessadorDeEventosNativos implements NativeKeyListener {
/**
* Esses algoritmos serão executados toda vez que a Tecla ENTER for pressionada.
*/
private Runnable[] algoritmosParaExecutar;
/**
* Os 'algoritmosParaExecutar' serão executado toda vez que a Tecla ENTER for
* pressionada.
*/
public ProcessadorDeEventosNativos(Runnable... algoritmosParaExecutar) {
this.algoritmosParaExecutar = algoritmosParaExecutar;
}
@Override
public void nativeKeyPressed(NativeKeyEvent e) {
// System.out.println(arg0.getKeyChar());
if (e.getKeyCode() == NativeKeyEvent.VC_ENTER) {
System.out.println("Tecla ENTER pressionada!");
for (Runnable algoritmo : algoritmosParaExecutar) {
if (algoritmo != null) {
System.out.println(algoritmo);
algoritmo.run();
}
}
}
}
@Override
public void nativeKeyReleased(NativeKeyEvent arg0) {
}
@Override
public void nativeKeyTyped(NativeKeyEvent arg0) {
}
}
Veja que esta última classe usa um NativeKeyEvent.VC_ENTER
, talvez você vá precisar usar também o NativeKeyEvent.VC_KP_ENTER
, (não li a documentação para saber a diferença).
Bem, eu testei aqui e funciou muito bem. Sempre que você teclar Enter a Janela do programa mostra o que ele capturou da Tela, que é a Área-da-Seleção (onde os Números estão, e onde aparece a Seleção laranja do Ctrl+F), Caso a Seleção Laranja tenha sido detectada aparece também a “areaComCheckboxBuscada”, que é a área da Tela em que o programa acredita que estará a Checkbox correta a ser Clicada.
Essa Janela também mostrará a imagem que você criou para ser a partedecheckbox.png
.
O programa clica na Checkbox encontrada (se ele encontrou) sempre que você Teclar Enter. Ele não vai desmarcar a Checkbox, porque os pixels dela vão estar diferentes quando ela estiver com o visto dentro.