我需要一个带有小按钮(或类似的东西)的 JPasswordField 和一个图标,当在该图标上按下鼠标左键时,该图标会显示到目前为止输入的密码。
这可以很容易地实现,创建一个由密码字段和右侧带有图标的标签组成的面板,如这个问题的答案中所建议的,但我更喜欢使用单个组件来完成这项工作。
经过一番分析,我找到了一个解决方案,就是带有特定边框的普通字段。该边框是原始边框的包装,并在显示图标的右侧放大。
由于我需要这样的组件,我认为其他人可能正在寻找同样的东西,我想分享我的解决方案。
请找到我的解决方案作为答案。
/**********************************************************************************************************************
* Package definition
*********************************************************************************************************************/
package test;
/**********************************************************************************************************************
* Import specifications
*********************************************************************************************************************/
import java.awt.*;
import java.awt.event.*;
import java.net.URL;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.*;
/**********************************************************************************************************************
* This class manages a password field with a little eye on the right side showing the password when the mouse is
* pressed.
*********************************************************************************************************************/
@SuppressWarnings("serial")
public class PreviewPasswordField extends JPasswordField
{
/********************************************************************************************************************
* The constructor initializes the object.
*******************************************************************************************************************/
public PreviewPasswordField()
{
// ---------------------
// Initialize attributes
// ---------------------
requestedBorder = getBorder();
requestedCursor = getCursor();
imageCursor = new Cursor(Cursor.HAND_CURSOR);
// --------------
// Set new border
// --------------
setBorder(new PasswordBorder());
// ---------------
// Listen to mouse
// ---------------
PasswordMouseListener listener;
listener = new PasswordMouseListener();
addMouseListener(listener);
addMouseMotionListener(listener);
addCaretListener(listener);
// ----
// Done
// ----
objectConstructed = true;
} // constructor
/********************************************************************************************************************
* This method sets the border of this component.
*******************************************************************************************************************/
@Override
public void setBorder(Border border)
{
// ------------------------------------------------------------------------------------------
// We're called also during the construction of the component when setting the default border
// Therefore, do normal stuff if construction not completed yet
// ------------------------------------------------------------------------------------------
if (!objectConstructed)
{
super.setBorder(border);
return;
}
// ----------------------------------------------------------
// Save requested border and make sure user sees what happens
// ----------------------------------------------------------
requestedBorder = border;
invalidate();
} // setBorder
/********************************************************************************************************************
* This method sets the cursor of this component.
*******************************************************************************************************************/
@Override
public void setCursor(Cursor cursor)
{
// ------------------------------------------------------------------------------------------
// We're called also during the construction of the component when setting the default cursor
// Therefore, do normal stuff if construction not completed yet
// ------------------------------------------------------------------------------------------
if (!objectConstructed)
{
super.setCursor(cursor);
return;
}
// ----------------------------------------------------------
// Save requested cursor and make sure user sees what happens
// ----------------------------------------------------------
requestedCursor = cursor;
invalidate();
} // setCursor
/********************************************************************************************************************
* This method sets the cursor to be shown when the mouse is above the preview image.
* <br>It defaults to {@link Cursor#HAND_CURSOR}.
*******************************************************************************************************************/
public void setPreviewCursor(Cursor cursor)
{
// ----------------------------------------------------------
// Save requested cursor and make sure user sees what happens
// ----------------------------------------------------------
imageCursor = cursor;
invalidate();
} // setPreviewCursor
/********************************************************************************************************************
* This method returns the cursor as it is be shown when the mouse is above the preview image.
* <br>It defaults to {@link Cursor#HAND_CURSOR}.
*******************************************************************************************************************/
public Cursor getPreviewCursor()
{
return (imageCursor);
}
/********************************************************************************************************************
* This class implements the border around the password field.
*******************************************************************************************************************/
private class PasswordBorder implements Border
{
/******************************************************************************************************************
* This method returns the insets of the border for specified component.
*****************************************************************************************************************/
@Override
public Insets getBorderInsets(Component c)
{
// ---------------------------------------------------------------------------
// Just the original insets with an added part of the right side for the image
// The latter equals the height of the field
// ---------------------------------------------------------------------------
Insets result;
if (requestedBorder == null)
result = new Insets(0, 0, 0, 0);
else
result = requestedBorder.getBorderInsets(c);
result.right += getHeight();
// ----
// Done
// ----
return (result);
} // getBorderInsets
/******************************************************************************************************************
* This method returns whether or not the border is opaque.
*****************************************************************************************************************/
@Override
public boolean isBorderOpaque()
{
// -------------------------------------------------
// Simply the original border's flag if specified
// Otherwise, we take care of filling the background
// -------------------------------------------------
if (requestedBorder == null)
return (true);
else
return (requestedBorder.isBorderOpaque());
} // isBorderOpaque
/******************************************************************************************************************
* This method paints the border for the specified component with the specified position and size.
*****************************************************************************************************************/
@Override
public void paintBorder(Component c,
Graphics g,
int x,
int y,
int width,
int height)
{
// -----------------------------------------------------------------------
// Determine where the icon starts
// Equals the specified position plus the total width minus the icon width
// The latter equals the component's height
// -----------------------------------------------------------------------
int iconWidth;
int iconOffset;
iconWidth = getHeight();
iconOffset = x + width - iconWidth;
// -----------------------------------------------------------------
// Paint normal border around the field, leaving the image untouched
// -----------------------------------------------------------------
if (requestedBorder != null)
requestedBorder.paintBorder(c, g, x, y, width - iconWidth, height);
// -------------------------------------------
// Set background where the image will be draw
// -------------------------------------------
Graphics iconGraph;
iconGraph = g.create();
iconGraph.setColor(getParent().getBackground());
iconGraph.fillRect(iconOffset, y, iconWidth, height);
// --------------------------------------------------------------------------------------------------
// Make sure icon has correct size
// When for instance a different border is set, the height of the component and therefore the size of
// the image may change
// --------------------------------------------------------------------------------------------------
if ((scaledImage == null) || (scaledImage.getIconHeight() != height))
scaledImage = new ImageIcon(previewImage.getImage().getScaledInstance(height, height, Image.SCALE_SMOOTH));
// --------------
// Now draw image
// --------------
scaledImage.paintIcon(c, iconGraph, iconOffset, y);
} // paintBorder
} // class PasswordBorder
/********************************************************************************************************************
* This class implements the listener for events.
*******************************************************************************************************************/
private class PasswordMouseListener extends MouseAdapter implements CaretListener
{
/******************************************************************************************************************
* This method is called when the caret position is updated.
*****************************************************************************************************************/
@Override
public void caretUpdate(CaretEvent e)
{
// ---------------------------
// Keep track of last position
// ---------------------------
lastCaret = e.getDot();
} // caretUpdate
/******************************************************************************************************************
* This method is called when the mouse moves within the component.
*****************************************************************************************************************/
@Override
public void mouseMoved(MouseEvent e)
{
// --------------------------------------------------------------------------------------
// Set cursor according to location within component
// We intercept the default behavior so make sure we invoke the method of the super class
// --------------------------------------------------------------------------------------
if (isMouseAboveImage(e.getPoint()))
PreviewPasswordField.super.setCursor(imageCursor);
else
PreviewPasswordField.super.setCursor(requestedCursor);
} // mouseMoved
/******************************************************************************************************************
* This method is called when a mouse button is pressed within the component.
*****************************************************************************************************************/
@Override
public void mousePressed(MouseEvent e)
{
// -----------------------------
// Ignore if not the left button
// -----------------------------
if (e.getButton() != MouseEvent.BUTTON1)
return;
// -----------------------
// Save original echo char
// -----------------------
echoChar = getEchoChar();
// ----------------------------------------------------------------
// Show password and restore caret position if the icon was pressed
// ----------------------------------------------------------------
if (isMouseAboveImage(e.getPoint()))
{
if (getCaretPosition() != lastCaret)
setCaretPosition(lastCaret);
setEchoChar('\0');
}
} // mousePressed
/******************************************************************************************************************
* This method is called when a mouse button is released within the component.
*****************************************************************************************************************/
@Override
public void mouseReleased(MouseEvent e)
{
// -----------------------------
// Ignore if not the left button
// -----------------------------
if (e.getButton() != MouseEvent.BUTTON1)
return;
// -------------
// Hide password
// -------------
setEchoChar(echoChar);
} // mouseReleased
/****************************************************************************************************************
* This method returns if the specified position, relative to the component, is above the 'preview' image.
***************************************************************************************************************/
private boolean isMouseAboveImage(Point relativePosition)
{
// -------------------------
// Should be above component
// -------------------------
Dimension compSize;
compSize = getSize();
if ((relativePosition == null) ||
(relativePosition.x < 0) || (relativePosition.x >= compSize.width) ||
(relativePosition.y < 0) || (relativePosition.y >= compSize.height))
return (false);
// ----------------------------------------------------
// Above image if it's within the last part
// The width of the image equals the component's height
// ----------------------------------------------------
return (relativePosition.x >= (compSize.width - compSize.height));
} // mouseAboveImage
/******************************************************************************************************************
* This attribute represents the character shown when the password is hidden.
*****************************************************************************************************************/
private char echoChar;
private int lastCaret = 0;
} // class PasswordMouseListener
/********************************************************************************************************************
* This attribute indicates if the object's construction has completed.
* <br>We need it in order to be able to distinguish between setting the default border/cursor and a custom one.
*******************************************************************************************************************/
private boolean objectConstructed = false;
/********************************************************************************************************************
* This attribute represents the requested border of the component.
*******************************************************************************************************************/
private Border requestedBorder;
/********************************************************************************************************************
* This attribute represents the requested cursor when the mouse is above the component.
*******************************************************************************************************************/
private Cursor requestedCursor;
/********************************************************************************************************************
* This attribute represents the cursor when the mouse is above the image.
*******************************************************************************************************************/
private Cursor imageCursor;
/********************************************************************************************************************
* This attribute represents the icon to be shown next to the password, scaled to the correct size.
*******************************************************************************************************************/
private ImageIcon scaledImage = null;
/********************************************************************************************************************
* This attribute represents the icon to be shown next to the password.
*******************************************************************************************************************/
private static ImageIcon previewImage = null;
/********************************************************************************************************************
* This static initializer loads the image.
*******************************************************************************************************************/
static
{
// -------------------------------
// Determine directory of the icon
// -------------------------------
String directory;
directory = PreviewPasswordField.class.getPackage().getName().replace('.', '/');
// ---------
// Load icon
// ---------
URL name;
name = ClassLoader.getSystemResource(directory + "/passwordPreview.png");
previewImage = new ImageIcon(name);
} // static initializer
} // class PreviewPasswordField