Giter Site home page Giter Site logo

anvil's Introduction

Anvil - reactive views for Android

Join the chat at https://gitter.im/zserge/anvil Build Status Android Weekly


logo

Anvil is a small Java library for creating reactive user interfaces. Originally inspired by React, it suits well as a view layer for MVVM or Redux design patterns.


Features

  • Super small (4 hand-written classes + 1 generated class)
  • Easy to learn (top-level API is only 5 functions)
  • Fast (uses no reflection¹)
  • Efficient (views are updated lazily, if the data change didn't affect the view - it remains untouched)
  • Easy to read declarative syntax
  • Java 8 and Kotlin-friendly, but supports Java 6 as well
  • XML layouts are supported, too

¹Reflection is still used to inflate views once (standard XML inflater does the same thing, so no performance loss here).

Installation

// build.gradle
repositories {
    jcenter()
}
dependencies {
    compile 'co.trikita:anvil-sdk15:0.5.0'
}

Anvil comes in multiple builds for different minimal SDK versions:

  • anvil-sdk15 (ICS, 99.7% of devices)
  • anvil-sdk19 (Kitkat, 94.3% of devices)
  • anvil-sdk21 (Lollipop, 82.3% of devices)

API levels 16, 17, 18, 22 or 23 are not added because they had very few UI-related methods added.

Examples

Normally you would write your layouts in XMLs, then get your views by their IDs and set their listeners, finally you would observe your data and modify view properties as your data changes.

Anvil simplifies most of this boring routine.

First, add a static import that makes it much easier to write your view:

import static trikita.anvil.DSL.*;

Next, declare your layout, assign event listeners and bind data:

public int ticktock = 0;
public void onCreate(Bundle b) {
    super.onCreate(b);
    setContentView(new RenderableView(this) {
        @Override
        public void view() {
            linearLayout(() -> {
                size(MATCH, MATCH);
                padding(dip(8));
                orientation(LinearLayout.VERTICAL);

                textView(() -> {
                    size(MATCH, WRAP);
                    text("Tick-tock: " + ticktock);
                });

                button(() -> {
                    size(MATCH, WRAP);
                    text("Close");
                    // Finish current activity when the button is clicked
                    onClick(v -> finish());
                });
            });
        }
    });
}

Here we've created a very simple reactive view and added it to our Activity. We've declared our layout (a LinearLayout with a TextView inside). We've defined styles (width, height, orientation and padding). We've set OnClickListener to the button. We've also bound a variable "ticktock" to the text property inside a TextView.

Next, let's update your views as your data changes:

ticktock++;
Anvil.render();

At this point your layout will be updated and the TextView will contain text "Tick-Tock: 1" instead of "Tick-Tock: 0". However the only actual view method being called would be setText().

You should know that there is no need to call Anvil.render() inside your event listeners, it's already triggered after each UI listener call:

public void view() {
    linearLayout(() -> {
        textView(() -> {
            text("Clicks: " + numClicks);
        });
        button(() -> {
            text("Click me");
            onClick(v -> {
                numClicks++; // text view will be updated automatically
            });
        });
    });
}

Made with Anvil

Talalarmo Slide Spot Quilt

You may find more Anvil examples for Java 6, Java 8 and Kotlin at

How it works

No magic. When a renderable object is being constructed there are 3 types of operations: push view, modify some attribute of the current view, and pop view. If you're familiar with incremental DOM - Anvil follows the same approach.

Pushing a view adds it as a child to the parent view from the top of the stack. Attribute modification simply sets the given property to the current view on top of the stack. Pop unwinds the stack.

When you mount this layout (assuming the name is "John"):

linearLayout(() -> {
    textView(() -> {
        text("Hello " + name);
    });
});

It does the following sequence of actions:

Push LinearLayout (adding it to the root view)
Push TextView (adding it to the linear layout)
Attr text "Hello John" (calling setText of the TextView)
Pop
Pop

The only trick is that these actions are cached into a so called "virtual layout" - a tree-like structure matching the actual layout of views and their properties.

So when you call Anvil.render() next time it compares the sequence of actions with the cache and skips property values if they haven't change. Which means on the next Anvil.render() call the views will remain untouched. This caching technique makes render a very quick operation (having a layout of 100 views, 10 attributes each you can do about 4000 render cycles per second!).

Now, if you modify the name from "John" to "Jane" and call Anvil.render() it will do the following:

Push LinearLayout (noop)
Push TextView (noop)
Attr text "Hello Jane" (comparing with "Hello John" from the pervious render,
  noticing the difference and calling setText("Hello Jane") to the TextView)
Pop
Pop

So if you modify one of the variables "bound" to some of the attributes - the cache will be missed and attribute will be updated.

For all event listeners a "proxy" is generated, which delegates its method calls to your actual event listener, but calls Anvil.render() after each method. This is useful, because most of your data models are modified when the user interacts with the UI, so you write less code without calling Anvil.render() from every listener. Remember, no-op renders are very fast.

Supported languages and API levels

Anvil is written in Java 7, but its API is designed to use lambdas as well, so in modern times it's recommended to use Anvil with Java8/Retrolambda or Kotlin.

Syntax is a bit different for each language, but it's very intuitive anyway.

Java 6 without lambdas:

public void view() {
    o (linearLayout(),
      orientation(LinearLayout.VERTICAL),

        o (textView(),
          text("...")),
        
        o (button(),
          text("..."),
          onClick(myListener)));
}

Java 8 + RetroLambda:

public void view() {
    linearLayout(() -> {
        orientation(LinearLayout.VERTICAL);
        textView(() -> {
            text("....");
        });
        button(() -> {
            text(".....");
            onClick(v -> {
                ....
            });
        });
    });
}

Kotlin:

public override fun view() {
    linearLayout {
        orientation(LinearLayout.VERTICAL)
        textView {
            text("....")
        }
        button {
            text("....")
            onClick { v ->
                ...
            }
        }
    }
}

Anvil library contains only a few classes to work with the virtual layout, but most of the DSL (domain-specific language describing how to create views/layouts and set their attributes) is generated from android.jar.

API

Here's a list of classes and methods you need to know to work with Anvil like a pro:

  • Anvil.Renderable - functional interface that one should implement to describe layout structure, style and data bindings.

  • Anvil.mount(View, Anvil.Renderable) - mounts renderable layout into a View or a ViewGroup

  • Anvil.unmount(View) - unmounts renderable layout from the View removing all its child views if it's a ViewGroup

  • Anvil.render() - starts a new render cycle updating all mounted views

  • Anvil.currentView(): View - returns the view which is currently being rendered. Useful in some very rare cases and only inside the Renderable's method view() to get access to the real view and modifying it manually.

  • RenderableView - a most typical implementation of Anvil.Renderable. Extending this class and overriding its method view() allows to create self-contained reactive components that are mounted and unmounted automatically.

  • RenderableAdapter - extending this class and overriding its getCount(), getItem(int) and view(int) allows to create lists where each item is a standalone reactive renderable object.

  • RenderableAdapter.withItems(list, cb(index, item)) - a shortcut to create simple adapters for the given list of items. cb is a lambda that describes the layout and bindings of a certain list item at the given index.

DSL

The bottom part of the iceberg is Anvil DSL.

DSL consists of a few handwritten property setters, but most of it is generated from java classes in the android SDK.

See a full list of the DSL methods for each API level here.

Property setters

The setters are named as the view methods from Android SDK, but without the "set" prefix. E.g. "text(s)" instead of "setText(s)", "backgroundDrawable(d)" instead of "setBackgroundDrawable(d)" and so on.

Event listeners

Event listeners also have names from the Android SDK, but without the "set" prefix and the "Listener" suffix, e.g. "onClick" instead of "setOnClickListener".

Handwritten bindings

For LayoutParams the bindings can't be generated easily, so it was faster to write them manually:

  • size(width, height) - set width and height. Special constants like WRAP, FILL and MATCH are available.
  • dip(x) - returns the value in pixels for the dimension in density-independent pixels. Often used with size, padding or margin properties.
  • margin(m), margin(h, v), margin(l, t, r, b) - set view margin for all 4 sides, for horizontal/vertical dimensions or for each side individually.
  • weight(w) - modifies view layout weight.
  • layoutGravity(g) - modifies layout gravity of a view. Common constants like START, END, CENTER, CENTER_VERTICAL, etc. are available.
  • align(verb, anchor), align(verb) - base functions for relative layout params.
  • above(id), alignBaseline(id), alignBottom(id), alignEnd(id), alignLeft(id), alignParentBottom(), alignParentEnd(), alignParentLeft(), alignParentRight(), alignParentStart(), alignParentTop(), alignRight(id), alignTop(id), below(id), centerHorizontal(), centerVertical(), centerInParent(), toEndOf(id), toLeftOf(id), toRightOf(id), toStartOf(id) - all possible settings for relative layout params

A few bindings have been written for other use cases which we find useful:

  • init(Runnable) - executes a runnable once, useful to initialize custom views (see also Anvil.currentView()).
  • R() - returns a Resources object associated with the current view. Useful for multiple screen support (sizes, dpi, orientation etc).
  • isPortrait() - returns true if a screen is portrait-oriented. Useful for tweaking layouts for different orientations.
  • typeface(font) - loads font from assets by its file name and sets the typeface to a TextView
  • padding(p), padding(h, v), padding(l, t, r, b) - set view padding for all 4 sides, for horizontal/vertical dimensions or for each side individually.
  • visibility(flag) - sets the visibility property to View.VISIBLE or View.GONE depending on the flag boolean value
  • shadowLayer(radius, dx, dy, color) - sets shadow layer of a TextView
  • onTextChanged(textWatcher) - binds a text watcher to an EditText. No Anvil.render() is called in this case, because you're likely to get an infinite recursion.
  • text(StringBuilder) - binds a string builder to the edit text, so when you change its contents - the edit text is changed, and if you type something manually - the string builder gets modified. So far it's the only two-way data binding, becayse TextWatcher is a complicated beast.
  • onItemSelected(lambda) - accepts a functional interface to handle a Spinner events. onNothingSelected() method is omitted, because it's rarely used anyway.

If a binding you need is not in the list - please, check issue #27 and report it there.

A special case for animations is added:

  • anim(trigger, Animator) - starts animation when trigger is true, cancels it when the trigger becomes false.

Finally, a few low-level DSL functions are there, which you would no need unless you want to write your own property setters or custom view builders:

  • v(class, attrs...) - pushes view, applies attributes, doesn't pop the view.
  • o(), x() - names that look like bullets, actually pop the view. These are used in Java 6 syntax.
  • v(class, renderable) - pushes the view, applies the renderable to fulfil attributes and child views, pops the view. This is used in Java 8 and Kotlin syntax.
  • attr(func, value) - checks the cache for the given value of the given property setter function. Often used to create your own property setter binding.

XML layouts

If you're migrating an existing project to Anvil or if you prefer to keep your view layouts declared in XML - you can do so:

public override fun view() {
    // A designer gave us an XML with some fancy layout:
    // a viewgroup with a button and a progress bar in it
    xml(R.layout.my_layout) {
        backgroundColor(Settings.bgColor) // will modify root layout view color

        withId(R.id.my_button) {
            // button state may depend on some variable
            enabled(isMyButtonEnabled)
                // button click listener can be attached
                onClick { v ->
                    ...
                }
        }

        withId(R.id.my_progress_bar) {
            visible(isMyProgressBarShown)
            progress(someProgressValue)
        }
    }
}

Here xml() creates a view node, much like frameLayout() or textView(), except for it uses an XML file to inflate the views, not the direct view class constructor. Much like view "builders", xml() takes a renderable lambda as a parameter and uses that to modify the created view.

Any attribute setters will affect the root view from the XML layout.

If you want to modify attributes of the child views from the XML - you should use withId() to assign a renderable to a view with the given ID. You may not follow the hierarchy of the child views, e.g. all your withId() calls may be nested as the views are nested in the XML, or may be direct children of the xml() renderable. xml() calls can be nested, too.

xml() also allows you to create views that can not be inflated from Java code but only from XML (such as hostozintal progress bars) or any other views where AttributeSet must be given or the views that rely on the onFinishInflate method to be called.

withId() call is not limited to XML views, it can be used for views declared in code that have IDs. So if you have a custom viewgroup that creates its child views automatically and assigns some IDs to them - you can still modify their properties using withId(). Also, if any views are created inside the withId() or xml() - they will be appeneded to the view group:

v(MyComponent.java, () -> {
    withId(R.id.child_view, () -> {
        // R.id.child_view was implicitely created in MyComponent's constructor
        // but we still can modify it here
    });

    textView(() -> {
        // This textView will be appeneded to MyComponent layout
    });
});

License

Code is distributed under MIT license, feel free to use it in your proprietary projects as well.

anvil's People

Contributors

bitdeli-chef avatar corbt avatar danh-fissara avatar dmitry-zaitsev avatar gitter-badger avatar gmarques33 avatar krugloid avatar lalitmaganti avatar lukasvykuka avatar matthewmichihara avatar qumeric avatar sgrekov avatar thasmin avatar zserge avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

anvil's Issues

TextView setTextSize: sp vs pixels?

In Java code textView.setTextSize takes value in sp already, thus setTextSize(27) is 27sp.
In XML developers specify the unit explicitly.
Anvil now uses Java approach (because it blindly wraps SDK methods).
I think it would be more natural to change this behavior and expect a unit to be always passed, e.g. textSize(sp(27)) - as it makes more sense and developers are used to it.

LayoutParams from support libraries and custom views

Layout params are not handled by generated DSL now. They are handled by hand-written BaseDSL in anvil base library. There are custom views that use their own layout params that BaseDSL knows nothing about. So far there is no way to set such layout params except for calling layoutParams method directly.

Here's a list of all ViewGroup.LayoutParams implementations:

  • AbsListView.LayoutParams - same as normal LayoutParams
  • AbsoluteLayout.LayoutParams - adds x and y to LayoutParams, but AbsoluteLayout is deprecated
  • ActionBar.LayoutParams - adds gravity to MarginLayoutParams, not via XML
  • ActionMenuView.LayoutParams - adds cellsUsed, expandable, extraPixels, isOverflowButton, preventEdgeOffset to MarginLayoutParams, not via XML
  • AppBarLayout.LayoutParams - same as LinearLayout.LayoutParams
  • BaseCardView.LayoutParams - adds viewType to FrameLayout.LayoutParams, not via XML
  • CollapsingToolbarLayout.LayoutParams - adds CollapseMode and parallax multiplier to FrameLayout.LayoutParams, not via XML
  • CoordinatorLayout.LayoutParams - adds anchorGravity, gravity and keyline to MarginLayoutParams
  • DrawerLayout.LayoutParams - adds gravity to MarginLayoutParams
  • FrameLayout.LayoutParams - adds gravity to MarginLayoutParams
  • Gallery.LayoutParams - same as normal LayoutParams
  • GridLayout.LayoutParams - adds row and column spec to MarginLayoutParams
  • GridLayoutManager.LayoutParams - same as MarginLayoutParams
  • LinearLayout.LayoutParams - adds weight and gravity to MarginLayoutParams
  • LinearLayoutCompat.LayoutParams - also adds weight and gravity to MarginLayoutParams, rarely used and not via XML
  • PercentFrameLayout.LayoutParams - same as FrameLayout.LayoutParams
  • PercentRelativeLayout.LayoutParams - same as RelativeLayout.LayoutParams
  • RadioGroup.LayoutParams - same as LinearLayout.LayoutParams
  • RecyclerView.LayoutParams - same as MarginLayoutParams
  • RelativeLayout.LayoutParams - adds rules to MarginLayoutParams
  • SlidingPaneLayout.LayoutParams - adds weight to MarginLayoutParams
  • StaggeredGridLayoutManager.LayoutParams - same as MarginLayoutParams
  • TableLayout.LayoutParams - same as LinearLayout.LayoutParams
  • TableRow.LayoutParams - adds column and span to LinearLayout.LayoutParams
  • Toolbar.LayoutParams - same as ActionBar.LayoutParams, not via XML
  • ViewGroup.LayoutParams (base) has width and height
  • ViewGroup.MarginLayoutParams - adds margin to base LayoutParams
  • ViewPager.LayoutParams - adds gravity and isDecor to base LayoutParams, not via XML
  • WindowManager.LayoutParams - adds lots of fields, but none of them exporeted via XML

Anvil needs promotion

While Anvil has certain advantages over Anko, Google Data Binding library or React Native, only a few people know about this library. There is no Anvil tutorials around the web, documentation on Github and my blog suffers from my linguistic skills (English is my second language).

I wonder if any of you have finished any apps using Anvil, or maybe have a blog and would like to spread the word about Anvil? Any help is appreciated.

Handwritten setters that might be useful

Currently Anvil contains lots of autogenerated bindings for all setters of all
views in Android SDK, starting with "set" and having exactly one parameter
(normally a value to set). Some setters are not covered - the ones that start
with "add" (like addTextChangedListeer and the ones having more than a single
parameter (like setLineSpacing).

As it was discussed previously in issue #15 - missing setters will be added if
someone finds real need in them (most of them are really exotic), and makes a
pull-request. The list of setters has been taken from the recent android.jar of
API Level 23.

I suggest to split this in two phases:

  • If you find a missing setting really useful - mention it in the comments, and
    I will move it to the "TODO" task list.
  • If you want to implement one of the unimplemented setters from the task list
    please make a pull request, and I will mark the task as done.

TODO

  • addTextChangedListener(TextWatcher) in TextView (implemented as text and
    onTextChanged bindings)
  • setShadowLayer(float,float,float,int) in TextView
  • setPadding(int,int,int,int) - implemented as 1, 2 or 4 parameter function
  • setTag(int, Object) - beware, tag ID should be from R.id.something, not just a random number
  • setTypeface(Typeface,int) inTextView
  • check(int) in RadioGroup

Missing setters starting with "add", not "set"

  • addChildrenForAccessibility(ArrayList) : View
  • addFocusables(ArrayList,int) : View
  • addFocusables(ArrayList,int,int) : View
  • addFooterView(View) : ListView
  • addFooterView(View,Object,boolean) : ListView
  • addHeaderView(View) : ListView
  • addHeaderView(View,Object,boolean) : ListView
  • addJavascriptInterface(Object,String) : WebView
  • addOnAttachStateChangeListener(View$OnAttachStateChangeListener) : View
  • addOnGestureListener(GestureOverlayView$OnGestureListener) : GestureOverlayView
  • addOnGesturePerformedListener(GestureOverlayView$OnGesturePerformedListener) : GestureOverlayView
  • addOnGesturingListener(GestureOverlayView$OnGesturingListener) : GestureOverlayView
  • addOnLayoutChangeListener(View$OnLayoutChangeListener) : View
  • addSubtitleSource(InputStream,MediaFormat) : VideoView
  • addTab(TabHost$TabSpec) : TabHost
  • addTouchables(ArrayList) : View

Missing setters with more than one parameter

  • setAppWidget(int,android.appwidget.AppWidgetProviderInfo) : AppWidgetHostView
  • setChildIndicatorBounds(int,int) : ExpandableListView
  • setChildIndicatorBoundsRelative(int,int) : ExpandableListView
  • setColorFilter(int,PorterDuff$Mode) : ImageView
  • setColumnCollapsed(int,boolean) : TableLayout
  • setColumnShrinkable(int,boolean) : TableLayout
  • setColumnStretchable(int,boolean) : TableLayout
  • setCompoundDrawables(Drawable,Drawable,Drawable,Drawable) : TextView
  • setCompoundDrawablesRelative(Drawable,Drawable,Drawable,Drawable) : TextView
  • setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable,Drawable,Drawable,Drawable) : TextView
  • setCompoundDrawablesRelativeWithIntrinsicBounds(int,int,int,int) : TextView
  • setCompoundDrawablesWithIntrinsicBounds(Drawable,Drawable,Drawable,Drawable) : TextView
  • setCompoundDrawablesWithIntrinsicBounds(int,int,int,int) : TextView
  • setContentInsetsAbsolute(int,int) : Toolbar
  • setContentInsetsRelative(int,int) : Toolbar
  • setDate(long,boolean,boolean) : CalendarView
  • setEGLConfigChooser(int,int,int,int,int,int) : GLSurfaceView
  • setError(CharSequence,Drawable) : TextView
  • setHttpAuthUsernamePassword(String,String,String,String) : WebView
  • setImageState(int[],boolean) : ImageView
  • setImeActionLabel(CharSequence,int) : TextView
  • setInAnimation(Context,int) : AdapterViewAnimator
  • setInAnimation(Context,int) : ViewAnimator
  • setIndicatorBounds(int,int) : ExpandableListView
  • setIndicatorBoundsRelative(int,int) : ExpandableListView
  • setInputExtras(int) throws org.xmlpull.v1.XmlPullParserException,java.io.IOException : TextView
  • setInterpolator(Context,int) : ProgressBar
  • setItemChecked(int,boolean) : AbsListView
  • setLayerType(int,Paint) : TextureView
  • setLayerType(int,Paint) : View
  • setLayerType(int,Paint) : WebView
  • setLineSpacing(float,float) : TextView
  • setOutAnimation(Context,int) : AdapterViewAnimator
  • setOutAnimation(Context,int) : ViewAnimator
  • setPaddingRelative(int,int,int,int) : TextView
  • setPaddingRelative(int,int,int,int) : View
  • setParentTitle(CharSequence,CharSequence,View$OnClickListener) : FragmentBreadCrumbs
  • setPopupOffset(int,int) : KeyboardView
  • setPrevNextListeners(View$OnClickListener,View$OnClickListener) : MediaController
  • setQuery(CharSequence,boolean) : SearchView
  • setScrollIndicators(View,View) : AbsListView
  • setScrollIndicators(int,int) : View
  • setSelectedChild(int,int,boolean) : ExpandableListView
  • setSelection(int,boolean) : AbsSpinner
  • setSelection(int,int) : EditText
  • setSelectionFromTop(int,int) : AbsListView
  • setSubtitleTextAppearance(Context,int) : Toolbar
  • setSwitchTextAppearance(Context,int) : Switch
  • setSwitchTypeface(Typeface,int) : Switch
  • setText(CharSequence,TextView$BufferType) : EditText
  • setText(CharSequence,TextView$BufferType) : TextView
  • setText(CharSequence,boolean) : AutoCompleteTextView
  • setText(char[],int,int) : TextView
  • setText(int,TextView$BufferType) : TextView
  • setTextAppearance(Context,int) : TextView
  • setTextKeepState(CharSequence,TextView$BufferType) : TextView
  • setTextSize(int,float) : TextView
  • setTitle(CharSequence,CharSequence) : FragmentBreadCrumbs
  • setTitleTextAppearance(Context,int) : Toolbar
  • setVideoURI(Uri,Map) : VideoView

not able to set adapter for RenderableRecyclerViewAdapter

Hello Folks,
I have tried to set adapter for RenderableRecyclerViewAdapter but getting below error.
Error:(65, 28) error: no suitable method found for adapter(RenderableRecyclerViewAdapter)
method DSL.adapter(Adapter) is not applicable
(argument mismatch; RenderableRecyclerViewAdapter cannot be converted to Adapter)
method DSL.adapter(ExpandableListAdapter) is not applicable
(argument mismatch; RenderableRecyclerViewAdapter cannot be converted to ExpandableListAdapter)

Please suggest me how to resolve this error.

I have included below lines in build.gradle.
compile 'co.trikita:anvil-sdk15:0.4.0'
compile 'co.trikita:anvil-recyclerview-v7:0.4.0'
compile 'com.android.support:recyclerview-v7:23.0.1'

Looking forward for your response.

Thanks,
Devang

Missing textAppearance()

A method to set text appearance is missing from the set of generated attr methods.

I would expect to be able to do something like that (in Kotlin):

textView {
    textAppearance(android.R.style.TextAppearance_Material_Display1)
}

but currently there is no existing method for that.

I've seen you explain in one of the closed issues that methods for setters with multiple arguments are not generated and you have to add them by hand. This however takes just one argument, so it seems weird it's been omitted.

I can write the method myself, but I think it would be nice to be able to import from the DSL class like all the other methods. My implementation looks like that:

private fun textAppearance(textAppearance: Int) =
        attr(Anvil.AttrFunc { textView, new: Int, old ->
            (textView as TextView).setTextAppearance(new)
        }, textAppearance)

Anvil renders views in background activities

As I've been writing more Anvil views (I haven't counted, but my project now currently has several thousand lines of Anvil split between about a dozen views), I'm beginning to run into performance issues.

