W tym wpisie korzystałam z materiałów udostępnionych przez moją Panią.
Ten wpis jest kontynuacją wpisu: Formatowanie tekstu podpowiedzi (bez użycia HTML)

Klasy

Przygotowujemy 4 klasy:

  • ImageToolTip.java
  • ImagetoolTipUI.java
  • MyButton.java
  • Main.java
ImageToolTip.java
package tooltip2;

import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
import javax.swing.plaf.*;

public class ImageToolTip extends JToolTip {
    private static final long serialVersionUID = 2381804711089908434L;
    private BufferedImage[] images = new BufferedImage[0];
    private BufferedImage[] scaledImages = new BufferedImage[0];
    private int[] afterTokenNos = new int[0];

    /**
     * ustawia nowe ToolTipUI dla tej podpowiedzi
     * przekazujac do ToolTipUI obrazki i miejsce w ktorym
     * ma sie podpowiedz znalezc
     */
    public ImageToolTip() {
        //przekazuje puste tablize o zerowej dlugosci
        setUI(new ImageToolTipUI(scaledImages, afterTokenNos));
    }

    @Override
    public void setUI(ComponentUI newUI) {
        super.setUI(newUI);
    }

    /**
     * wstawia zeskalowany obrazek do
     */
    public void setImage(BufferedImage image, double scale, int afterTokenNo) {
        AffineTransform af = AffineTransform.getScaleInstance(scale, scale);
        AffineTransformOp atop = new AffineTransformOp(af,
                AffineTransformOp.TYPE_BICUBIC);
        int len = images.length; //okresla dlugosc tablicy (poczatkowo jest zerowa
        images = Arrays.copyOf(images, len + 1);//przedluza tablice o 1
        scaledImages = Arrays.copyOf(scaledImages, len + 1);//przedluza tablice o 1
        afterTokenNos = Arrays.copyOf(afterTokenNos, len + 1);//przedluza tablice o 1
        BufferedImage scaledImage = atop.filter(image, null);//skaluje obrazek
        images[len] = image;//wstawia obrazek do tablicy
        scaledImages[len] = scaledImage;//wstawia skalowany obrazek do tablicy
        afterTokenNos[len] = afterTokenNo;// wstawia token do tablicy
        setUI(new ImageToolTipUI(scaledImages, afterTokenNos));// ustawia
        // nowy ToolTipUI - przekazuje tablice obrazkow i tokenow do ToolTipUI
    }

    public void removeImage(int imageNumber) {
        images = shortenArray(images, imageNumber);
        scaledImages = shortenArray(scaledImages, imageNumber);
        afterTokenNos = shortenArray(afterTokenNos, imageNumber);
        setUI(new ImageToolTipUI(scaledImages, afterTokenNos));// ustawia
        // nowy ToolTipUI - przekazuje tablice obrazkow i tokenow do ToolTipUI
    }

    public void removeImages() {
        images = new BufferedImage[0];
        scaledImages = new BufferedImage[0];
        afterTokenNos = new int[0];
        setUI(new ImageToolTipUI(scaledImages, afterTokenNos));// ustawia
        // nowy ToolTipUI - przekazuje tablice obrazkow i tokenow do ToolTipUI
    }

    public BufferedImage[] getImages() {
        return images;
    }

    public BufferedImage[] getScaledImages() {
        return scaledImages;
    }

    //zamienia nr slowa na nr tokenu (tokenami sa tez spacje)
    //dlatego mnozenie przez 2 i dodanie 1 (uwzglednienie tego slowa)
    public static int findTokenNo(String text, String wordToFind,
                                  String delimiter) {
        StringTokenizer st = new StringTokenizer(text, delimiter);
        ArrayList<String> al = new ArrayList<>();
        while (st.hasMoreTokens()) {
            al.add(st.nextToken());
        }
        return al.indexOf(wordToFind) * 2 + 1;
    }

