(Solved) Change Adapter from other Class (null errors)


#1

Hello,

I am new to Kotlin, but am enjoying programming with it. But now I run into a problem. I have a fragment (ContactsFragment) which loads data from an URL into a ListView, via a List. This all works fine.

Now I would like to add a new contact, from my activity (AddContactActivity). So I make an https POST request and get a new list of contacts back. So far everything works correct.But now I would like to update the ListView to show the new list. But I can’t get that to work. Not via calling a function from the ContactsFragment class (then I have context problems on creating the new adapter), but also not by creating a local new adapter and assigning that to the list view.

This is my code:

          override fun success(list: List<Contact>, response: Response) {
              val adapter = ContactsAdapter(getApplicationContext(), list)
              contacts_list.adapter = adapter
          }

But I get a null exception error to the contact_list at the moment. How can I update the contact_list (or the adapter) to show the new list?

(I call the Acitivity from a particular button, but that just shows a new layout, whereas the new updated list is only retrieved after a button click on that layout (so I cannot add any parameters to the activity start).

Thanks in advance!


#2

Is it a compiler error or a runtime error?
Can contacts_list be null?

try:

contacts_list?.adapter = adapter


#3

Thanks for your reply. It is a runtime error. It get the error in LogCat when I try to add the contact.

Your code does solve the runtime error, but I do not get the new list presented in my ListView
(sounds reasonable, since contacts_list somehow is null, but I do not understand (yet) how to call the variable (update the adapter) from within the activity)


#4

Can you post the exception you get?
Where contacts_list comes from?


#5

The exception is:

java.lang.IllegalStateException: contacts_list must not be null
at com.loserchooser.app.loserchooser.AddContactActivity$onCreate$1$1.success(AddContactActivity.kt:59)
at com.loserchooser.app.loserchooser.AddContactActivity$onCreate$1$1.success(AddContactActivity.kt:52)
at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

Contacts_list is a Listview on the fragment_contacts.xml

I have a ContactsFragment, which inflates the fragment_contacts (where the contact_list) is a ListView. That ContactFragment has a button (contactAddButton) and in the OnViewCreated the listener is added like:

    contactAddButton.setOnClickListener(View.OnClickListener {
        val AddContact = AddContactActivity.showAddContact(context)
        startActivity(AddContact)
    })

AddContactActivity has an onCreate where the button there (btnAddContact) get a listener:

  btnAddContact.setOnClickListener(View.OnClickListener {

      //Creating a rest adapter
      val adapter = RestAdapter.Builder()
              .setEndpoint(DataFragment.ROOT_URL)
              .build()

      //Creating an object of our api interface
      val api = adapter.create<ContactAddAPI>(ContactAddAPI::class.java)

      //Defining the method
      api.saveContact(DataFragment.version, txtContactName.text.toString(), object : Callback<List<Contact>> {
          override fun success(list: List<Contact>, response: Response) {
              //Here I want to update the contacts_list with the newly retrieved List
              val adapter = ContactsAdapter(getApplicationContext(), list)
              contacts_list.adapter = adapter
          }
      })

      finish()

  })

#6

Looks like you are never initializing contacts_list.

edit: you need to findViewById(R.id.contacts_list) or something like that before use it.


#7

Thanks for your answer, but I assumed that was not required (anymore), since I am importing the class as:

import kotlinx.android.synthetic.main.fragment_contacts.*

As per: https://antonioleiva.com/kotlin-android-extensions/

But let me look into the findViewById.


#8

If you are using android extensions plugin it should not be necessary.
Never used it before so I can’t help much. Does it work for other Activities?


#9

Well, this is the first Activity where I am trying to change something from a different Class/Layout. But it does work within the same Class.

update:
Using FindViewById does not solve the problem:

          var tmp_list = findViewById(R.id.contacts_list) as ListView
          tmp_list.adapter = adapter

Since I get an “kotlin.TypeCastException: null cannot be cast to non-null type android.widget.ListView”

Could it be a problem with context or something (just guessing here, since I am pretty new to Kotlin), that it does not exist in the current context or something?


#10

Are you setting the content view of your activity?
This error is saying that findViewById(R.id.contacts_list) returned null.


#11

This is currently my onCreate from the Activity, so yes I set the ContentView

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_contact_add)

  btnAddContact.setOnClickListener(View.OnClickListener {
      RetrieveData()
  })

}

Ps; RetrieveData is now the function for the API call (to keep the code smaller here).


#12

But is that the same xml where the contacts_list is? You said on a previous post that it was on fragment_contacts.xml.
If that is the case you need to findViewById from the view inflated from that xml.

For example:

val view = layoutInflater.inflate(fragment_contacts.xml, ...)
val list = view.findViewById(R.id.contacts_list) as ListView

#13

Slowly we are getting there. I don’t get an error anymore with this new code, but my ListView is also not showing the new information. So I have a feeling that this layoutInflater is creating a new instance of the layout and changing the adapter there. But 2 seconds later the activity is finished and the (new?) view is being destroyed and I am returned back to the previous page, showing the previous ContactList (from the backstack)

Could that be the case? Or do I have to call a refresh on the ListView or something?


#14

You might need to call adapter.notifyDataSetChanged() if you update the adapter items after its creation.


#15

But I am not changing the values on the adapter itself, but rather building a new adapter and switching the adapter on the ListView. So calling an ‘refresh’ on the adapter does not do the trick. I should be calling the ‘refresh’ on the ListView instead. But

list.invalidateViews();

Also does not work, I still do not see the ListView with updates values. So I am still thinking that the layoutInflater creates a new instance from the fragment_contacts and is not loading the one already on the backstack.


#16

Sure the inflater will create a new instance of your view. You should only call it once when creating your fragment.


#17

Okay, thanks for all your help and time. So I have to think of something completely different so solve my problem. I didn’t think it would be this hard to update a ListView with a new list :frowning: (that’s basically all I want to do)


#18

It’s not suppose to be hard. Maybe you should look on how to do what you want in java first.


#19

How would that make it easier? You can do everything Java can do in Kotlin already, so you would gain nothing by switching the language to Java.


#20

Yes, I wanted to do it in Kotlin. And I have found a solution now. I am passing the complete ListView as a parameter to the activity and build a new adapter in the activity and then assign the new adapter to the ListView before calling the finish on the activity. This is working. So I have a solution for now (not the nicest I guess, but well…).

Thanks for all your help and thinking with me here.