One problem is that Anvil continues rendering all views even when they're in the background. Specifically, the starting activity of my project has a quite expensive view (it requires loading many images from disk). Every time Anvil re-renders on any page I've noticed that it re-renders that view as well.

I need to optimize that view so it doesn't keep repeating (and that's my first step), but would it be possible for Anvil to only re-render views that are in the current activity/are currently visible?

Incremental size() calls

Attribute modifier size() always overrides width/height of the existing layout.
The only LayoutNode constructor has width and height arguments. You can't update the margin or weight without updating the width/height as well.

Quirk with xml views and IDs

Not sure if much can be done about this, but perhaps it should be documented somewhere.

If I inflate a view tree where view IDs arent't globally unique, e.g.

linearlayout id=1
   edittext id=2, text=a
   edittext id=2, text=b
   edittext id=2, text=c

When the edittexts' state is saved and restored, the wrong value is set on 2/3 of them because view state is keyed by the view's ID.

linearlayout id=1
   edittext id=2, text=a
   edittext id=2, text=a
   edittext id=2, text=a

This creates a problem when setting the text with Anvil, as for example text("b") is a no-op because as far as Anvil's concerned the view's value is already "b".

Adding anvil to local project causes gradle build to fail

This likely isn't a real issue, and I'm probably doing something wrong, but I thought I'd pass it on.