    //skraca dlugosc tablicy obrazkow dla typow obiektowych
    public static <T> T[] shortenArray(T[] tabl, int indexDeleted) {
        int len = tabl.length;
        //sprawdza czy index miesci sie w tablicy
        if ((indexDeleted > len - 1) || (indexDeleted < 0)) {
            throw new IndexOutOfBoundsException("index poza tablicą");
        } else {//jesli tak
            //to przepisuje rekordy za indeksem o 1 w lewo
            for (int i = indexDeleted; i < tabl.length - 1; i++) {
                tabl[i] = tabl[i + 1];
            }
        }
        //a nastepnie skraca tablice o 1
        return Arrays.copyOf(tabl, len - 1);
    }

    //to samo tylko dla typow prymitywnych
    public static double[] shortenArray(double[] tabl, int indexDeleted) {
        int len = tabl.length;
        if ((indexDeleted > len - 1) || (indexDeleted < 0)) {
            throw new IndexOutOfBoundsException("index poza tablicą");
        } else {
            for (int i = indexDeleted; i < tabl.length - 1; i++) {
                tabl[i] = tabl[i + 1];
            }
        }
        return Arrays.copyOf(tabl, len - 1);
    }

    public static int[] shortenArray(int[] tabl, int indexDeleted) {
        int len = tabl.length;
        if ((indexDeleted > len - 1) || (indexDeleted < 0)) {
            throw new IndexOutOfBoundsException("index poza tablicą");
        } else {
            for (int i = indexDeleted; i < tabl.length - 1; i++) {
                tabl[i] = tabl[i + 1];
            }
        }
        return Arrays.copyOf(tabl, len - 1);
    }
}
ImagetoolTipUI.java
package tooltip2;

import java.awt.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
import javax.swing.plaf.metal.*;

/* 
 * wyswietla podpowiedz uzywajac MetalLookAndFeel 
 */
public class ImageToolTipUI extends MetalToolTipUI {
    private String[] tokeny;//tokeny
    private int rectW = 0;//szerokosc wyswietlonej podpowiedzi
    private final BufferedImage[] scaledImages;//skalowane obrazki
    private final int[] afterTokenNos;//pozycje obrazkow w tekscie
    private final int[] scaledWidths;//szerokosci obrazkow
    private final int[] scaledHeights;//wysokosci obrazkow

    public ImageToolTipUI(BufferedImage[] scaledImages, int[] afterTokenNos) {
        this.scaledImages = scaledImages;
        this.afterTokenNos = afterTokenNos;
        int len = scaledImages.length;
        scaledWidths = new int[len];//utowrzenie tablicy
        scaledHeights = new int[len];//j.w.
        //wypelnienie tablic
        for (int i = 0; i < scaledImages.length; i++) {
            scaledWidths[i] = scaledImages[i].getWidth();
            scaledHeights[i] = scaledImages[i].getHeight();
        }
    }

