Skip to content

Latest commit

 

History

History
186 lines (151 loc) · 7.01 KB

README_TODO.md

File metadata and controls

186 lines (151 loc) · 7.01 KB

Adapt and AdaptUI

What it is?

AdaptUI came as a solution to reduce context switches between code and various XML files. Along with the ability to sligthly tweak some XML layouts, drawables or styles without copying data just in order to change an attribute or two.

With AdaptUI it is possible to create reusable, configurable and tweakable views and drawables without leaving a single file in which they are defined. Combine that with the build-in layout preview that comes with latest versions of Android Studio and you can have a truly single-file components on Android. A single-file component reduces context switches, allows inspection and modification of view building blocks meanwhile giving an immediate feedback with the help of the preview. It keeps the loop running without the breaks.

the problem is actually that SwiftUI is a complexity-hiding abstraction. No a complexity-hiding abstraction, if it does not work, go to the native view layer directly. Almost disappearing framework? Can be used to create view, create and update or just update?... no, this is not true, we cannot update without being created, well, in theory, we could use an Element and pass it a view, but this is crazy

Copying when needed a minor change, non extensible, no configurable, values are limited to be provided by xml, make a padding @dimen/content_padding + @dimen/additional_padding is impossible leads to generating more layers of indirection Comment a line in XML - error, cannot do it

Moreover, with AdaptUI does not make commitment.. It does not require a special compiler - all it is using is Kotlin code. All it operates on - native Android views. It does provide conveniences on top of native views, but does not restrict access. You still can access a view underneath, create own elements, or extensions on elements.

AdaptUI has been inspired by Flutter, Combine and SwiftUI. Important difference from Flutter is the direction is which elements are build - in Flutter you wrap target views with customization views, like Padding, SizedBox, etc, so in the end initial view becomes wrapped under multiple layers of customization - like a cabbage. It hurts readability and discoverability. Views also expect to receive all its arguments in constructor, which leaves little Flutter is like a pockemon cabbage. Target (conceptually important) views are hidden by multiple pockemon layers with weird names and zero discoverability. In order to find what it is inside (what view) it holds you need to unwrap the layers, which make a lot of noise whilst you do that. Easier to copy code, see how it behaves, just comment related code lines, no need to modify structure. The same is re

Another things that {{positively}} distinguishes AdaptUI is AdaptUI does not come with any state system, it can work with any.

easily discoverable customizations ease of creating a quick sketch with primary views, then gradually updating them to match final design ability to comment parts of customization (flutter complicated, XML does not allow comments for attributes)

How?

// creates a Text `ViewElement` that wraps `TextView`
Text()

Text() is an extension function of a ViewFactory. Elements should be created inside ViewFactory context, for example:

// TextView would be returned for the `ViewFactory.createView`
val textView = ViewFactory.createView(context) {
    Text()
}

AdaptUI provides these view elements out-of-box (adding a new one is a metter of creating a new extension function):

  • Text() -> TextView
  • HScroll() -> HorizontalScrollView
  • HStack() -> LinearLayout with HORIZONTAL orientation
  • Image() -> ImageView
  • Pager() -> androidx.viewpager.ViewPager (androidx.viewpager should be added to your dependencies list explicitly)
  • Progress() -> ProgressBar with indeterminate progress
  • Spacer() -> View, an element that can be used only as a child of HStack and VStack (LinearLayout), specifies layout_weight
  • TextInput() -> EditText
  • View() -> View
  • VScroll() -> ScrollView
  • VStack() -> LinearLayout with VERTICAL orientation
  • ZStack() -> FrameLayout

Those are the building blocks defined directly in AdaptUI. But any android view or view-group can be represented as one

VStack {
    Element(::CheckBox) // ViewElement<CheckBox, LinearLayout.LayoutParams>
    // the same as
    Element { context -> CheckBox(context) }
}

Each element also holds information about LayoutParams, so it is possible to configure them in a type-safe manner

VStack {
    Text() // ViewElement<TextView, LinearLayout.LayoutParams>
        .layoutWeight(1F)
    Image() // ViewElement<ImageView, LinearLayout.LayoutParams>
        .layoutGravity(Gravity.trailing.end)
    // is available only for LinearLayout.LayoutParams
    Spacer()
}

AdaptUI follows a simple naming patterns for customization. For example, all layout customizations that affect LayoutParams start with layout* - layoutWeigth, layoutGravity, layoutMargin etc. Individual elements prefix customization functions with its name. Those for example are some customizations available for the Text element:

  • text(CharSequence)
  • textGravity(Gravity)
  • textColor(Int)
  • textHideIfEmpty()
  • and others

All of the customizations that affect dimensions (width, height, padding, margin, etc) are already in DIP (density independent pixels), there is no need to convert them explicitly to pixels

Text()
    .padding(16) // 16.dp
    .layoutMargin(vertical = 8) // 8.dp
    .layout(FILL, 128) // 128.dp

When building static layouts all normal code flows are working as expected

VStack {

    if (someCondition) {
        Text("Some condition was met")
    } else {
        HStack {
            Image(error)
            Text("Condition was not met")
        }
    }

    // creates 10 Texts
    (0..9).forEach {
        Text("$it")
    }
}

Static means here - after view is build it is not going to be modified if someCondition boolean changes. This condition is checked only when view is built initially. In order to build dynamic layouts - adapt items can be used.

Elements can still be referenced as regular objects:

VStack {

    // it is referenced, but still added to the layout
    val image = Image() // ViewElement<ImageView, LinearLayout.LayoutParams>

    Text("Button-like")
        .padding(16)
        .background(Colors.accent)
        .onClick {
            // modify element (automatically will render by posting to the main thread)
            image.background(Colors.primary)
            // or render explicitly immediately in this thread
            image.background(Colors.primary).render()

            // or access view directly
            // here we can access the view because it has been already initialized
            image.view.setBackgroundColor(Colors.primary)
        }
}

Pass elements and items around, allowing wide usage Preview in layout Use as an argument to achieve dynamism

Drive a customer/user crazy -> change position on click (easy list manipulation)