When I copy the anvil project to a subdirectory of my project and add it as a dependency (in dependencies I add compile project(':libs:anvil:library')), my gradle build fails with the following error:

12:04:01.150 [ERROR] [org.gradle.BuildExceptionReporter] Script '/Users/kyle/proj/android-emberall/libs/anvil/library/publish.gradle' line: 8
12:04:01.150 [ERROR] [org.gradle.BuildExceptionReporter]
12:04:01.151 [ERROR] [org.gradle.BuildExceptionReporter] * What went wrong:
12:04:01.152 [ERROR] [org.gradle.BuildExceptionReporter] A problem occurred evaluating script.
12:04:01.152 [ERROR] [org.gradle.BuildExceptionReporter] > org.gradle.api.internal.project.ProjectInternal.getPluginManager()Lorg/gradle/api/internal/plugins/PluginManagerInternal;

I've experimentally determined that commenting out either apply from: 'publish.gradle' or the line in publish.gradle apply plugin: 'com.github.dcendents.android-maven' make this issue go away, and that's what I'm doing right now. But I wonder if it's necessary to include the publishing script in the same build.gradle, as that may make it harder for people to use experimental versions of the library.

Support library v4 bindings

I scanned support v4 JAR and got a list of all view classes and their public
setters.

A hand-written library "anvil-support-v4" should be written to handle these
cases. Method names should not conflict with the ones from DSL.java.