    @Override
    public void paint(Graphics g, JComponent c) {
        FontMetrics fm = g.getFontMetrics(g.getFont());
        int w = c.getWidth();//szerokosc komponentu dla ktorego jest podpowiedz
        int h = c.getHeight();//j.w. ytlko wysokosc
        g.setColor(c.getBackground());
        g.fillRect(0, 0, w, h);
        g.setFont(c.getFont());
        g.setColor(c.getForeground());
        int curX = 5;//polozenie tekstu na komponencie
        int curY = 15;//polozenie tekstu na komponencie
        int akt;//szerokosc aktualnie wypisywanego slowa
        @SuppressWarnings("unused")
        int rows = 1;//liczba linii tekstu
        if (afterTokenNos.length > 0) {//jesli istnieja obrazki
            for (int i = 0; i < tokeny.length; i++) {//przebieg przez tokeny
                for (int k = 0; k < afterTokenNos.length; k++) {//przebieg przez pozycje obrazkow
                    if (i != afterTokenNos[k]) {//jesli po tym tokenie nie ma obrazka
                        if (k == 0) {//ogranicznik. Gdyby go nie bylo to
                            //kazde slowo byloby wyswietlone 3 razy, np. JestJestJest
                            //,a tak wyswietla slowo tylko raz
                            akt = fm.stringWidth(tokeny[i]);//sprawdza szerokosc stringu
                            //w pikselach
                            if (!tokeny[i].endsWith("\n")) {//jesli token nie ma znaku lamania
                                if ((curX + akt) < rectW + 5) {//jesli sie miesci w ramce
                                    g.drawString(tokeny[i], curX, curY);//to wypisuje
                                    curX += akt;//zmienia pozycje "glowicy"
                                } else {//jesli sie nie miesci
                                    curX = 5;//to przenosi "glowice" na poczatk
                                    curY += fm.getHeight();//i do nowego wiersza
                                    rows++;//dodaje nowy rzad
                                    g.drawString(tokeny[i], curX, curY);//wypisuje string
                                    curX += akt;//zmienia pozycje "glowicy"
                                }
                            } else {
                                curX = 5;//to przenosi "glowice" na poczatk
                                curY += fm.getHeight();//i do nowego wiersza
                                rows++;//dodaje nowy rzad
                                g.drawString(tokeny[i], curX, curY);//wypisuje string
                                curX += akt;//zmienia pozycje "glowicy"
                            }
                        }
                    } else {//jesli po tym tokenie jest obrazek
                        curX = 5;//przenosi glwice na poczatek
                        curY += fm.getHeight();//i do nowego wiersza
                        g.drawImage(scaledImages[k], curX, curY,//rysuje obrazek
                                scaledWidths[k], scaledHeights[k], null);
                        curX = 5;//przenosi kursor na poczatek
                        curY += scaledHeights[k] + fm.getHeight();//i do nowego wiersza
                        rows += scaledHeights[k] / fm.getHeight() + 2;//uaktualnia liczbe
                        //rzedow uwzgledniajac szerokosc obraz oraz linie ponad i pod obrazkiem
                    }
                }
            }
        } else {//jesli nie istnieja obrazki
            for (String s : tokeny) {
                akt = fm.stringWidth(s);//sprawdza szerokosc stringu
                //w pikselach
                //w pikselach
                if (!s.endsWith("\n")) {//jesli token nie ma znaku lamania
                    if ((curX + akt) < rectW + 5) {//jesli sie miesci w ramce
                        g.drawString(s, curX, curY);//to wypisuje
                        curX += akt;//zmienia pozycje "glowicy"
                    } else {//jesli sie nie miesci
                        curX = 5;//to przenosi "glowice" na poczatk
                        curY += fm.getHeight();//i do nowego wiersza
                        rows++;//dodaje nowy rzad
                        g.drawString(s, curX, curY);//wypisuje string
                        curX += akt;//zmienia pozycje "glowicy"
                    }
                } else {
                    curX = 5;//to przenosi "glowice" na poczatk
                    curY += fm.getHeight();//i do nowego wiersza
                    rows++;//dodaje nowy rzad
                    g.drawString(s, curX, curY);//wypisuje string
                    curX += akt;//zmienia pozycje "glowicy"
                }
            }
        }
    }

