-
Notifications
You must be signed in to change notification settings - Fork 36
Namespaces
This page contains a detailed description of neko namespaces describing most of the possible usages for each of them. To inspect all functions and their documentation see the Marginalia docs.
NOTE: This document assumes the reader to be already proficient with the Android platform. Neko doesn’t try to completely hide the Java side of the Android, neither it tries to replace the key Android concepts. So even if you want to develop for Android in Clojure, you still need to study how it is developed in Java. Fortunately there are lots of educational materials to get you started.
Provides utilities to modify application’s action bar. For detailed information see Action bar page.
This namespace is deprecated and will be removed in the next major release. Please use Java if you want to write custom Application class.
This namespace stores facilities related to the Activity class.
Since defining a new activity requires to inherit the Activity
class, defactivity
macro simplifies this work. As well as
defapplication
this macro takes optional arguments to specify
different activity events. For example:
(defactivity your.package.name.MainActivity :on-create (fn [this savedInstanceState] ...))
Other event handlers that you can define in the macro:
- on-create-options-menu
- on-options-item-selected
- on-activity-result
- on-new-intent
- on-start
- on-restart
- on-resume
- on-pause
- on-stop
- on-destroy
If :on-create
option is used, it also binds the Activity object
to the var which is the activity class name with the first letter
lowercased (so for MainActivity the var will be mainActivity).
You can provide a custom name for it using :def
option:
(defactivity your.package.name.MainActivity :def a :on-create ...)
In this example inside onCreate
method the Activity object will
be bound to a
. Note that this will happen only if you provided
the handler for :on-create
.
You can also specify event handlers for your activity like for any
gen-classed class. defactivity
macro sets the default prefix to
be ActivityName-.
(defactivity your.package.name.FooActivity ...)
:
(defn FooActivity-onResume [this] ...)
Since 3.0.0 Neko tries to move away from dynamic bindings as much
as possible. New code doesn’t use them, but some old functions
expect *activity*
to be bound. Most of those functions provide
both options - either to specify the activity explicitly or take
it out of the dynamic scope. with-activity
macro takes the
Activity object and establishes the dynamic binding for the
underlying code.
(with-activity a (find-view :ok-button))
set-content-view!
operates on either provided or currently bound
to *activity*
Activity instance and sets its content view. The
view argument could be either an actual View instance or a layout
ID.
request-window-features!
requests the given features for the
activity. The features should be keywords such as :no-title
or :indeterminate-progress corresponding FEATURE_NO_TITLE and
FEATURE_INDETERMINATE_PROGRESS, respectively. Returns a sequence
of boolean values corresponding to each feature, where a true
value indicates the requested feature is supported and now
enabled. This function should be called before
set-content-view!
.
simple-fragment
function allows you to create a trivial Fragment
object that contains specified view. Takes optional context and a
View object or neko.ui tree.
(simple-fragment [:linear-layout {:orientation :vertical}
[:text-view {:text "Text"}]
[:button {:text "Button"}]])
Functions in this namespace are responsible for instantiating the dynamic compilation. Unless you are not satisfied with neko’s default behavior with regard to dynamic compilation, you won’t probably need to use this namespace.
This namespaces contains utilities to aid in working with a context.
Inside the onCreate
of the Application its context object is
caught and bound to the context
var. This allows many neko
functions to implicitly use it as a singleton rather then require
it being passed everywhere.
You can use this var in your code too instead of getting the
application context out of the activity context. Another advantage
is that context
is already type-hinted so you don’t need to do
it yourself.
Alternatively to calling .getSystemService
on context instance
you can use this macro to get services more conveniently. Please
keep in mind, that it is a macro with all its implications. You
must provide the keyword argument denoting the service directly to
the macro.
(get-service :alarm)
You can use this function to inflate a layout given the resource ID. It returns an inflated View instance.
(inflate-layout R$id/item1)
Provides functions to save and restore the application data (Bundle, SharedPreferences) and transfer data across applications (Intent).
like-map
function when called instance of the above-mentioned
classes returns a thin wrapper around it that allows treating the
object like a map (in destructuring, for example).
(defactivity ... :on-create (fn [this bundle]) (let [{:keys [sharks-count lazers?]} (like-map bundle)] ...))
Note that you can use either strings or keywords to extract
values. So (get (like-map bundle) :foo)
is equal to (get
(like-map bundle) "foo")
.
/Warning: an instance wrapped with like-map
cannot be used as an
original one. So for example, if you need to pass bundle
to
another activity, you have to use the original instance and not
the wrapped one./
First of all, get-shared-preferences
function returns a
SharedPreferences object for the given name and access mode.
Available modes: :private
, :world-readable
and
:world-writeable
.
(def prefs (get-shared-preferences "my_preferences" :private))
Then you can extract primitive values from the retrieved object by
wrapping it with like-map
.
To store a value inside the SharedPreferences object assoc!
function is used. It takes a SharedPreferences.Editor object, a
key and a value. The key could be either a string or a keyword.
The number of types SharedPreferences support is very limiting. But since it includes String you can serialize and save any Clojure structure in it (as long as this structure can be parsed by the reader). Here’s the example of storing an integer value and a vector:
(-> (.edit prefs) (assoc! :sharks-count 5) (assoc-arbitrary! :shark-weights [300 350 330 320 250]) .commit)
To get a complex value out use get-arbitrary
function:
(get-arbitrary (like-map prefs) :shark-weights)
Contains useful tools to be used while developing the application.
By default the application crashes if the code that was running on
the UI thread throws an unhandled exception. This greatly disturbs
productivity as you have to rerun your application and re-evaluate
all new forms. Wrapping the code with safe-for-ui
macro ensures
that the missed exception won’t be so disastrous.
do-ui
uses this macro to wrap its code by default.
(safe-for-ui (/ 1 0))
Warning: do not directly use the functional version available by the name =safe-for-ui*= because it will always be compiled regardless of the build type.
When an exception or error happens in the code wrapped with
safe-for-ui
, you will see a Toast notifying you what happened.
You can get the Throwable object to examine it in more detail by
calling ui-e
function.
TODO: this namespace will be changed.
TODO: not quite integrated yet.
Contains functions for neko initialization and setting runtime
flags. Functions in this namespace are responsible for neko
initialization and starting the nREPL server, and depend on the
build type of the project (debug or release). The only useful thing
for you here is init
which takes application context and some
optional arguments. The call of this function is already done after
the splash, if you created your project with the latest lein-droid.
Subnamespaces in this namespace provide different utilities to create event listeners.
Every listener function takes a callback function as its argument and returns corresponding Listener object. Later you can attach the returned object to the UI element using necessary methods.
Functional versions of listeners have -call
suffix.
The number of arguments a callback function should accept for every event listener can be found in docstrings for the listeners.
Every listener is also available in macro version (without the
-call
suffix that takes body to execute as argument. It also
establishes implicit arguments in the lexical scope. You can find
arguments’ names in docstrings for listeners.
For example, here are two identical declarations:
(.setOnClickListener ok-button (on-click-call (fn [view] (toast (str "View clicked: " view) :long))))
:
(.setOnClickListener ok-button (on-click (toast (str "View clicked: " view) ;; "view" is implicit :long)))
Uility functions and macros for setting listeners corresponding to the android.view.View class.
List of listeners in this namespace include:
- on-click
- on-create-context-menu
- on-drag
- on-focus-change
- on-key
- on-long-click
- on-touch
Uility functions and macros for creating listeners corresponding to the android.widget.TextView class.
List of listeners in this namespace include:
- on-editor-action
Uility functions and macros for creating listeners corresponding to the android.widget.AdapterView class.
List of listeners in this namespace include:
- on-item-click
- on-item-long-click
- on-item-selected
Uility functions and macros for setting listeners corresponding to the android.content DialogInterface interface.
List of listeners in this namespace include:
- on-cancel
- on-click
- on-dismiss
- on-key
- on-multi-choice-click
Contains logging utilities. You can use the following functions directly from this namespace:
-
d
- debug -
e
- error -
i
- info -
v
- verbose -
w
- warning
Each logging function takes a tag, a message and optionally a Throwable instance as arguments.
You can also use deflog
macro to establish five logging
functions (log-d
, log-e
, log-i
, log-v
and log-w
) in the
current namespace with the given tag. For example:
(ns foo
(:require neko.log))
(neko.log/deflog "foo")
(log-i "Everything is OK")
Provides convenient wrappers for Toast and Notification APIs.
To show user a Toast do the following:
(toast "My message" :long)
First argument is a message, second is the toast duration (could
be either :long
or :short
).
Note that unlike in Java API toast
function creates a toast and
immediately shows it. If for some reason you need to create a
reusable Toast, feel free to use Toast constructor and then call
.show
on it.
To create a Notification object use notification
function. You
have to provide the following arguments to it:
- icon (optional if the default icon is set)
- ticker-text
- content-title
- content-text
- action
- when (now by default)
Action should be in the form of vector where the first element is
a PendingIntent type (:activity
, :broadcast
, :service
) and
the second one is an action to create Intent from.
You can set the default notification icon by calling
set-default-notification-icon!
that takes one argument - either
an actual image resource or a Android resource ID.
To show the created notification call fire
on it. Two-argument
version takes an ID (either integer or keyword) as the first
argument that allows to cancel
the notification in future.
(fire :new-mail (notification :icon R$drawable/ic_launcher :ticker-text "You've got mail" :content-title "One new message" :content-text "FROM: [email protected]" :action [:activity "my.package.VIEW_MAIL"]))
:
(cancel :new-mail)
Provides utilities to resolve application resources.
get-resource
macro provides an ability to find any resource by
the given resource type and the resource name. Both type and name
are denoted by keywords. Keyword name is transformed into a static
field using the default convention (dashes and dots are replaced
by underscores).
(= (get-resource :drawable :ic-launcher) org.my.app.R$drawable.ic_launcher)
By default the root application package is used. You can use a namespace-qualified keyword to choose another package for R class.
(= (get-resource :layout :android/simple-list-item-1) android.R$layout/simple_list_item_1)
Some attributes (such as View’s :text
or ImageView’s :image
allow specifying resource keywords directly, without calling
get-resource.
[:linear-layout {}
[:text-view {:text :app-name}]
[:image-view {:image :ic-launcher}]]
There are a few shortcut macros for different kinds of resources. They take only name keyword as argument.
The list includes:
-
get-id
- same as(get-resource :id ...)
-
get-layout
- same as(get-resource :layout ...)
-
get-drawable
- like(get-resource :drawable ...)
, but returns Drawable object for the given resource keyword -
get-string
- like(get-resource :string ...)
, but returns string for the given resource keyword. Can also take optional format values to the resource string.
The described functions use reflection to go from keywords to
integer IDs. In case this becomes a bottle-neck, Neko provides
macros that mirror functions above in compile-time. get-resource
becomes resolve-resource
, all other functions have resolve
instead of get
in them. The difference is in resolve-drawable
and resolve-string
macros that return just the resource ID
rather than final object.
Please note, that since resolve-...
are macros, you have to
provide keywords to them directly (i.e. not through a variable).
You can use special data readers for turning keywords into integer
IDs in compile-time. They are: #res/id
, #res/layout
,
#res/string
and #res/drawable
. Using these data readers before
resource keywords is equivalent to calling resolve macros
manually. The benefit of these readers is that you don’t have to
import anything into your namespace to use them (and also less
parentheses).
[:linear-layout {}
[:text-view {:text #res/string :app-name}]
[:image-view {:image #res/drawable :ic-launcher}]]
Utilities used to manage multiple threads on Android.
Any code that touches the user interface has to be run on the
application UI thread. Neko provides a macro called on-ui
that
takes arbitrary code to be run it on the main UI thread.
(on-ui (.setText ok-button "OK"))
If the current thread is already the UI one, then the code will be
directly executed.
on-ui
automatically establishes *activity*
binding for the
code being run if this var was bound in the surrounding dynamic
scope.
Code inside on-ui
is automatically wrapped in try..catch block
so if the exception happened on UI thread it won’t crash the
application.
You can also use on-ui*
function which takes a zero-argument
function as argument.
One more useful function is on-ui-thread?
which tells whether
the thread it is executed on is the UI one.
Alternatively you can add code on the message queue of a specific
View object you’d like this code to operate on. post
macro is
similar to on-ui
but additionally takes a View object as first
argument.
(post ok-button (.setText ok-button "OK"))
There is also post-delayed
macro that takes additional time
argument in milliseconds, and executes its body after this time
passes.
(post-delayed ok-button 3000 ;; Wait for 3 seconds (.setText ok-button "OK"))
Functional version are available as post*
and post-delayed*
.
Tools for defining and manipulating Android UI elements. See User interface page for detailed instructions.
The most important thing here is make-ui
macro. It allows to
declaratively define the UI of your application. This macro is
intended as a replacement for XML-defined user interface. make-ui
returns a View and its return value should be set as a content view
for the acitivity, usually in the activity’s onCreate
method.
make-ui
takes a UI tree as an argument. The tree is a vector that
contains of these elements:
- element keyword
- attribute map
- & other elements
(make-ui [:linear-layout {:layout-height :fill}
[:button {:text "A button"
:enabled true}]])
The element keyword represents the element you want to create. Thus
:button
stands for android.widget.Button, :linear-layout
- for
android.widget.LinearLayout and so on. All elements are defined in
the neko.ui.mapping namespace and new elements can be added.
The attribute map consists of key-value pairs where keys are
Clojure keywords. By default attributes are processed in such
fashion that these pairs are trasformed into setter calls. So for
instance, :editable false
is transformed into (.setEditable
edit-wdg false)
. For non-standard attributes the so
called traits can be defined. Trait is a function that takes an
attribute map and performs some actions for the attribute(s) it
represents. All traits of the element are applied to the attribute
map before the default handler kicks in. Every element has its own
traits list. In the following example a special on-click trait
will take specified anonymous function, wrap it in OnClickListener
and then generate call to .setOnClickListener
.
(make-ui [:button {:on-click (fn [w] (toast "Clicked!"))}])
Attribute values are also treated specially. If value is a Clojure
keyword, then it is looked up in the element’s value map. This map
contains a mapping of element-specific keywords to real values. If
the keyword is not present there, it is transformed into a static
field following a rule: all letters are uppercased, and dashes are
replaced with with underscores. For example, the value
:choice-mode-multiple
defined for the :list-view
element will
be transformed into ListView/CHOICE_MODE_MULTIPLE
.
You can use config
function to change existing element’s state.
It works similarly to make-ui
, but receives attributes as
optional arguments rather than in one single map.
(config! ok-button
:text "OK"
:on-click (fn [_] (execute-operation)))
This namespaces stores the keywords-to-elements mapping and provides utilities to define your own elements.
You can create new element using defelement
macro. The first
argument it takes is the keyword name of the element. Other
(optional) arguments:
- classname - which class the element represents (if any)
- traits - the list of traits for this element
- attributes - a map of default attributes for this element
- values - a map of specific attribute values
- inherits - see below.
(defelement :text-view
:classname android.widget.TextView
:traits [:def :layout-params]
:attributes {:text :default-text}
:values {:default-text "I am a textview"})
You also can define an element that inherits a base one. It is
achieved by providing :inherits
option to the defelement
.
Values and attributes maps provided are merged with the original
ones (the newer rewrite the older), provided traits are appended
to the original.
(defelement :long-thin-button
:inherits :button
:attributes {:layout-width :fill
:layout-height :wrap})
This namespace contains helper functions to manipulate ListViews.
Currently it has only two functions: get-checked
and
set-checked!
, both of them operate on ListViews with multiple
check items enabled.
get-checked
takes a ListView instance and returns a vector of
numbers. These numbers represent the numbers of those items in the
list that are checked. Two-argument version takes a ListView and an
arbitrary sequence (usually the one from which ListView adapter was
constructed) and returns only those elements that are checked. Be
careful to provide a sequence not exceeding the number of ListView
elements itself.
set-checked!
takes a ListView and a sequence of numbers and sets
the respective ListView items to be checked.
This namespace provides custom adapters for ListView and Spinner.
ref-adapter
function creates an Adapter that watches over a
reference type and updates itself when the reference is updated.
ref-adapter
takes three arguments:
-
create-view-fn
- a function of zero arguments that returns a new View instance that acts as single ListView item; -
update-view-fn
- a function that takes four arguments: item’s position in the ListView, View instance of the item, parent container and the data for the current item. The function should update the View instance with the provided data; -
ref-type
is a ref or an atom that stores data; -
access-fn
(optional) - a function that is called on the dereferencedref-type
to get the list that should be displayed by ListView. By defaultidentity
function is used.
ref-adapter implements View caching technique. If View for the
element has not been created yet, create-view-fn
is called to
create it. Then update-view-fn
is called on the View with data
provided.
access-fn
allows a reference type to store some broader data
structure (a map, for example) and to display only part of it in
a ListView.
When the reference type is updated, adapter updates automatically.
The following example demonstrates usage of ref-adapter along with ListView:
(def alphabet
(atom {:type :phonetic
:letters ["alpha" "bravo" "charlie" "delta"]}))
(defn make-adapter []
(ref-adapter
(fn [] (make-ui [:text-view {}]))
(fn [position view _ data]
(.setText ^TextView view (str position ". " data)))
alphabet
:letters))
;; Somewhere in Activity.onCreate()
... (make-ui [:list-view {:adapter (make-adapter)}])
;; Now the created ListView displays four items:
;; 1. alpha
;; 2. bravo
;; 3. charlie
;; 4. delta
(swap! alphabet update-in :letters conj "echo")
;; ListView automatically appended another item: 5. echo
Namespaces
- neko.action-bar
- neko.activity
- neko.context
- neko.data
- neko.data.shared-prefs
- neko.debug
- neko.dialog.alert
- neko.find-view
- neko.intent
- neko.listeners
- neko.log
- neko.notify
- neko.resource
- neko.threading
- neko.ui
- neko.ui.mapping
- neko.ui.listview
- neko.ui.adapters
User interface
Action bar
SQLite
Logging