So, i want to make SetOnClickListener
that will be responsible for opening gallery
. It works, but when i try to add code to commit
to github
i can't, because i used Redundant SAM-Constructor
. So my question is, how to change my code, so it will work without it?
class MainActivity : AppCompatActivity() {
private var dialogView: View? = null
private val getPreviewImage = registerForActivityResult(ActivityResultContracts.GetContent(), ActivityResultCallback {
it?.let { uri ->
dialogView?.findViewById<ImageView>(R.id.imageChange)?.setImageURI(it)
}?:run {
Log.e("MainActivity", "URI not present")
}
})
private val getPreviewVideo = registerForActivityResult(ActivityResultContracts.GetContent(), ActivityResultCallback {
it?.let { uri ->
dialogView?.findViewById<VideoView>(R.id.videoChange)?.setVideoURI(it)
}?: run{
Log.e("MainActivity", "URI not present")
}
})
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
supportActionBar?.hide()
bottomNavigationView.background = null
bottomNavigationView.menu.findItem(R.id.placeholder).isEnabled = false
replaceFragment(HomeFragment())
bottomNavigationView.setOnItemSelectedListener {
when (it.itemId) {
R.id.home -> replaceFragment(HomeFragment())
R.id.player -> replaceFragment(PlayerFragment())
R.id.profile -> replaceFragment(ProfileFragment())
R.id.settings -> replaceFragment(SettingsFragment())
}
true
}
popupAddButton.setOnClickListener {
showDialog()
}
}
private fun replaceFragment(fragment: Fragment) {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, fragment)
transaction.commit()
}
@SuppressLint("InflateParams")
private fun showDialog() { //this is for popupWindow
dialogView = layoutInflater.inflate(R.layout.popup, null)
val dialog = Dialog(this)
val titleEditText = dialogView?.findViewById<EditText>(R.id.titleEdit) //popUp edit field title
val descEditText = dialogView?.findViewById<EditText>(R.id.description) //popUp edit field description
dialogView?.addImage?.setOnClickListener {
getPreviewImage.launch("image/*")
}
dialogView?.addVideo?.setOnClickListener {
getPreviewVideo.launch("video/*")
}
dialogView?.addButton?.setOnClickListener {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
if (titleEditText?.text?.isEmpty() == true || descEditText?.text?.isEmpty() == true){
Toast.makeText(applicationContext, "add required data", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(applicationContext, "Added", Toast.LENGTH_SHORT).show()
}
}
dialog.setContentView(dialogView!!)
dialog.show()
}
}
CodePudding user response:
I'm guessing that what you mean is that some lint rule for the Git repo you're trying to push to doesn't allow redundant SAM constructors.
A SAM constructor is when you use a fake "constructor" supplied by Kotlin for functional interfaces. For any Java functional interface (SAM) (or Kotlin fun
interface), Kotlin creates an implicit inline function that looks like a constructor. It uses the name of that interface as the function name and takes a parameter of a functional reference matching the signature of the SAM.
So for example, if you define this interface in Java
public interface Foo {
public int bar(String value);
}
or if you defined it in Kotlin as
fun interface Foo {
fun bar(value: String): Int
}
Kotlin implicitly creates this function. You can't see it anywhere but you can use it. It's inline only so cannot be accessed in Java or by reflection.
inline fun Foo(crossinline function: (value: String)->Int) = object: Foo {
override fun bar(value: String): Int = function(value)
}
This fake constructor is for when you want to create an instance of that interface to store in a property. You don't need to use this SAM constructor when you are passing a lambda as the last argument of a function, because you can just pass the lambda directly without calling this constructor. Kotlin automatically knows what interface to build out of the lambda.*
So your code like
private val getPreviewImage = registerForActivityResult(ActivityResultContracts.GetContent(), ActivityResultCallback {
it?.let { uri ->
dialogView?.findViewById<ImageView>(R.id.imageChange)?.setImageURI(it)
}?:run {
Log.e("MainActivity", "URI not present")
}
})
is using a redundant SAM constructor and can be replaced with
private val getPreviewImage = registerForActivityResult(ActivityResultContracts.GetContent()) {
it?.let { uri ->
dialogView?.findViewById<ImageView>(R.id.imageChange)?.setImageURI(it)
}?:run {
Log.e("MainActivity", "URI not present")
}
}
You also used a redundant SAM constructor for val getPreviewVideo
.
* An exception is when there are overloads of a function that take different interfaces as the last parameter. Then you would need the fake constructor to distinguish between them for the constructor.
Side note: it is bad practice to chain scope functions like that (?.let
...?:run
). Aside from being hard to read, it is very easy to accidentally do something that will cause both blocks to be executed. For example, if the URI is not null but dialogView
is null, your run
block will be executed anyway because let
will evaluate to null. I would write it like this:
private val getPreviewImage = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri != null) {
dialogView?.findViewById<ImageView>(R.id.imageChange)?.setImageURI(uri)
} else {
Log.e("MainActivity", "URI not present")
}
}