    /**
     * ta metoda wywolywana jest zawsze przed paint()
     */
    @Override
    public Dimension getPreferredSize(JComponent c) {
        FontMetrics fm = c.getFontMetrics(c.getFont());
        String tipText = ((JToolTip) c).getTipText();
        if (tipText == null) {
            tipText = "";
        }
        StringTokenizer st = new StringTokenizer(tipText, " ", true);
        int tokNum = st.countTokens();
        tokeny = new String[tokNum];
        int i = -1;
        int maxStrLen = 0;
        String maxString = null;
        while (st.hasMoreTokens()) {
            i++;
            String val = st.nextToken();//pobiera token
            tokeny[i] = val;//przypisuje token do tablicy
            int valLen = val.length();//sprawdza ilosc znakow w tokenie
            if (valLen > maxStrLen) {//sprawdza czy token jest najdluzszy
                maxStrLen = valLen;//jesli tak to ustawia dlugosc
                maxString = tokeny[i];//i pobiera ten string
            }
        }
        int textH = fm.getHeight();//wysokosc tekstu
        int textW = fm.stringWidth(tipText);//dlugosc stringu bez obrazkow
        int textArea = textH * textW;//powierzchnia zajmowana przez tekst
        rectW = (int) Math.round(Math.sqrt(textArea / 0.62));//zloty podzial
        int len1 = fm.stringWidth(maxString);//dlugosc najdluzszego slowa
        if (rectW < len1 + 5) {//jesli szerokosc jest mniejsza niz najdluzsze slowo
            rectW = len1 + 10;//powieksza obrazek
        }
        int curX = 5;//polozenie tekstu na scenie
        @SuppressWarnings("unused")
        int curY = 15;//polozenie tekstu na scenie
        int akt;//szerokosc aktualnie wypisywanego slowa
        int rows = 1;//liczba linii tekstu
        //wszystko dokladnie tak samo jak w metodzie paint
        if (afterTokenNos.length > 0) {
            for (i = 0; i < tokeny.length; i++) {
                for (int k = 0; k < afterTokenNos.length; k++) {
                    if (i != afterTokenNos[k]) {
                        if (k == 0) {
                            akt = fm.stringWidth(tokeny[i]);//sprawdza szerokosc stringu
                            //w pikselach
                            if (!tokeny[i].endsWith("\n")) {
                                if ((curX + akt) < rectW + 5) {//jesli sie miesci w ramce
                                    curX += akt;//zmienia pozycje "glowicy"
                                } else {//jesli sie nie miesci
                                    curX = 5;//to przenosi "glowice" na poczatk
                                    curY += fm.getHeight();//i do nowego wiersza
                                    rows++;//dodaje nowy rzad
                                    curX += akt;//zmienia pozycje "glowicy"
                                }
                            } else {
                                curX = 5;//to przenosi "glowice" na poczatk
                                curY += fm.getHeight();//i do nowego wiersza
                                rows++;//dodaje nowy rzad
                                curX += akt;//zmienia pozycje "glowicy"
                            }
                        }
                    } else {
                        curY += textH;
                        curX = 5;
                        curY += scaledHeights[k] + textH;
                        rows += scaledHeights[k] / textH + 2;
                    }
                }
            }
        } else {
            for (i = 0; i < tokeny.length; i++) {
                akt = fm.stringWidth(tokeny[i]);//sprawdza szerokosc stringu
                //w pikselach
                //w pikselach
                if (!tokeny[i].endsWith("\n")) {//jesli token nie ma znaku lamania
                    if ((curX + akt) < rectW + 5) {//jesli sie miesci w ramce
                        curX += akt;//zmienia pozycje "glowicy"
                    } else {//jesli sie nie miesci
                        curX = 5;//to przenosi "glowice" na poczatk
                        curY += fm.getHeight();//i do nowego wiersza
                        rows++;//dodaje nowy rzad
                        curX += akt;//zmienia pozycje "glowicy"
                    }
                } else {
                    curX = 5;//to przenosi "glowice" na poczatk
                    curY += fm.getHeight();//i do nowego wiersza
                    rows++;//dodaje nowy rzad
                    curX += akt;//zmienia pozycje "glowicy"
                }
            }
        }
        int dismissDelay = 4000;//czas wyswietalnia
        if (rows > 4) {
            dismissDelay = rows * 1300;//uzalezniony od dlugosci tekstu do
            //przeczytania
        }
        //ustawienie tych wartosci
        ToolTipManager.sharedInstance().setDismissDelay(dismissDelay);
        //okreslenie wymiarow z uwzglednieniem margnesow(8) i (5) oraz
        //odstepow miedzytekstowych(+1)
        return new Dimension(rectW + 8, rows * (fm.getHeight() + 1) + 5);
    }
}
MyButton.java
package tooltip2;

import java.awt.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;

public class MyButton extends JButton {
    private static final long serialVersionUID = 3602182470147405262L;
    private BufferedImage[] bimages = new BufferedImage[0];
    private double[] scales = new double[0];
    private int[] afterTokenNos = new int[0];
    private final ImageToolTip tip = new ImageToolTip();

    MyButton() {
        super();
    }

    MyButton(Action a) {
        super(a);
    }

    MyButton(Icon icon) {
        super(icon);
    }

    MyButton(String text) {
        super(text);
    }

    MyButton(String text, Icon icon) {
        super(text, icon);
    }

    /**
     * Tworzy podpowiedz dla komponentu MyButton
     */
    @Override
    public JToolTip createToolTip() {
        tip.setComponent(this);
        tip.setBackground(Color.GREEN);
        tip.setForeground(Color.BLUE);
        tip.setFont(new Font("Dialog", Font.ITALIC, 14));
        return tip;
    }

