Solution 1 :
First you need to pass the data from your cardView’s onclick and then start activity, finally handle those data in your desired item details activity…
You’ll need context/AppCompatActivity
while starting activity, so you can modify your Adapter’s constructor to receive Context
:
class ArticleAdapter(
private val context: Context,
private var articleList: ArrayList<Article>
) : RecyclerView.Adapter<ArticleViewHolder>()
Use this constructor while initializing it from your fragment
:
articleAdapter = ArticleAdapter(activity, articleList) // activity => getActivity()
In your item click listener:
override fun onBindViewHolder(articleViewHolder: ArticleViewHolder, itemIndex: Int) {
val article: Article = articleList?.get(itemIndex)
setPropertiesForArticleViewHolder(articleViewHolder, article)
articleViewHolder.cardView.setOnClickListener {
val titleString = article.title
val descString = article.description
val urlString = article.url
val toPass = Bundle()
toPass.putString("url", urlString)
toPass.putString("title", titleString)
toPass.putString("desc", descString)
val intent =
Intent(context, YourActivity::class.java) //context we got from constructor
intent.putExtras(toPass)
context.startActivity(intent) // or we can use ContextCompat
}
}
Now handle this data and set views accordingly:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val bundle = intent.extras
val url = bundle?.get("url")
val title = bundle?.get("title")
val desc = bundle?.get("desc")
// now handle those...
titleTextView.text = title!!
// ...
}
Solution 2 :
Update: I see someone already gave a similar answer. However, I’m still keeping it as I see some problems in that answer. You can’t start an activity from Adapter with startActivity
, it has to be activity.startActivity
I will break the thing for you.
At first, you need to pass the list and context/activity to the adapter. You can pass it through the constructor of the adapter.
class ArticleAdapter(
val activity: AppCompatActivity
val itemList: MutableList<String>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
}
So, from the fragment, you need to send the context/activity like this:
recyclerView.adapter = ArticleAdapter(activity!!, articleList)
Now, in your adapter, in the onBindViewHolder
method, you need to listen the click.
articleViewHolder.itemView.setOnClickListener {
val intent = Intent(activity, YourDesiredActivity::class.java)
activity.startActivity(intent)
}
If there is an error in itemView
, check the ArticleViewHolder
class and rename the view from its constructor. Let me know if you face any problem.
Solution 3 :
Pass Fragment context while initializing adapter class
val adapter = ArticleAdapter(listItems, context);
Inside onClickListener
articleViewHolder.cardView.setOnClickListener {
context.startActivity(context, TargetActivity);
}
Problem :
I have a fragment which has a RecyclerView
with items are in CardViews
. I have an adapter which will populate the RecyclerView
with data from newsapi.org
. what I need to achieve is when I click on a item(CardView) to load an activity with image, title and description. I’m quite new to kotlin and find I’m stuck in here and need help to follow up. Would be really helpful. I’ll attach my adapter and fragment(which has the RecyclerView).
What bugs me is should i start the activity within onBindViewHolder -> ….cardView.setOnClickListener or else? and the confusing part is to set the image(which comes from url) into passing value to the details view.
Adapter Class
class ArticleAdapter(
private var articleList: ArrayList<Article>
) : RecyclerView.Adapter<ArticleViewHolder>() {
private val placeHolderImage = "https://picsum.photos/200/200/?blur"
private lateinit var viewGroupContext: Context
override fun onCreateViewHolder(viewGroup: ViewGroup, p1: Int): ArticleViewHolder {
viewGroupContext = viewGroup.context
val itemView: View =
LayoutInflater.from(viewGroup.context).inflate(R.layout.article_item, viewGroup, false)
return ArticleViewHolder(itemView)
}
override fun getItemCount(): Int {
return articleList.size
}
override fun onBindViewHolder(articleViewHolder: ArticleViewHolder, itemIndex: Int) {
val article: Article = articleList.get(itemIndex)
setPropertiesForArticleViewHolder(articleViewHolder, article)
articleViewHolder.cardView.setOnClickListener {
//do something
}
}
private fun setPropertiesForArticleViewHolder(
articleViewHolder: ArticleViewHolder,
article: Article
) {
checkForUrlToImage(article, articleViewHolder)
articleViewHolder.title.text = article?.title
articleViewHolder.description.text = article?.description
articleViewHolder.url.text = article?.url
}
private fun checkForUrlToImage(article: Article, articleViewHolder: ArticleViewHolder) {
if (article.urlToImage == null || article.urlToImage.isEmpty()) {
Picasso.get()
.load(placeHolderImage)
.centerCrop()
.fit()
.into(articleViewHolder.urlToImage)
} else {
Picasso.get()
.load(article.urlToImage)
.centerCrop()
.fit()
.into(articleViewHolder.urlToImage)
}
}
fun setArticles(articles: ArrayList<Article>) {
articleList = articles
notifyDataSetChanged()
}
}
//interface ItemClickListener{
// fun onItemClick(articleList: Article, position:Int)
//}
Fragment
class HomeFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
// private lateinit var homeViewModel: HomeViewModel
private val ENDPOINT_URL by lazy { "https://newsapi.org/v2/" }
private lateinit var topHeadlinesEndpoint: TopHeadlinesEndpoint
private lateinit var newsApiConfig: String
private lateinit var articleAdapter: ArticleAdapter
private lateinit var articleList: ArrayList<Article>
private lateinit var userKeyWordInput: String
// RxJava related fields
private lateinit var topHeadlinesObservable: Observable<TopHeadlines>
private lateinit var compositeDisposable: CompositeDisposable
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
swipe_refresh.setOnRefreshListener {
queryTopHeadlines()
// refreshAction() //refresh the list
swipe_refresh.isRefreshing = false
}
//Network request
val retrofit: Retrofit = generateRetrofitBuilder()
topHeadlinesEndpoint = retrofit.create(TopHeadlinesEndpoint::class.java)
newsApiConfig = resources.getString(R.string.api_key)
swipe_refresh.setOnRefreshListener(this)
swipe_refresh.setColorSchemeResources(R.color.colorAccent)
articleList = ArrayList()
articleAdapter = ArticleAdapter(articleList)
// userKeyWordInput = ""
compositeDisposable = CompositeDisposable()
recycler_viewHome.setHasFixedSize(true)
recycler_viewHome.layoutManager = LinearLayoutManager(context)
recycler_viewHome.itemAnimator = DefaultItemAnimator()
recycler_viewHome.adapter = articleAdapter
}
override fun onStart() {
super.onStart()
queryTopHeadlines()
}
override fun onDestroy() {
super.onDestroy()
compositeDisposable.clear()
}
override fun onRefresh() {
queryTopHeadlines()
}
private fun queryTopHeadlines() {
swipe_refresh.isRefreshing = true
topHeadlinesObservable = topHeadlinesEndpoint.getTopHeadlines("us", newsApiConfig)
subscribeObservableOfArticle()
}
private fun subscribeObservableOfArticle() {
articleList.clear()
compositeDisposable.add(
topHeadlinesObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap {
Observable.fromIterable(it.articles)
}
.subscribeWith(createArticleObserver())
)
}
private fun createArticleObserver(): DisposableObserver<Article> {
return object : DisposableObserver<Article>() {
override fun onNext(article: Article) {
if (!articleList.contains(article)) {
articleList.add(article)
}
}
override fun onComplete() {
showArticlesOnRecyclerView()
}
override fun onError(e: Throwable) {
Log.e("createArticleObserver", "Article error: ${e.message}")
}
}
}
private fun showArticlesOnRecyclerView() {
if (articleList.size > 0) {
empty_text.visibility = View.GONE
retry_fetch_button.visibility = View.GONE
recycler_viewHome.visibility = View.VISIBLE
articleAdapter.setArticles(articleList)
} else {
recycler_viewHome.visibility = View.GONE
empty_text.visibility = View.VISIBLE
retry_fetch_button.visibility = View.VISIBLE
// retry_fetch_button.setOnClickListener { checkUserKeywordInput() }
}
swipe_refresh.isRefreshing = false
}
private fun generateRetrofitBuilder(): Retrofit {
return Retrofit.Builder()
.baseUrl(ENDPOINT_URL)
.addConverterFactory(GsonConverterFactory.create())
//Add RxJava2CallAdapterFactory as a Call adapter when building your Retrofit instance
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
}
Comments
Comment posted by some user
why don’t you start your activity from your item’s onclick listener..? pass necessary data while creating
Comment posted by jasonz
sorry to be sound so foolish, but kotlin is new to me and i maybe okay with a sample code
Comment posted by jasonz
thank you sir for the help, im still blank on how to pass the ulrToImage or image from list item click to details view to be viewed.
Comment posted by some user
firstly, please stop this sir thing, we all are learners, right? secondly, I’m going to update answer according to your need
Comment posted by jasonz
when i do “articleAdapter = ArticleAdapter(activity, articleList)” in fragment. it says type mismatch, required AppcompactActivity? found FragmentActivity.
Comment posted by some user
Happy coding 🙂
Comment posted by aleem md
Can u update your fragment code. It looks like your fragment and adapter code is same
Comment posted by aleem md
Modify your adater definition to this
Comment posted by aleem md
And pass context from fragment class
Comment posted by jasonz
i should put “articleAdapter = ArticleAdapter(articleList, context)) ” this in my Adapter class or in fragment class ?
Comment posted by aleem md
Replace