CLickable images.

How can I use images insted of buttons?
But still have the image do the same things buttons do when clicked.
Could you please post the code, any tips and how it works.
I need to have a image like button.gif insted of a button.
Thanks!
Would this work in both applets and applications?
LVL 1
dvd99Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

nil_dibCommented:
0
sgomsCommented:
import java.awt.*;
import java.net.*;
import java.awt.image.*;     // For ImageFilter stuff

// This appears in Core Web Programming from
// Prentice Hall Publishers, and may be freely used
// or adapted. 1997 Marty Hall, hall@apl.jhu.edu.

//======================================================
/**
 * A button class that uses an image instead of a
 * textual label. Clicking and releasing the mouse over
 * the button triggers an ACTION_EVENT, so you can add
 * behavior in the same two ways as you with a normal
 * Button (in Java 1.0):
 * <OL>
 *  <LI>Make an ImageButton subclass and put the
 *      behavior in the action method of that subclass.
 *  <LI>Use the main ImageButton class but then catch
 *      the events in the action method of the Container.
 * </OL>
 * <P>
 * Normally, the ImageButton's preferredSize (used,
 * for instance, by FlowLayout) is just big enough
 * to hold the image. However, if you give an explicit
 * resize or reshape call <B>before</B> adding the
 * ImageButton to the Container, this size will
 * override the defaults.
 * <P>
 * @author Marty Hall (hall@apl.jhu.edu)
 * @see Icon
 * @see GrayFilter
 * @version 1.0 (1997)
 */

public class ImageButton extends ImageLabel {
  //----------------------------------------------------

  /** Default width of 3D border around image.
   *  Currently 4.
   * @see ImageLabel#setBorder
   * @see ImageLabel#getBorder
   */
  protected static final int defaultBorderWidth = 4;

  /** Default color of 3D border around image.
   *  Currently a gray with R/G/B of 160/160/160.
   *  Light grays look best.
   * @see ImageLabel#setBorderColor
   * @see ImageLabel#getBorderColor
   */
  protected static final Color defaultBorderColor =
    new Color(160, 160, 160);
 
  private boolean mouseIsDown = false;

  //----------------------------------------------------
  // Constructors
 
  /** Create an ImageButton with the default image.
   * @see ImageLabel#getDefaultImageString
   */
  public ImageButton() {
    super();
    setBorders();
  }

  /** Create an ImageButton using the image at URL
   *  specified by the string.
   * @param imageURLString A String specifying the URL
   *        of the image.
   */
  public ImageButton(String imageURLString) {
    super(imageURLString);
    setBorders();
  }

  /** Create an ImageButton using the image at URL
   *  specified.
   * @param imageURL The URL of the image.
   */
  public ImageButton(URL imageURL) {
    super(imageURL);
    setBorders();
  }

  /** Creates an ImageButton using the file in
   *  the directory specified.
   * @param imageDirectory The URL of a directory
   * @param imageFile File in the above directory
   */
  public ImageButton(URL imageDirectory,
                     String imageFile) {
    super(imageDirectory, imageFile);
    setBorders();
  }

  /** Create an ImageButton using the image specified.
   *  You would only want to use this if you already
   *  have an image (e.g. created via createImage).
   * @param image The image.
   */
  public ImageButton(Image image) {
    super(image);
    setBorders();
  }

  //----------------------------------------------------
  /** Draws the image with the border around it. If you
   *  override this in a subclass, call super.paint().
   */
  public void paint(Graphics g) {
    super.paint(g);  
    if (grayImage == null)
      createGrayImage(g);
    drawBorder(true);
  }

  //----------------------------------------------------
  // You only want mouseExit to repaint when mouse
  // is down, so you have to set that flag here.

  /** When the mouse is clicked, reverse the 3D border
   *  and draw a dark-gray version of the image.
   *  The action is not triggered until mouseUp.
   */
  public boolean mouseDown(Event event, int x, int y) {
    mouseIsDown = true;
    Graphics g = getGraphics();
    int border = getBorder();
    if (hasExplicitSize())
      g.drawImage(grayImage, border, border,
                  getWidth()-2*border,
                  getHeight()-2*border,
                  this);
    else
      g.drawImage(grayImage, border, border, this);
    drawBorder(false);
    return(true);
  }