    public void setToolTipBimage(BufferedImage bimage, double scale,
                                 int afterTokenNo) {
        int len = bimages.length;
        bimages = Arrays.copyOf(bimages, len + 1);
        scales = Arrays.copyOf(scales, len + 1);
        afterTokenNos = Arrays.copyOf(afterTokenNos, len + 1);
        bimages[len] = bimage;
        scales[len] = scale;
        afterTokenNos[len] = afterTokenNo;
        tip.setImage(bimage, scale, afterTokenNo);
    }

    public void removeToolTipBimage(int imageNumber) {
        bimages = ImageToolTip.shortenArray(bimages, imageNumber);
        scales = ImageToolTip.shortenArray(scales, imageNumber);
        afterTokenNos = ImageToolTip.shortenArray(afterTokenNos, imageNumber);
        tip.removeImage(imageNumber);
    }

    public void removeTooltipBimages() {
        bimages = new BufferedImage[0];
        scales = new double[0];
        afterTokenNos = new int[0];
        tip.removeImages();
    }
}
Main.java
package tooltip2;

import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;

/**
 * ramka + panel
 */
public class Main extends JFrame {
    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Main());
    }

    public Main() {
        setLayout(null);
        setPreferredSize(new Dimension(800, 800));
        int frameHeight = 800;
        int frameWidth = 800;
        setBounds((Toolkit.getDefaultToolkit().getScreenSize().width / 2)
                        - (frameWidth / 2), (Toolkit.getDefaultToolkit()
                        .getScreenSize().height / 2) - (frameHeight / 2), frameWidth,
                frameHeight);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Ramka Graficzna");
        //  setResizable(false);
        JPanel panel = new JPanel();
        panel.setLayout(null);
        panel.setBounds(0, 0, frameWidth, frameHeight);
        MyButton but = new MyButton("Button");
        BufferedImage bimage = fileToBimage("swings/src/assets/smok.jpg");
        BufferedImage bimage1 = fileToBimage("swings/src/assets/smok1.jpg");
        BufferedImage bimage2 = fileToBimage("swings/src/assets/smok2.jpg");
        String str = "Jest to moment konkretyzowania planów. Wskazuje na początek dziaia albo nowy fakt w życiu pytającego. Należy uważnie rozważyć karty sąsiednie. Karta ta oznacza przede wszystkim pracę, plany i dzieci. Może wskazywać na początek nowego, ważnego etapu w życiu pytającego. Spotkanie z drugim człowiekiem (w towarzystwie karty Diabeł - akt seksualny). Niezależność jest korzystna dla pytającego. Pytający może narzucić swoją wolę, kontynuować wysiłek i zrealizować plany. Karta ta często reprezentuje młodą osobę (o ile nie występuje w towarzystwie karty Pustelnik), albo pytającego, o ile nie przekroczył czterdziestki. Mag w sąsiedztwie karty Słoñce reprezentuje miłość męską.";
        but.setToolTipBimage(bimage, 0.2,
                ImageToolTip.findTokenNo(str, "planów.", " "));
        but.setToolTipBimage(bimage1, 0.5,
                ImageToolTip.findTokenNo(str, "działania", " "));
        but.setToolTipBimage(bimage2, 0.7,
                ImageToolTip.findTokenNo(str, "uważnie", " "));
        but.removeToolTipBimage(0);
        but.setToolTipText(str);
        but.setBounds(100, 100, 100, 40);
        panel.add(but);
        add(panel);
        setVisible(true);
    }

    /**
     * Wczytuje obrazek z pliku i zamienia go na BufferedImage
     * Ta metoda, z nieznanych mi przyczyn nie chciała działać z filtrami
     * <code>RescaleOp</code> i <code>ConvolveOp</code>, czyli np. blur(),
     * sharp(), brightness()
     *
     * @param plik String - wskazuje plik z obrazkiem
     * @return BufferedImage - zwraca obrazek buforowany znajdujący się w pliku
     */
    public static BufferedImage fileToBimage(String plik) {
        File f = new File(plik);
        BufferedImage bimage = null;
        try {
            bimage = ImageIO.read(f);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bimage;
    }
}

Wynik

Po uruchomieniu klasy Main i najechaniu kursorem myszy na przycisk zobaczymy:


Rys. 162. Obrazek wstawiony do podpowiedzi

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *