Creating Applications Using JRuby and Java

Alex Wong

Contents

  • Basic JRuby Info
  • Building a Desktop Application
  • Looking Forward

Basic JRuby Info

Why Java?

Why JRuby instead of Plain Old Ruby?

JRuby Performance

Calling Java from Ruby

   include Java
   
   # With the 'include Java' above, we can now refer to things that are part of the
   # standard Java platform via their full paths.
   frame = javax.swing.JFrame.new("Window") # Creating a Java JFrame.
   label = javax.swing.JLabel.new("Hello")
   
   # We can transparently call Java methods on Java objects, just as if they were defined in Ruby.
   frame.getContentPane.add(label)  # Invoking the Java method 'getContentPane'.
   frame.setDefaultCloseOperation(javax.swing.JFrame::EXIT_ON_CLOSE)
   frame.pack
   frame.setVisible(true)

Syntactic Sugar

# Standard calls
button.setVisible( true )

# This also works:
button.set_visible true 

# And this:
button.visible = true

Ruby Trappings

 irb(main):032:0> java.net.NetworkInterface.networkInterfaces.methods
 => ["__jsend!", "has_more_elements", "hasMoreElements", "next_element", "nextElement", "each", "reject", 
 "member?", "grep", "include?", "min", "sort", "any?", "partition", "each_with_index", "collect", "find_all", "to_a", 
 "inject", "detect", "map", "zip", "sort_by", "max", "entries", "all?", "find", "select", "hashCode", "notifyAll", 
 "getClass", "to_string", "toString", "get_class", "notify_all", "equals", "hash_code", "wait", "notify", "__jcreate!", 
 "java_class", "eql?", "synchronized", "to_java_object", "equal?", "java_object", "java_object=", "to_s", "==", 
 "hash", "java_kind_of?", "handle_different_imports", "include_class", "display", "object_id", "frozen?", "org", 
 "__id__", "clone", "__send__", "id", "__jtrap", "instance_eval", "singleton_methods", "is_a?", "extend", 
 "instance_variable_set", "freeze", "remove_instance_variable", "=~", "private_methods", "methods", 
 "instance_variable_get", "nil?", "send", "untaint", "com", "type", "class", "===", "instance_of?", "protected_methods", 
 "tainted?", "kind_of?", "javax", "inspect", "java", "instance_exec", "taint", "dup", "public_methods", 
 "instance_variable_defined?", "respond_to?", "method", "instance_variables"]

Namespace Fun

import javax.swing.JPanel

@panel = JPanel.new()
@panel.opaque = true
  // Java
  org.foo.department.Widget
  # Ruby
  Java::OrgFooDepartment::Widget

Building a Desktop Application

Twelve Key Musicianship

Integration

Main View Includes

require './swing-layout-1.0.3.jar'
...
include_class javax.swing.JPanel
include_class javax.swing.JButton
include_class javax.swing.JScrollPane
include_class java.awt.BorderLayout
include_class java.awt.KeyboardFocusManager
include_class org.jdesktop.layout.GroupLayout

class MainView
  include ActionListener
...

Main View

  def layout_view
    @panel = JPanel.new()
    @layout = GroupLayout.new @panel
    @panel.layout = @layout
    @panel.opaque = true
    @layout.autocreate_gaps = true
    @background = Color.new( 152, 152, 152 )
    @panel.background = @background
#    @layout.autocreate_container_gaps = true
  end

  def create_display
    @display = JPanel.new( BorderLayout.new() )
    @welcome_display = WelcomeDisplay.new
    @display.add( @welcome_display.view, BorderLayout::CENTER )
  end

  def create_piano_view
    piano_view = PianoView.new
    @piano = piano_view.view
    @keyboard = piano_view.keyboard
  end

Swing Setup

  def setup_frame
    @frame = JFrame.new @title
    @frame.set_content_pane @panel
    @frame.default_close_operation = JFrame::EXIT_ON_CLOSE
    @frame.minimum_size = Dimension.new( WIDTH_MIN, WIDTH_PREF )
    @frame.preferred_size = Dimension.new( WIDTH_PREF, HEIGHT_PREF )
  end

  def center_frame
    screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    frameSize = @frame.size
    w = frameSize.width
    h = frameSize.height
    @frame.setLocation( screenSize.width/2 - w/2, screenSize.height/2 - h/2 )
  end

Event Listeners

  def add_listeners()
    @play_button.add_action_listener @controller 
    @next_button.add_action_listener @controller 
    @options_button.add_action_listener @controller 
    @cancel_button.add_action_listener @controller 
    @two_button_switch.add_action_listener @controller 
  end

Event Handling

include_class 'java.awt.event.ActionListener'

class ButtonHandler 
  include ActionListener

...

  def actionPerformed(evt)
    if( @view.play_button == evt.source )
      @view.arcade.game.play()

Mouse Handling

include Java

class MouseOverController
  include java.awt.event.MouseListener

  def initialize()
    @highlight = Color.blue
  end

  def mouseEntered( evt )
    evt.source.set_foreground @highlight
  end

  def mouseExited( evt )
    evt.source.set_foreground Color.black
  end

  def mousePressed( evt )
  end
  def mouseReleased( evt )
  end
  def mouseClicked( evt )
  end
end

Integrating Custom Java Components

require './synth_widget.jar'

class PianoView
  attr_reader :view, :keyboard
...
  def layout_view()
    @view = JPanel.new()
    @layout = GroupLayout.new( @view )
    @view.set_layout( @layout )
    @view.opaque = true 
    border = Java::RoundEtchedBorder.new( Java::RoundEtchedBorder::LOWERED, 5 )
    @background = Color.new( 209, 216, 225 )
    border.background_color = @background
    @view.border = border
    @view.background = Color.new 152, 152, 152
  end

  def create_elements()
    @piano = Java::MidiSynth.new
    @piano.open
    @keyboard = @piano.piano
  end

Hybrid Syntax

include_class javax.swing.JProgressBar
include_class javax.swing.border.EmptyBorder
include_class java.awt.Insets

class PlayProgressView
  attr :view

  def initialize
    @view = JProgressBar.new( 0, 100 )
    @view.set_value 0
    @view.set_string_painted false
#    @view.set_border MatteBorder.new( Insets.new( 10, 100, 10, 100 ), Color.white)
#    @view.set_border EmptyBorder.new( 10, 100, 10, 100 )

    @view.set_border_painted true
    @view.set_maximum_size( Dimension.new( 100, 20 ))
  end

Looking Forward

Gotchas

Looking Forward

Resources

Author