  //----------------------------------------------------
  /** If cursor is still inside, trigger the action
   *  event and redraw the image (non-gray, button
   *  "out"). Otherwise ignore this.
   */
  public boolean mouseUp(Event event, int x, int y) {
    mouseIsDown = false;
    if (inside(x,y)) {
      paint(getGraphics());
      event.id = Event.ACTION_EVENT;
      event.arg = (Object)getImage();
      return(action(event, event.arg));
    } else
      return(false);
  }

  //----------------------------------------------------
  /** Generated when the button is clicked and released.
   *  Override this in subclasses to give behavior to
   *  the button. Alternatively, since the default
   *  behavior is to pass the ACTION_EVENT along to the
   *  Container, you can catch events for a bunch of
   *  buttons there.
   * @see Component#action
   */
 
  public boolean action(Event event, Object arg) {
    debug("Clicked on button for " +
          getImageString() + ".");
    return(false);
  }
 
  //----------------------------------------------------
  /** If you move the mouse off the button while the
   *  mouse is down, abort and do <B>not</B> trigger
   *  the action. Ignore this if button was not
   *  already down.
   */
  public boolean mouseExit(Event event, int x, int y) {
    if (mouseIsDown)
      paint(getGraphics());
    return(true);
  }

  //----------------------------------------------------

  /** The darkness value to use for grayed images.
   * @see #setDarkness
   */
  public int getDarkness() {
    return(darkness);
  }

  /** An int whose bits are combined via "and" ("&")
   *  with the alpha, red, green, and blue bits of the
   *  pixels of the image to produce the grayed-out
   *  image to use when button is depressed.
   *  Default is 0xffafafaf: af combines with r/g/b
   *  to darken image.
   */

  public void setDarkness(int darkness) {
    this.darkness = darkness;
  }

  // Changing darker is consistent with regular buttons
 
  private int darkness = 0xffafafaf;

  //----------------------------------------------------
  /** The gray image used when button is down.
   * @see #setGrayImage
   */
 
  public Image getGrayImage() {
    return(grayImage);
  }

  /** Sets gray image created automatically from regular
   *  image via an image filter to use when button is
   *  depressed. You won't normally use this directly.
   */
  public void setGrayImage(Image grayImage) {
    this.grayImage = grayImage;
  }
 
  private Image grayImage = null;
 
  //----------------------------------------------------

  private void drawBorder(boolean isUp) {
    Graphics g = getGraphics();
    g.setColor(getBorderColor());
    int left = 0;
    int top = 0;
    int width = getWidth();
    int height = getHeight();
    int border = getBorder();
    for(int i=0; i<border; i++) {
      g.draw3DRect(left, top, width, height, isUp);
      left++;
      top++;
      width = width - 2;
      height = height - 2;
    }
  }

  //----------------------------------------------------

  private void setBorders() {
    setBorder(defaultBorderWidth);
    setBorderColor(defaultBorderColor);
  }

  //----------------------------------------------------
  // The first time the image is drawn, update() is
  // called, and the result does not come out correctly.
  // So this forces a brief draw on loadup, replaced
  // by real, non-gray image.
 
  private void createGrayImage(Graphics g) {
    ImageFilter filter = new GrayFilter(darkness);
    ImageProducer producer =
      new FilteredImageSource(getImage().getSource(),
                              filter);
    grayImage = createImage(producer);
    int border = getBorder();
    if (hasExplicitSize())
      prepareImage(grayImage, getWidth()-2*border,
                   getHeight()-2*border, this);
    else
      prepareImage(grayImage, this);
    super.paint(g);
  }

  //----------------------------------------------------
}

//======================================================

/** Builds an image filter that can be used to gray-out
 *  the image.
 * @see ImageButton
 */

class GrayFilter extends RGBImageFilter {

  //----------------------------------------------------

  private int darkness = 0xffafafaf;
 
  //----------------------------------------------------

  public GrayFilter() {
    canFilterIndexColorModel = true;
  }

  public GrayFilter(int darkness) {
    this();
    this.darkness = darkness;
  }

  //----------------------------------------------------

  public int filterRGB(int x, int y, int rgb) {
    return(rgb & darkness);
  }
 
  //----------------------------------------------------
}

//======================================================


import java.awt.*;
import java.net.*;

// This appears in Core Web Programming from
// Prentice Hall Publishers, and may be freely used
// or adapted. 1997 Marty Hall, hall@apl.jhu.edu.

//======================================================
/**
 * A class for displaying images. It places the Image
 * into a canvas so that it can moved around by layout
 * managers, will get repainted automatically, etc.
 * No mouseXXX or action events are defined, so it is
 * most similar to the Label Component.
 * <P>
 * By default, with FlowLayout the ImageLabel takes
 * its minimum size (just enclosing the image). The
 * default with BorderLayout is to expand to fill
 * the region in width (North/South), height
 * (East/West) or both (Center). This is the same
 * behavior as with the builtin Label class. If you
 * give an explicit resize or
 * reshape call <B>before</B> adding the
 * ImageLabel to the Container, this size will
 * override the defaults.
 * <P>
 * Here is an example of its use:
 * <P>
 * <PRE>
 * public class ShowImages extends Applet {
 *   private ImageLabel image1, image2;
 *
 *   public void init() {
 *     image1 = new ImageLabel(getCodeBase(),
 *                             "some-image.gif");
 *     image2 = new ImageLabel(getCodeBase(),
 *                             "other-image.jpg");
 *     add(image1);
 *     add(image2);
 *   }
 * }
 * </PRE>
 *
 * @author Marty Hall (hall@apl.jhu.edu)
 * @see Icon
 * @see ImageButton
 * @version 1.0 (1997)
 */

public class ImageLabel extends Canvas {
  //----------------------------------------------------
  // Instance variables.
 
  // The actual Image drawn on the canvas.
  private Image image;

  // A String corresponding to the URL of the image
  // you will get if you call the constructor with
  // no arguments.
  private static String defaultImageString
    = "http://java.sun.com/lib/images/" +
      "logo.java.color-transp.55x60.gif";

  // The URL of the image. But sometimes we will use
  // an existing image object (e.g. made by
  // createImage) for which this info will not be
  // available, so a default string is used here.
  private String imageString = "<Existing Image>";

  // Turn this on to get verbose debugging messages.
  private boolean debug = false;

  /** Amount of extra space around the image. */
  private int border = 0;

  /** If there is a non-zero border, what color should
   *  it be? Default is to use the background color
   *  of the Container.
   */
  private Color borderColor = null;
 
  // Width and height of the Canvas. This is the
  //  width/height of the image plus twice the border.
  private int width, height;

  /** Determines if it will be sized automatically.
   *  If the user issues a resize() or reshape()
   *  call before adding the label to the Container,
   *  or if the LayoutManager resizes before
   *  drawing (as with BorderLayout), then those sizes
   *  override the default, which is to make the label
   *  the same size as the image it holds (after
   *  reserving space for the border, if any).
   *  This flag notes this, so subclasses that
   *  override ImageLabel need to check this flag, and
   *  if it is true, and they draw modified image,
   *  then they need to draw them based on the width
   *  height variables, not just blindly drawing them
   *  full size.
   */
  private boolean explicitSize = false;
  private int explicitWidth=0, explicitHeight=0;
 
  // The MediaTracker that can tell if image has been
  // loaded before trying to paint it or resize
  // based on its size.
  private MediaTracker tracker;
 
  // Used by MediaTracker to be sure image is loaded
  // before paint & resize, since you can't find out
  // the size until it is done loading.
  private static int lastTrackerID=0;
  private int currentTrackerID;
  private boolean doneLoading = false;

  private Container parentContainer;

  //----------------------------------------------------
  /** Create an ImageLabel with the default image.
   *
   * @see #getDefaultImageString
   * @see #setDefaultImageString
   */
  // Remember that the funny "this()" syntax calls
  // constructor of same class
  public ImageLabel() {
    this(defaultImageString);
  }
 
  /** Create an ImageLabel using the image at URL
   *  specified by the string.
   *
   * @param imageURLString A String specifying the
   *   URL of the image.
   */
  public ImageLabel(String imageURLString) {
    this(makeURL(imageURLString));
  }

