Learning Android with RubyMotion - Chapter 2
This is part of series of posts in which I work through the book Android Programming: The Big Nerd Ranch Guide. by Bill Phillips and Brian Hardy, and write the coding exercises with RubyMotion instead of Java. If you’re just jumping in, you may want to look over the introduction to the series to get yourself oriented.
Today we’ll look at Chapter 2, which builds on the GeoQuiz app we started in Chapter 1. This will be a fairly short post, as the chapter primarily focusses on the Model-View-Controller pattern and presents only a few new concepts related to Android programming.
In the last post, we went through all of the steps needed to create the project and get it running. At this point, I’ll assume you know how to do all that, and will jump straight into the new code.
Creating a Model
First, we create a new model for GeoQuiz. This is a pretty simple step in Ruby - we just need to add a PORO (Plain Old Ruby Object), to the
class TrueFalse attr_accessor :question attr_accessor :is_true def initialize(question, is_true) @question = question @is_true = is_true end def true? is_true == true end end
(Note: Normally we could implement the
true? method by using method_alias, but as of this writing, that causes a crash in RubyMotion)
Updating the View Layer
These changes happen entirely in our XML resource files, so you just need to follow the instructions in Listings 2.3 - 2.5. As before, resource files work exactly the same way in RubyMotion as they do in Java, but they’re stored in the
resources subdirectory, rather than
Updating the Controller Layer
We now need to make several changes to our
QuizActivity class. There’s not much new here in terms of Android programming; it’s mostly a matter of converting the logic from Java to Ruby. Here’s what I ended up with:
class QuizActivity < Android::App::Activity attr_accessor :true_button attr_accessor :false_button attr_accessor :next_button attr_accessor :question_view attr_reader :question_bank attr_accessor :current_index Toast = Android::Widget::Toast def onCreate(savedInstanceState) super setContentView(R::Layout::Activity_quiz) @question_bank = [ TrueFalse.new(R::String::Question_oceans, true), TrueFalse.new(R::String::Question_mideast, false), TrueFalse.new(R::String::Question_africa, false), TrueFalse.new(R::String::Question_americas, true), TrueFalse.new(R::String::Question_asia, true), ] @current_index = 0 @question_view = findViewById(R::Id::Question_text_view) @question_view.setText(question_bank[current_index].question) @true_button = findViewById(R::Id::True_button) @false_button = findViewById(R::Id::False_button) @true_button.onClickListener = @false_button.onClickListener = self end def onClick(view) message_id = (view == true_button ? R::String::Incorrect_toast : R::String::Correct_toast) Toast.makeText(self, message_id, Toast::LENGTH_SHORT).show() end end
At this point, you should be able to run the app and see the new question appear in the text view.
Hooking Up the Buttons
We’ve already added the “Next” button to the UI via the XML file. We now need to hook it up to our code. We can do this easily with the now-familiar
findViewById method, and give it an
@next_button = findViewById(R::Id::Next_button) @next_button.onClickListener = self
For now, we can continue to use our
QuizActivity class as the click listener for all of the buttons. It complicates the logic of the
onClick method somewhat, but not to the point of its being too unwieldy. As our UIs become more complex, we’ll find other ways of implementing event handlers, but for now:
def onClick(view) if view == next_button self.current_index = (current_index + 1) % question_bank.length question_view.setText(question_bank[current_index].question) else message_id = (view == true_button ? R::String::Incorrect_toast : R::String::Correct_toast) Toast.makeText(self, message_id, Toast::LENGTH_SHORT).show() end end
The book correctly points out that we’ve got duplicate code to update the question, so we can extract that logic out to a method:
private def update_question question_view.setText(question_bank[current_index].question) end
And finally, we need to update the logic that checks to see if the user got the right answer, now that the questions and answers are encapsulated in our new model class:
def onClick(view) if view == next_button self.current_index = (current_index + 1) % question_bank.length update_question else user_clicked_true = (view == true_button) message_id = if user_clicked_true == question_bank[current_index].true? R::String::Correct_toast else R::String::Incorrect_toast end Toast.makeText(self, message_id, Toast::LENGTH_SHORT).show() end end
And that’s it. You’re ready to launch your app and test your geography knowledge.
Adding an Icon
This section introduces you to the joys of supporting all of the different pixel densities that are available in the vast spectrum of Android devices. Fortunately, the book’s authors have done the tedious work of getting the icon files into the right sizes for you, so it’s just a matter of copying them into the right directories, and updating your XML file. Again, the only difference here is that in RubyMotion you put your resources in the
resources directory, rather than
The last section of this chapter suggests a few more features to add to the app, but instead of providing the code, it merely specs out the features and challenges you to come up with the implementation yourself.
In keeping with that spirit, I won’t walk through the challenges here, but they’re implemented in the Github repo for this project, so you can look there if you feel like you need some help. I would only do that as a last resort, however; you’ll learn a lot more if you can work through the challenges yourself.
Good luck!comments powered by Disqus