刷新回收者视图时无法保存图标状态

时间:2019-02-13 19:10:59

标签: android kotlin android-recyclerview rx-java android-room

我有某种新闻应用程序,我正在加载经过改造的新闻,并将其放入回收者视图。我使用rxjava进行分页,并使用房间将新闻保存到房间数据库中。看起来是这样的: https://imgur.com/a/7TbQn4C

当我点击“保存”图标时,应该像这样更改图标 https://imgur.com/a/nndEfdB

并将该新闻保存到DB。它更改了图标并保存了新闻,但它保存了另一个新闻,而不是它应该保存 https://imgur.com/a/cLRrDvE

当我继续滚动以获取更多新闻时,我发现所有图标都进入了第一个状态,就像第一张图片一样,但是在DB中仍然没有正确的新闻。所以我有两个问题: 1.如何保存图标状态 2.如何保存我需要的新闻

这是我的回收站视图适配器的代码(在这里,我单击图像并保存到数据库)

package com.hfad.cointask.adapter

import android.content.Context
import android.content.Intent
import android.graphics.drawable.TransitionDrawable
import android.util.Log
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.widget.ImageView
import android.widget.TextView
import com.hfad.cointask.R
import com.hfad.cointask.helper.AppDatabase
import com.hfad.cointask.helper.CallableNews
import com.hfad.cointask.model.News
import com.hfad.cointask.service.ItemClickListener
import com.squareup.picasso.Picasso
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import java.util.*
import java.util.function.Consumer


@Suppress("DEPRECATION")
class CoinAdapter(var values: List<News>, var context: Context): androidx.recyclerview.widget.RecyclerView.Adapter<CoinAdapter.CoinViewHolder>() {

    val intent = Intent(context, NewsViewActivity::class.java)

    lateinit var item: News

    var db:AppDatabase = AppDatabase.getInstance(context) as AppDatabase
    private var compositeDispossable: CompositeDisposable = CompositeDisposable()

    object NewsFields {
        var title = ""
        var id = ""
        var newsThumb = ""
        var label: String? = ""
    }


    override fun onBindViewHolder(p0: CoinViewHolder, p1: Int) {


        item = values[p1]

        NewsFields.title = item.getTitle()
        p0.newsTitle.text = NewsFields.title

        NewsFields.id = item.getId().toString()
        val idItem = NewsFields.id

        NewsFields.label = item.getBadge()?.getLabel()
        val labelItem = NewsFields.label

        NewsFields.newsThumb = item.getThumb()
        val thumbItem = NewsFields.newsThumb



        if (labelItem == "default") {

            p0.newsThumb.setImageDrawable(null)

            } else {
            p0.newsThumb.visibility = View.VISIBLE
            Picasso.get()
                    .load(thumbItem)
                    .into(p0.newsThumb)
        }

        p0.setItemClickListener(object: ItemClickListener {
            override fun onClick(v: View, position: Int, isLongClick: Boolean) {

                intent.putExtra("id", idItem)
                context.startActivity(intent)

            }

        })

        setOnImageClickListener(p0.saveNews)


    }

    override fun getItemCount(): Int {

        return values.size

    }

    fun saveToDb(data: News, db: AppDatabase) {

        Completable.fromAction{ db.newsDao().insert(data) }
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({}, {
                    Log.d("SaveToDb", "Again", it)
                })

    }

    fun deleteFromDb(data: News, db: AppDatabase) {

        Completable.fromAction{ db.newsDao().delete(data) }
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({}, {
                    Log.d("SaveToDb", "Again", it)
                })

    }


    private fun setOnImageClickListener(img: ImageView) {

        img.setOnClickListener {
            if (img.tag == "saved") {
                img.setImageResource(R.drawable.save_icon_outline_24)
                img.tag = "not saved"
                deleteFromDb(item, db)
            } else {
                img.setImageResource(R.drawable.save_icon_24)
                img.tag = "saved"
                saveToDb(item, db)
            }

        }

        img.setOnLongClickListener {
            img.setImageResource(R.drawable.save_icon_24)
             true
        }

    }

    override fun onCreateViewHolder(p0: ViewGroup, p1: Int): CoinViewHolder {

        val view: View = LayoutInflater.from(p0.context).inflate(R.layout.news_item_view, p0, false)
        return CoinViewHolder(view)

    }


    class CoinViewHolder(itemView: View): ViewHolder(itemView), View.OnLongClickListener, View.OnClickListener {

        private lateinit var itemClickListener:ItemClickListener
        lateinit var transition: TransitionDrawable

        fun setItemClickListener(itemClickListener: ItemClickListener) {

            this.itemClickListener = itemClickListener

        }

        init {
            itemView.setOnLongClickListener(this)
            itemView.setOnClickListener(this)
        }


        override fun onClick(v: View) {

            itemClickListener.onClick(v, adapterPosition, false)
            v.startAnimation(AnimationUtils.loadAnimation(v.context, R.anim.tap))

        }

        override fun onLongClick(v: View): Boolean {

            //itemClickListener.onClick(v, adapterPosition, true)
            //v.startAnimation(AnimationUtils.loadAnimation(v.context, R.anim.tap))
            return true

        }

        var newsTitle: TextView = itemView.findViewById(R.id.item_title)
        var newsThumb: ImageView = itemView.findViewById(R.id.thumb_image)
        var saveNews: ImageView = itemView.findViewById(R.id.save_icon)

    }
}

