A small, an iota, side-effect tracking library and layout DSL
libraryDependencies += "com.hanhuy.android" %% "iota" % "1.0.4"
Clear proguard warnings, these will not cause any runtime crashes (scala 2.10 macro backward compat support)
proguardOptions += "-dontwarn iota.Internal210**"
import iota._ or import individual components from iota.std._ and iota.IO
The basic type is iota.IO which is a side-effect tracking monad. It offers
the functions perform, performMain, map, flatMap and >>= which is an
alias for flatMap. Any side-effects passed to IO will not run until
perform is invoked. IO objects can be created using the IO(any) factory,
and can be composed using >>= and map
All compositions are functions, no special classes.
Combinators._- defines the K-combinatorkestrel[A](f: A => _):A => IO[A]AutoK._- definesk[A]macro, used to turn any function onAinto a K-combinator, e.g.TextView#setHintcan be accessed ask.hintandk.setHintSingle._- definessingle[A]which can be used to implement anonymous abstract classes and all interfaces. e.g.,single[TextWatcher].onTextChanged((s: CharSequence, x: Int, y: Int, z: Int) => println(s))can implement aTextWatcher#onTextChangedwhile doing a no-op for theafterTextChangedandbeforeTextChangedmethods.Kleisli._- provides>=>(andThen) to composeA => IO[B]functionsTernary._-condK(condition ? (A => IO[B]) | (A => IO[B])) compositionViews._- hasw[View],l[View]andc[View]functions,wandlbehave like in macroid,wfor creating widgets, andlfor creating ViewGroups with children.cis a helper function for providing the ViewGroup type for LayoutParams creation when not inside ofIO[_ <: ViewGroup].apply().Contexts._- implicit materializer forandroid.content.Contextdepending on what class one is in (Activity,Fragment,HasContext, etc), also implements a type-safesystemService[T]lookupConfigurations._- has functions for detecting the current configuration:sw(smallest-width),v(version),landscapeandportraitFutureCombinators._- defines combinatorsdeferanddeferFfor working withIO[Future[A]]LayoutCombinators._- hasmargins,lpandlpKfunctions for working with layout paramsViewCombinators._- genericViewmanipulation, id,hook*, animate, etc.ViewCombinatorExtras._- more genericViewmanipualation, backgrounds, visibility, etc.TextCombinators._-TextViewmanipulators, fortext,inputType,hintand othersImageCombinators._- setimageResourceandimageScaleforImageViewsMainThreadExecutionContext- anExecutionContextfor working withFutures that should execute their callbacks on the UI thread
The following creates a LinearLayout with 2 buttons, "Click Me" and
"Click Me 2", vertically. It also registers onClick listeners to each button
using two different approaches, one where the inputs to the listener are
ignored, and one where the input is used.
import iota._
import android.app._
import android.os._
import android.content._
import android.view._
import android.widget._
class MyActivity extends Activity {
// compose multiple A => IO[A] together, re-use them anywhere we would like
// c[LinearLayout] is necessary to hint to `lpK` that LinearLayout.LayoutParams
// need to be created.
val button2Adjustments: Kestrel[Button] = c[LinearLayout](
k.text("Click Me 2") >=> hook.onClick((v: View) => IO {
Toast.makeText(this, s"button ${v.getId} was clicked", Toast.LENGTH_SHORT).show()
}) >=> lpK(WRAP_CONTENT, WRAP_CONTENT, 1.0f){ p: LinearLayout.LayoutParams =>
p.gravity = Gravity.CENTER
margins(all = 16.dp)(p)
}
)
// use a view holder pattern, such as the declaration below, for optimal static safety
lazy val button2 = new Button(this)
val layout = l[LinearLayout](
w[Button] >>= k.text("Click Me") >>= hook.onClick { v: View =>
// this example will animate the button's alpha to transparent and back upon click
// the text will change to "Alpha 0" when the first animation completes, and will
// change to "Alpha 1" when the second animation completes
IO(v.asInstanceOf[TextView]) >>=
animate(_.alpha(0)) >>= defer(k.text("Alpha 0")) >>=
deferF(animate(_.alpha(1))) >>= defer(k.text("Alpha 1"))
} >>= lp(WRAP_CONTENT, WRAP_CONTENT, 1.0f),
IO(button2) >>= button2Adjustments >>= id(Id.button2),
w[ListView] >>= lp(WRAP_CONTENT, WRAP_CONTENT, 1.0f) >>=
hookM.scroll.onScrollStateChanged((list: AbsListView, state: Int) => IO {
// partially implemented listeners with direct method name overrides
// can also be used as `hookM0.scroll.onScrollStateChanged` if the input
// parameters can be ignored.
Toast.makeText(this, "Scroll state changed: " + state, Toast.LENGTH_SHORT).show()
})
) >>= k.orientation(LinearLayout.VERTICAL)
override def onCreate(b: Bundle) {
setContentView(layout.perform())
button2.setText("new label")
}
}This sample is a sandbox for
trying out all the macros and features available in iota.
As a long time user of macroid I enjoy what it brought to layout dsl, but do
not like how certain features work: mixing in Contexts[A], extremely verbose
lp layout parameter creation, failures in type inference, lack of type-safety
in ID lookups, and operators that don't necessarily make sense (i.e. <~, et
al). The design goal here is to be as pure as possible while remaining terse
and using an existing vocabulary: kestrel (K-combinator), >>= (flatMap,
bind), >=> (kleisli composition), etc.