  /** Create an ImageLabel using the image at URL
   *  specified.
   *
   * @param imageURL The URL of the image.
   */
  public ImageLabel(URL imageURL) {
    this(loadImage(imageURL));
    imageString = imageURL.toExternalForm();
  }

  /** Create an ImageLabel using the image in the file
   *  in the specified directory.
   *
   * @param imageDirectory Directory containing image
   * @param file Filename of image
   */
  public ImageLabel(URL imageDirectory, String file) {
    this(makeURL(imageDirectory, file));
    imageString = file;
  }
 
  /** Create an ImageLabel using the image specified.
   *  The other constructors eventually call this one,
   *  but you may want to call it directly if you
   *  already have an image (e.g. created via
   *  createImage).
   *
   * @param image The image
   */
  public ImageLabel(Image image) {
    this.image = image;
    tracker = new MediaTracker(this);
    currentTrackerID = lastTrackerID++;
    tracker.addImage(image, currentTrackerID);
  }

  //----------------------------------------------------
  /** Makes sure that the Image associated with the
   *  Canvas is done loading before returning, since
   *  loadImage spins off a separate thread to do the
   *  loading. Once you get around to drawing the
   *  image, this will make sure it is loaded,
   *  waiting if not. The user does not need to call
   *  this at all, but if several ImageLabels are used
   *  in the same Container, this can cause
   *  several repeated layouts, so users might want to
   *  explicitly call this themselves before adding
   *  the ImageLabel to the Container. Another
   *  alternative is to start asynchronous loading by
   *  calling prepareImage on the ImageLabel's
   *  image (see getImage).
   *
   * @param doLayout Determines if the Container
   *   should be re-laid out after you are finished
   *   waiting. <B>This should be true when called
   *   from user functions</B>, but is set to false
   *   when called from preferredSize to avoid an
   *   infinite loop. This is needed when
   *   using BorderLayout, which calls preferredSize
   *   <B>before</B> calling paint.
   */
  public void waitForImage(boolean doLayout) {
    if (!doneLoading) {
      debug("[waitForImage] - Resizing and waiting for "
            + imageString);
      try { tracker.waitForID(currentTrackerID); }
      catch (InterruptedException ie) {}
      catch (Exception e) {
        System.out.println("Error loading "
                           + imageString + ": "
                           + e.getMessage());
        e.printStackTrace();
      }
      if (tracker.isErrorID(0))
        new Throwable("Error loading image "
                      + imageString).printStackTrace();
      doneLoading = true;
      if (explicitWidth != 0)
        width = explicitWidth;
      else
        width = image.getWidth(this) + 2*border;
      if (explicitHeight != 0)
        height = explicitHeight;
      else
        height = image.getHeight(this) + 2*border;
      resize(width, height);
      debug("[waitForImage] - " + imageString + " is "
            + width + "x" + height + ".");

      // If no parent, you are OK, since it will have
      // been resized before being added. But if
      // parent exists, you have already been added,
      // and the change in size requires re-layout.
      if (((parentContainer = getParent()) != null)
          && doLayout) {
        setBackground(parentContainer.getBackground());
        parentContainer.layout();
      }
    }
  }

  //----------------------------------------------------
  /** Moves the image so that it is <I>centered</I> at
   *  the specified location, as opposed to the move
   *  method of Component which places the top left
   *  corner at the specified location.
   *  <P>
   *  <B>Note:</B> The effects of this could be undone
   *  by the LayoutManager of the parent Container, if
   *  it is using one. So this is normally only used
   *  in conjunction with a null LayoutManager.
   *
   * @param x The X coord of center of the image
   *          (in parent's coordinate system)
   * @param y The Y coord of center of the image
   *          (in parent's coordinate system)
   * @see java.awt.Component#move
   */

  public void centerAt(int x, int y) {
    debug("Placing center of " + imageString + " at ("
          + x + "," + y + ")");
    move(x - width/2, y - height/2);
  }

  //----------------------------------------------------
  /** Determines if the x and y <B>(in the ImageLabel's
   *  own coordinate system)</B> is inside the
   *  ImageLabel. Put here because Netscape 2.02 has
   *  a bug in which it doesn't process inside() and
   *  locate() tests correctly.
   */
  public synchronized boolean inside(int x, int y) {
    return((x >= 0) && (x <= width)
           && (y >= 0) && (y <= height));
  }
 
