So at this point you have an app that shows an activity (a single screen) with a text field and a button. Now we'll add some code that starts a new activity (takes you to a new screen) when the user clicks the Send button. In non-Ruboto android development, the dogma is we always create an intent and then pass the intent to the method that starts our activity.

At first I tried to write a tutorial showing how to do this in Ruboto without using Ruboto's helpers, but I kept hitting dead-ends. From what I can tell, because of the way Ruboto is set up, you're better off just doing this the Ruboto way!

First let's look at the Ruboto way to respond to that send button and then we'll talk all about starting a new activity in Ruboto.

Respond to the Send Button

As before, when the button gets clicked we can tell Ruboto that we want a block or a proc to get called, here's what that looks like

  • Proc
require 'ruboto/widget'
require 'ruboto/util/toast'

ruboto_import_widgets :LinearLayout, :EditText, :Button

class FirstAppActivity
  def on_create(bundle)
    super
    layout = linear_layout orientation: :horizontal do
      edit_text layout: { weight: 1.0 }, hint: 'Enter Some Text!'
      button text: 'Send', on_click_listener: proc { howdy }
    end
    self.content_view = layout
  end

  def howdy
    toast 'Howdy!'
  end
end
  • Block
require 'ruboto/widget'
require 'ruboto/util/toast'

ruboto_import_widgets :LinearLayout, :EditText, :Button

class FirstAppActivity
  def on_create(bundle)
    super
    layout = linear_layout orientation: :horizontal do
      edit_text layout: { weight: 1.0 }, hint: 'Enter Some Text!'
      b = button text: 'Send'
      b.set_on_click_listener { howdy }
    end
    self.content_view = layout
  end

  def howdy
    toast 'Howdy!'
  end
end

Pretty simple isn't it? You may also notice that this time I used a convenience accessor to Toasting that Ruboto comes with. Pretty slick!

Starting A New Activity

First of all, there's a nice Wiki write-up on the subject of Intents and Activities over on the Ruboto Wiki, you should give that a look. It explains that we have two basic ways to start a new activity if we're using an internal activity (for external activities like Maps or the Camera see the Wiki page). Let's take a look at each of them:

Block-based

We'll want to start activities this way primarily when we want a small simple activity that needs outside information (such as porting text typed into the Edit Text into a new view).

First we need to call start_ruboto_activity with a block. Here's the template from the page on the subject:

# start_ruboto_activity is a method on Activity, so it must be called in that context
start_ruboto_activity do
  def on_create(bundle)
    super
    ## Set up the activity
  end

## Other unique activity code
end

And here's how that looks in our code:

require 'ruboto/widget'

ruboto_import_widgets :LinearLayout, :EditText, :Button, :TextView

class FirstAppActivity
  def on_create(bundle)
    super
    layout = linear_layout orientation: :horizontal do
      edit_text layout: { weight: 1.0 }, hint: 'Enter Some Text!'
      button text: 'Send', on_click_listener: proc { send_message }
    end
    self.content_view = layout
  end

  def send_message
    start_ruboto_activity do
      def on_create(bundle)
        super
        message = 'Testing test test. Breadsticks. Breadsticks. Breadsticks. Very good.'
        self.content_view = text_view text: message
      end
    end
  end
end

Did it work on your device / emulator? Activitatious! I love the smell of breadsticks in my phone!

Now let's learn how to do it with

Starting a Class-based Activity

From the aforementioned Ruboto Wiki:

For most Activities, you'll create a Ruby class. RubotoActivity will find that class and create an instance of it to associate it with the Java instance.

class MyActivity
  def on_create(bundle)
    super
    set_content_view(text_view(:text => 'Hello, World'))
  end
end


# To start MyActivity, call the following method from the context of an Activity.
start_ruboto_activity 'MyActivity'

For our little app it looks like this:

require 'ruboto/widget'

ruboto_import_widgets :LinearLayout, :EditText, :Button

class FirstAppActivity
  def on_create(bundle)
    super
    self.content_view =
      linear_layout orientation: :horizontal do
        edit_text layout: { weight: 1.0 }, hint: 'Enter Some Text!'
        button text: 'Send', on_click_listener: proc { send_message }
      end
  end

  def send_message
    start_ruboto_activity 'DisplayMessageActivity'
  end
end

# => Meanwhile over in display_message_activity.rb  . . .
require 'ruboto/widget'
ruboto_import_widgets :TextView

class DisplayMessageActivity
  def on_create(bundle)
    super
    message = 'Testing test test. Breadsticks. Breadsticks. Breadsticks. Very good.'
    self.content_view = text_view text: message
  end
end

Send Info From One Activity to Another

The last thing we need in order to finish out this app is to get the text entered in the text field (EditText) and hand it over to the new activity.

Block-based

The block based approach might be a little surprising:

require 'ruboto/widget'

ruboto_import_widgets :LinearLayout, :EditText, :Button, :TextView

class FirstAppActivity
  def on_create(bundle)
    super
    layout = linear_layout orientation: :horizontal do
      @input_box = edit_text layout: { weight: 1.0 }, hint: 'Enter Some Text!'
      button text: 'Send', on_click_listener: proc { send_message }
    end
    self.content_view = layout
  end

  def send_message
    message = @input_box.text

    start_ruboto_activity do
      @@message = message
      def on_create(bundle)
        super
        self.content_view = text_view text: @@message
      end
    end
  end
end

First of all we needed the text from the edit box. So we named it @input_box and called the text method on it. So far so good.

Next we need to handle the special scoping that goes on here. The trouble is that an instance variable like @input_box isn't available in the scope of the start_ruboto_activity block, but a regular variable (like message above) is available. So we stash @input_box.text onto message.

Now message is available inside of the block, but it's not available inside the on_create. This is one of those rare cases where a class-variable is not a bad idea. The class variable is available inside of on_create so we stash the contents of message into @@message and set that as the TextView.

The final result (if you edit the size of the text, I'll leave that to you as an exercise!) is:

Class-based

If the new activity is sitting in its own class, then any data you want passed on should be passed into start_ruboto_activity in an extras field like this:

def send_message
  message = @input_box.text
  start_ruboto_activity 'DisplayMessageActivity', extras: { message: message.to_s }
end

and then the extra is retrieved in the other class like this:

def on_create(bundle)
  super
  message = intent.getExtra 'message'
  self.content_view = text_view text: message
end

An extra in Android is like a hash in Ruby. Extras are used to pass information around in Intents just like you see in the above example. If you look at the Intent documentation you'll see lots of methods for putting information into the Intent (put methods) and lots of methods for getting the information back out. Internally, start_ruboto_activity stores the hash you give it as a list of StringExtras. That means you can access values here as getStringExta or getExtra followed by the key from the hash as shown above.


Gotcha - One gotcha from above is that I needed to call message.to_s but I didn't need the to_s for the block-based. That's because the getText method from an EditText doens't return a string! The block-based approach didn't notice because we set the text of the text-view using some Ruby tricks that did the to_s along the way. But in the class-based approach. If we don't append to_s then the Extra will pass a non-string onto the next activity and you'll run into some issues.


Conclusion

OK, did you follow all of that?!

We covered how to build a UI programmatically, using XML, and the Ruboto way. We also learned how to set-up callbacks, start new activities and pass information to them. This last part about starting new activities and passing data around is a little heavy!

There's A LOT more to learn, but this is a good start. If you are reading this, and realize looking back that you didn't absorb very much then I encourage you to take a break, get some sleep and review this all tomorrow. The Android API mixed with Ruboto will take some getting used to (I'm still working on it!) so don't get frustrated. Just patiently keep coming back to it until it's second nature.