Below is a list of all classes and their methods that Anvil should (and should
not) support.

Any comments are welcome!

Anvil should support

android.support.v4.view.ViewPager
    addFocusables(ArrayList,int,int)
    addOnPageChangeListener(ViewPager$OnPageChangeListener)
    addTouchables(ArrayList)
    setAdapter(PagerAdapter)
    setCurrentItem(int)
    setCurrentItem(int,boolean)
    setOffscreenPageLimit(int)
    setOnPageChangeListener(ViewPager$OnPageChangeListener)
    setPageMargin(int)
    setPageMarginDrawable(Drawable)
    setPageMarginDrawable(int)
    setPageTransformer(boolean,ViewPager$PageTransformer)

android.support.v4.widget.DrawerLayout
    addFocusables(ArrayList,int,int)
    setChildInsets(Object,boolean)
    setDrawerElevation(float)
    setDrawerListener(DrawerLayout$DrawerListener)
    setDrawerLockMode(int)
    setDrawerLockMode(int,View)
    setDrawerLockMode(int,int)
    setDrawerShadow(Drawable,int)
    setDrawerShadow(int,int)
    setDrawerTitle(int,CharSequence)
    setScrimColor(int)
    setStatusBarBackground(Drawable)
    setStatusBarBackground(int)
    setStatusBarBackgroundColor(int)

