简体   繁体   中英

Android like Toast in Swing

I am trying to develop a Toast (Android) like feature in my Swing application. As a standalone, its working perfectly. But when integrated into the application, its posing problems.

The Class file is:

import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.geom.RoundRectangle2D;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JLabel;
import net.mindcrew.utils.LayoutHelper.Packer;

public class Toast extends JDialog {

    String text;

    public Toast(String text) {
        this.text = text;
        initComponents();
    }

    private void initComponents(){
        setLayout(new GridBagLayout());
        addComponentListener(new ComponentAdapter() {
            // Give the window an rounded rect shape. LOOKS GOOD
            // If the window is resized, the shape is recalculated here.
            @Override
            public void componentResized(ComponentEvent e) {
                setShape(new RoundRectangle2D.Double(0,0,getWidth(),getHeight(),50,50));
            }
        });

        setUndecorated(true);
        setSize(300,100);
        setLocationRelativeTo(null);
        getContentPane().setBackground(Color.BLACK);

        // Determine what the GraphicsDevice can support.
        GraphicsEnvironment ge = 
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        final boolean isTranslucencySupported = 
            gd.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.TRANSLUCENT);

        //If shaped windows aren't supported, exit.
        if (!gd.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSPARENT)) {
            System.err.println("Shaped windows are not supported");
        }

        //If translucent windows aren't supported, 
        //create an opaque window.
        if (!isTranslucencySupported) {
            System.out.println(
                "Translucency is not supported, creating an opaque window");
        }

        // Set the window to 70% translucency, if supported.
        if (isTranslucencySupported) {
            setOpacity(0.9f);
        }

        ImageIcon loading = new ImageIcon(Toast.class.getResource("/net/mindcrew/utils/userinterface/resources/loading-photo.gif"));

        JLabel label = new JLabel(text);
        label.setForeground(Color.WHITE);
        label.setIcon(loading);
        Packer packer = new Packer(this);
        packer.pack(label).fillboth().west().inset(0, 50, 0, 20);
    }

    public static Toast showDailog(String textToDisplay){
        final Toast toast = new Toast(textToDisplay);
        // Display the window.
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                toast.setVisible(true);
            }
        });
        thread.start();
        return toast;
    }

    @Override
    public void hide(){
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                setVisible(false);
                dispose();
            }
        });
    }

    public static void main(String... args){
        Toast toast = Toast.showDailog("Display");
        try{
            Thread.sleep(5000);
        }
        catch (Exception e){}
        toast.hide();
    }

}

There may be some implementation faults, but this is the basic thing.

This works well. But when I try to put it up in the way of a resource intensive operation, its tripping. As in the GIF animation is not showing up, which I think implies that its sort of stalled.

The use is:

  Toast toast = Toast.showDailog("Generating PDF");

//resource intensive operation. Takes about 3-5seconds to execute

    toast.hide();

To add to my misery, even after the "Toast" has been disposed, the application is becoming dreadfully slow. I am pretty sure that the slowing down is not because of the operation in question, since its working perfectly if I do away with the "Toast".

Can somebody please point out what is wrong here???

I went through this question . But the things there are far too complicated than what I am looking for. What I am looking for is a simple dialog. Not a full blown frame which needs to accommodate several components.

Try this code for toast:

public class Test extends JFrame {

    private JPanel contentPane;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Test frame = new Test();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public Test() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout(0, 0));
        setContentPane(contentPane);

        JButton btnTestToast = new JButton("Test Toast");
        btnTestToast.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                ToastMessage toastMessage = new ToastMessage("Sample text to toast ",3000);
                toastMessage.setVisible(true);
            }
        });
        contentPane.add(btnTestToast, BorderLayout.SOUTH);
    }

}

