I'm trying to create a simple RSS reader with Rome.
My barebones implementation uses ListView
and my custom Adapter
to fill it with feed_stub.xml
. Clicking on a ListView item will start another activity.
So far I have two problems:
My ListView items are not clickable - my setOnItemClickListener
isn't triggered.
If I click on some items many times, my app crashes with out of bound exception. Which means my Adapter probably has errors.
ListView
from 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"/>
My 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
is simple:
class ViewHolder(view: View) {
val tvName: TextView = view.findViewById(R.id.tvName)
val tvSummary: TextView = view.findViewById(R.id.tvSummary)
}
My ListView
uses 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
}
}
Most operations are performed by my pseudo-controller:
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
}
}
So why are my ListView
items not clickable?
And why does my app crash after multiple clicks with java.lang.IndexOutOfBoundsException: Invalid index 10, size is 0
?
EDIT: In FeedAdapter
s getView()
I added this 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)
}
Now I'm able to click on it SOMETIMES. I have absolutely no idea why that is but if I click randomly enough, I will get the intended behavior of going to another activity.
this is because you haven't implemented onClickListener for viewHolder class. you have implemented for listview onItemClick Listener which simply won't work for custom adapters. for custom adapters you need to implement onClickListener inside your getView Method.
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){
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.