android.support.v4.widget.SlidingPaneLayout
    setCoveredFadeColor(int)
    setPanelSlideListener(SlidingPaneLayout$PanelSlideListener)
    setParallaxDistance(int)
    setShadowDrawable(Drawable)
    setShadowDrawableLeft(Drawable)
    setShadowDrawableRight(Drawable)
    setShadowResource(int)
    setShadowResourceLeft(int)
    setShadowResourceRight(int)
    setSliderFadeColor(int)

android.support.v4.widget.SwipeRefreshLayout
    setColorScheme(int[])
    setColorSchemeColors(int[])
    setColorSchemeResources(int[])
    setDistanceToTriggerSync(int)
    setNestedScrollingEnabled(boolean)
    setOnRefreshListener(SwipeRefreshLayout$OnRefreshListener)
    setProgressBackgroundColor(int)
    setProgressBackgroundColorSchemeColor(int)
    setProgressBackgroundColorSchemeResource(int)
    setProgressViewEndTarget(boolean,int)
    setProgressViewOffset(boolean,int,int)
    setRefreshing(boolean)
    setSize(int)

Anvil should not support

- FragmentTabHost (it's normally inflated from XML and has no data bindings
  nor dynamic styles, so there is no benefit from using Anvil bindings)
- PagerTabStrip - also, inflated from XML and has no dynamic behaviour
- PagerTitleStrip - see PagerTabStrip
- NestedScrollView - it's rarely used, and it has a large/complex API
- Space - a normal View class can be used as well in most cases

Use of singletons in view bindings

In all the generated attribute application functions, a corresponding singleton is generated. Unfortunately, across the 4 projects there are now hundreds of these and all are intialised on startup of the app. This is extremely expensive to say the least and is especially bad considering many of them are quite obscure and would never be used.

I propose to turn them into lazy singletons instead which would ensure that only the functions required would be cached.

Link in documentation 404's

Just a note;
This link 'full list of available generators to bind data and listeners to your views' in the Documentation isn't found.

Thanks,
Dan.

Checkbox: onCheckedChange is ambiguous

onCheckedChange attribute setter takes either RadioGroup listener or a CompoundButton listener. That's why you always must specify types to resolve ambiguity.

Overload resolution ambiguity with adapter(RenderableArrayAdapter)

I have the following test activity in kotlin that is causing an error that appears to be related to the implementation of RenderableArrayAdapter:

import android.app.Activity
import android.os.Bundle

import android.content.Context;
import android.util.Log
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView
import android.widget.TextView;
import trikita.anvil.RenderableView;

import trikita.anvil.RenderableView;
import trikita.anvil.BaseAttrs.*;
import trikita.anvil.Nodes
import trikita.anvil.RenderableArrayAdapter
import trikita.anvil.v10.Attrs.*;

class UsersList: Activity() {
    override fun onCreate(b: Bundle?) {
        super<Activity>.onCreate(b)

        var testNames: Array<String> = array("Bob Jones", "John Black", "Other Name")

        setContentView(object: RenderableView(this) {
            public override fun view() =
                    v<LinearLayout> {
                        - size(FILL, FILL)
                        - orientation(LinearLayout.VERTICAL)

                        v<TextView> {
                            - text("Names")
                        }

                        v<ListView> {
                            adapter(object : RenderableArrayAdapter<String>(testNames) {
                                override fun itemView(p0: Int, p1: String?): Nodes.ViewNode? {
                                    throw UnsupportedOperationException()
                                }
                            })
                        }
                    }
        });
    }
}

The compiler throws the following error for the adapter() function call:

Error:(37, 29) Overload resolution ambiguity:
public open fun adapter(arg: android.widget.ListAdapter!): trikita.anvil.Nodes.AttrNode! defined in trikita.anvil.v10.Attrs
public open fun adapter(arg: android.widget.Adapter!): trikita.anvil.Nodes.AttrNode! defined in trikita.anvil.v10.Attrs
public open fun adapter(arg: android.widget.SpinnerAdapter!): trikita.anvil.Nodes.AttrNode! defined in trikita.anvil.v10.Attrs

It looks like this is because RenderableArrayAdapter implements ListAdapter and SpinnerAdapter (and I guess Adapter as well). But since there are different implementation bodies the method doesn't resolve. Is there something I'm doing wrong here, or should adapter be combined into a single method call that dispatches internally based on its argument type?

Drop the support of Android 2.3.3?

Currently Anvil's minSdk is API level 10 (2.3.3).

Dropping it would mean losing 5.6% of devices. What API levels do you normally support in your new apps?

On the other hand, if we focus on API levels 15 and higher - we could move Anim.java into Anvil's core (it relies on Honeycomb animations).

Probably it would be better to drop v10 and add v19 (KitKat) instead? So that v15 would cover 95% of the devices, while v19 would cover 50% of the devices, the modern ones.

Anvil versions: naming convention

Anvil contains "core" which is independent of Android version.
Also it contains Attrs.java, which is a generated file with all views and their attributes for the given Android API level.

