Просмотр списка недоступен (пользовательский адаптер)

#android #listview #kotlin #clickable

#Android #просмотр списка #kotlin #интерактивно

Вопрос:

Я пытаюсь создать простой RSS-ридер с помощью Rome.

Моя реализация barebones использует ListView и мой пользовательский Adapter интерфейс для его заполнения feed_stub.xml . Нажатие на элемент ListView запустит другое действие.

Пока у меня две проблемы:

  1. Мои элементы ListView недоступны для просмотра — мой setOnItemClickListener не запускается.

  2. Если я нажимаю на некоторые элементы много раз, мое приложение выходит из строя с исключением out of bound. Это означает, что в моем адаптере, вероятно, есть ошибки.

ListView От activity_main.xml

 <ListView
            android:layout_width="0dp"
            android:layout_height="0dp" android:layout_marginTop="8dp"
            app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
            android:layout_marginStart="8dp"
            android:layout_marginBottom="8dp" app:layout_constraintBottom_toBottomOf="parent"
            android:id="@ id/rssListView" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp"
            android:clickable="true" app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintVertical_bias="0.0"/>
  

Мой MainActivity

 import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import cz.cvut.lenguduy.rss.Downloader
import cz.cvut.lenguduy.view.ViewHolder
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    private val TAG = "MainActivity"

    private val downloader by lazy { Downloader(this, rssListView) }

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

        rssListView.setOnItemClickListener { parent, _, position, _ ->
            Log.d(TAG, "rssListView: item @pos $position clicked")
            val intent = Intent(this, SecondActivity::class.java)
            val item = parent.getItemAtPosition(position) as ViewHolder

            Toast.makeText(this, "clicked", Toast.LENGTH_LONG).show()

            intent.putExtra("title", item.tvName.text)
            intent.putExtra("text", item.tvSummary.text)
            this.startActivity(intent)
        }

        downloader.execute("http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/topfreeapplications/limit=25/xml")
    }

    override fun onDestroy() {
        super.onDestroy()
        downloader.cancel(true)
    }
}
  

ViewHolder просто:

 class ViewHolder(view: View) {
    val tvName: TextView = view.findViewById(R.id.tvName)
    val tvSummary: TextView = view.findViewById(R.id.tvSummary)
}
  

Мои ListView использования feed_stub.xml :

 <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                             xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
                                             android:layout_height="match_parent">

    <TextView
            android:text="TextView"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:id="@ id/tvName" android:layout_marginTop="8dp"
            app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginEnd="16dp" app:layout_constraintStart_toStartOf="parent"
            android:layout_marginStart="16dp" android:textSize="20sp" android:textStyle="bold" android:maxLines="2"
            android:clickable="true"/>
    <TextView
            android:text="TextView"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:id="@ id/tvSummary"
            app:layout_constraintTop_toBottomOf="@ id/tvName" app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginEnd="16dp" app:layout_constraintStart_toStartOf="parent"
            android:layout_marginStart="16dp" android:maxLines="3" android:clickable="true"/>
</android.support.constraint.ConstraintLayout>
  

And finally my FeedAdapter :

 class FeedAdapter(context: Context,
                  private val resource: Int, private val feed: MutableList<Any?>)
    : ArrayAdapter<Article>(context, resource) {

    private val inflater = LayoutInflater.from(context)

    override fun getCount(): Int {
        return feed.size
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val view: View
        val viewHolder: ViewHolder
        if(convertView == null) {
            view = inflater.inflate(resource, parent, false)
            viewHolder = ViewHolder(view)
            view.tag = viewHolder
        } else {
            view = convertView
            viewHolder = view.tag as ViewHolder
        }

        val currentEntry = feed[position] as SyndEntry

        viewHolder.tvName.text = currentEntry.title
        viewHolder.tvSummary.text = currentEntry.description.value

        return view
    }

    override fun isEnabled(position: Int): Boolean {
        return true
    }
}
  

