Создание собственного JButton из изображений, содержащих прозрачные пиксели - PullRequest
7 голосов
/ 18 июля 2011

Прочтите edit 2, чтобы узнать, чего мне на самом деле не хватает, чтобы заставить его работать

В настоящее время я пытаюсь создать несколько пользовательских кнопок JBton с использованием изображений, созданных в фотошопе и имеющих альфа-параметр.

Пока что переопределение метода paint () для рисования изображения работало в том смысле, что кнопка нарисована, показывая правильное изображение.Хотелось бы улучшить его, сделав его форму (область клика) такой же, как у видимых пикселей на изображении (прямо сейчас, если я нарисую границу кнопки, это квадрат).

Есть липростой способ сделать это, или мне нужно проанализировать изображение и найти альфа-пиксели, чтобы создать пользовательскую рамку?

Какие методы мне придется переопределить, чтобы она работала так, как я хочу?

Кроме того, еще один вопрос, который я собираюсь задать позже: было бы лучше использовать какой-то алгоритм для изменения цвета изображений, чтобы создать впечатление, будто на него нажимают, когда люди нажимают на него, или мне лучшесоздать второе изображение и нарисовать его, пока кнопка активна?

Редактировать: Я только что прочитал другой вопрос, что мне нужно переопределить paintComponent () вместо paint (), яХотелось бы узнать, почему, так как переопределение paint () работает нормально?

Edit 2: Я изменил все, чтобы убедиться, что мои JButton создаются с помощью конструктора по умолчанию со значком.То, что я пытаюсь сделать, это получить координаты X и Y того места, где был зарегистрирован щелчок, и захватить пиксель значка в этой позиции и проверить его альфа-канал, чтобы увидеть, равен ли он 0 (если это так, ничего не делать, иначе сделатьдействие, которое он должен выполнять).

Дело в том, что альфа-канал всегда возвращает 255 (а синий, красный и зеленый имеют значение 238 для прозрачных пикселей).На других пикселях все возвращает значение, которое должно возвращаться.

Вот пример (попробуйте с другим изображением, если хотите), который воссоздает мою проблему:

public class TestAlphaPixels extends JFrame
{
  private final File FILECLOSEBUTTON = new File("img\\boutonrondX.png");  //My round button with transparent corners
  private JButton closeButton = new JButton(); //Creating it empty to be able to place it and resize the image after the button size is known


  public TestAlphaPixels() throws IOException
  {
    setLayout(null);
    setSize(150, 150);

    closeButton.setSize(100, 100);
    closeButton.setContentAreaFilled(false);
    closeButton.setBorderPainted(false);

    add(closeButton);

    closeButton.addMouseListener(new MouseListener()
      {

        public void mouseClicked(MouseEvent e)
        {
        }

        public void mousePressed(MouseEvent e)
        {
        }

        public void mouseReleased(MouseEvent e)
        {
          System.out.println("Alpha value of pixel (" + e.getX() + ", " + e.getY() + ") is: " + clickAlphaValue(closeButton.getIcon(), e.getX(), e.getY()));
        }

        public void mouseEntered(MouseEvent e)
        {
        }

        public void mouseExited(MouseEvent e)
        {
        }
      });
    Image imgCloseButton = ImageIO.read(FILECLOSEBUTTON);

    //Resize the image to fit the button
    Image newImg = imgCloseButton.getScaledInstance((int)closeButton.getSize().getWidth(), (int)closeButton.getSize().getHeight(), java.awt.Image.SCALE_SMOOTH);
    closeButton.setIcon(new ImageIcon(newImg));


  }