public class ToastMessage extends JDialog {
    int miliseconds;
    public ToastMessage(String toastString, int time) {
        this.miliseconds = time;
        setUndecorated(true);
        getContentPane().setLayout(new BorderLayout(0, 0));

        JPanel panel = new JPanel();
        panel.setBackground(Color.GRAY);
        panel.setBorder(new LineBorder(Color.LIGHT_GRAY, 2));
        getContentPane().add(panel, BorderLayout.CENTER);

        JLabel toastLabel = new JLabel("");
        toastLabel.setText(toastString);
        toastLabel.setFont(new Font("Dialog", Font.BOLD, 12));
        toastLabel.setForeground(Color.WHITE);

        setBounds(100, 100, toastLabel.getPreferredSize().width+20, 31);


        setAlwaysOnTop(true);
        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
        int y = dim.height/2-getSize().height/2;
        int half = y/2;
        setLocation(dim.width/2-getSize().width/2, y+half);
        panel.add(toastLabel);
        setVisible(false);

        new Thread(){
            public void run() {
                try {
                    Thread.sleep(miliseconds);
                    dispose();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

I implemented it as similar as tooltip. It's tested and working.

import java.awt.Color;
import java.awt.Label;
import java.awt.Point;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.Popup;
import javax.swing.PopupFactory;

public class Toast {

    private final JComponent component;
    private Point   location;
    private final String  message;
    private long duration; //in millisecond

    public Toast(JComponent comp, Point toastLocation, String msg, long forDuration) {
        this.component = comp;
        this.location = toastLocation;
        this.message = msg;
        this.duration = forDuration;

        if(this.component != null)
        {

            if(this.location == null)
            {
                this.location = component.getLocationOnScreen();
            }

            new Thread(new Runnable()
            {
                @Override
                public void run() 
                {
                    Popup view = null;
                    try 
                    {
                        Label tip = new Label(message);
                        tip.setForeground(Color.red);
                        tip.setBackground(Color.white);
                        view = PopupFactory.getSharedInstance().getPopup(component, tip , location.x + 30, location.y + component.getHeight() + 5);
                        view.show();
                        Thread.sleep(duration);
                    } catch (InterruptedException ex) 
                    {
                        Logger.getLogger(Toast.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    finally
                    {
                        view.hide();
                    }
                }
            }).start();
        }
    }



    public static void showToast(JComponent component, String message)
    {
        new Toast(component, null, message, 2000/*Default 2 Sec*/);
    }

    public static void showToast(JComponent component, String message, Point location, long forDuration)
    {
        new Toast(component, location, message, forDuration);
    }
}

To use this class you just need to call showToast()

可能是JOptionPane是您需要的吗?

I think you can solve your problem by not overriding the hide method in Dialog and modify the showDialog method to be something like this:

public static void showDailog(String textToDisplay){
    final Toast toast = new Toast(textToDisplay);
    // Display the window.
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            try{
                toast.setVisible(true);
                Thread.sleep(5000);
                toast.setVisible(false);
                toast.dispose();
            }
            catch(Exception ex){
                ex.printStackTrace();
            }
        }
    });
    thread.start();
}

我做了一些对我有用的更改,希望这对使用Java Swing的Toast这样的Android有所帮助

You can use a rounded self-disposing JFrame as overlay which is positioned relative to your application window. By fading it in an out it looks like an Android Toast. Here's the code:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.RoundRectangle2D;

class Toast extends JFrame {

    private final float MAX_OPACITY = 0.8f;
    private final float OPACITY_INCREMENT = 0.05f;
    private final int FADE_REFRESH_RATE = 20;

    private final int WINDOW_RADIUS = 15;
    private final int CHARACTER_LENGTH_MULTIPLIER = 9;
    private final int DISTANCE_FROM_PARENT_BOTTOM = 100;


    public Toast(JFrame owner, String toastText) {
        setTitle("Transparent JFrame Demo");
        setLayout(new GridBagLayout());

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setUndecorated(true);
        setFocusableWindowState(false);

        setOpacity(0.4f);


        // setup the toast lable
        JLabel b1 = new JLabel(toastText);
        b1.setForeground(Color.WHITE);
        b1.setOpaque(false);
        add(b1);

        setSize(toastText.length() * CHARACTER_LENGTH_MULTIPLIER, 50);

        int x = (int) (owner.getLocation().getX() + (owner.getWidth() / 2));
        int y = (int) (owner.getLocation().getY() + owner.getHeight() - DISTANCE_FROM_PARENT_BOTTOM);
        setLocation(new Point(x, y));


        // configure frame
        setShape(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), WINDOW_RADIUS, WINDOW_RADIUS));
        getContentPane().setBackground(new Color(0, 0, 0, 170));

    }


    public void fadeIn() {
        setOpacity(0);
        setVisible(true);

        final Timer timer = new Timer(FADE_REFRESH_RATE, null);
        timer.setRepeats(true);
        timer.addActionListener(new ActionListener() {
            private float opacity = 0;


            @Override
            public void actionPerformed(ActionEvent e) {
                opacity += OPACITY_INCREMENT;
                setOpacity(Math.min(opacity, MAX_OPACITY));
                if (opacity >= MAX_OPACITY) {
                    timer.stop();
                }
            }
        });

        timer.start();
    }


    public void fadeOut() {
        final Timer timer = new Timer(FADE_REFRESH_RATE, null);
        timer.setRepeats(true);
        timer.addActionListener(new ActionListener() {
            private float opacity = MAX_OPACITY;


            @Override
            public void actionPerformed(ActionEvent e) {
                opacity -= OPACITY_INCREMENT;
                setOpacity(Math.max(opacity, 0));
                if (opacity <= 0) {
                    timer.stop();
                    setVisible(false);
                    dispose();
                }
            }
        });

        setOpacity(MAX_OPACITY);
        timer.start();
    }


    public static void makeToast(final JFrame owner, final String toastText, final int durationSec) {


        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Toast toastFrame = new Toast(owner, toastText);
                    toastFrame.fadeIn();
                    Thread.sleep(durationSec * 1000);
                    toastFrame.fadeOut();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }).start();


    }