  //----------------------------------------------------
  /** Draws the image. If you override this in a
   *  subclass, be sure to call super.paint.
   */
  public void paint(Graphics g) {
    if (!doneLoading)
      waitForImage(true);
    else {
      if (explicitSize)
        g.drawImage(image, border, border,
                    width-2*border, height-2*border,
                    this);
      else
        g.drawImage(image, border, border, this);
      drawRect(g, 0, 0, width-1, height-1,
               border, borderColor);
    }
  }

  //----------------------------------------------------
  /** Used by layout managers to calculate the usual
   *  size allocated for the Component. Since some
   *  layout managers (e.g. BorderLayout) may
   *  call this before paint is called, you need to
   *  make sure that the image is done loading, which
   *  will force a resize, which determines the values
   *  returned.
   */
  public Dimension preferredSize() {
    if (!doneLoading)
      waitForImage(false);
    return(super.preferredSize());
  }

  //----------------------------------------------------
  /** Used by layout managers to calculate the smallest
   *  size allocated for the Component. Since some
   *  layout managers (e.g. BorderLayout) may
   *  call this before paint is called, you need to
   *  make sure that the image is done loading, which
   *  will force a resize, which determines the values
   *  returned.
   */
   public Dimension minimumSize() {
     if (!doneLoading)
       waitForImage(false);
     return(super.minimumSize());
   }
 
  //----------------------------------------------------
  // LayoutManagers (such as BorderLayout) might call
  // resize or reshape with only 1 dimension of
  // width/height non-zero. In such a case, you still
  // want the other dimension to come from the image
  // itself.

  /** Resizes the ImageLabel. If you don't resize the
   *  label explicitly, then what happens depends on
   *  the layout manager. With FlowLayout, as with
   *  FlowLayout for Labels, the ImageLabel takes its
   *  minimum size, just enclosing the image. With
   *  BorderLayout, as with BorderLayout for Labels,
   *  the ImageLabel is expanded to fill the
   *  section. Stretching GIF/JPG files does not always
   *  result in clear looking images. <B>So just as
   *  with builtin Labels and Buttons, don't
   *  use FlowLayout if you don't want the Buttons to
   *  get resized.</B> If you don't use any
   *  LayoutManager, then the ImageLabel will also
   *  just fit the image.
   *  <P>
   *  Note that if you resize explicitly, you must do
   *  it <B>before</B> the ImageLabel is added to the
   *  Container. In such a case, the explicit size
   *  overrides the image dimensions.
   *
   * @see #reshape
   */
  public void resize(int width, int height) {
    if (!doneLoading) {
      explicitSize=true;
      if (width > 0)
        explicitWidth=width;
      if (height > 0)
        explicitHeight=height;
    }
    super.resize(width, height);
  }

  /** Resizes the ImageLabel. If you don't resize the
   *  label explicitly, then what happens depends on
   *  the layout manager. With FlowLayout, as with
   *  FlowLayout for Labels, the ImageLabel takes its
   *  minimum size, just enclosing the image. With
   *  BorderLayout, as with BorderLayout for Labels,
   *  the ImageLabel is expanded to fill the
   *  section. Stretching GIF/JPG files does not always
   *  result in clear looking images. <B>So just as
   *  with builtin Labels and Buttons, don't
   *  use FlowLayout if you don't want the Buttons to
   *  get resized.</B> If you don't use any
   *  LayoutManager, then the ImageLabel will also
   *  just fit the image.
   *  <P>
   *  Note that if you resize explicitly, you must do
   *  it <B>before</B> the ImageLabel is added to the
   *  Container. In such a case, the explicit size
   *  overrides the image dimensions.
   *
   * @see #resize
   */
  public void reshape(int x, int y,
                      int width, int height) {
    if (!doneLoading) {
      explicitSize=true;
      if (width > 0)
        explicitWidth=width;
      if (height > 0)
        explicitHeight=height;
    }
    super.reshape(x, y, width, height);
  }
 
  //----------------------------------------------------
  // You can't just set the background color to
  // the borderColor and skip drawing the border,
  // since it messes up transparent gifs. You
  // need the background color to be the same as
  // the container.
 