  private int clickAlphaValue(Icon icon, int posX, int posY) 
  {
    int width = icon.getIconWidth();
    int height = icon.getIconHeight();

    BufferedImage tempImage = (BufferedImage)createImage(width, height);
    Graphics2D g = tempImage.createGraphics();

    icon.paintIcon(null, g, 0, 0);

    g.dispose();

    int alpha = (tempImage.getRGB(posX, posY) >> 24) & 0x000000FF;

    return alpha;
  } 
  public static void main(String[] args)
  {
    try
    {
      TestAlphaPixels testAlphaPixels = new TestAlphaPixels();
      testAlphaPixels.setVisible(true);
      testAlphaPixels.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    catch(IOException ioe)
    {
      ioe.printStackTrace();
    }
  }
}

What this sample actually displays

Это просто дикая догадка, но возможно ли, что когда мое изображение будет преобразовано в Icon, оно потеряет свойство Alpha и, следовательно, не вернет правильное значение?В любом случае, я был бы очень признателен, если бы кто-то действительно помог мне и сказал, что я должен изменить, чтобы получить правильное значение.

Я предполагаю это, потому что, когда я пытаюсь сделать это с исходным изображением,значение альфа-канала в порядке, но я не могу использовать этот BufferedImage, потому что я изменил его размер, поэтому я на самом деле получаю значения канала изображения с оригинальным размером ...

Ответы [ 6 ]

6 голосов
/ 18 июля 2011

Я думаю, что вы на неправильном пути. Вам не нужно переопределять ни методы paint (), ни paintComponent (). JButton уже «знает», что будет показано только с изображением:

ImageIcon cup = new ImageIcon("images/cup.gif");
JButton button2 = new JButton(cup);

См., Например, следующий учебник: http://www.apl.jhu.edu/~hall/java/Swing-Tutorial/Swing-Tutorial-JButton.html

Кроме того, качели полностью настроены. Вы можете управлять непрозрачностью, рамкой, цветом и т. Д. Вам, вероятно, следует изменить некоторые из упомянутых методов, чтобы изменить функциональность. Но в большинстве случаев есть лучшее и более простое решение.

4 голосов
/ 19 июля 2011

Поскольку в нескольких ответах были хорошие элементы, но ни один из ответов не был завершен сам по себе, я отвечу на свой вопрос, чтобы другие люди, имеющие такую ​​же проблему, могли попробовать что-то подобное.

Я создал свои кнопки, используя новый класс, расширяющий JButton, с новым конструктором, который принимает BufferedImage в качестве параметра вместо значка. Причина этого в том, что когда я делал что-то вроде myButton.getIcon (), он возвращал Icon, тогда мне приходилось делать различные манипуляции с ним, чтобы сделать его BufferedImage подходящего размера, и в итоге он не работал в любом случае, потому что, похоже, что первое приведение к Icon заставило его потерять альфа-данные в пикселях, поэтому я не мог проверить, нажимал ли пользователь на прозрачные пиксели или нет.

Итак, я сделал что-то подобное для конструктора:

public class MyButton extends JButton
{
   private BufferedImage bufImg;

   public MyButton(BufferedImage bufImg)
   {
      super(new ImageIcon(bufImg));
      this.bufImg = bufImg;
   }
 }

Затем я создал аксессор для моего bufImg, который изменил размер изображения, чтобы он соответствовал JButton, используя метод getSize (), а затем вернул изображение, размер которого был изменен до нужного размера. Я делаю преобразования в методе доступа getBufImg (), потому что размер изображения может измениться при изменении размера окна. Когда вы вызываете getBufImg (), обычно это происходит из-за того, что вы нажимаете кнопку и, следовательно, в настоящее время вы не изменяете размер окна.

Что-то вроде этого вернет изображение в нужном размере:

 public BufferedImage getBufImg()
    {
      BufferedImage newImg = new BufferedImage(getSize().getWidth(), getSize().getHeight(), BufferedImage.TYPE_INT_ARGB); //Create a new buffered image the right size
      Graphics2D g2d = newImg.createGraphics();
      g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

      g2d.drawImage(bufImg, 0, 0, getSize().getWidth(), getSize().getHeight(), null);
      g2d.dispose();

      return newImg;
    }

С этим буферизованным изображением вы можете кодировать метод, подобный этому:

  private int clickAlphaValue(BufferedImage bufImg, int posX, int posY) 
  {
    int alpha;

    alpha = (bufImg.getRGB(posX, posY) >>24) & 0x000000FF; //Gets the bit that contains alpha information

    return alpha;
  }

Вы вызываете кнопку, которая реализует MouseListener, например:

myButton.addMouseListener(new MouseListener()
    {

    public void mouseClicked(MouseEvent e)
    {
    }

    public void mousePressed(MouseEvent e)
    {
    }

    public void mouseReleased(MouseEvent e)
    {
      if(clickAlphaValue(((myButton)e.getSource()).getBufImg(), e.getX(), e.getY()) != 0) //If alpha is not set to 0
        System.exit(0); //Or other things you want your button to do
    }

    public void mouseEntered(MouseEvent e)
    {
    }

    public void mouseExited(MouseEvent e)
    {
    }
  });

И вуаля! Кнопка выполнит действие, только если вы нажали непрозрачные пиксели.

Спасибо всем за помощь, я не смог бы придумать это самостоятельно.

3 голосов
/ 18 июля 2011

Если вы хотите иметь точки щелчка, характерные для фигуры, вам лучше использовать Shape и их метод contains. Если вы хотите, вы можете создать фигуру при создании своего пользовательского класса кнопки, как часть его, и реализовать метод содержимого с помощью метода contains формы.

Что касается пользовательского JButton, создайте класс, расширяющий JButton, например:

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

public class CustomButton extends JButton{

