This commit is contained in:
juntao
2020-08-20 21:01:40 +08:00
parent 7e79b4e328
commit cdbefd2d4f
19 changed files with 319 additions and 145 deletions

View File

@ -0,0 +1,20 @@
package com.gh.common.view.vertical_recycler
import android.content.Context
import android.graphics.PointF
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView
class SnappingLinearLayoutManager(val context: Context) : LinearLayoutManager(context) {
override fun smoothScrollToPosition(recyclerView: RecyclerView?, state: RecyclerView.State?, position: Int) {
val smoothScroller = object : LinearSmoothScroller(context) {
override fun getVerticalSnapPreference(): Int = SNAP_TO_START
override fun computeScrollVectorForPosition(targetPosition: Int): PointF =
this@SnappingLinearLayoutManager.computeScrollVectorForPosition(targetPosition)
?: PointF(0F, 0F)
}
smoothScroller.targetPosition = position
startSmoothScroll(smoothScroller)
}
}

View File

@ -1,11 +1,12 @@
package com.gh.gamecenter.adapter.viewholder;
import androidx.annotation.StringRes;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.StringRes;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.base.OnListClickListener;
import com.gh.gamecenter.R;
@ -88,6 +89,35 @@ public class FooterViewHolder extends BaseRecyclerViewHolder {
});
}
public void bindFooterDefaultEmpty(ListViewModel viewModel, boolean isLoading, boolean isNetworkError, boolean isOver) {
if (isNetworkError) {
lineLeft.setVisibility(View.GONE);
lineRight.setVisibility(View.GONE);
loading.setVisibility(View.GONE);
hint.setVisibility(View.VISIBLE);
hint.setText(R.string.loading_failed_retry);
} else if (isOver) {
lineLeft.setVisibility(View.GONE);
lineRight.setVisibility(View.GONE);
loading.setVisibility(View.GONE);
hint.setVisibility(View.VISIBLE);
hint.setText(R.string.ask_loadover_hint);
} else if (isLoading) {
lineLeft.setVisibility(View.GONE);
lineRight.setVisibility(View.GONE);
loading.setVisibility(View.VISIBLE);
hint.setVisibility(View.VISIBLE);
hint.setText(R.string.loading);
} else {
lineLeft.setVisibility(View.GONE);
lineRight.setVisibility(View.GONE);
loading.setVisibility(View.GONE);
hint.setVisibility(View.GONE);
}
itemView.setOnClickListener(v -> {
if (isNetworkError) viewModel.load(LoadType.RETRY);
});
}
public void initFooterViewHolder(boolean isLoading, boolean isNetworkError, boolean isOver, @StringRes int loadOverHint) {
if (isNetworkError) {

View File

@ -149,7 +149,7 @@ public abstract class ListFragment<T, VM extends BaseListViewModel /* 该泛型
}
}
});
mReuseNoConn.setOnClickListener(view1 -> onLoadRefresh());
if (mReuseNoConn != null) mReuseNoConn.setOnClickListener(view1 -> onLoadRefresh());
}
private Class<VM> getViewModelClass() {

View File

@ -62,7 +62,7 @@ class MeEntity(@SerializedName("is_community_voted")
@SerializedName("is_version_requested")
var isVersionRequested: Boolean = false,
@SerializedName("is_follower",alternate = ["is_follow"])
@SerializedName("is_follower", alternate = ["is_follow"])
var isFollower: Boolean = false, // 是否已经关注该用户
@SerializedName("is_favorite")

View File

@ -26,7 +26,6 @@ import com.gh.gamecenter.ImageViewerActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.SuggestionActivity
import com.gh.gamecenter.baselist.ListAdapter
import com.gh.gamecenter.baselist.ListFragment
import com.gh.gamecenter.baselist.LoadType
import com.gh.gamecenter.entity.CommentEntity
import com.gh.gamecenter.entity.CommunityEntity
@ -47,20 +46,11 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.util.*
class ArticleDetailFragment : ListFragment<CommentItemData, ArticleDetailViewModel>() {
@BindView(R.id.reuse_none_data)
lateinit var mNoneData: View
@BindView(R.id.reuse_no_connection)
lateinit var mNoConn: View
class ArticleDetailFragment : BaseArticleDetailCommentFragment<CommentItemData, ArticleDetailViewModel>() {
@BindView(R.id.reuse_tv_none_data)
lateinit var mNoDataText: TextView
@BindView(R.id.reuse_ll_loading)
lateinit var mLoading: View
private var mElapsedHelper: TimeElapsedHelper? = null
private var mIsShowCommentManager: Boolean = false
@ -95,12 +85,6 @@ class ArticleDetailFragment : ListFragment<CommentItemData, ArticleDetailViewMod
mElapsedHelper = TimeElapsedHelper(this)
}
override fun onResume() {
super.onResume()
replyTv.text = mViewModel.getCommentDraft(mViewModel.articleId)?.draft ?: "说点什么吧"
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == ArticleDetailActivity.ARTICLE_PATCH_REQUEST && resultCode == Activity.RESULT_OK) {
@ -108,7 +92,7 @@ class ArticleDetailFragment : ListFragment<CommentItemData, ArticleDetailViewMod
mViewModel.detailEntity = it
updateView()
}
mNoConn.performClick() //重新刷新
mReuseNoConn?.performClick() //重新刷新
} else if (requestCode == ImageViewerActivity.REQUEST_FOR_VIEWED_IMAGE && resultCode == Activity.RESULT_OK) {
val imageSet = data?.extras?.get(ImageViewerActivity.VIEWED_IMAGE) as HashSet<Integer>
mAdapter?.articleDetailVH?.run {
@ -125,17 +109,17 @@ class ArticleDetailFragment : ListFragment<CommentItemData, ArticleDetailViewMod
}
} else if (requestCode == CommentActivity.REQUEST_CODE && resultCode == Activity.RESULT_OK) {
val commentCount = data?.getIntExtra(CommentActivity.COMMENT_COUNT, 0)
if (commentCount != 0) {
mViewModel.detailEntity?.count?.comment = commentCount!!
if (commentCount != 0 && commentCount != null) {
mViewModel.detailEntity?.count?.comment = commentCount
bottomCommentTv.text = String.format("评论 %s", NumberUtils.transSimpleCount(mViewModel.detailEntity?.count?.comment!!))
// TODO 更新筛选 item 的评论数
updateFixedTopFilterView()
if (EntranceUtils.ENTRANCE_WELCOME == mEntrance) {
LogUtils.uploadCommentFromWelcomeDialog()
}
mViewModel.load(LoadType.REFRESH)
}
mViewModel.load(LoadType.REFRESH)
}
}
@ -210,13 +194,13 @@ class ArticleDetailFragment : ListFragment<CommentItemData, ArticleDetailViewMod
}
mReuseNoConn?.setOnClickListener {
mNoConn.visibility = View.GONE
mLoading.visibility = View.VISIBLE
mReuseNoConn?.visibility = View.GONE
mListLoading?.visibility = View.VISIBLE
mViewModel.getArticleDetail()
}
bottomCommentContainer.setOnClickListener {
mListRv.smoothScrollToPosition(3)
mListRv.smoothScrollToPosition(1)
}
if (mIsShowCommentManager) {
@ -238,8 +222,8 @@ class ArticleDetailFragment : ListFragment<CommentItemData, ArticleDetailViewMod
HistoryHelper.deleteArticleEntity(mViewModel.articleId)
mNoConn.visibility = View.GONE
mNoneData.visibility = View.VISIBLE
mReuseNoConn?.visibility = View.GONE
mReuseNoData?.visibility = View.VISIBLE
if (mIsRecommendsContent) {
val data = Intent()
data.putExtra(EntranceUtils.KEY_ANSWER_ID, mViewModel.articleId)
@ -257,11 +241,11 @@ class ArticleDetailFragment : ListFragment<CommentItemData, ArticleDetailViewMod
}
}
} else {
mNoConn.visibility = View.VISIBLE
mNoneData.visibility = View.GONE
mReuseNoConn?.visibility = View.VISIBLE
mReuseNoData?.visibility = View.GONE
}
mLoading.visibility = View.GONE
mListLoading?.visibility = View.GONE
bottomContainer.visibility = View.GONE
bottomShadowView.visibility = View.GONE
}
@ -446,8 +430,8 @@ class ArticleDetailFragment : ListFragment<CommentItemData, ArticleDetailViewMod
private fun updateView() {
val articleDetail = mViewModel.detailEntity ?: return
mNoConn.visibility = View.GONE
mLoading.visibility = View.GONE
mReuseNoConn?.visibility = View.GONE
mListLoading?.visibility = View.GONE
bottomContainer.visibility = View.VISIBLE
bottomShadowView.visibility = View.VISIBLE
@ -472,10 +456,6 @@ class ArticleDetailFragment : ListFragment<CommentItemData, ArticleDetailViewMod
updateLikeView(articleDetail.me.isCommunityArticleVote, articleDetail.count.vote)
}
override fun onLoadRefresh() {
// do nothing
}
@SuppressLint("SetTextI18n")
private fun updateLikeView(alreadyLiked: Boolean, likeCount: Int) {
mAdapter?.articleDetailVH?.updateLikeView(alreadyLiked, likeCount)

View File

@ -10,6 +10,7 @@ import com.gh.common.syncpage.SyncPageRepository
import com.gh.common.util.CollectionUtils
import com.gh.common.util.ErrorHelper
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.LoadStatus
import com.gh.gamecenter.entity.CommentEntity
import com.gh.gamecenter.entity.VoteEntity
import com.gh.gamecenter.eventbus.EBCollectionChanged
@ -62,8 +63,12 @@ class ArticleDetailViewModel(application: Application,
add(CommentItemData(articleDetail = detailEntity))
add(CommentItemData(filter = true))
}
commentList?.forEach {
add(CommentItemData(commentNormal = it))
if (commentList.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_EMPTY) {
add(CommentItemData(errorEmpty = true))
} else {
commentList?.forEach {
add(CommentItemData(commentNormal = it))
}
}
}
mResultLiveData.postValue(mergedList)

View File

@ -11,6 +11,7 @@ import com.gh.common.util.*
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
import com.gh.gamecenter.baselist.ListAdapter
import com.gh.gamecenter.baselist.LoadStatus
import com.gh.gamecenter.databinding.ItemArticleDetailCommentBinding
import com.gh.gamecenter.databinding.PieceArticleDetailCommentFilterBinding
import com.gh.gamecenter.entity.CommentEntity
@ -27,6 +28,22 @@ abstract class BaseArticleDetailCommentAdapter(context: Context,
var filterVH: ArticleDetailCommentFilterViewHolder? = null
override fun loadChange(status: LoadStatus?) {
if (status == LoadStatus.INIT) {
mIsNetworkError = false
mIsOver = false
mIsLoading = true
return
} else {
super.loadChange(status)
}
}
override fun setListData(updateData: MutableList<CommentItemData>?) {
mEntityList = ArrayList<CommentItemData>(updateData ?: arrayListOf())
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
ItemViewType.ITEM_FOOTER -> {
@ -45,11 +62,11 @@ abstract class BaseArticleDetailCommentAdapter(context: Context,
}
ITEM_ERROR_EMPTY -> {
object : RecyclerView.ViewHolder(mLayoutInflater.inflate(R.layout.item_article_detail_comment_empty, parent, false)) {}
ArticleDetailCommentErrorViewHolder(mLayoutInflater.inflate(R.layout.item_article_detail_comment_empty, parent, false))
}
ITEM_ERROR_CONNECTION -> {
object : RecyclerView.ViewHolder(mLayoutInflater.inflate(R.layout.item_article_detail_comment_empty, parent, false)) {}
ArticleDetailCommentErrorViewHolder(mLayoutInflater.inflate(R.layout.item_article_detail_comment_empty, parent, false))
}
else -> throw IllegalAccessException()
@ -81,11 +98,12 @@ abstract class BaseArticleDetailCommentAdapter(context: Context,
is FooterViewHolder -> {
holder.initItemPadding()
holder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver, R.string.ask_loadover_hint)
holder.bindFooterDefaultEmpty(mViewModel, mIsLoading, mIsNetworkError, mIsOver)
}
}
}
inner class ArticleDetailCommentErrorViewHolder(view: View) : RecyclerView.ViewHolder(view)
inner class ArticleDetailCommentFilterViewHolder(var binding: PieceArticleDetailCommentFilterBinding) : RecyclerView.ViewHolder(binding.root) {
fun bindView(article: ArticleDetailEntity? = null, comment: CommentEntity? = null) {
binding.run {
@ -140,12 +158,13 @@ abstract class BaseArticleDetailCommentAdapter(context: Context,
binding.root.context.startActivity(this)
}
}
binding.commentCountTv.text = viewModel.getCommentText(comment.reply, "评论")
} else {
// 评论详情用的样式
binding.floorHintTv.text = CommentUtils.getCommentTime(comment.time)
binding.commentCountTv.setOnClickListener {
commentClosure?.invoke(comment)
}
binding.commentCountTv.setOnClickListener { commentClosure?.invoke(comment) }
binding.commentCountTv.setCompoundDrawables(null, null, null, null)
binding.commentCountTv.text = "回复"
binding.replyUserContainer.goneIf(comment.parentUser == null)
comment.parentUser?.let {
binding.replyUserNameTv.text = it.name
@ -155,10 +174,8 @@ abstract class BaseArticleDetailCommentAdapter(context: Context,
binding.replyUserNameTv.setOnClickListener {
DirectUtils.directToHomeActivity(binding.root.context, comment.user.id, 1, "", "")
}
binding.commentCountTv.text = "回复"
}
}
binding.commentCountTv.text = viewModel.getCommentText(comment.reply, "评论")
}
@SuppressLint("SetTextI18n")

View File

@ -0,0 +1,97 @@
package com.gh.gamecenter.qa.article.detail
import android.graphics.drawable.InsetDrawable
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import com.gh.common.util.DisplayUtils
import com.gh.common.view.CustomDividerItemDecoration
import com.gh.common.view.vertical_recycler.SnappingLinearLayoutManager
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.ListFragment
abstract class BaseArticleDetailCommentFragment<T, VM : BaseArticleDetailCommentViewModel> : ListFragment<T, VM>() {
@BindView(R.id.fixedTopFilterView)
lateinit var fixedTopFilterView: View
@BindView(R.id.filterLatestTv)
lateinit var filterLatestTv: TextView
@BindView(R.id.filterOldestTv)
lateinit var filterOldestTv: TextView
@BindView(R.id.commentHintTv)
lateinit var commentHintTv: TextView
@BindView(R.id.commentHintCountTv)
lateinit var commentHintCountTv: TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mListRv.layoutManager = SnappingLinearLayoutManager(requireContext()).apply { mLayoutManager = this }
mListRv.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val firstCompletelyVisiblePosition = mLayoutManager.findFirstCompletelyVisibleItemPosition()
if (RecyclerView.NO_POSITION == firstCompletelyVisiblePosition) return
val filterView = mLayoutManager.findViewByPosition(1)
if (firstCompletelyVisiblePosition >= 2 && filterView == null) {
fixedTopFilterView.visibility = View.VISIBLE
updateFixedTopFilterView()
} else {
filterView?.let {
if (it.top <= 0 && fixedTopFilterView.visibility == View.GONE) {
fixedTopFilterView.visibility = View.VISIBLE
updateFixedTopFilterView()
} else if (it.top > 0 && fixedTopFilterView.visibility == View.VISIBLE) {
fixedTopFilterView.visibility = View.GONE
}
}
}
}
})
filterLatestTv.setOnClickListener {
getFilterVH()?.binding?.filterLatestTv?.performClick()
updateFixedTopFilterView()
}
filterOldestTv.setOnClickListener {
getFilterVH()?.binding?.filterOldestTv?.performClick()
updateFixedTopFilterView()
}
}
override fun onLoadRefresh() {
// do nothing
}
override fun onLoadEmpty() {
// do nothing
}
override fun getItemDecoration(): RecyclerView.ItemDecoration {
val insetDivider = InsetDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.divider_article_detail_comment), DisplayUtils.dip2px(20F), 0, DisplayUtils.dip2px(20F), 0)
val itemDecoration = CustomDividerItemDecoration(requireContext(), notDecorateTheFirstTwoItems = true, notDecorateTheLastItem = true)
itemDecoration.setDrawable(insetDivider)
return itemDecoration
}
fun updateFixedTopFilterView() {
filterLatestTv.setTextColor(getFilterVH()?.binding?.filterLatestTv?.textColors)
filterOldestTv.setTextColor(getFilterVH()?.binding?.filterOldestTv?.textColors)
commentHintTv.text = getFilterVH()?.binding?.commentHintTv?.text
commentHintCountTv.text = getFilterVH()?.binding?.commentHintCountTv?.text
}
private fun getFilterVH(): BaseArticleDetailCommentAdapter.ArticleDetailCommentFilterViewHolder? {
return (provideListAdapter() as BaseArticleDetailCommentAdapter).filterVH
}
}

View File

@ -6,6 +6,8 @@ import com.gh.common.util.CommentDraftContainer
import com.gh.common.util.PostCommentUtils
import com.gh.common.util.ToastUtils
import com.gh.gamecenter.baselist.ListViewModel
import com.gh.gamecenter.baselist.LoadParams
import com.gh.gamecenter.baselist.LoadStatus
import com.gh.gamecenter.baselist.LoadType
import com.gh.gamecenter.entity.CommentDraft
import com.gh.gamecenter.entity.CommentEntity
@ -20,6 +22,38 @@ abstract class BaseArticleDetailCommentViewModel(application: Application, var a
val loadResultLiveData = MutableLiveData<LoadResult>()
override fun loadStatusControl(size: Int) {
if (mCurLoadParams.loadOffset == LoadParams.DEFAULT_OFFSET) { // 初始化列表
when {
size == 0 -> {
mLoadStatusLiveData.setValue(LoadStatus.INIT_EMPTY)
}
size == REQUEST_FAILURE_SIZE -> {
// TODO 处理列表加载失败问题
mLoadStatusLiveData.setValue(LoadStatus.INIT_LOADED)
}
size < mOverLimitSize -> {
// 避免一个屏幕出现两次分页
mLoadStatusLiveData.setValue(LoadStatus.INIT_OVER)
}
else -> mLoadStatusLiveData.setValue(LoadStatus.INIT_LOADED)
}
} else {
when (size) {
REQUEST_FAILURE_SIZE -> mLoadStatusLiveData.setValue(LoadStatus.LIST_FAILED)
0 -> mLoadStatusLiveData.setValue(LoadStatus.LIST_OVER)
else -> mLoadStatusLiveData.setValue(LoadStatus.LIST_LOADED)
}
}
if (size == REQUEST_FAILURE_SIZE) {
mRetryParams = mCurLoadParams
} else {
mRetryParams = null
mCurLoadParams.loadOffset = mCurLoadParams.loadOffset + 1 // 页数 + 1
}
}
fun changeSort(sortType: SortType) {
if (sortType != currentSortType) {
currentSortType = sortType

View File

@ -8,12 +8,9 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.LoadStatus
import com.gh.gamecenter.databinding.ItemArticleDetailCommentBinding
import com.gh.gamecenter.entity.CommentEntity
import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentAdapter
import com.gh.gamecenter.qa.article.detail.CommentItemData
import java.util.*
class ArticleDetailCommentAdapter(context: Context,
var mViewModel: ArticleDetailCommentViewModel,
@ -21,27 +18,13 @@ class ArticleDetailCommentAdapter(context: Context,
commentClosure: ((CommentEntity) -> Unit)? = null)
: BaseArticleDetailCommentAdapter(context, mViewModel, type, commentClosure) {
override fun loadChange(status: LoadStatus?) {
if (status == LoadStatus.INIT) {
mIsNetworkError = false
mIsOver = false
mIsLoading = true
return
} else {
super.loadChange(status)
}
}
override fun setListData(updateData: MutableList<CommentItemData>?) {
mEntityList = ArrayList<CommentItemData>(updateData ?: arrayListOf())
notifyDataSetChanged()
}
var topCommentVH: ArticleDetailTopCommentViewHolder? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
ITEM_COMMENT_TOP -> {
val binding: ItemArticleDetailCommentBinding = DataBindingUtil.inflate(mLayoutInflater, R.layout.item_article_detail_comment, parent, false)
ArticleDetailTopCommentViewHolder(binding)
ArticleDetailTopCommentViewHolder(binding).apply { topCommentVH = this }
}
else -> super.onCreateViewHolder(parent, viewType)
@ -55,19 +38,17 @@ class ArticleDetailCommentAdapter(context: Context,
}
is ArticleDetailCommentFilterViewHolder -> {
holder.bindView(comment = mViewModel.commentDetailLiveData.value)
holder.bindView(comment = mViewModel.commentDetail)
}
else -> super.onBindViewHolder(holder, position)
}
}
private inner class ArticleDetailTopCommentViewHolder(var binding: ItemArticleDetailCommentBinding)
inner class ArticleDetailTopCommentViewHolder(var binding: ItemArticleDetailCommentBinding)
: RecyclerView.ViewHolder(binding.root) {
fun bindView(comment: CommentEntity) {
binding.comment = comment
val constraintSet = ConstraintSet()
constraintSet.clone(binding.contentTv.parent as ConstraintLayout)
@ -75,11 +56,13 @@ class ArticleDetailCommentAdapter(context: Context,
constraintSet.connect(binding.contentTv.id, ConstraintSet.START, binding.iconContainer.id, ConstraintSet.START)
constraintSet.applyTo(binding.contentTv.parent as ConstraintLayout)
binding.comment = comment
binding.moreIv.visibility = View.GONE
binding.floorHintTv.text = "1F"
binding.commentCountTv.text = mViewModel.getCommentText(comment.reply, "回复")
binding.commentCountTv.setOnClickListener { commentClosure?.invoke(comment) }
ArticleDetailCommentViewHolder.bindComment(binding, mViewModel, comment)
binding.commentCountTv.text = mViewModel.getCommentText(comment.reply, "回复")
binding.floorHintTv.text = "1F"
}
}

View File

@ -1,43 +1,26 @@
package com.gh.gamecenter.qa.article.detail.comment
import android.app.Activity
import android.content.Intent
import android.graphics.drawable.InsetDrawable
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import com.gh.common.history.HistoryHelper
import com.gh.common.util.*
import com.gh.common.view.CustomDividerItemDecoration
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.ListAdapter
import com.gh.gamecenter.baselist.ListFragment
import com.gh.gamecenter.baselist.LoadType
import com.gh.gamecenter.entity.CommentEntity
import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentAdapter
import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentFragment
import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentViewModel
import com.gh.gamecenter.qa.article.detail.CommentItemData
import com.gh.gamecenter.qa.comment.CommentActivity
import com.halo.assistant.HaloApp
import com.qq.gdt.action.ActionType
import kotlinx.android.synthetic.main.fragment_article_detail.*
import kotlinx.android.synthetic.main.fragment_article_detail_comment.toolbar
import kotlinx.android.synthetic.main.fragment_article_detail_comment.*
import kotlinx.android.synthetic.main.piece_article_input_container.*
class ArticleDetailCommentFragment : ListFragment<CommentItemData, ArticleDetailCommentViewModel>() {
@BindView(R.id.reuse_none_data)
lateinit var mNoneData: View
@BindView(R.id.reuse_no_connection)
lateinit var mNoConn: View
@BindView(R.id.reuse_ll_loading)
lateinit var mLoading: View
class ArticleDetailCommentFragment : BaseArticleDetailCommentFragment<CommentItemData, ArticleDetailCommentViewModel>() {
private var mAdapter: ArticleDetailCommentAdapter? = null
private lateinit var mViewModel: ArticleDetailCommentViewModel
@ -63,10 +46,12 @@ class ArticleDetailCommentFragment : ListFragment<CommentItemData, ArticleDetail
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == CommentActivity.REQUEST_CODE && resultCode == Activity.RESULT_OK) {
val commentCount = data?.getIntExtra(CommentActivity.COMMENT_COUNT, 0) ?: 0
val commentCount = data?.getIntExtra(CommentActivity.COMMENT_COUNT, 0)
if (commentCount != null && commentCount != 0) {
mViewModel.commentDetail?.reply = commentCount
// TODO 更新筛选 item 的评论数
updateFixedTopFilterView()
mViewModel.load(LoadType.REFRESH)
mViewModel.commentDetailLiveData.value?.reply = commentCount
}
}
@ -79,10 +64,6 @@ class ArticleDetailCommentFragment : ListFragment<CommentItemData, ArticleDetail
arguments?.getString(EntranceUtils.KEY_ARTICLE_COMMENT_ID) ?: ""))
}
override fun onLoadRefresh() {
// do nothing
}
override fun provideListAdapter(): ListAdapter<*> {
return mAdapter
?: ArticleDetailCommentAdapter(requireContext(), mViewModel, BaseArticleDetailCommentAdapter.AdapterType.SUB_COMMENT) {
@ -92,8 +73,8 @@ class ArticleDetailCommentFragment : ListFragment<CommentItemData, ArticleDetail
}
private fun initView() {
ViewCompat.setOnApplyWindowInsetsListener(toolbar) { _, insets ->
(toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop
ViewCompat.setOnApplyWindowInsetsListener(toolbarContainer) { _, insets ->
(toolbarContainer.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop
insets.consumeSystemWindowInsets()
}
@ -103,7 +84,7 @@ class ArticleDetailCommentFragment : ListFragment<CommentItemData, ArticleDetail
replyTv.text = "说点什么吧"
replyTv.setRoundedColorBackground(R.color.text_F0F0F0, 19F)
replyTv.setDebouncedClickListener {
val comment = mCurrentComment ?: mViewModel.commentDetailLiveData.value
val comment = mCurrentComment ?: mViewModel.commentDetail
comment?.let { startCommentActivity(it) }
}
}
@ -126,19 +107,12 @@ class ArticleDetailCommentFragment : ListFragment<CommentItemData, ArticleDetail
return super.onBackPressed()
}
override fun getItemDecoration(): RecyclerView.ItemDecoration {
val insetDivider = InsetDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.divider_article_detail_comment), DisplayUtils.dip2px(20F), 0, DisplayUtils.dip2px(20F), 0)
val itemDecoration = CustomDividerItemDecoration(requireContext(), notDecorateTheLastItem = true)
itemDecoration.setDrawable(insetDivider)
return itemDecoration
}
private fun initObserver() {
mViewModel.loadResultLiveData.observeNonNull(this) {
when (it) {
BaseArticleDetailCommentViewModel.LoadResult.SUCCESS -> {
mNoConn.visibility = View.GONE
mLoading.visibility = View.GONE
mReuseNoConn?.visibility = View.GONE
mListLoading?.visibility = View.GONE
GdtHelper.logAction(ActionType.PAGE_VIEW,
GdtHelper.CONTENT_TYPE, "QA_ARTICLE",
@ -146,11 +120,8 @@ class ArticleDetailCommentFragment : ListFragment<CommentItemData, ArticleDetail
}
else -> {
if (it == BaseArticleDetailCommentViewModel.LoadResult.DELETED) {
HistoryHelper.deleteArticleEntity(mViewModel.articleId)
mNoConn.visibility = View.GONE
mNoneData.visibility = View.VISIBLE
mReuseNoConn?.visibility = View.GONE
mReuseNoData?.visibility = View.VISIBLE
toast(R.string.content_delete_toast)
toolbar.menu?.run {
@ -159,11 +130,11 @@ class ArticleDetailCommentFragment : ListFragment<CommentItemData, ArticleDetail
}
}
} else {
mNoConn.visibility = View.VISIBLE
mNoneData.visibility = View.GONE
mReuseNoConn?.visibility = View.VISIBLE
mReuseNoData?.visibility = View.GONE
}
mLoading.visibility = View.GONE
mListLoading?.visibility = View.GONE
bottomContainer.visibility = View.GONE
bottomShadowView.visibility = View.GONE
}
@ -175,7 +146,7 @@ class ArticleDetailCommentFragment : ListFragment<CommentItemData, ArticleDetail
val intent = CommentActivity.getArticleCommentReplyIntent(
requireContext(),
mViewModel.articleId,
comment.reply,
mViewModel.commentDetail?.reply,
true,
comment.id ?: "",
comment,

View File

@ -2,9 +2,9 @@ package com.gh.gamecenter.qa.article.detail.comment
import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.gamecenter.baselist.LoadStatus
import com.gh.gamecenter.entity.CommentEntity
import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentViewModel
import com.gh.gamecenter.qa.article.detail.CommentItemData
@ -13,6 +13,7 @@ import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import retrofit2.HttpException
class ArticleDetailCommentViewModel(application: Application,
articleId: String = "",
@ -20,7 +21,7 @@ class ArticleDetailCommentViewModel(application: Application,
var commentId: String = "")
: BaseArticleDetailCommentViewModel(application, articleId, communityId) {
var commentDetailLiveData = MutableLiveData<CommentEntity?>()
var commentDetail: CommentEntity? = null
override fun provideDataObservable(page: Int): Observable<List<CommentEntity>>? = null
@ -29,18 +30,22 @@ class ArticleDetailCommentViewModel(application: Application,
}
private fun mergeListData(commentList: List<CommentEntity>?) {
commentDetailLiveData.value?.let { commentDetail ->
commentDetail?.let { it ->
val mergedList = arrayListOf<CommentItemData>().apply {
if (mResultLiveData.value != null && mResultLiveData.value?.size != 0) {
// 保持头两个 item 的内存地址不变
add(mResultLiveData.value!![0])
add(mResultLiveData.value!![1])
} else {
add(CommentItemData(commentTop = commentDetail))
add(CommentItemData(commentTop = it))
add(CommentItemData(filter = true))
}
commentList?.forEach {
add(CommentItemData(commentNormal = it))
if (commentList.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_EMPTY) {
add(CommentItemData(errorEmpty = true))
} else {
commentList?.forEach {
add(CommentItemData(commentNormal = it))
}
}
}
mResultLiveData.postValue(mergedList)
@ -59,13 +64,17 @@ class ArticleDetailCommentViewModel(application: Application,
.subscribe(object : BiResponse<CommentEntity>() {
@SuppressLint("CheckResult")
override fun onSuccess(data: CommentEntity) {
commentDetailLiveData.postValue(data)
commentDetail = data
loadResultLiveData.postValue(LoadResult.SUCCESS)
mergeListData(mListLiveData.value)
}
override fun onFailure(exception: Exception) {
commentDetailLiveData.postValue(null)
if (exception is HttpException && exception.code().toString().contains("404")) {
loadResultLiveData.postValue(LoadResult.DELETED)
} else {
loadResultLiveData.postValue(LoadResult.NETWORK_ERROR)
}
}
})
}

View File

@ -104,11 +104,6 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
mSendingDialog?.dismiss()
toast("发表成功")
if (mShowInputOnly) {
requireActivity().finish()
return@Observer
}
if (mCommentEntity != null) { // 补充默认草稿
mCommentEntity = null // 清空当前评论实体
commentEt.hint = getString(R.string.message_detail_comment_hint)
@ -120,7 +115,6 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
mCommentCount++
updateCommentCount()
onRefresh()
if (mCommentListener != null) {
mCommentListener?.onCountChange(mCommentCount)
@ -138,6 +132,13 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
SyncFieldConstants.ANSWER_COMMENT_COUNT,
mCommentCount))
}
if (mShowInputOnly) {
requireActivity().finish()
return@Observer
} else {
onRefresh()
}
}
apiResponse.httpException != null -> {
mSendingDialog?.dismiss()
@ -182,6 +183,9 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
super.onViewCreated(view, savedInstanceState)
if (mShowInputOnly) {
if (mCommentEntity != null) {
commentEt.hint = "回复:${mCommentEntity?.user?.name}"
}
commentContainer.visibility = View.GONE
}

View File

@ -11,8 +11,7 @@
style="@style/Base_ToolbarStyle"
android:layout_width="match_parent"
android:layout_height="@dimen/appbar_height"
android:background="@color/white"
app:layout_collapseMode="pin">
android:background="@color/white">
<LinearLayout
android:layout_width="match_parent"
@ -20,7 +19,7 @@
android:background="@color/transparent"
android:gravity="center_vertical"
android:orientation="horizontal"
android:translationX="-25dp">
android:translationX="-32dp">
<com.gh.common.view.GameIconView
android:id="@+id/forumGameIv"
@ -65,6 +64,15 @@
</RelativeLayout>
<include
android:id="@+id/fixedTopFilterView"
layout="@layout/piece_article_detail_comment_filter"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_below="@id/toolbar"
android:visibility="gone"
tools:visibility="visible" />
<View
android:id="@+id/bottomShadowView"
android:layout_width="match_parent"

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<com.gh.common.view.MaterializedRelativeLayout 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"
android:background="@color/white"
@ -9,14 +10,14 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/toolbarContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="@dimen/appbar_height"
android:fitsSystemWindows="true">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
style="@style/Base_ToolbarStyle"
android:layout_width="match_parent"
android:layout_height="@dimen/appbar_height"
android:layout_height="match_parent"
android:background="@color/white"
app:layout_constraintTop_toTopOf="parent" />
@ -35,7 +36,8 @@
<RelativeLayout
android:id="@+id/listContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_above="@id/bottomContainer"
android:layout_below="@id/toolbarContainer"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true">
@ -43,7 +45,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="match_parent" />
<include
layout="@layout/reuse_loading"
@ -53,8 +55,17 @@
</RelativeLayout>
<include
android:id="@+id/fixedTopFilterView"
layout="@layout/piece_article_detail_comment_filter"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_below="@id/toolbarContainer"
android:visibility="gone"
tools:visibility="visible" />
<View
android:id="@+id/article_detail_line3"
android:id="@+id/bottomShadowView"
android:layout_width="match_parent"
android:layout_height="6dp"
android:layout_above="@id/bottomContainer"

View File

@ -4,6 +4,7 @@
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="288dp"
android:background="@color/white"
@ -16,10 +17,11 @@
android:src="@drawable/ic_article_detail_comment_empty" />
<TextView
android:id="@+id/hintTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="还没有人评论噢~说说你的看法吧"
android:text="@string/article_detail_empty_comment"
android:textColor="@color/text_cccccc"
android:textSize="12sp" />
</LinearLayout>

View File

@ -96,6 +96,7 @@
android:id="@+id/badgeContainer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="4dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -8,6 +8,7 @@
android:layout_width="match_parent"
android:layout_height="44dp"
android:background="@color/white"
android:clickable="false"
android:paddingLeft="20dp"
android:paddingRight="20dp"
tools:showIn="@layout/fragment_article_detail">

View File

@ -648,6 +648,7 @@
<string name="questions_edit_choose_forum">选择论坛<Data><![CDATA[<font color="#ff4147">*</font>]]></Data></string>
<string name="article_placeholder">请撰写帖子...</string>
<string name="article_detail_empty_comment">还没有人评论噢~说说你的看法吧</string>
<string name="share_community_article_url">https://www.ghzs.com/communities/%1$s/articles/%2$s.html</string>
<string name="share_community_article_title">%1$s发布了帖子%2$s %3$d赞同</string>