Kotlin / anko

Pleasant Android application development
Apache License 2.0
15.89k stars 1.29k forks source link

[discussion] set layout pattern #66

Closed nschwermann closed 7 years ago

nschwermann commented 9 years ago

The problem: To me there was no apparent way to get references to the root view of DSL scripts. So I thought of this way to sort of mimic the framework setContent api and get us a reference to our DSL root. I'm new to kotlin and anko so feedback is much appreciated.

The solution:

abstract class BaseActivity : RxAppCompatActivity(), AnkoLogger{

    var layout : View by Delegates.notNull()

    fun setLayout(inflate : () -> View ){
        layout = with(this){
            inflate()
        }
    }
}

usage

class Main : BaseActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setLayout{
            frameLayout {
                backgroundColor = Color.RED
            }
        }

    }

    override fun onResume(){
        super.onResume()
        debug(layout)
    }
}
yanex commented 9 years ago

You can get a content View in Android.

http://stackoverflow.com/questions/5273436/how-to-get-activitys-content-view

nschwermann commented 9 years ago

These solutions all require a known id to be set and have the function call overhead of finding the view by that id. Also, without testing it looks like most of these solutions would return the parent of my layout rather than the root of my layout.

Knowing the id wouldn't be horrible if we had some good syntax for generating a known id. ie @+id/ on the xml side. Creating a val, assigning it, and holding onto it globally is a lot of work and kind of defeats the purpose of this library I think.

The only reason I should ever need an id is when using something like RelativeLayout that requires it. In that case generating an id from kotlin is fine and easy because we can just say id = genId() and never need to know or care about what that id was.

On Jun 21, 2015 5:50 AM, "Yan Zhulanow" notifications@github.com wrote:

You can get a content View in Android.

http://stackoverflow.com/questions/5273436/how-to-get-activitys-content-view

— Reply to this email directly or view it on GitHub https://github.com/JetBrains/anko/issues/66#issuecomment-113884677.

nschwermann commented 9 years ago

That gives me an idea. Perhaps a global id generator class could be useful in this library. You could create ids with an overloaded operator like this.

id = Id + "textview"

The + operator should check an inner map for an id that already matches "textview" to reuse or create a new one store it in the map and return the id.

Then in kotlin would it be possible to expose ids by keys like this?

//shows up automatically now that we have called Id + "textview"
Id.textview : Int
nschwermann commented 9 years ago

Ok well this isn't exactly as handy as I originally thought. I didn't realize you could just do this.

    var layout : FrameLayout by Delagates.notNull()
    var toolbar : Toolbar by Delagates.notNull()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        layout = frameLayout {
             toolbar = toolBar{}
        }
    }
yanex commented 9 years ago

I have been thinking about implementing a "id generator" some time, but now it is unclear where should generator cache live.

The most appropriate candidate is UiHelper, though there are many questions to find answers for.

nschwermann commented 9 years ago

I think the id generator would need to be some kind of statically generated class, unless there is a way to expose mutable map keys as properties in kotlin.

On Tue, Jun 23, 2015 at 2:00 PM, Yan Zhulanow notifications@github.com wrote:

I have been thinking about implementing a "id generator" some time, but now it is unclear where should generator cache live.

The most appropriate candidate is UiHelper, though there are many questions to find answers for.

— Reply to this email directly or view it on GitHub https://github.com/JetBrains/anko/issues/66#issuecomment-114608911.

Nathan Schwermann 785-312-0080

EddieRingle commented 9 years ago

@yanex I did this in an application I was working on, but being paranoid about id collisions I ended up sticking to defining IDs in an XML resource instead. I was hoping Kotlin had some sort of sugar for this akin to Ruby Symbols.

nschwermann commented 9 years ago

@EddieRingle you are guaranteed not to collide with this. Found on gist https://gist.github.com/schwiz/0152caee426e247f90ce

private object ViewCounter {
    private val viewCounter = AtomicInteger(1)
    public fun generateViewId(): Int {
        while (true) {
            val result = viewCounter.get()
            // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
            var newValue = result + 1
            if (newValue > 16777215) newValue = 1 // Roll over to 1, not 0.
            if (viewCounter.compareAndSet(result, newValue)) {
                return result
            }
        }
    }
}

fun View.generateViewIdCompat(): Int {
    if (android.os.Build.VERSION.SDK_INT >= 19)
        return View.generateViewId()
    else
        return ViewCounter.generateViewId()
}
EddieRingle commented 9 years ago

@schwiz That's very similar to the generator I wrote.

I'm not worried about colliding with IDs from the same generator, but more paranoid about colliding with IDs generated from XML resources. Of course, I use the term "paranoid" because the integers generated in the R class begin at 0x7f000000 and my worrying about colliding with those IDs is crazy. :)

Still, it'd be nice if this was baked into the language runtime (again, like Ruby symbols).

nschwermann commented 9 years ago

@EddieRingle Yes that is what I'm trying to say, this function clamps the generated ids to be lower than what aapt generates. Its backported from kitkat.

EddieRingle commented 9 years ago

@schwiz I know. I'm more or less just complaining that Kotlin doesn't have sugar for this, that's all. :)

nschwermann commented 9 years ago

I see. Can you give an example? I'm not familiar with ruby. On Jun 26, 2015 4:53 PM, "Eddie Ringle" notifications@github.com wrote:

@schwiz https://github.com/schwiz I know. I'm more or less just complaining that Kotlin doesn't have sugar for this, that's all. :)

— Reply to this email directly or view it on GitHub https://github.com/JetBrains/anko/issues/66#issuecomment-115900838.

yanex commented 7 years ago

The problem with referencing Views from inside the DSL can be solved by using lateinit var properties. Some View id generator would be very handy, though it's not clear how it could be done without writing the Kotlin compiler plugin. I mark the issue as "closed". Please create the new issue if you still have any troubles.