AndroidXml
gem install android-xml
Quick start
generate_xml.rb
require 'android-xml'
AndroidXml.file('res/values/strings.xml') do
resource do
string(name: 'app_name') { 'AppAppApp' }
end
end
AndroidXml.run
> ruby generate_xml.rb
✓ res/values/strings.xml
> cat res/values/strings.xml
<!-- Do not edit this file. It was generated by AndroidXml. -->
<resources>
<string name="app_name">AppAppApp</string>
</resources>
About
With AndroidXml you can generate an XML file with much less fuss. It will
take care of prefixing attributes with android:
when required, it includes the
<?xml ?>
tag, includes the xmlns:
attribute on your root node (but only when
required)... all that good stuff!
You still need to understand the format that the various Android XML files expect, but hopefully you get a good productivity boost.
More examples
# The dreaded AndroidManifest file!
AndroidXml.file('AndroidManifest.xml') do
manifest package: 'com.your_name_here.AppAppApp', versionCode: 1, versionName: 1.0 do
uses_sdk minSdkVersion: 11
application label: '@string/app_name', icon: '@drawable/ic_launcher' do
activity name: 'MainActivity', label: '@string/app_name' do
intent_filter do
# these helpers are defined in `android-xml/defaults.rb`,
# and you can easily add your own with an AndroidXml.setup block (see
# below)
main_action
launcher_category
end
end
activity name: 'DisplayMessageActivity',
label: '@string/app_name',
parentActivityName: 'com.your_name_here.AppAppApp.MainActivity'
end
end
end
# You can generate multiple files from one .rb file
AndroidXml.file('res/values-jp/strings.xml') do
resource do
string(name: 'app_name') { 'アッピアッピアッピ' }
end
end
# If you want, you can replace `file` with the root node name
AndroidXml.resource('res/values/strings.xml') do
string(name: 'app_name') { 'AppAppApp' }
end
# `clean_up` finds files that were generated by AndroidXml and removes them.
# Files created by `write_all` are not removed. Accepts a string or array of
# strings.
AndroidXml.write_all
AndroidXml.clean_up 'res/'
# Outputs any missing string tags (looks for @string/name attributes and
# <string name="name"> tags)
AndroidXml.missing_strings?
# Calling AndroidXml.run will call write_all, clean_up, and missing_strings?
Setup / Helpers
Here's how we create the main_action helper, or if you need to specify that some
attributes don't need the android:
prefix.
AndroidXml.setup do
# the hash syntax specifies the `shortcut => actual-tag-name`
tag :main_action => 'action' do
# then we can assign default attributes
defaults name: 'android.intent.action.MAIN'
end
tag :style do
# <style name="..."> is correct - NOT <style android:name="...">
rename :name
end
# if you want to add your own shorthands, go nuts:
tag :activity do
rename :parent => :parentActivityName
end
# global changes
all do
rename :style # always style="", never android:style=""
end
# adds the xmlns attribute to the root node, no matter WHAT the root node is
root do
defaults 'xmlns:android' => 'http://schemas.android.com/apk/res/android'
end
# disable the xmlns attribute on the resource node
tag :resources do
defaults 'xmlns:android' => nil
end
end
Re-using tags
When building a layout with variations for landscape/portrait/size, you will often have big chunks that are reusable. Let's say you have a button that is in a FrameLayout, and centered at the bottom:
# create a <Button> tag
close_button = AndroidXml.Button(
id: '@+id/close_button',
layout_width: 'wrap_content',
layout_height: 'wrap_content',
layout_gravity: 'bottom|center',
text: '@string/close_button'
)
# and include it in a layout
AndroidXml.file('res/layout/some_activity.xml') do
RelativeLayout(...) do
# ...
include close_button
end
end
You can clone
a tag and make changes to it:
AndroidXml.file('res/layout-land/some_activity.xml') do
RelativeLayout(...) do
# ...
include close_button(padding: 8.dp)
end
end
You can create a tag with sub nodes, too!
warning = AndroidXml.LinearLayout(
layout_width: 'match_parent',
layout_height: 'match_parent',
orientation: 'horizontal'
) do
Image(padding: 24.dp, src: '@drawable/warning_icon')
TextView(layout_width: 'wrap_content',
layout_height: 'wrap_content',
text: '@string/warning_text')
end
AndroidXml.file('layout.xml') do
LinearLayout layout_width: 'match_parent',
layout_height: 'match_parent',
gravity: 'center',
orientation: 'vertical' do
include warning
# if you want to replace the contents:
include warning.clone do
Image(padding: 24.dp, src: '@drawable/error_icon')
TextView(layout_width: 'wrap_content',
layout_height: 'wrap_content',
text: '@string/error_text')
end
end
end
Note: this could also be accomplished using AndroidXml.setup
, but setup is
meant for global conventions. Layout-specific things should be included with
include
, in my opinion. But, up to you:
AndroidXml.setup do
tag :close_button => 'Button' do
defaults id: '@+id/close_button',
layout_width: 'wrap_content',
layout_height: 'wrap_content',
layout_gravity: 'bottom|center',
text: '@string/close_button'
contents do
end
end
end
Rakefile to build the XML files
Generates all files that are in the ./android-xml
folder.
Rakefile
require 'rake'
require 'android-xml'
# require 'bundler'
# Bundler.require
task :default => :generate
desc 'Generate XML files'
task :generate do
# the :in option can point to a folder of AndroidXml files (subdirectories
# are included)
AndroidXml.run(in: 'android-xml')
end
Errata
# If you want to check the output before committing to `write_all`
AndroidXml.output_all
# If you want to wipe out all the defaults and settings
AndroidXml.reset
# If you want the defaults back
AndroidXml.setup_defaults