这是我的供稿代码:

package com.hfad.cointask.fragments


import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.Toast
import com.google.gson.GsonBuilder
import com.hfad.cointask.FeedActivity

import com.hfad.cointask.R
import com.hfad.cointask.adapter.CoinAdapter
import com.hfad.cointask.model.News
import com.hfad.cointask.service.CoinClient
import io.reactivex.Flowable
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.processors.PublishProcessor
import io.reactivex.schedulers.Schedulers
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.scalars.ScalarsConverterFactory

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

/**
 * A simple [Fragment] subclass.
 *
 */
class FeedFragment : androidx.fragment.app.Fragment() {

    private var news = mutableListOf<News>()
    private val gson = GsonBuilder().setDateFormat("yyyy-mm-dd HH:mm:ss.SSSSSS").create()
    private lateinit var newsRecyclerView: androidx.recyclerview.widget.RecyclerView
    private lateinit var newsAdapter: CoinAdapter
    private lateinit var layoutManager: androidx.recyclerview.widget.LinearLayoutManager
    private val client = FeedFragment.mCoinClient().build()
    var offset = 0
    var length = 10
    private lateinit var compositeDispossable: CompositeDisposable
    private lateinit var pagination: PublishProcessor<Int>
    private val TAG = "FeedActivity"
    private var totalItemCount = 0
    private var lastVisibleItem = 0
    private var loading = false
    private val VISIBLE_TRESHOLD = 1
    private lateinit var progressBar: ProgressBar

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment
        var view = inflater.inflate(R.layout.fragment_feed, container, false)

        progressBar = view.findViewById(R.id.progressBar)
        newsRecyclerView = view.findViewById(R.id.news_recycler_view)
        newsRecyclerView.isNestedScrollingEnabled = false
        layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this.requireContext(), androidx.recyclerview.widget.LinearLayoutManager.VERTICAL, false)
        newsRecyclerView.layoutManager = layoutManager
        newsAdapter = CoinAdapter(news, this.requireContext())
        newsRecyclerView.adapter = newsAdapter
        compositeDispossable = CompositeDisposable()
        pagination = PublishProcessor.create()

        news.clear()

        headerNews()
        setUpLoadMoreListener()
        subscribeForData()

        return view
    }

    private fun setUpLoadMoreListener() {
        newsRecyclerView.addOnScrollListener(object: androidx.recyclerview.widget.RecyclerView.OnScrollListener() {

            override fun onScrolled(recyclerView: androidx.recyclerview.widget.RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)

                totalItemCount = newsRecyclerView.layoutManager!!.itemCount
                lastVisibleItem = layoutManager.findLastVisibleItemPosition()

                if (!loading && totalItemCount <= (lastVisibleItem + VISIBLE_TRESHOLD)) {

                    offset += length
                    pagination.onNext(offset)
                    loading = true

                }

            }

        })
    }

    private fun subscribeForData() {

        var disposable: Disposable = pagination
                .onBackpressureDrop()
                .concatMap {
                    loading = true
                    progressBar.visibility = View.VISIBLE
                    getNews(offset)
                }
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe ({ t ->
                    jsonLatest(t)
                    newsAdapter.notifyDataSetChanged()
                    loading = false
                    progressBar.visibility = View.INVISIBLE
                }
                        ,{
                    Log.e(TAG, it.toString())
                })

        compositeDispossable.add(disposable)
        pagination.onNext(offset)
    }

    private fun headerNews() {
        getHeader().enqueue(object: Callback<String> {
            override fun onResponse(call: Call<String>, response: Response<String>) {
                var headNews = response.body()
                if (headNews != null) {
                    jsonHeader(headNews)
                }
                newsAdapter.notifyDataSetChanged()
            }

            override fun onFailure(call: Call<String>?, t: Throwable?) {
                val toast = Toast.makeText(FeedActivity(), t.toString(), Toast.LENGTH_SHORT)
                toast.show()
            }

        })
    }

    private fun getHeader() = client.getHead()

    private fun getNews(offset: Int): Flowable<String> {
        return client.getNews(offset, length)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
    }

    class mCoinClient {
        private val builder = Retrofit
                .Builder()
                .baseUrl("https://api.cointelegraph.com/")
                .addConverterFactory(ScalarsConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())

        private val retrofit: Retrofit by lazy {
            builder.build()
        }

        private val client: CoinClient by lazy {
            retrofit.create(CoinClient::class.java)
        }

        fun build() = client
    }

    private fun jsonLatest(jsonString: String) {

        var latestNews = jsonString.substringAfter("\"type\":\"latest\",\"data\":")
        latestNews = latestNews.substringBefore("}]}}")
        val listNews = gson.fromJson(latestNews, Array<News>::class.java).asList()

        news.addAll(listNews)
    }

    fun jsonHeader(jsonString: String) {

        var headerNews = jsonString.substringAfter("\"type\":\"header\",\"data\":")
        headerNews = headerNews.substringBefore("},{\"type\":")
        val header: News = gson.fromJson(headerNews, News::class.java)

        news.add(header)

    }

    override fun onDestroy() {
        super.onDestroy()
        compositeDispossable.dispose()
    }


}

0 个答案:

没有答案