#android #listview #kotlin #clickable
#Android #просмотр списка #kotlin #интерактивно
Вопрос:
Я пытаюсь создать простой RSS-ридер с помощью Rome.
Моя реализация barebones использует ListView
и мой пользовательский Adapter
интерфейс для его заполнения feed_stub.xml
. Нажатие на элемент ListView запустит другое действие.
Пока у меня две проблемы:
-
Мои элементы ListView недоступны для просмотра — мой
setOnItemClickListener
не запускается. -
Если я нажимаю на некоторые элементы много раз, мое приложение выходит из строя с исключением 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 и укажите его в вопросе.