diff --git a/app/src/main/java/com/gh/base/fragment/BaseFragment.java b/app/src/main/java/com/gh/base/fragment/BaseFragment.java index 4e726c12a7..935c1e6add 100644 --- a/app/src/main/java/com/gh/base/fragment/BaseFragment.java +++ b/app/src/main/java/com/gh/base/fragment/BaseFragment.java @@ -12,6 +12,9 @@ import android.view.ViewGroup; import com.gh.base.OnListClickListener; import com.gh.base.OnRequestCallBackListener; import com.gh.common.constant.Constants; +import com.gh.common.syncpage.ISyncAdapterHandler; +import com.gh.common.syncpage.SyncDataEntity; +import com.gh.common.syncpage.SyncPageRepository; import com.gh.gamecenter.BuildConfig; import com.gh.gamecenter.eventbus.EBMiPush; import com.lightgame.OnTitleClickListener; @@ -31,10 +34,12 @@ import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; +import androidx.recyclerview.widget.RecyclerView; import butterknife.ButterKnife; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; +import kotlin.Pair; import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE; @@ -141,6 +146,29 @@ public abstract class BaseFragment extends Fragment implements OnRequestCallB ButterKnife.bind(this, mCachedView); initView(mCachedView); + + RecyclerView syncRecyclerView = provideSyncPageRecyclerView(); + if (syncRecyclerView != null) { + initSyncPageObserver(syncRecyclerView); + } + } + + private void initSyncPageObserver(RecyclerView syncRecyclerView) { + SyncPageRepository.INSTANCE.getSyncPageLiveData().observe(this, syncDataEntities -> { + if (syncDataEntities == null || syncDataEntities.isEmpty()) return; + RecyclerView.Adapter adapter = syncRecyclerView.getAdapter(); + if (!(adapter instanceof ISyncAdapterHandler)) return; + for (int i = 0; i < adapter.getItemCount(); i++) { + Pair syncKey = ((ISyncAdapterHandler) adapter).getSyncData(i); + if (syncKey == null) return; + for (SyncDataEntity syncDataEntity : syncDataEntities) { + if (syncDataEntity.getSyncId().equals(syncKey.getFirst())) { + boolean isSuccess = SyncPageRepository.INSTANCE.handleSyncData(syncKey.getSecond(), syncDataEntity); + if (isSuccess) adapter.notifyItemChanged(i); + } + } + } + }); } // 必须的有subscribe才能register @@ -255,4 +283,7 @@ public abstract class BaseFragment extends Fragment implements OnRequestCallB return this; } + protected RecyclerView provideSyncPageRecyclerView() { + return null; + } } diff --git a/app/src/main/java/com/gh/common/syncpage/IAdapterHandler.kt b/app/src/main/java/com/gh/common/syncpage/IAdapterHandler.kt deleted file mode 100644 index 1b02d1a419..0000000000 --- a/app/src/main/java/com/gh/common/syncpage/IAdapterHandler.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.gh.common.syncpage - -interface IAdapterHandler { - - /** - * Pair first: current position - * Pair second: current data entity - */ - fun findDataBySyncId(syncId: String): Pair? - - fun getSyncKey(position: Int): String? - -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/syncpage/ISyncAdapterHandler.kt b/app/src/main/java/com/gh/common/syncpage/ISyncAdapterHandler.kt new file mode 100644 index 0000000000..4fafd0ad62 --- /dev/null +++ b/app/src/main/java/com/gh/common/syncpage/ISyncAdapterHandler.kt @@ -0,0 +1,12 @@ +package com.gh.common.syncpage + +interface ISyncAdapterHandler { + + /** + * @param position position to query + * @return Pair first: item sync id + * Pair second: item data entity + */ + fun getSyncData(position: Int): Pair? + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/syncpage/SyncDataEntity.kt b/app/src/main/java/com/gh/common/syncpage/SyncDataEntity.kt index ca7f49c469..ba97123714 100644 --- a/app/src/main/java/com/gh/common/syncpage/SyncDataEntity.kt +++ b/app/src/main/java/com/gh/common/syncpage/SyncDataEntity.kt @@ -5,7 +5,7 @@ import androidx.annotation.Keep @Keep data class SyncDataEntity(val syncId: String, val syncFieldName: String?, - val syncFieldValue: Any, + val syncFieldValue: Any?, val remove: Boolean = true, val checkInherited: Boolean = false, val checkFieldEntity: Boolean = false) \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/syncpage/SyncPageAdapter.kt b/app/src/main/java/com/gh/common/syncpage/SyncPageAdapter.kt deleted file mode 100644 index 540a67c518..0000000000 --- a/app/src/main/java/com/gh/common/syncpage/SyncPageAdapter.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.gh.common.syncpage - -import android.content.Context -import com.gh.gamecenter.baselist.ListAdapter - -abstract class SyncPageAdapter(context: Context) : ListAdapter(context), IAdapterHandler { - -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/syncpage/SyncPageRepository.kt b/app/src/main/java/com/gh/common/syncpage/SyncPageRepository.kt index f2b7859c4e..5a42265689 100644 --- a/app/src/main/java/com/gh/common/syncpage/SyncPageRepository.kt +++ b/app/src/main/java/com/gh/common/syncpage/SyncPageRepository.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.MutableLiveData import com.gh.common.annotation.SyncPage import com.halo.assistant.HaloApp import java.lang.reflect.Field +import java.util.concurrent.CopyOnWriteArrayList import kotlin.collections.ArrayList @@ -12,14 +13,23 @@ object SyncPageRepository { // 只有在新增操作时,才需要进行页面刷新 val syncPageLiveData = MutableLiveData>() - // todo 同步操作 - val syncDataList = ArrayList() + val syncDataList = CopyOnWriteArrayList() + // todo 对于不是即使删除的数据源(remove = false),该何时删除?? fun clearSyncData() { syncDataList.clear() } fun postSyncData(entity: SyncDataEntity) { + // 检查是否存在重复操作 + for (syncDataEntity in syncDataList) { + if (syncDataEntity.syncId == entity.syncId && syncDataEntity.syncFieldName == entity.syncFieldName) { + syncDataList.remove(syncDataEntity) + syncDataList.add(entity) + return + } + } + syncDataList.add(entity) syncPageLiveData.postValue(syncDataList) } @@ -35,11 +45,12 @@ object SyncPageRepository { if (isNeedNotify) break } - if (syncData.remove) syncDataList.remove(syncData) // 注意!ConcurrentModificationException + if (syncData.remove) syncDataList.remove(syncData) return isNeedNotify } + // 替换同步数据 private fun replaceSyncData(field: Field, extractObject: Any, syncData: SyncDataEntity): Boolean { if (field.getAnnotation(SyncPage::class.java)?.syncName == syncData.syncFieldName) { field.isAccessible = true diff --git a/app/src/main/java/com/gh/common/syncpage/example/ExampleAdapter.kt b/app/src/main/java/com/gh/common/syncpage/example/ExampleAdapter.kt index d2ab9f1332..21aa34a0ea 100644 --- a/app/src/main/java/com/gh/common/syncpage/example/ExampleAdapter.kt +++ b/app/src/main/java/com/gh/common/syncpage/example/ExampleAdapter.kt @@ -5,9 +5,10 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.ItemViewType -import com.gh.common.syncpage.SyncPageAdapter +import com.gh.common.syncpage.ISyncAdapterHandler import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder +import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.databinding.CommunityAnswerItemBinding import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.qa.answer.CommunityAnswerItemViewHolder @@ -15,10 +16,9 @@ import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.entity.AnswerEntity import com.gh.gamecenter.qa.entity.Questions -import com.gh.gamecenter.qa.questions.detail.AnswerViewHolder import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity -class ExampleAdapter(context: Context) : SyncPageAdapter(context) { +class ExampleAdapter(context: Context) : ListAdapter(context), ISyncAdapterHandler { override fun areItemsTheSame(oldItem: AnswerEntity?, newItem: AnswerEntity?): Boolean { return oldItem?.id == newItem?.id @@ -85,22 +85,10 @@ class ExampleAdapter(context: Context) : SyncPageAdapter(context) } } - - /** - * todo 有没有更简洁的获取操作? - */ - override fun findDataBySyncId(syncId: String): Pair? { - for (i in 0 until mEntityList.size) { - val entity = mEntityList[i] - if (entity.id == syncId) { - return Pair(i, entity) - } - } - return null - } - - override fun getSyncKey(position: Int): String? { - return mEntityList[position].id + override fun getSyncData(position: Int): Pair? { + if (position >= mEntityList.size) return null + val entity = mEntityList[position] + return Pair(entity.id ?: "", entity) } fun getPath(): String { diff --git a/app/src/main/java/com/gh/common/syncpage/example/ExampleFragment.kt b/app/src/main/java/com/gh/common/syncpage/example/ExampleFragment.kt index 543fa78e09..636b000f2d 100644 --- a/app/src/main/java/com/gh/common/syncpage/example/ExampleFragment.kt +++ b/app/src/main/java/com/gh/common/syncpage/example/ExampleFragment.kt @@ -5,17 +5,15 @@ import android.view.View import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.RecyclerView -import com.gh.common.syncpage.IAdapterHandler +import com.gh.common.syncpage.ISyncAdapterHandler import com.gh.common.syncpage.SyncPageRepository import com.gh.common.view.VerticalItemDecoration import com.gh.gamecenter.baselist.ListFragment import com.gh.gamecenter.baselist.NormalListViewModel import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.qa.entity.AnswerEntity -import com.gh.gamecenter.qa.recommends.newest.RecommendNewestAdapter import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp -import com.lightgame.utils.Utils import io.reactivex.Observable class ExampleFragment : ListFragment>() { @@ -44,17 +42,23 @@ class ExampleFragment : ListFragment() apiResponse.data = response mVoteLiveData.postValue(apiResponse) + + SyncPageRepository.postSyncData(SyncDataEntity(answerId, "vote", answerDetail!!.vote)) + SyncPageRepository.postSyncData(SyncDataEntity(answerId, "answer_vote", answerDetail?.me?.isAnswerVoted, checkFieldEntity = true)) } override fun onFailure(e: HttpException?) {