I need to know how to implement functions to a ConstraintLayout in Kotlin.
I need something like this:
fun applyCustomPropeties(){
//some stuff
}
val rootLayout = findViewById<ConstraintLayout>(R.id.rootLayout)
rootLayout.applyCustomPropeties()
Thanks.
You can add an extension function:
fun ConstraintLayout.applyCustomProperties() {
//some stuff
//you can use "this" keyword here
}
That extension is resolved "statically", so no matter where you put that code. Now, you can do what you want:
val rootLayout = findViewById<ConstraintLayout>(R.id.rootLayout)
rootLayout.applyCustomPropeties()
Related
I am using data binding and having trouble solving multiple quick click. I do not want to put a logic in every click instead, I want to create a solution once and expect it to work throughout my project.
I found one solution here. Code snippet from that page is as follows:
class SafeClickListener(
private var defaultInterval: Int = 1000,
private val onSafeCLick: (View) -> Unit
) : View.OnClickListener {
private var lastTimeClicked: Long = 0
override fun onClick(v: View) {
if (SystemClock.elapsedRealtime() - lastTimeClicked < defaultInterval) {
return
}
lastTimeClicked = SystemClock.elapsedRealtime()
onSafeCLick(v)
}
}
And using extension funciton:
fun View.setSafeOnClickListener(onSafeClick: (View) -> Unit) {
val safeClickListener = SafeClickListener {
onSafeClick(it)
}
setOnClickListener(safeClickListener)
}
Now, for any view we can simply call:
anyView.setSafeOnClickListener {
doYourStuff()
}
Which is awesome. But it only applies if I am calling setOnClickListener to a view but I am using data binding. Where I am using something like this:
android: onClick="#{(view) -> myViewModel.UIEvents(SomeUIEvent.showDialog)}"
I am aware that if I can create a binding adapter, I would be able to solve the problem. But, I couldn't make one that works.
How can I achieve something that I can use with data binding and that works globally like the above example?
Thanks
maybe you can use #BindingAdapter.
I would like to know if it's possible to populate multiple fields in one line in Kotlin (just for cleaner code)
My Code:
val evh = ExampleViewHolder(binding.root)
evh.mImageView = binding.myImageView
evh.mTextView1 = binding.text1
evh.mTextView2 = binding.text2
I would like to achieve something like this:
(evh.mImageView, evh.mTextView1, evh.mTextView2) = (binding.myImageView, binding.text1, binding.text2)
Is this somewhat possible?
is this somewhat possible?
No, you can't set properties* in such a way in Kotlin.
If you'd like to initialize or change values on some properties in a cleaner way, you can use one of the scope functions:
The Kotlin standard library contains several functions whose sole purpose is to execute a block of code within the context of an object. When you call such a function on an object with a lambda expression provided, it forms a temporary scope. In this scope, you can access the object without its name. Such functions are called scope functions. There are five of them: let, run, with, apply, and also.
In this particular case it seems like apply is the best fit:
val evh = ExampleViewHolder(binding.root).apply {
mImageView = binding.myImageView
mTextView1 = binding.text1
mTextView2 = binding.text2
}
*note that similar inline syntax is valid in Kotlin and it's used in destructing declarations:
val (first, second) = listOf("firstValue", "secondValue")
You could also pass views as arguments to ExampleViewHolder, like this:
class ExampleViewHolder(
val rootView: View, val imageView: ImageView,
val textView1: TextView, val textView2: TextView
) : RecyclerView.ViewHolder(rootView) {...}
And then instantiate the ViewHolder using the apply scope function:
val viewHolder = binding.run {
ExampleViewHolder(root, myImageView, text1, text2)
}
I found this post which says it's possible if you put all views in a data class:
data class ExampleViewHolder(val mImageView: ImageView, val mTextView: TextView, val mTextView2: TextView, val binding: Binding) : RecyclerView.ViewHolder(binding.root)
and then do something like this:
val (mImageView, mTextView1, mTextView2, binding) = ExampleViewHolder(binding.myImageView, binding.text1, binding.text2, binding)
Alternatively you can always put semi-columns in between assignments to one-line it, but it does not get more readable imo:
evh.mImageView = binding.myImageView; evh.mTextView1 = binding.text1; evh.mTextView2 = binding.text2
I would just assign the fields inside your ExampleViewHolder:
class ExampleViewHolder(binding: Binding) : RecyclerView.ViewHolder(binding.root) {
val mImageView = binding.myImageView
val mTextView = binding.text1
val mTextView2 = binding.text2
}
Or even let the ExampleViewHolder do the binding:
class ExampleViewHolder(val binding: Binding) : RecyclerView.ViewHolder(binding.root) {
fun bindTo(item: Any) {
binding.myImageView.visibility = View.GONE
}
}
If an object has already been created, to change several fields you can use scope function "run", like:
evh.run {
mImageView = binding.myImageView
mTextView1 = binding.text1
mTextView2 = binding.text2
}
I'm trying to create an extension function in Kotlin. I did try several tutorials, but didn't quite understand, how to implement this one.
I'm trying to create a setWidth() function as such
//Find my_view in the fragment
val myView = v.findViewById<RelativeLayout>(R.id.my_view)
//Then use the extension function
myView.setNewWidth(500)
This is how I've defined my extension function
private fun View?.setNewWidth(i: Int) {
val layoutParams: ViewGroup.LayoutParams = View.layoutParams
layoutParams.width = i
View.layoutParams = layoutParams
}
I don't understand what I need to do here.
I want to call the extension function as myView.ExtensionFunction(), but I don't know how to do that. The tutorials, were un-informative.
I think the main problem here is how the extension function is defined, in particular, the lines that have View.layoutParams - this is calling a static property on View that doesn't exist. You need to use the one from the instance. If you'd write the extension function like so:
private fun View?.setNewWidth(i: Int) {
val layoutParams = this?.layoutParams
layoutParams?.width = i
this?.layoutParams = layoutParams
}
Then you can call the method like you want. Personally, I don't find this so readable and I'd remove the nullability here and write it as:
private fun View.setNewWidth(i: Int) {
val newLayoutParams = layoutParams
newLayoutParams?.width = i
layoutParams = newLayoutParams
}
The only difference is that now you need ?. to call the method if the view is nullable, which I personally find fine - myView?.setNewWidth(123). I assume most of the time you won't have a nullable view.
Ok, So my issue was that I didn't know how to get reference to the calling View. i.e., I didn't know how to call myView and set its property inside the extension function setNewWidth()
So, I tried using this? and it worked.
Then, I did a few changes to the extension function to work for myView which is a Relative Layout.
This is what I worked out:
private fun RelativeLayout?.setWidth(i: Int) {
val layoutParams: ViewGroup.LayoutParams? = this?.layoutParams
layoutParams?.width = i
this?.layoutParams = layoutParams
}
Here's my MainActivity:
MainActivity.kt
class MainActivity : AppCompatActivity() {
...
}
fun becomeClickable(v: Button){
v.alpha = 1f
v.isClickable = true
}
I would like to use becomeClickable() in other activities. What is the best practice for doing that?
I previously had becomeClickable() as a private function inside MainActivity but removed the private keyword and moved the function outside of MainActivity.
Another Kotlin extension proposal would be to add the extension function to the button rather than activity. This will make the code more readable as the operation is really done on the button rather than the activity. In addition, the function will be available even in fragments and custom views if you decide to use them. You can create a file called ButtonExtensions.kt and add the following
fun Button.becomeClickable() {
alpha = 1f
isClickable = true
}
Now you can simply use this extension function anywhere in your activity/activities by writing buttonVariable.becomeClickable()
Hope this helps!
Use Companion Object
class CommonUtils {
companion object {
fun becomeClickable(v: Button){
v.alpha = 1f
v.isClickable = true
}
}
}
In MainActivity use like
CommmonUtils.becomeClickable(yourButton)
Kotlin Extensions would be neat here.
Just append becomeClickable with Activity.
fun Activity.becomeClickable(v: Button){
v.alpha = 1f
v.isClickable = true
}
Now it is available across all activities. :)
What is a good way to do a horizontalLayout in anko / kotlin ? verticalLayout works fine - could set orientation on it but it feels wrong. Not sure what I am missing there.
Just use a linearLayout() function instead.
linearLayout {
button("Some button")
button("Another button")
}
Yeah, LinearLayout is by default horizontal, but I tend to be extra specific and rather use a separate horizontalLayout function for that.
You can simply add the horizontalLayout function to your project:
val HORIZONTAL_LAYOUT_FACTORY = { ctx: Context ->
val view = _LinearLayout(ctx)
view.orientation = LinearLayout.HORIZONTAL
view
}
inline fun ViewManager.horizontalLayout(#StyleRes theme: Int = 0, init: _LinearLayout.() -> Unit): _LinearLayout {
return ankoView(HORIZONTAL_LAYOUT_FACTORY, theme, init)
}
I have opened a feature request at Anko: https://github.com/Kotlin/anko/issues/413