Большинство операций выполняются моим псевдоконтроллером:

 class Downloader(context: Context, listView: ListView): AsyncTask<String, Void, String>() {
    private val TAG = "Downloader"

    private var propContext: Context by Delegates.notNull()
    private var propListView: ListView by Delegates.notNull()

    init {
        propContext = context
        propListView = listView
    }

    private lateinit var list: MutableList<Any?>

    override fun doInBackground(vararg url: String?): String {
        val input = SyndFeedInput()
        val feed = input.build(XmlReader(URL(url[0])))

        list = feed.entries

        return feed.toString()
    }

    override fun onPostExecute(result: String?) {
        super.onPostExecute(result)

        val feedAdapter = FeedAdapter(propContext, R.layout.feed_stub, list)
        propListView.adapter = feedAdapter
    }
}
  

Итак, почему мои ListView элементы не доступны для просмотра?

И почему мое приложение выходит из строя после нескольких щелчков с java.lang.IndexOutOfBoundsException: Invalid index 10, size is 0 ?

РЕДАКТИРОВАТЬ: В FeedAdapter s getView() я добавил этот OnClickListener:

 convertView?.setOnClickListener() { v ->
            Log.d(TAG, "getView(): item clicked")

            val intent = Intent(context, SecondActivity::class.java)
            intent.putExtra("title", currentEntry.title)
            intent.putExtra("summary", currentEntry.description.value)

            context.startActivity(intent)
        }
  

Теперь я могу иногда нажимать на него. Я абсолютно не представляю, почему это так, но если я нажму достаточно случайным образом, я получу предполагаемое поведение перехода к другому действию.

Комментарии:

1. Здравствуйте, не могли бы вы добавить фрагмент кода, в котором вы создали объект FeedAdapter и присвоили ему ‘rssListView’?

2. val item = parent.getItemAtPosition(position) as ViewHolder неверно. getItemAtPosition в вашем случае должно быть Article

3. @Jatin добавил код.

Ответ №1:

это потому, что вы не реализовали OnClickListener для класса ViewHolder. вы внедрили для listview прослушиватель onItemClick, который просто не будет работать для пользовательских адаптеров. для пользовательских адаптеров вам необходимо реализовать OnClickListener внутри вашего метода getView.

 override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val view: View
        val viewHolder: ViewHolder
        if(convertView == null) {
            view = inflater.inflate(resource, parent, false)
            viewHolder = ViewHolder(view)
            view.tag = viewHolder
        } else {
            view = convertView
            viewHolder = view.tag as ViewHolder
        }

        val currentEntry = feed[position] as SyndEntry

        viewHolder.tvName.text = currentEntry.title
        viewHolder.tvSummary.text = currentEntry.description.value

        // set onClickListener for your ConvertView...
        convertView.setOnClickListener(context);    

        return view
    }

// now implement your on Click 
@Overriden
void onClick(View view){

}
  

Комментарии:

1. Я не понимаю, как мне реализовать onClickLIstener() для ViewHolder , когда он ни от чего не наследуется? И я также совсем не понимаю вас onClick , поскольку это не то, как вы создаете функцию.

2. это переопределенная функция, и да, convertView будет работать как ViewHolder для listview в этом сценарии пользовательского адаптера. смотрите, я не внедряю setOnClickListener для ViewHolder, я делаю это для convertView. Проверьте код.

3. Я смотрю на код, и это очень запутанно, поскольку вы смешиваете Java с Kotlin. В любом случае, я реализовал setOnClickListener использование лямбд (потому что я до сих пор абсолютно не представляю, что вам onClick() предлагается делать или где это вообще должно быть), и теперь мои элементы доступны для просмотра, но только иногда, и я понятия не имею, почему (см. Последнее редактирование).

4. @LeNguyenDuyAnh, смотри, мой код написан на Java, но его легко понять. Я просто настраиваю OnClickListener в convertView, потому что это представление, на которое вы нажимаете. и затем вам нужно реализовать метод onClick, как и любой прослушиватель viewOnClick. вот и все, внутри onClick вы можете поместить свой код для выполнения своей работы при нажатии на элемент внутри ListView,

5. в редактировании вы описываете «Теперь я могу иногда нажимать на него». так что теперь ваша проблема может заключаться в чем-то другом. проверьте свой logcat и укажите его в вопросе.