    public static void main(String args[]) {
        final JFrame frame = new JFrame("Cloud Tester");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


        JPanel jPanel = new JPanel();
        jPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        JButton toastButton = new JButton("show toast");
        jPanel.add(toastButton);

        toastButton.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Toast.makeToast(frame, "a toast!", 3);
            }
        });


        frame.add(jPanel);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }
}

在此处输入图片说明

Class below is ToastMessage.java, should provide an Android-ish toast message. Call it from your project using new ToastMessage(this, "Short message here!"); Note this can be any component or panel from your calling JFrame, used to determine the location on screen.

import java.awt.Color;
import java.awt.GridBagLayout;
import java.awt.Window;
import java.awt.geom.RoundRectangle2D;

import javax.swing.*;

public class ToastMessage extends JDialog {

    private static final long serialVersionUID = 1L;
    private static Boolean spamProtect = false;
    private final int milliseconds = 1500;

    public ToastMessage(JComponent caller, String toastString) {
        if(spamProtect) {
            return;
        }
        setUndecorated(true);
        setAlwaysOnTop(true);
        setFocusableWindowState(false);
        setLayout(new GridBagLayout());

        JPanel panel = new JPanel();
        panel.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
        panel.setBackground(new Color(160, 160, 160));
        JLabel toastLabel = new JLabel(toastString);
        toastLabel.setForeground(Color.WHITE);
        panel.add(toastLabel);
        add(panel);
        pack();

        Window window = SwingUtilities.getWindowAncestor(caller);
        int xcoord = window.getLocationOnScreen().x + window.getWidth() / 2 - getWidth() / 2;
        int ycoord = window.getLocationOnScreen().y + (int)((double)window.getHeight() * 0.75) - getHeight() / 2;
        setLocation(xcoord, ycoord);
        setShape(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), 30, 30));
        setVisible(true);

        new Thread(){
            public void run() {
                try {
                    spamProtect = true;
                    Thread.sleep(milliseconds);
                    dispose();
                    spamProtect = false;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

Holger Brandl's answer worked fine, however my system (kubuntu) didn't support the setOpacity method and crashed. So I just had to override that method with a try/catch and have an opaque Toast, which was good enough for me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM