Android Motion Query

android_motion_query was created to make android development on RubyMotion as enjoyable and productive as possible. It also tries to make an android app look just like a ruby app, without losing any native functionality.

If you don't like dealing with XML layouts and long method names, android_motion_query might be for you.

android_motion_query was inspired by the wonderful rmq gem for iOS.

Installation

Add this line to your application's Gemfile:

  gem 'android_motion_query', '~> 0.2.0'

And then execute:

$ bundle

Or install it yourself as:

$ gem install android_motion_query

Usage

The general rule is to create a top-level layout and add views to it. Each view accepts a style name as an argument.

Tiny Example:

To create a LinearLayout and add a TextView widget to it:

amq.add(:linear_layout, :layout_style) do |my_layout|
  my_layout.add(:text_view, :some_information)
end

The style for the LinearLayout is :layout_style and the TextView has a :some_information style.

How do you define styles?

Styles are defined in a separate class that inherits from AMQStylesheet.

Each style is passed a wrapper of the android view:

def layout_style(st)
  st.width = :mp # or :wc for MATCH_PARENT and WRAP_CONTENT respectively
  st.height = :mp # could also provide an integer to be set directly
  st.background_color = '#CF7D33' # or you can do :white, :black, :green, etc
  st.orientation = :vertical # or :horizontal
end

def some_information(st)
  st.width = :mp
  st.height = :wc
  st.text = 'Hello Android Motion Query'
  st.text_color = :blue
  st.text_alignment = :center # or :bottom, :top, :center_right, etc
  st.margin_top = 10
end

Complete Example:

This code creates the following simple calculator app:

Sample Screenshot

class CalculatorScreen < AMQScreen
  def on_create(state)
    setup_calculator_variables
    amq.stylesheet = CalculatorStyle
    amq.add(:linear_layout, :top_layout) do |top|
      @result_label = top.add(:text_view, :result_label)
      top.add(:linear_layout, :buttons_layout) do |bottom|
        bottom.add(:linear_layout, :row_layout) do |row|
          row.add(:button, :ac).tap { reset_calculator }
          row.add(:button, :plus_minus).tap { amq.toast('This is not supported yet') }
          row.add(:button, :percentage).tap { amq.toast('This is not supported yet') }
          row.add(:button, :division).tap { save_result_with_operation(:div) }
        end

        bottom.add(:linear_layout, :row_layout) do |row|
          row.add(:button, :seven).tap { add_digit('7') }
          row.add(:button, :eight).tap { add_digit('8') }
          row.add(:button, :nine).tap { add_digit('9') }
          row.add(:button, :multiplication).tap { save_result_with_operation(:mul) }
        end

        bottom.add(:linear_layout, :row_layout) do |row|
          row.add(:button, :four).tap { add_digit('4') }
          row.add(:button, :five).tap { add_digit('5') }
          row.add(:button, :six).tap { add_digit('6') }
          row.add(:button, :minus).tap { save_result_with_operation(:min) }
        end

        bottom.add(:linear_layout, :row_layout) do |row|
          row.add(:button, :one).tap { add_digit('1') }
          row.add(:button, :two).tap { add_digit('2') }
          row.add(:button, :three).tap { add_digit('3') }
          row.add(:button, :plus).tap { save_result_with_operation(:add) }
        end

        bottom.add(:linear_layout, :row_layout) do |row|
          row.add(:button, :zero).tap { add_digit('0') }
          row.add(:button, :decimal_point).tap { add_decimal }
          row.add(:button, :equals).tap { calculate_result }
        end
      end
    end
  end

  def setup_calculator_variables
    @start_new_number = true
    @first_number = 0
    @result = 0
    @operation = nil
  end

  def add_digit(digit)
    @result_label.text = '' if @start_new_number
    display = @result_label.text + digit
    if display['.']
      display = display.to_f unless digit == '0'
    else
      display = display.to_i
    end
    @result_label.text = display.to_s
    @start_new_number = false
  end

  def add_decimal
    display = @result_label.text
    @result_label.text = display + '.'
  end

  def reset_calculator
    @result_label.text = '0'
    @result = 0
    @first_number = 0
    @start_new_number = true
    @operation = nil
  end

  def save_result_with_operation(op)
    display = @result_label.text
    @first_number = display.to_f
    @operation = op
    @start_new_number = true
  end

  def calculate_result
    display = @result_label.text.to_f
    case @operation
    when :add
      @result = @first_number + display
    when :min
      @result = @first_number - display
    when :mul
      @result = @first_number * display
    when :div
      @result = @first_number / display
    end
    @result_label.text = @result.to_s
  end
end

Please check the examples folder for the complete calculator code in addition to other demo apps.

Todo List

  • [ ] Refactor - create more "single responsibility" classes and have smaller functions (this will never be "done", it's just here as a constant reminder)
  • [ ] Set automatic IDs for views
  • [ ] Add wrappers for all built-in android widgets (currently supports around 10 widgets)
  • [x] Add support for LinearLayouts
  • [ ] Add support for RelativeLayouts
  • [x] Add support for FrameLayouts
  • [ ] Add support for AbsoluteLayouts (worth it? AbsoluteLayout is deprecated a long time ago)
  • [x] Add support for working with custom widgets/views (through amq.new_view())
  • [x] Add support for amq.tap { block of code } for onClickListener events
  • [ ] Add support for @string values (strings.xml)
  • [x] Add support for @drawable values (images in the resources/drawable directory)
  • [ ] Add support for easy and quick animations
  • [ ] Add support for rounded corners (first attempt failed, this is harder than I thought)

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/aesmail/android_query.

License

The gem is available as open source under the terms of the MIT License.