    /** Filename of the image to be used as the button's icon. */
    private String fileName;
    /** The width of the button */
    private int width;
    /** The height of the button. */
    private int height;

 public CustomButton(String fileName, int width, int height){
    this.fileName = fileName;
    this.width = width;
    this.height = height;
    createButton();
}

/**
 * Creates the button according to the fields set by the constructor.
 */
private void createButton(){
    this.setIcon(getImageIcon(filename));
    this.setPreferredSize(new Dimension(width, height));
    this.setMaximumSize(new Dimension(width, height));
    this.setFocusPainted(false);
    this.setRolloverEnabled(false);
    this.setOpaque(false);
    this.setContentAreaFilled(false);
    this.setBorderPainted(false);
    this.setBorder(BorderFactory.createEmptyBorder(0,0,0,0)); 
  }
}


Вот как вы можете загрузить ImageIcon, если вы хотите сделать это следующим образом.

  public ImageIcon getImageIcon(String fileName){
    String imageDirectory = "images/"; //relative to classpath
    URL imgURL = getClass().getResource(imageDirectory + fileName);
    return new ImageIcon(imgURL);
  }

Это даст вам кнопку, которая будет хотя бы выглядеть как ваше изображение. Я задал похожий вопрос относительно событий на основе изображений при нажатии, и формы помогли чудеса. Я полагаю, все сводится к тому, насколько сложны ваши изображения кнопок. Вот ссылка в любом случае:
Как обнаружить событие щелчка мыши на объекте Image в Java?

PS: Возможно, посмотрите на создание фигур из изображений, которые охватывают все непрозрачные пиксели. Не знаю, возможно ли это, но это будет означать, что кнопка будет «нажата», только если пользователь нажмет на часть изображения. Просто мысль.

2 голосов
/ 18 июля 2011

Если вы хотите, чтобы макет вашей кнопки соответствовал непрозрачным пикселям вашего изображения, вам следует переопределить метод paintComponent().Это самый правильный способ сделать это (переопределение paint () работало в старые времена, но сейчас не рекомендуется).

Однако я думаю, что это не совсем то, что вы хотите: вы хотите, чтобы нажатие на кнопку былообнаруживается только если он находится на непрозрачном пикселе, верно?В этом случае вам нужно проанализировать ваше изображение и при щелчке сравнить координаты мыши с альфа-каналом вашего изображения в пикселях, поскольку JButton не имеет такой функции.

1 голос
/ 19 июля 2011

Если у вас есть круглая кнопка, это именно то, что вам нужно:

  public class RoundButton extends JButton {

       public RoundButton() {
         this(null, null);
      }
       public RoundButton(Icon icon) {
         this(null, icon);
      }
       public RoundButton(String text) {
         this(text, null);
      }
       public RoundButton(Action a) {
         this();
         setAction(a);
      }

       public RoundButton(String text, Icon icon) {
         setModel(new DefaultButtonModel());
         init(text, icon);
         if(icon==null) return;
         setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
         setContentAreaFilled(false);
         setFocusPainted(false);
         initShape();
      }

    protected Shape shape, base;
    protected void initShape() {
      if(!getBounds().equals(base)) {
        Dimension s = getPreferredSize();
        base = getBounds();
        shape = new Ellipse2D.Float(0, 0, s.width, s.height);
      }
    }
    @Override public Dimension getPreferredSize() {
      Icon icon = getIcon();
      Insets i = getInsets();
      int iw = Math.max(icon.getIconWidth(), icon.getIconHeight());
      return new Dimension(iw+i.right+i.left, iw+i.top+i.bottom);
    }

    @Override public boolean contains(int x, int y) {
      initShape();
      return shape.contains(x, y);
      //or return super.contains(x, y) && ((image.getRGB(x, y) >> 24) & 0xff) > 0;
    }
  }

У JButton есть метод contains(). Переопределите это и вызовите его на mouseReleased ();

1 голос
/ 18 июля 2011

paintComponent() вместо paint() зависит, если вы paint() внутри XxxButtonUI или просто переопределите paintComponent(), но существует опция JButton # setIcon .

...