Previously I was bundling API level 10 and API level 15 and distributed it as the default Anvil package. Now I implement API-dependent bindings as gradle frlavors, so as a result I have multiple AARs being built - one for each supported API level (so far they are Android 2.3.3, Android 4.0 and Android 4.4). Each AAR contains Anvil "core" classes as well.

So previously co.trikita:anvil:+ has been the only latest version of Anvil. Now I see the following options:

  1. Use different artifact names, like anvil-v10, anvil-v15 and anvil-v19. Use version to define Anvil core version. Pros: versioning is simple. Cons: requires creating multiple projects on maven central and bintray for each API level.

  2. Use the same artifact name, e.g. anvil, but use major version number as API level, e.g. co.trikita:anvil:10.1.0 would be Anvil 1.0 for API level 10, co.trikita:anvil:15.1.0 would be Anvil 1.0 for API level 15 etc. Pros: single project to deploy. Cons: one must know this little trick with the versions, otherwise he might use co.trikita:anvil:+ and get KitKat minSdk without knowing why.

  3. Like the first option, but publish only one most common API level, e.g. v15 as co.trikita:anvil:1.0.0. Leave API levels 10, 19 and others not-so-popular ones for local Anvil builds (e.g. git clone + gradle build publishToMavenLocal).

Please, vote and let me know your thoughts!

Problems with import of anvil Methods

First example:
When I try to import the xml Method with Android-Studio:

screen shot 2016-04-07 at 15 43 45

This happens:

screen shot 2016-04-07 at 15 44 39

I need to manually add import trikita.anvil.DSL.xml

Second example:

screen shot 2016-04-07 at 15 50 21

When I add import trikita.anvil.DSL.backgroundColor it works.

I'm using Android-Studio 1.5.1 with Kotlin 1.0.0 and anvil-sdk15:0.3.1.

Support library difficulties and ugliness

Hi, I love this library so far, it's so refreshing! But, there is one thing holding me back and making me wanna go back to XML, which is that the appcompat-v7 and support-v4 DSLs have conflicting methods with the core DSL. Also, it kills me that we have to write appCompatButton instead of just button and have it automatically use the appropriate version.