  /** Draws a rectangle with the specified OUTSIDE
   *  left, top, width, and height.
   *  Used to draw the border.
   */
  protected void drawRect(Graphics g,
                          int left, int top,
                          int width, int height,
                          int lineThickness,
                          Color rectangleColor) {
    g.setColor(rectangleColor);
    for(int i=0; i<lineThickness; i++) {
      g.drawRect(left, top, width, height);
      if (i < lineThickness-1) {  // Skip last iteration
        left = left + 1;
        top = top + 1;
        width = width - 2;
        height = height - 2;
      }
    }
  }
 
  //----------------------------------------------------
  /** Calls System.out.println if the debug variable
   *  is true; does nothing otherwise.
   *
   * @param message The String to be printed.
   */
  protected void debug(String message) {
    if (debug)
      System.out.println(message);
  }
 
  //----------------------------------------------------
  // Creates the URL with some error checking.
 
  private static URL makeURL(String s) {
    URL u = null;
    try { u = new URL(s); }
    catch (MalformedURLException mue) {
      System.out.println("Bad URL " + s + ": " + mue);
      mue.printStackTrace();
    }
    return(u);
  }

  private static URL makeURL(URL directory,
                             String file) {
    URL u = null;
    try { u = new URL(directory, file); }
    catch (MalformedURLException mue) {
      System.out.println("Bad URL " +
                         directory.toExternalForm() +
                         ", " + file + ": " + mue);
      mue.printStackTrace();
    }
    return(u);
  }

  //----------------------------------------------------
  // Loads the image. Needs to be static since it is
  // called by the constructor.
 
  private static Image loadImage(URL url) {
    return(Toolkit.getDefaultToolkit().getImage(url));
  }

  //----------------------------------------------------
  /** The Image associated with the ImageLabel. */

  public Image getImage() {
    return(image);
  }
 
  //----------------------------------------------------
  /** Gets the border width. */

  public int getBorder() {
    return(border);
  }

  /** Sets the border thickness. */
 
  public void setBorder(int border) {
    this.border = border;
  }

  //----------------------------------------------------
  /** Gets the border color. */

  public Color getBorderColor() {
    return(borderColor);
  }

  /** Sets the border color. */
 
  public void setBorderColor(Color borderColor) {
    this.borderColor = borderColor;
  }
 
  //----------------------------------------------------
  // You could just call size().width and size().height,
  // but since we've overridden resize to record
  // this, we might as well use it.
 
  /** Gets the width (image width plus twice border). */

  public int getWidth() {
    return(width);
  }

  /** Gets the height (image height plus 2x border). */

  public int getHeight() {
    return(height);
  }
 
  //----------------------------------------------------
  /** Has the ImageLabel been given an explicit size?
   *  This is used to decide if the image should be
   *  stretched or not. This will be true if you
   *  call resize or reshape on the ImageLabel before
   *  adding it to a Container. It will be false
   *  otherwise.
   */
  protected boolean hasExplicitSize() {
    return(explicitSize);
  }

  //----------------------------------------------------
  /** Returns the string representing the URL that
   *  will be used if none is supplied in the
   *  constructor.
   */
  public static String getDefaultImageString() {
    return(defaultImageString);
  }

  /** Sets the string representing the URL that
   *  will be used if none is supplied in the
   *  constructor. Note that this is static,
   *  so is shared by all ImageLabels. Using this
   *  might be convenient in testing, but "real"
   *  applications should avoid it.
   */
  public static void setDefaultImageString(String file) {
    defaultImageString = file;
  }

  //----------------------------------------------------
  /** Returns the string representing the URL
   *  of image.
   */
  protected String getImageString() {
    return(imageString);
  }

  //----------------------------------------------------
  /** Is the debugging flag set? */
   
  public boolean isDebugging() {
    return(debug);
  }

  /** Set the debugging flag. Verbose messages
   *  will be printed to System.out if this is true.
   */
  public void setIsDebugging(boolean debug) {
    this.debug = debug;
  }

  //----------------------------------------------------
}
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
sgomsCommented:
Check out,
http://www.apl.jhu.edu/~hall/CWP-Chapter13/CWP-Chapter13.3.html

for examples on how to use ImageButton.

-sgoms
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Java

From novice to tech pro — start learning today.