3

I have created a Kotlin extension function on the Android ViewGroup class so that it can do layout inflation calls easier. So, here is my extension:

fun ViewGroup.inflate(layoutRes: Int): View {
    return LayoutInflater.from(context).inflate(layoutRes, this, false)
}

and then I can use it like this in an adapter class to inflate a layout:

 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    val v = parent.inflate(R.layout.view_item)
    return ViewHolder(v)
}

I have declared this extension in the adapter class I am using for a RecyclerView so that it can make inflation calls easier. But I want ALL adapters or I should say all ViewGroup classes to see this extension. I want it permanent.

It seems to just stick within where I declared it not globally. Where should I declare this extension so that anytime I use a view group this method extension will be available?

j2emanue
  • 60,549
  • 65
  • 286
  • 456
  • 2
    i dont think i need to pass in a context. ViewGroup already has a context, getContext(). so it should use its own context. What you have created is a package level function with no class. i guess i can put utils in here . thanks for all the help. – j2emanue Jan 09 '18 at 10:12

3 Answers3

4

Problems:

If you define extension function here (1), it will be accessible globally, but this is bad style since this is the class file of (a special adapter) MyArrayAdapter and inflate is meant to be used project-wide.

If you define the extension function here (2), it will be only accessible in the context of MyArrayAdapter.

// (1)
class MyArrayAdapter: ArrayAdapter<String> {
   // (2)
}

What I would do:

Create a file Util.kt with only top-level declarations which serve as utilities.

This is what your Util.kt file would look like:

package com.yourpackagepath // package in which Utils.kt resides

// written with expression body, which makes it more concise
fun ViewGroup.inflate(layoutRes: Int) = LayoutInflater.from(getContext()).inflate(layoutRes, this, false)

This way the extension is available project-wide and it is good style, because Util.kt implies global utilities meant to be used everywhere.

You would call it like this:

val v = parent.inflate(R.layout.view_item)

If you don't use it in the same package, import it like this:

import com.yourpackagepath.inflate
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
2

You can make extension funtion global by declaring in object class as shown below:

object ViewExtension{

 fun Spinner.validate(): Boolean  {
    if(!this.isSelected){
        this.setSpinnerHint(this.context.getString(R.string.please_select))
        return false
    }
    return true
 }

 fun TextInputLayout.validate(): Boolean{
    if(TextUtils.isEmpty(editText?.text)){
        this.error =  this.context.getString(R.string.required)
        return false
    }
    return true
 }
}

How to use extension funtions

fun validateFields(): Boolean{
    var allSelected = true

    textInput.validate().apply {
        if(!this)
            allSelected = false
    }
    
    spinner.validate().apply {
        if(!this)
            allSelected = false
    }

 }

Also we need to import the validate method before we use it any class :

import com.example.ViewExtension.validate
Alok Gupta
  • 1,806
  • 22
  • 21
1

I have declared this extension in the adapter class i am using for a recyclerview so that it can make inflation calls more easier.

Like this, it is only usable in the context of the adapter class.

Either declare it on package level (top-level) or consider defining an object containing the extension:

object ViewGroupExt {
    //extensions go here
}

Now on the caller side you need to bring ViewGroupExt into scope:

with(ViewGroupExt){
   //call extensions
}

This, compared to the package-level declaration, has the advantage that its access has to be made explicit.

s1m0nw1
  • 76,759
  • 17
  • 167
  • 196
  • i tested this and it works. i wish there was a way not to use the with closure. i know it makes it explicit but sometimes i want to add functionality for all class uses and not use with closure. if you think of anyway, let me know. i saw the other answer tried to use your solution without the with closure but it wasn't working for me. – j2emanue Jan 09 '18 at 07:03
  • if i declare it as package level then do i still need to use the with closure ? – j2emanue Jan 09 '18 at 07:09
  • No you don’t. But you have to import it into other classes – s1m0nw1 Jan 09 '18 at 09:48