I would gladly work with someone on the issue (I'm not too good at making APIs on my own, hehe).

Thanks in advance,
Graknol

Supporting multiple screens

People often ask how Anvil supports multiple screens. Currently it only provides an isPortrait helper.
All "media queries" (in terms of CSS) end up with checking current Configuration, which can be retrieved from Resources assiciated with the current Context:

if (getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
  // portrait layout
} else {
 // landscape layout
}

To keep Anvil simple we can remove isPortrait and make users write media queries manually. Or we can find a minimal subset of media queries to satisfy most needs.

Configuration allows you to detect:

  • Orientation (landscape, portrait)
  • Screen size category (small, normal, large, xlarge)
  • Screen DPI
  • Screen physical size in inches
  • Screen geometry (square, normal, long, or specific aspect ratio)

From the above I find the most practical to detect orientation and to tell normal (phone) layouts from large (tablet) layouts. This means that isPortrait is here to stay, but the screen size could be checked with different functions:

  • isTablet(), isTv(), isPhone()
  • isLarge(), isXLarge()
  • displaySize() that would returns size in inches, e.g. if (displaySize() > 6.5) { tablet(); } else { phone(); }

etc, etc. What are your thoughts?

P.S. Anko supports lots of configuration media queries - https://github.com/Kotlin/anko/blob/master/doc/ADVANCED.md#configuration-qualifiers while Scaloid supports only a few - https://github.com/pocorall/scaloid/blob/master/scaloid-common/src/main/st/org/scaloid/util/Configuration.scala

config() called many times on startup

I need to add a photo-taking activity to my application, and I'm using Anvil for the view. I've just updated to 0.0.9.

I'm trying out the new config() functionality (I use my homemade ref system in my existing activities) and am running into an issue.

Basically, config() function is being called on every user interaction, and even multiple times without any interaction at startup (it seems to be called 4 times at activity startup without any callbacks or calls to Anvil.render()). The issue is that in my config() function I wire up a SurfaceView to a camera instance. When a picture is taken, the config() block is called again (even though the SurfaceView has never disappeared from the screen, or changed in any way, nor any of its parents) and tries to set up the SurfaceView again, which fails because the camera is no longer in the right mode.

I think I can work around this by adding a boolean flag to indicate that the camera has been initialized and then only calling the code in config if it hasn't been called before, but my understanding is that config should do this itself, and only be called when there has been an actual change to an element or its parents. Is my understanding correct?

contributing for the artifacts with external dependencies (recyclerview)

Hey, I wanted to ask about that recyclerview patch.

How do you see external dependencies added to the project?
I tried adding a recyclerView {} flavor and recyclerViewCompile "com.android.support:recyclerview-v7:$supportLibrary" dependency, but BaseDsl expects each flavor to implement trikita.anvil.DSL

I am thinking about using flavor dimentions but that might introduce an unnecessary complexity to the publishing process and also something like sdk19RecyclerView variant doesn't make much sense.

What do you think? Do you have any vision on how to solve this? I personally would go for a module for each released artifacts and a core, something like:

  • core (sdk10)
  • sdk15
  • sdk19
  • recycler-view

But I'm not really sure how to handle the attrs part

Animate view navigation in single-activity apps

Hi! i would like to try your library but before coding i would like to know if is possibile to animate navigation between RenderableViews. I haven't found any reference in readme. Thanks

Support library v7 bindings

Android Support library v7 includes several components: appcompat, grid,
cardview, recyclerview and palette.

Palette is of little interest to Anvil since it contains no View classes.

CardView, Grid and RecyclerView contain one view each + all the classes to work
with that view.

AppCompat contains a number of typical views from Android SDK wrapped with
slightly enhanced functionality and changed look-and-feel.

I expect Anvil to support all views listed in the support libraries and provide bindings for the most typical methods.

AppCompat Views

AppCompat.autoCompleteTextView()
    setDropDownBackgroundResource(int)
AppCompat.button()
AppCompat.checkBox()
AppCompat.checkedTextView()
AppCompat.editText()
AppCompat.multiAutoCompleteTextView()
AppCompat.radioButton()
AppCompat.ratingBar()
AppCompat.spinner()
AppCompat.textView()
AppCompat.linearLayout()
AppCompat.searchView()
AppCompat.switch()
    setChecked(boolean)
    setShowText(boolean)
    setSplitTrack(boolean)
    setSwitchMinWidth(int)
    setSwitchPadding(int)
    setSwitchTextAppearance(android.content.Context,int)
    setSwitchTypeface(android.graphics.Typeface)
    setSwitchTypeface(android.graphics.Typeface,int)
    setTextOff(java.lang.CharSequence)
    setTextOn(java.lang.CharSequence)
    setThumbDrawable(android.graphics.drawable.Drawable)
    setThumbResource(int)
    setThumbTextPadding(int)
    setTrackDrawable(android.graphics.drawable.Drawable)
    setTrackResource(int)
AppCompat.toolbar()
    setCollapsible(boolean)
    setContentInsetsAbsolute(int,int)
    setContentInsetsRelative(int,int)
    setLogo(android.graphics.drawable.Drawable)
    setLogo(int)
    setLogoDescription(int)
    setLogoDescription(java.lang.CharSequence)
    setMenu(internal.view.menu.MenuBuilder,ActionMenuPresenter)
    setMenuCallbacks(MenuPresenter$Callback,MenuBuilder$Callback)
    setNavigationContentDescription(int)
    setNavigationContentDescription(java.lang.CharSequence)
    setNavigationIcon(android.graphics.drawable.Drawable)
    setNavigationIcon(int)
    setNavigationOnClickListener(android.view.View$OnClickListener)
    setOnMenuItemClickListener(Toolbar$OnMenuItemClickListener)
    setOverflowIcon(android.graphics.drawable.Drawable)
    setPopupTheme(int)
    setSubtitle(int)
    setSubtitle(java.lang.CharSequence)
    setSubtitleTextAppearance(Context,int)
    setSubtitleTextColor(int)
    setTitle(int)
    setTitle(java.lang.CharSequence)
    setTitleTextAppearance(Context,int)
    setTitleTextColor(int)

AppCompat.actionMenu()
    setExpandedActionViewsExclusive(boolean)
    setMenuCallbacks(MenuPresenter$Callback,MenuBuilder$Callback)
    setOnMenuItemClickListener(ActionMenuView$OnMenuItemClickListener)
    setOverflowIcon(Drawable)
    setOverflowReserved(boolean)
    setPopupTheme(int)
    setPresenter(ActionMenuPresenter)

AppCompat methods

For all tintable views:

setSupportAllCaps(boolean)
setSupportBackgroundTintList(android.content.res.ColorStateList)
setSupportBackgroundTintMode(android.graphics.PorterDuff$Mode)

Card views and methods

Card.card() to create android.support.v7.widget.CardView

    setRadius(float)
    setShadowPadding(int,int,int,int)
    setPadding(int,int,int,int)
    setPaddingRelative(int,int,int,int)
    setUseCompatPadding(boolean)
    setContentPadding(int,int,int,int)
    setCardBackgroundColor(int)
    setCardElevation(float)
    setMaxCardElevation(float)
    setPreventCornerOverlap(boolean)

Grid views and methods

Grid.grid()
    setOrientation(int)
    setRowCount(int)
    setColumnCount(int)
    setUseDefaultMargins(boolean)
    setAlignmentMode(int)
    setRowOrderPreserved(boolean)
    setColumnOrderPreserved(boolean)
    setPrinter(android.util.Printer)

Recycler views and methods

Recycler.recyclerView()
    addFocusables(java.util.ArrayList,int,int)
    addItemDecoration(RecyclerView$ItemDecoration)
    addItemDecoration(RecyclerView$ItemDecoration,int)
    addOnChildAttachStateChangeListener(RecyclerView$OnChildAttachStateChangeListener)
    addOnItemTouchListener(RecyclerView$OnItemTouchListener)
    addOnScrollListener(RecyclerView$OnScrollListener)

    setAccessibilityDelegateCompat(RecyclerViewAccessibilityDelegate)
    setAdapter(RecyclerView$Adapter)
    setChildDrawingOrderCallback(RecyclerView$ChildDrawingOrderCallback)
    setClipToPadding(boolean)
    setHasFixedSize(boolean)
    setItemAnimator(RecyclerView$ItemAnimator)
    setItemViewCacheSize(int)
    setLayoutFrozen(boolean)
    setLayoutManager(RecyclerView$LayoutManager)
    setNestedScrollingEnabled(boolean)
    setOnScrollListener(RecyclerView$OnScrollListener)
    setRecycledViewPool(RecyclerView$RecycledViewPool)
    setRecyclerListener(RecyclerView$RecyclerListener)
    setScrollingTouchSlop(int)
    setViewCacheExtension(RecyclerView$ViewCacheExtension)

Manually getting a view

Ok, so I need to manually get a view from a layout managed by Anvil. I have a TextureView that I need to hook up to the camera. The view I need is always visible in the activity, so Anvil should always render it. Currently what I'm trying is based on a technique described here:

v<AutoFitTextureView> {
    -size(FILL, 400)
    -id(R.id.camera_preview)
}

And then later on, still within onCreate:

mTextureView = findViewById(R.id.camera_preview) as AutoFitTextureView

but Android isn't able to find the view (findViewById returns null). Is there a better way to capture a reference to a view object than what I'm doing here? I think the ideal would be to implement something similar to React's refs, but I'd be happy with a workaround.

Upgrading 0.0.12 to 0.1.2 -- config equivalent?

I'm very excited about the changes you've made in the 0.1 release! I just got around to updating my dependency now, and in the last hour I've been working on updating to the new DSL. The migration has been very straightforward, given how much Anvil has changed on the inside! I've already been able to update all my custom attributes and about half of my views. Most of it has been an easy, mechanical, copy-paste type process, which is nice. :)

However, I'm not sure how to get ahold of the underlying view to perform the equivalent of the config option from 0.0.12, which seems to be missing. Is there a way to make that work? Thanks.

Using tree packages of anvil

Hello, I'm starting to use Anvil and porting an existing app that use XML only. I need to use anvil-appcompat-v7, anvil-design and anvil-sdk15 packages, I have added the next code to gradle:

compile 'co.trikita:anvil-sdk15:0.2.0'
compile 'co.trikita:anvil-design:0.3.4'
compile 'co.trikita:anvil-appcompat-v7:0.3.4'

But, building throws this error: Error: more than one library with package name 'trikita.anvil', I'm doing it in the wrong way?

illegalArgumentException: The key must be an application-specific resource id

After introducing a few more view to my app using anvil, it starts throwing these when I switch views calling setContentView:

09-12 21:15:13.137  19964-19964/net.kjeldahl.secdrones E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: net.kjeldahl.secdrones, PID: 19964
    java.lang.IllegalArgumentException: The key must be an application-specific resource id.
            at android.view.View.setTag(View.java:18542)
            at trikita.anvil.BaseAttrs$9.apply(BaseAttrs.java:308)
            at trikita.anvil.Anvil.inflateNode(Anvil.java:249)
            at trikita.anvil.Anvil.inflateNode(Anvil.java:233)
            at trikita.anvil.Anvil.inflateNode(Anvil.java:233)
            at trikita.anvil.Anvil.inflateNode(Anvil.java:233)
            at trikita.anvil.Anvil.render(Anvil.java:106)
            at trikita.anvil.RenderableView.onMeasure(RenderableView.java:84)
            at android.view.View.measure(View.java:18788)

