Load Details view from Kotlin RecyclerView(cardview) onclick

jasonz :

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()
}
}
some user :

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!!
//            ...

}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=11761&siteId=1