-2

I want to migrate a very solid project structure that I use to Kotlin. I first tried the basics: Activities and Fragment transactions. It appears so easy and simple:

class MainActivity : AppCompatActivity(), SomeInterface {
     override fun onCreate(savedInstanceState: Bundle?) {
         setContentView(R.layout.activity_main)

         val mainFragment = supportFragmentManager.findFragmentById(R.id.fragment_main) as MainActionsFragment?
                    ?: MainActionsFragment.newInstance()
         supportFragmentManager.inTransaction {
              add(R.id.container_main, mainFragment)
         }
    }

    private val anotherFragment by lazy {
    supportFragmentManager.findFragmentById(R.id.another_fragment) as AnotherFragment?
            ?: AnotherFragment.newInstance()
    }

    override fun myInterfaceMethod() {
        replaceFragment(anotherFragment, R.id.container_main)
    }
}

class MainActionsFragment : Fragment() {
    val btnSale: Button by bindView(R.id.btn_sale)
    val btnVisit: Button by bindView(R.id.btn_visit)

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater!!.inflate(R.layout.fragment_main, container, false)
    }

    override fun onResume() {
        super.onResume()

        btnSale.setOnClickListener{ _ ->
            listener.requestAction(SaleType.SALE)
        }

        btnVisit.setOnClickListener{ _ ->
            listener.requestAction(SaleType.VISIT)
        }
    }
}

Extensions.kt

fun AppCompatActivity.replaceFragment(fragment: Fragment, @IdRes frameId: Int) {
    supportFragmentManager.inTransaction {
        replace(frameId, fragment)
                .addToBackStack(fragment.tag)
    }
}

inline fun FragmentManager.inTransaction(func: FragmentTransaction.() -> FragmentTransaction) {
    beginTransaction()
            .func()
            .commit()
}

Now, MainActionsFragment holds two buttons. Everything works as expected, the first click on either one takes me to the desired fragment. However, once I press the back button and I see my two buttons again, their click listener is gone. This is pretty much the standard way of doings things translated to Kotlin, nothing fancy besides its cool new features. I tried moving setting the onClickListeners to onCreateView() but it crashes:

Caused by: kotlin.KotlinNullPointerException at kotterknife.ButterKnifeKt$viewFinder$7.invoke(ButterKnife.kt:95) at kotterknife.ButterKnifeKt$viewFinder$7.invoke(ButterKnife.kt) at kotterknife.ButterKnifeKt$required$1.invoke(ButterKnife.kt:104) at kotterknife.ButterKnifeKt$required$1.invoke(ButterKnife.kt) at kotterknife.Lazy.getValue(ButterKnife.kt:125)

So, in spite of Kotlin being so cool, I'm having trouble doing the basics and I'm getting disheartened to migrate. Am I really doing things so wrong? Or how come setting a simple click listener is so frustrating?

Thank you.

Chisko
  • 3,092
  • 6
  • 27
  • 45

2 Answers2

0

You have to set your variables that are bound by ButterKnife as lateinit var instead of val. Try

@BindView(R.id.btn_sale)
lateinit var title: Button

@BindView(R.id.btn_visit)
lateinit var title: Button
Casper
  • 471
  • 7
  • 12
  • I am using Kotterknife, if I change it to var it gives an error – Chisko Nov 13 '17 at 16:37
  • Ah, sorry. I guess you can't add the listeners in `onCreate` as the buttons haven't been created then yet. Maybe you can add them to `onViewCreated`? I'm not too used to Fragments but `onResume` is only called when the Activity's `onResume` is called, which might be why they work the first time as that's when the Activity is created but not for other fragments being created in the same Activity which would not have `onResume` called? – Casper Nov 13 '17 at 16:50
  • I did not move them to `onCreate()`, I did it to `onCreateView()`. I miss `@OnClick` for Kotlin but Jake says there is no plan to do it – Chisko Nov 13 '17 at 16:59
  • Yeah, I meant to write `onCreateView`. Try moving the listeners to `onViewCreated` and see if that works. – Casper Nov 13 '17 at 17:05
  • So I switched to regular ButterKnife, and now it says the lateinit vars have not been initialized. I am trying to use them right after `ButterKnife.bind(this)` in MainActivity, is there a special method where I need to place that? – Chisko Nov 13 '17 at 17:54
0

Solution: solved it by getting rid of KotterKnife and ButterKnife and use android's findViewById. I guess one of the advantages of the language and the framework's progress make some libraries obsolete or unnecessary.

Chisko
  • 3,092
  • 6
  • 27
  • 45