Any ideas why, and/or what I need to do to fix it?

Add attribute generator for animations

Animations should not be tied to views and state as tightly as they are now.
Ideally, it should look like CSS animations - you trigger some boolean variable and some animation starts. You change it back - another animation happens reverting the existing one.

Rerendering list item in RenderableArrayAdapter

It seems that individual item views in a RenderableArrayAdapter are only rerendered when they scroll off/on the screen.

Basically, I have a list of questions that can be selected by the user. Questions that have been answered already are in one color, the current selected question is in another, and questions to be answered are in a third.

Right now, when a question is selected no change occurs in its view within the RenderableArrayAdapter. It only changes to the selected color when I've scrolled it off/on the screen. I'd like to either be able to force a re-render when the question is clicked, or (even better) have Anvil re-render all the visible cells as part of the Anvil.render() cycle. Is there any reason why this isn't the case?

onTextChanged implementation

Previously EditText::onTextChanged (in Kotlin) accepted a lambda that was called with the updated text. So I could do something like:

-onTextChanged { txt -> setVal(txt); Anvil.render() }

Now, onTextChanged requires a full TextWatcher implementation. Of greater concern, it appears to be adding the watcher to the EditText over and over again, with every render. So after a few updates to the field the UI begins lagging noticeably.

The old onTextChanged syntax was sufficient for my needs and more concise. I'm planning on creating my own onTextChanged using the old syntax. Is this something that you would be interested in getting a PR for or not?

Setting styles

Really happy with how Anvil is working, and I'm writing new functionality for my app using it.

I'm having trouble assigning styles to elements created by anvil. For example, I want to add a shadow to the text on a button. Using standard layouts I can do something like the following:

<style name="ButtonText">
        <item name="android:shadowColor">#000000</item>
        <item name="android:shadowDx">1</item>
        <item name="android:shadowDy">1</item>
        <item name="android:shadowRadius">2</item>
    </style>

and then set the style from XML. However, it doesn't look like there is a way to set the style on an element programmatically, and thus no way to do it from anvil. Am I missing something?

is it abandoned due to reactnative?

react native has been announced over a month. I don't think it'll be ready any time soon. besides, a pure java implementation will still have its space due to performance concerns over js-java bridge.

Add attribute generator for color scales

Add syntax sugar for color functions like in lesscss, e.g.:

v(MyView.java,
  backgroundColor(ColorUtils.darken(Theme.BASE_COLOR, 0.2f)))

It could be nice to extend the standard android.graphics.Color class and make a chainable API to adjust color, so that out could write backgroundColor(Theme.BASE_COLOR.darken(0.2f).greyscale()).

Two questions

This seems really cool, but I had a couple questions. Sorry to put these in an issue, wasn't sure how else to ask:

  1. How do you handle resource overrides? By screen dimensions, landscape vs. portrait, etc.
  2. Have you considered connecting the reactivity all the way back to the server, similar to how Facebook is doing it in React native? So a view might be connected all the way back to a json path on a server somewhere and a change on the server could propagate automatically all the way to the UI.

Thanks for doing this project, great to have some strong alternative views on Android dev!

Add syntax sugar for various views and layouts

Generate Views.java from the view classes inside android.jar and make convenient wrapper over v() function. Possible advantages are: less imports in the calling class, cleaner layouts when you use standard views:

linearLayout(size(FILL, FILL),
  orientation(VERTICAL),
  textView(size(FILL, WRAP)).margin(dip(8),
  button(size(FILL, WRAP),
    onClick(clickHandler)));

(you don't have to import LinearLayout, TextView and Button classes anymore, you may only import Views.java)

This may be not convenient when using lots of custom views, since no wrappers would exist for them, so the traditional syntax should remain in the Anvil core.

Alternative view listener injection

Currently, the easiest form of binding a view listener is to use Java 8 closures:

v(Button.class,
  onClick(v => doSomething()));

This is compact and clean, but it creates a new onClickListener object each time. Of course one can write listeners inside a Renderable class, but that becomes somewhat bloated:

View.OnClickListener onButtonClicked  = (v) => doSomething();
...
v(Button.class,
  onClick(onButtonClicked));

I'm looking for some lightweight approach to use Renderable methods instead, e.g:

public void onButtonClicked(View v) {
  doSomething();
}
...
v(Button.class,
  onClick(???)); // refer to onButtonClicked somehow

The first thing coming to my mind is and event bus. An event instance will be passed into a view node, then the real listener bound by Anvil will submit that event instance to the event bus and the Renderable (or any other class) would handle it.

public void onEvent(ButtonClicked e) {
  doSomething();
}
...
v(Button.class,
  onClick(new ButtonClicked()));

Event objects are lightweight, which is great. I just worry about a massive amount of various event classes for each listener for each view. Any ideas?

How about nested components?

How would you make nested components in this amazing library, is it currently supported?

If not, the API could look like this for example (quick sketch):

void view() {
    linearLayout(() -> {
        MyComponent.v(this, "x", 5);
    });
}

class MyComponent extends Component {
    static void v(Parent parent, String arg1, int arg2) {
        // not sure what should be here
    }

    Parent parent;
    String arg1;
    int arg2;

    // This is called automatically by Anvil
    void set(Parent parent, String arg1, int arg2) {
        // Copy
    }

    void view() {
    }
}

What do you think?

Renderable lifecycle

Anvil allows writing components as lambdas or as RenderableViews. Some components may be stateful and may depent on the hosting Activity/Fragment/... lifecycle.

Currenclty, RenderableView has onAttachedToWindow and onDetachedFromWindow hooks, but they are not helpful for saving/restoring instance state, for handling pause/resume events etc.

How about adding a few functions to BaseDSL that would allow to register a listener to a certain type of lifecycle events, e.g:

linearLayout(() -> {
  onPause(() -> {
    // handle pause here
  });
  onResume(() -> {
    // handle resume here
  });
});

BaseDSL would keep a static list of weak references to all listeners, also Anvil would provide certain functions to emit lifecycle events, e.g. Anvil.pause(), Anvil.resume(), Anvil.save(), Anvil.restore().

I'm not sure which lifecycle events should be supports and about details of the implementation, but ideally it should support Activities, Fragments and Conductor controllers.

Other libraries provide either a full set of events (RxLifeCycle, Navi etc) - https://github.com/trello/navi/blob/master/navi/src/main/java/com/trello/navi/Event.java
CREATE, START, RESUME, PAUSE, STOP, DESTROY (Activity lifecycle), ATTACH, CREATE, CREATE_VIEW, START, RESUME, PAUSE, STOP, DESTROY_VIEW, DESTROY, DETACH (Fragment lifecycle) or a small subset of those (see: Conductor).

Seekbar: verbose listener

Seekbar uses a listener with 3 methods so it's can't be a lambda. However, only one method is normally used. A lambda syntax sugar would be preferred.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.