View Full Version : [JAVA] Mouse event
Dolcezeus
06-05-2010, 09:48
Sto cercando di creare un bottone personalizzato e con questo codice ci sono quasi riuscito:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class OvalButton extends JComponent implements MouseListener
{
private static final long serialVersionUID = 1L;
public OvalButton()
{
super();
enableInputMethods(false);
addMouseListener(this);
setFocusable(true);
}
public void setBounds(){
this.setBounds(0, 0, 200, 120);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D antiAlias = (Graphics2D)g;
antiAlias.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.RED);
g.fillOval(0, 0, 200, 120);
}
@Override
public void mouseClicked(MouseEvent e) {
System.out.println(""+e);
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
Se andate ad aggiungere il bottone ad un jpanel ho il problema che viene stampato nella console ovunque click del mouse anche fuori dall'ovale, dove ho sbagliato? Cosa Manca?
Vi prego è urgente, devo consegnare un elaborato.
Sbrizzolo86
06-05-2010, 10:36
Potresti postare anche il codice in cui istanzi il button e gli aggiungi un ascoltatore?
Dolcezeus
06-05-2010, 16:02
visto che il listener già l'ho aggiunto alla classe basta fare:
Ovalbutton button = new OvalButton();
JFrame frame= new JFrame();
frame.add(button);
lupoxxx87
06-05-2010, 16:05
penso che il problema stia nel fatto che tu vai ad "ascoltare" un qualsiasi click del mouse, non solo i click del mouse sopra al tuo componente...
Il componente occupa con buona probabilità tutta la superficie del suo contenitore.
C'è un setBounds nel tuo codice che per come è messo corre il rischio di essere irrilevante.
Puoi controllare eliminando quel setBounds e scrivendo una cosa tipo questa:
OvalButton button = new OvalButton();
button.setPreferredSize(new Dimension(200, 120));
JPanel container = new JPanel(new FlowLayout());
container.add(button);
finestra.add(container);
Dolcezeus
06-05-2010, 16:31
grazie delle risposte l'idea del setbounds e del container è buona ma nella console viene stampato click anche fuori dall'ovale mi spiego è come se avessi un bottone rettangolare con l'ovale in mezzo.. sto pensando che sia sbagliato estendere JComponent.. forse dovrei estendere JButton ma poi come faccio a fare l'override sulla forma del bottone?
Sbrizzolo86
06-05-2010, 16:45
grazie delle risposte l'idea del setbounds e del container è buona ma nella console viene stampato click anche fuori dall'ovale mi spiego è come se avessi un bottone rettangolare con l'ovale in mezzo.. sto pensando che sia sbagliato estendere JComponent.. forse dovrei estendere JButton ma poi come faccio a fare l'override sulla forma del bottone?
Prima che PGI-Bis scrivesse stavo per scrivere la stessa cosa.
Se vuoi che quel setBounds() abbia un senso (se proprio vuoi), prova a richiamare .setLayout(null) sul panel contenitore dell'OvalButton. Ovviamente così facendo rinunci alle potenzialità dei Layout Manager.
Per renderti conto di quello che sta succendendo, puoi fare così:
1) eredita da JPanel invece che da JComponent
2) nel costruttore scrivi
this.setOpaque(true);
//va bene qualsiasi colore che crei contrasto così te ne accorgi
this.setBackground(Color.gray);
3) sorpresa :D Sicuramente vedrai che l'area del componente è ben maggiore di quella dell'ovale, come ha detto PGI.
Dolcezeus
06-05-2010, 16:49
ho esposto già la "sorpresa" per piacere aiutatemi devo creare sto maledetto bottone ovale...
Sbrizzolo86
06-05-2010, 16:50
Forse ora ho capito meglio.
A te dà fastidio che l'area cliccabile sia il rettangolo in cui è inscritto l'ovale e non l'ovale.
Questo è un bel problema. Non credo che con JButton risolvi molto.
Sbrizzolo86
06-05-2010, 16:53
Al tuo posto, avendo questa esigenza, risolverei il tutto con un controllo sulle coordinate del mouse al click. Se sono all'interno dell'ovale faccio qualcosa, altrimenti niente.
Alessandro
Dolcezeus
06-05-2010, 16:54
il problema sta nel fatto che l'ovale dovrà spostarsi sullo schermo all'interno di un jFrame ad ogni click
Sbrizzolo86
06-05-2010, 16:57
il problema sta nel fatto che l'ovale dovrà spostarsi sullo schermo all'interno di un jFrame ad ogni click
Non fa niente, puoi fare un controllo sulle coordinate RELATIVE all'interno dell'ovale.
Per ottenere le coordinate relative puoi usare i metodi per ottenere la risoluzione dello schermo e le dimensioni della finestra. Il resto sono calcoli...
Se aspetti un minuto vedo se ritrovo il pezzo di codice dove ho calcolato queste coordinate...ho avuto un problema simile un po' di tempo fa.
banryu79
06-05-2010, 16:58
La verifica la puoi fare anche così:
(Il tuo bottone, con delle dimensioni di default)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;
public class OvalButton extends JComponent implements MouseListener
{
private static final long serialVersionUID = 1L;
private static final Dimension DEFAULT_SIZE = new Dimension(100, 70);
public OvalButton() {
super();
enableInputMethods(false);
addMouseListener(this);
setFocusable(true);
setVisible(true);
// size the component
sizeMe();
}
public void sizeMe() {
setSize(DEFAULT_SIZE);
setPreferredSize(DEFAULT_SIZE);
setMinimumSize(DEFAULT_SIZE);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D antiAlias = (Graphics2D)g;
antiAlias.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
antiAlias.setPaintMode();
antiAlias.setColor(Color.RED);
Rectangle rect = getBounds();
antiAlias.fillOval(1, 1, rect.width-2, rect.height-2);
}
@Override
public void paintBorder(Graphics g) {
super.paintBorder(g);
Rectangle rect = getBounds();
g.setColor(Color.BLACK);
g.drawRect(0, 0, rect.width-1, rect.height-1);
}
@Override public void mouseClicked(MouseEvent e) {
System.out.println(""+e);
}
@Override public void mouseEntered(MouseEvent e) {}
@Override public void mouseExited(MouseEvent e) {}
@Override public void mousePressed(MouseEvent e) {}
@Override public void mouseReleased(MouseEvent e) {}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("Bottone?");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
OvalButton but = new OvalButton();
//but.setPreferredSize(new Dimension(140, 90));
JPanel pan = new JPanel();
pan.setBorder(new TitledBorder("Pannello"));
pan.add(but);
frame.add(pan, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Questo disegna l'ovale e disegna anche i bordi del componente.
Tutti i click che vanno a finire dentro i bordi del componente (cioè nel componente) vengono "sentiti".
Il fatto è che i componenti sono tutti delle aree rettangolari.
@EDIT:
potresti associare all'area "ovale" del tuo componente una java.awt.Shape. E poi, ad ogni click che il componente riceve, controllare se il click cade dentro la Shape. (Vedi package java.awt.geom).
Dolcezeus
06-05-2010, 16:59
ti ringrazio ma mi sà di una cosa poco "pulita" il mio prof mi ammazzerebbe :D è che nn sò come cambiare l'aspetto ad un bottone ereditandone tutte le altre ottime proprietà.
Sbrizzolo86
06-05-2010, 17:02
Mah, potrei anche sbagliarmi, ma non mi pare che si possano personalizzare le forme dei componenti. Non credo che il tuo professore pretenda questo.
Comunque non ho trovato il codice, però ricordo gli "ingredienti" principali:
evt.getYOnScreen()
evt.getXOnScreen()
this.getLocationOnScreen().y
this.getLocationOnScreen().x
evt.getYOnScreen()-this.getLocationOnScreen().y dovrebbe essere già la coordinata Y relativa che ti serve.
EDIT:
una volta risolto il problema delle coordinate relative, l'approccio di banryu79 (analogo a quello che ti ho consigliato ma più elegante dato usi funzioni già pronte con gli Shape) non mi sembra così "sporco" come dici.
Dolcezeus
06-05-2010, 17:05
banryu79 ma non risolvo il problema...
banryu79
06-05-2010, 17:06
Mah, potrei anche sbagliarmi, ma non mi pare che si possano personalizzare le forme dei componenti. Non credo che il tuo professore pretenda questo.
Beh, delle strade si trovano sempre -> http://today.java.net/pub/a/today/2008/03/18/translucent-and-shaped-swing-windows.html
Però se il suo prof pretende questo, mi sa che non ha le idee chiare :D
Sbrizzolo86
06-05-2010, 17:11
Beh, delle strade si trovano sempre -> http://today.java.net/pub/a/today/2008/03/18/translucent-and-shaped-swing-windows.html
Però se il suo prof pretende questo, mi sa che non ha le idee chiare :D
Grandissime AWTUtilities...le usai per creare meravigliosi effetti di trasparenza.
Beh in informatica quasi tutto è possibile, però sto prof ha un po' esagerato.
Dolcezeus
06-05-2010, 17:12
io non sono un guro di java ma se la filosofia del riuso e dell'oggetto è quella che si lege nei libri è possibile fare qualsiasi cosa con un componente... solo che io non riesco a capì come... :D
grazie delle risposte l'idea del setbounds e del container è buona ma nella console viene stampato click anche fuori dall'ovale mi spiego è come se avessi un bottone rettangolare con l'ovale in mezzo.. sto pensando che sia sbagliato estendere JComponent.. forse dovrei estendere JButton ma poi come faccio a fare l'override sulla forma del bottone?
Puoi usare uno Shape (Ellipse2D nel tuo caso). Super-pseudo codice
private Shape shape = new Ellipse2D.Double(0, 0, 200, 120);
...
paintComponent(Graphics g) {...
Graphics2D graphics= (Graphics2D) g;
graphics.fill(shape) o graphics.draw(shape);
Nel tuo mouseClicked verifichi se il punto del click è contenuto nello shape:
if(shape.contains(e.getX(), e.getY()) {
...è stato cliccato il componente E il punto di click è contenuto nello shape.
}
Questo tiene conto della posizione del componente sullo schermo (le coordinate del mouseClick sono relative all'origine del componente).
Sbrizzolo86
06-05-2010, 17:18
io non sono un guro di java ma se la filosofia del riuso e dell'oggetto è quella che si lege nei libri è possibile fare qualsiasi cosa con un componente... solo che io non riesco a capì come... :D
"Qualsiasi cosa" significa tutto e niente, dato che comunque ci troviamo ad un livello non basso (per capirci, non stiamo gestendo i singoli pixel, ma c'è qualcosa che lo fa per noi) pertanto qualche limite è da mettere in conto.
Che poi stiamo facendo tutto sto discorso per un problema che si può "aggirare" con i consigli che ti abbiamo dato. Magari non cambi la forma del componente, però ottieni l'effetto desiderato.
Dolcezeus
06-05-2010, 17:19
grazie delle infinite risposte tutte molto valide ma credo di dover creare un bottone ereditando tutti i metodi "normali" relativi ai bottoni facendo l'override del solo metodo "paint" o cmq quello che attribuisce la "forma" al bottone come si può fare?
Sbrizzolo86
06-05-2010, 17:26
grazie delle infinite risposte tutte molto valide ma credo di dover creare un bottone ereditando tutti i metodi "normali" relativi ai bottoni facendo l'override del solo metodo "paint" o cmq quello che attribuisce la "forma" al bottone come si può fare?
Il metodo paint disegna il componente all'interno di un'area rettangolare. Non puoi cambiare la forma di quest'area facendo l'overriding di paint, ma solo disegnarne a piacimento il contenuto.
Dovresti crearti un delegato ui ma, alla buona, puoi fare come per JComponent avendo cura di eliminare il bordo e impostare a false il disegno del contenuto.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import javax.swing.JButton;
import javax.swing.JFrame;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* @author pierluigi
*/
public class Test implements Runnable {
public static void main(String[] args) {
EventQueue.invokeLater(new Test());
}
public void run() {
JButton button = new JButton("hello") {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(0, 0, getWidth(), getHeight());
}
@Override
protected void processMouseEvent(MouseEvent e) {
Shape shape = new Ellipse2D.Double(0, 0, getWidth(), getHeight());
if(shape.contains(e.getPoint())) {
super.processMouseEvent(e);
} else {
e.consume();
}
}
};
button.setContentAreaFilled(false);
button.setFocusPainted(false);
button.setBorderPainted(false);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("ACTION!!!");
}
});
JFrame window = new JFrame("test");
window.add(button);
window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
window.pack();
window.setVisible(true);
}
}
Nota che il pulsante è visivamente inerte (non cambia nulla se cliccki) ma reagisce all'interazione. Se vuoi "vivacizzarlo" devi personalizzare l'aspetto (cioè il paintComponent) in base allo stato (isPressed, isFocused eccetera).
Sbrizzolo86
06-05-2010, 17:39
Non vorrei essere pedante, ma questi problemi li abbiamo avuti tanti in classe quando dovemmo realizzare interfacce grafiche realizzate con Photoshop (altro che "semplici ovali", i pulsanti a volte avevano forme nemmeno contemplate dagli Shapes).
Nessuno di noi ha mai realizzato la soluzione "diretta", ovvero quella di modificare la forma del JComponent, e abbiamo cercato in lungo ed in largo. Alla fine si è usata la fantasia e l'ingegno per trovare soluzioni indirette.
Inoltre gli effetti grafici più "sbalorditivi" (per Java, visto che alla fine rientrano ormai nella normalità), laddove erano richiesti, sono stati ottenuti con le AWTUtilities (vedi link di prima o google) che però non mi pare possano modificare la forma di un button, quanto piuttosto quella di una finestra (JFrame o JDialog).
Spero per te di sbagliarmi (ma non credo), l'unico consiglio a questo punto è di spulciare in queste AWTUtilities, ma senza troppe speranze. Forse a questo punto dovrebbe venirti il dubbio di aver capito male quello che intendeva il prof o che il prof non conosca bene Java.
Professionalmente useresti Batik: esporti la gui creata con Photoshop in formato SVG e la carichi così com'è in Java. Eventualmente aggiungi qualche tocco "meccanico": non conosco photoshop, non se sia possibile usarlo come script-editor, in ogni caso è possibile controllare via java la dinamica dei nodi svg.
Si tratta comunque di una soluzione poco usata. Per il genere di applicazioni in cui Java va per la maggiore (molto enterprise e poco divertimento) le forme predefinite dei look and feel di Swing persino eccessive.
Sbrizzolo86
06-05-2010, 18:06
Professionalmente useresti Batik: esporti la gui creata con Photoshop in formato SVG e la carichi così com'è in Java. Eventualmente aggiungi qualche tocco "meccanico": non conosco photoshop, non se sia possibile usarlo come script-editor, in ogni caso è possibile controllare via java la dinamica dei nodi svg.
Si tratta comunque di una soluzione poco usata. Per il genere di applicazioni in cui Java va per la maggiore (molto enterprise e poco divertimento) le forme predefinite dei look and feel di Swing persino eccessive.
Ho avuto l'occasione di prendere confindenza con Batik giusto poco tempo fa, per un progetto di geometria computazionale che doveva fare uso di SVG per la pubblicazione web del risultato (anche in maniera dinamica, ovvero compresa la costruzione del risultato).
Avevo sentito parlare delle potenzialità (che ho effettivamente riscontrato) dell'SVG e pure che qualcuno lo usasse per le interfacce, ma pensavo si trattasse dell'ennesima pretesa di un markup language tutto fare, invece devo ammettere che ho visto ottimi risultati in rete.
Tuttavia non lo userei mai per tale scopo...come hai ben detto Java Swing basta e avanza. E se ci sono problemi come quello che abbiamo visto in questo thread, si aggirano facilmente.
Dolcezeus
07-05-2010, 08:20
Grazie PGI-Bis la tua sembra la soluzione "più pulita" adesso non capisco come posso creare una classe a parte nn ci riesco.. alla fine mi dovrei trovare solo con l'oggetto da istanziare OvalButton
package test;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import javax.swing.JButton;
public class OvalButton extends JButton {
public OvalButton() {
setContentAreaFilled(false);
setFocusPainted(false);
setBorderPainted(false);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(0, 0, getWidth(), getHeight());
}
@Override
protected void processMouseEvent(MouseEvent e) {
Shape shape = new Ellipse2D.Double(0, 0, getWidth(), getHeight());
if (shape.contains(e.getPoint())) {
super.processMouseEvent(e);
} else {
e.consume();
}
}
@Override
public final void setContentAreaFilled(boolean b) {
super.setContentAreaFilled(b);
}
@Override
public final void setFocusPainted(boolean b) {
super.setFocusPainted(b);
}
@Override
public final void setBorderPainted(boolean b) {
super.setBorderPainted(b);
}
}
Dolcezeus
07-05-2010, 08:36
grazie della risposta "in tempo reale" adesso però ho un altro problema :D dovrei estendere Observable come posso fare?
Puoi usare la composizione. Cioè usi un campo. In effetti sarebbe preferibile usare la composizione sia per l'estensione di JButton che per Observable ma non mi sembra il caso di redigere un trattato.
Comunque è paleontologia, Observable e Observer si usavano nel triassico (uno dei problemi è che proprio la mancanza di un'interfaccia radice di Observable).
Dolcezeus
07-05-2010, 08:54
grazie PGI-Bis ma potresti essere più chiaro ovvero cn un esempio sono un niubbio :S
crei una sottoclasse di Observable (chiamiamola ObservableExt) che sovrascrive il metodo setChanged e il metodo clearChanged rendedoli pubblici.
In OvalField aggiungi un campo di tipo ObservableExt:
private final ObservableExt obs = new ObservableExt();
Poi puoi scegliere se pubblicare il campo, aggiungendo cioè un metodo:
public Observable getObservable() { return obs; }
o dichiarare un metodo:
public void addObserver(Observer o) { obs.addObserver(o); }
Il secondo sarebbe una composizione con rinvio ma non essendoci un'interfaccia da implementare è "alla pescatora".
Dopodichè quando hai una variabile OvalField e vuoi aggiungergli un Observer usi oval.addObserver(o) oppure oval.getObservable().addObserver(o), a seconda che tu abbia optato per il metodo di accesso o per il metodo che aggiunge un Observer.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.