feat: 论坛文章详情WebView预加载

This commit is contained in:
曾祥俊
2023-10-12 13:45:11 +08:00
parent 95d87858f1
commit adcb8a4693
12 changed files with 268 additions and 130 deletions

View File

@ -128,6 +128,10 @@ public class RichEditor extends WebView {
private WebResourceRequestInterceptor mWebResourceRequestInterceptor;
private PageFinishedListener mPageFinishedListener;
private OnLinkClickListener mOnLinkClickListener;
private ImageListener mImageListener;
public RichEditor(Context context) {
this(context, null);
}
@ -146,6 +150,8 @@ public class RichEditor extends WebView {
ExtensionsKt.fixUiModeIfNeeded(this);
addJavascriptInterface(new NativeCallBack(), "NativeCallBack");
addJavascriptInterface(new LinkClickNativeCallback(), "OnLinkClickListener");
addJavascriptInterface(new ImageNativeCallback(), "imagelistener");
setVerticalScrollBarEnabled(false);
setHorizontalScrollBarEnabled(false);
@ -195,6 +201,14 @@ public class RichEditor extends WebView {
mInitialLayoutCallback = layoutCallback;
}
public void setOnLinkClickListener(OnLinkClickListener onLinkClickListener) {
this.mOnLinkClickListener = onLinkClickListener;
}
public void setImageListener(ImageListener imageListener) {
this.mImageListener = imageListener;
}
private void callback(String text) {
mContents = text.replaceFirst(CALLBACK_SCHEME, "");
if (mTextChangeListener != null) {
@ -864,6 +878,96 @@ public class RichEditor extends WebView {
}
}
class LinkClickNativeCallback {
/**
* 链接点击回调
* @param content 回调内容
*/
@JavascriptInterface
public void onClick(String content) {
if (mOnLinkClickListener != null) {
mOnLinkClickListener.onClick(content);
}
}
/**
* 视频点击回调
* @param content 回调内容
*/
@JavascriptInterface
public void onVideoClick(String content) {
if (mOnLinkClickListener != null) {
mOnLinkClickListener.onVideoClick(content);
}
}
/**
* 视频点击回调
* @param url 视频链接
* @param poster 视频封面
*/
@JavascriptInterface
public void onVideoClick(String url, String poster) {
if (mOnLinkClickListener != null) {
mOnLinkClickListener.onVideoClick(url, poster);
}
}
/**
* 视频点击回调
* @param url 视频链接
* @param poster 视频封面
* @param position 视频下标位置
*/
@JavascriptInterface
public void onVideoClick(String url, String poster, long position) {
if (mOnLinkClickListener != null) {
mOnLinkClickListener.onVideoClick(url, poster, position);
}
}
}
class ImageNativeCallback {
/**
* 图片点击回调
* @param url 图片链接
*/
@JavascriptInterface
public void imageClick(String url) {
if (mImageListener != null) {
mImageListener.onImageClick(url);
}
}
/**
* 图片加载回调
* @param url 图片链接
*/
@JavascriptInterface
public void imageArr(String url) {
if (mImageListener != null) {
mImageListener.onImageLoad(url);
}
}
}
public interface OnLinkClickListener {
void onClick(String content);
void onVideoClick(String content);
void onVideoClick(String url, String poster);
void onVideoClick(String url, String poster, long position);
}
public interface ImageListener {
void onImageLoad(String url);
void onImageClick(String url);
}
@Override
public void invalidate() {
super.invalidate();

View File

@ -32,7 +32,7 @@ import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity
import com.gh.gamecenter.fragment.MainWrapperFragment
import com.gh.gamecenter.qa.article.detail.ArticleWebCacheManager
import com.gh.gamecenter.qa.article.detail.ArticleDetailWebCacheManager
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity
import com.gh.gamecenter.qa.video.publish.VideoPublishActivity
@ -62,7 +62,7 @@ class CommunityHomeFragment : LazyFragment() {
override fun onFragmentFirstVisible() {
ArticleWebCacheManager.init(requireContext())
ArticleDetailWebCacheManager.init(requireContext().applicationContext)
mViewModel = viewModelProvider()

View File

@ -255,8 +255,8 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
mRichEditor = viewHolder.binding.newsdetailItemWvContent;
if (viewHolder.binding.newsdetailItemWvContent.getTag() == null) {
viewHolder.binding.newsdetailItemWvContent.addJavascriptInterface(new OnLinkClickListener(mContext, mEntrance, "", mNewsDetailEntity.getTitle(), "新闻详情"), "OnLinkClickListener");
viewHolder.binding.newsdetailItemWvContent.addJavascriptInterface(new JsInterface(viewHolder.binding.newsdetailItemWvContent), "imagelistener");
viewHolder.binding.newsdetailItemWvContent.setOnLinkClickListener(new OnLinkClickListener(mContext, mEntrance, "", mNewsDetailEntity.getTitle(), "新闻详情"));
viewHolder.binding.newsdetailItemWvContent.setImageListener(new ImageListener(viewHolder.binding.newsdetailItemWvContent));
mWebSettings = viewHolder.binding.newsdetailItemWvContent.getSettings();
mWebSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
@ -776,15 +776,15 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
}
}
public class JsInterface {
public class ImageListener implements RichEditor.ImageListener {
private RichEditor editor;
public JsInterface(RichEditor editor) {
public ImageListener(RichEditor editor) {
this.editor = editor;
}
@JavascriptInterface
public void imageClick(String url) {
@Override
public void onImageClick(String url) {
if (url.contains("web_load_dfimg_icon.png")) {
editor.post(() -> editor.replaceAllDfImageExcludeGif());
} else {
@ -801,8 +801,8 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
}
}
@JavascriptInterface
public void imageArr(String url) {
@Override
public void onImageLoad(String url) {
String defUrl = url.split("\\?")[0];
//web_load_dfimg_icon.png 查看大图按钮
if (!mNewsImgs.contains(defUrl) && !url.contains("web_load_dfimg_icon.png")) {

View File

@ -124,10 +124,9 @@ open class AnswerDetailFragment : ToolbarFragment() {
@SuppressLint("AddJavascriptInterface")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mBinding.richEditor.addJavascriptInterface(JsInterface(), "imagelistener")
mBinding.richEditor.addJavascriptInterface(
OnLinkClickListener(requireContext(), "", "", mEntrance, "回答详情"),
"OnLinkClickListener"
mBinding.richEditor.setImageListener(ImageListener())
mBinding.richEditor.setOnLinkClickListener(
OnLinkClickListener(requireContext(), "", "", mEntrance, "回答详情")
)
mSkeletonScreen =
Skeleton.bind(mBinding.skeletonMask).load(R.layout.fragment_answer_detail_skeleton).shimmer(false).show()
@ -1256,9 +1255,9 @@ open class AnswerDetailFragment : ToolbarFragment() {
dialog.setContentView(viewDialog)
}
inner class JsInterface {
@JavascriptInterface
fun imageClick(url: String) {
inner class ImageListener : RichEditor.ImageListener {
override fun onImageClick(url: String) {
when {
url.contains("web_load_dfimg_icon.png") -> mBaseHandler.post { mBinding.richEditor.replaceAllDfImageExcludeGif() }
//url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> mBaseHandler.post { mBinding.richEditor.replaceDfImageByUrl(url) }
@ -1281,8 +1280,8 @@ open class AnswerDetailFragment : ToolbarFragment() {
}
}
@JavascriptInterface
fun imageArr(url: String) {
override fun onImageLoad(url: String) {
val defUrl = url.split("\\?".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
if (!mAnswersImages!!.contains(defUrl) && !url.contains("web_load_dfimg_icon.png")) {
mAnswersImages.add(defUrl)

View File

@ -8,14 +8,12 @@ import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager.LayoutParams
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.UnAvaliableWebviewViewHolder
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.databinding.TabItemBinding
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.utils.visibleIf
import com.gh.gamecenter.core.utils.MtaHelper

View File

@ -7,8 +7,6 @@ import android.text.SpannableStringBuilder
import android.text.Spanned
import android.view.Gravity
import android.view.View
import android.webkit.JavascriptInterface
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.widget.ImageView
import android.widget.LinearLayout
@ -42,6 +40,26 @@ class ArticleDetailContentViewHolder(
var viewModel: ArticleDetailViewModel
) : RecyclerView.ViewHolder(binding.root) {
val richEditor = ArticleDetailWebCacheManager
.attachToRichEditor(binding.richEditorContainer)
.apply {
setInputEnabled(false)
setPadding(16, 4, 16, 4)
enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
setTransparentBackground()
setLayoutCallback { viewModel.articleRenderedLiveData.postValue(true) }
setPageFinishedListener {
viewModel.articlePageFinishedLiveData.postValue(true)
}
setChromeClientListener(object : RichEditor.WebChromeClientListener {
override fun onPageFinished(view: WebView?, url: String?) {}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "帖子详情")
}
})
}
private var mEntrance = ""
val articleImgUrlList = ArrayList<String>()
@ -59,41 +77,6 @@ class ArticleDetailContentViewHolder(
titleTv.setTextColor(R.color.text_title.toColor(binding.root.context))
gameName.setTextColor(R.color.text_subtitleDesc.toColor(binding.root.context))
richEditor.enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
richEditor.setTransparentBackground()
richEditor.setInputEnabled(false)
richEditor.setPadding(16, 4, 16, 4)
richEditor.addJavascriptInterface(JsInterface(article.status ?: ""), "imagelistener")
richEditor.addJavascriptInterface(
OnLinkClickListener(
root.context,
article.title,
article.status ?: "",
mEntrance,
"帖子详情"
), "OnLinkClickListener"
)
richEditor.setLayoutCallback(object : EmptyCallback {
override fun onCallback() {
viewModel.articleRenderedLiveData.postValue(true)
}
})
richEditor.setPageFinishedListener {
viewModel.articlePageFinishedLiveData.postValue(true)
}
richEditor.setChromeClientListener(object : RichEditor.WebChromeClientListener {
override fun onPageFinished(view: WebView?, url: String?) {
}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "帖子详情")
}
})
richEditor.setWebResourceRequestInterceptor { view, url ->
ArticleWebCacheManager.shouldInterceptRequest(view, url)
}
approvalStatusTv.goneIf(article.status == "pass")
statusContainer.goneIf(article.status == "pass")
when (article.status) {
@ -186,6 +169,16 @@ class ArticleDetailContentViewHolder(
userNameTv.text = article.user.name
userIconIv.display(article.user.border, article.user.icon, article.user.auth?.icon)
richEditor.setContentOwner(article.me.isContentOwner)
richEditor.setImageListener(ImageListener(article.status ?: ""))
richEditor.setOnLinkClickListener(
OnLinkClickListener(
root.context,
article.title,
article.status ?: "",
mEntrance,
"帖子详情"
)
)
// 避免列表频繁刷新
if (richEditor.currentContent != article.content) {
richEditor.setHtml(article.content, true)
@ -325,7 +318,7 @@ class ArticleDetailContentViewHolder(
* 回调列表视频播放结束时的时间
*/
fun onVideoPlayedCallback(url: String, position: Int) {
binding.richEditor.onVideoPlayedCallback(url, position)
richEditor.onVideoPlayedCallback(url, position)
}
fun updateFollowBtn(isFollowed: Boolean) {
@ -344,12 +337,12 @@ class ArticleDetailContentViewHolder(
}
inner class JsInterface(val status: String) {
@JavascriptInterface
fun imageClick(url: String) {
inner class ImageListener(val status: String) : RichEditor.ImageListener {
override fun onImageClick(url: String) {
when {
url.contains("web_load_dfimg_icon.png") -> {
runOnUiThread { binding.richEditor.replaceAllDfImageExcludeGif() }
runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
}
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
@ -378,12 +371,21 @@ class ArticleDetailContentViewHolder(
}
}
@JavascriptInterface
fun imageArr(url: String) {
override fun onImageLoad(url: String) {
val defUrl = url.split("\\?".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
if (!articleImgUrlList.contains(defUrl) && !url.contains("web_load_dfimg_icon.png")) {
articleImgUrlList.add(defUrl)
}
}
}
fun destroy() {
richEditor.setLayoutCallback(null)
richEditor.setImageListener(null)
richEditor.setOnLinkClickListener(null)
richEditor.setChromeClientListener(null)
richEditor.setContentOwner(false)
richEditor.setPageFinishedListener(null)
ArticleDetailWebCacheManager.detachFromRichEditor(binding.richEditorContainer)
}
}

View File

@ -26,7 +26,6 @@ import com.gh.gamecenter.common.baselist.LoadType
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.AdditionalParamsEntity
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.entity.NormalShareEntity
import com.gh.gamecenter.common.eventbus.EBReuse
@ -41,6 +40,7 @@ import com.gh.gamecenter.eventbus.EBDeleteCommentDetail
import com.gh.gamecenter.eventbus.EBDeleteDetail
import com.gh.gamecenter.eventbus.EBTopCommunityChanged
import com.gh.gamecenter.feature.entity.ArticleDraftEntity
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.feature.entity.Permissions
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
@ -119,11 +119,11 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
mAdapter?.articleDetailVH?.run {
if (articleImgUrlList.size > 0) {
if (imageSet.size == articleImgUrlList.size) {
binding.richEditor.replaceAllDfImage()
richEditor.replaceAllDfImage()
} else {
for (i in imageSet) {
val url = articleImgUrlList[i.toInt()]
binding.richEditor.replaceDfImageByUrl(url)
richEditor.replaceDfImageByUrl(url)
}
}
}
@ -165,6 +165,9 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
override fun onDestroyView() {
super.onDestroyView()
mAdapter?.articleDetailVH?.destroy()
if (mViewModel.detailEntity != null) {
HistoryHelper.insertArticleEntity(mViewModel.detailEntity!!)

View File

@ -2,8 +2,11 @@ package com.gh.gamecenter.qa.article.detail
import android.annotation.SuppressLint
import android.content.Context
import android.content.MutableContextWrapper
import android.view.ViewGroup
import android.webkit.WebResourceResponse
import android.webkit.WebView
import com.gh.common.view.RichEditor
import com.gh.gamecenter.common.utils.EnvHelper
import com.gh.gamecenter.core.utils.MD5Utils
import io.reactivex.Single
@ -25,23 +28,26 @@ import java.util.concurrent.TimeUnit
/**
* 社区文章详情-Web资源缓存管理类
*/
object ArticleWebCacheManager {
@SuppressLint("StaticFieldLeak")
object ArticleDetailWebCacheManager {
private lateinit var cacheDir: File
private lateinit var cacheRichEditor: RichEditor
private var isInit = false
fun init(context: Context) {
init(File(context.cacheDir, "article/web"))
}
@SuppressLint("CheckResult")
fun init(cacheDir: File) {
fun init(applicationContext: Context) {
if (isInit) return
isInit = true
this.cacheDir = cacheDir.apply {
this.cacheRichEditor = RichEditor(
MutableContextWrapper(applicationContext)
)
this.cacheDir = File(applicationContext.cacheDir, "article/web").apply {
if (!exists()) mkdirs()
}
@ -92,10 +98,29 @@ object ArticleWebCacheManager {
.subscribe({}, {})
}
fun attachToRichEditor(parent: ViewGroup): RichEditor {
val richEditor = this.cacheRichEditor.apply {
(this.context as MutableContextWrapper).baseContext = parent.context
setWebResourceRequestInterceptor { view, url -> shouldInterceptRequest(view, url) }
}
parent.addView(
richEditor,
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
)
return cacheRichEditor
}
fun detachFromRichEditor(parent: ViewGroup) {
this.cacheRichEditor.setHtml("", true)
this.cacheRichEditor.setWebResourceRequestInterceptor(null)
(this.cacheRichEditor.context as MutableContextWrapper).baseContext = parent.context.applicationContext
parent.removeView(this.cacheRichEditor)
}
/**
* 拦截WebView的资源请求并返回URL对应的缓存数据
*/
fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
private fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
val urlWithoutQueryParams = url.split("?")[0]
val cacheFile = File(cacheDir, MD5Utils.getUrlMD5(urlWithoutQueryParams))

View File

@ -5,6 +5,7 @@ import android.webkit.JavascriptInterface
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.core.AppExecutor
import com.gh.common.util.*
import com.gh.common.view.RichEditor
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.core.utils.GsonUtils
import com.gh.gamecenter.common.utils.clickToastByStatus
@ -25,9 +26,9 @@ class OnLinkClickListener(
val status: String = "",
val entrance: String,
val path: String,
) {
@JavascriptInterface
fun onClick(content: String) {
) : RichEditor.OnLinkClickListener {
override fun onClick(content: String) {
AppExecutor.uiExecutor.execute {
tryWithDefaultCatch {
val insertEntity = GsonUtils.fromJson(content, EditorInsertEntity::class.java)
@ -70,8 +71,7 @@ class OnLinkClickListener(
}
}
@JavascriptInterface
fun onVideoClick(content: String) {
override fun onVideoClick(content: String) {
AppExecutor.uiExecutor.execute {
tryWithDefaultCatch {
val videoEntity = GsonUtils.fromJson(content, MyVideoEntity::class.java)
@ -82,15 +82,13 @@ class OnLinkClickListener(
}
}
@JavascriptInterface
fun onVideoClick(url: String, poster: String) {
override fun onVideoClick(url: String, poster: String) {
clickToastByStatus(status) {
FullScreenVideoActivity.start(context, title, url, poster)
}
}
@JavascriptInterface
fun onVideoClick(url: String, poster: String, position: Long) {
override fun onVideoClick(url: String, poster: String, position: Long) {
clickToastByStatus(status) {
FullScreenVideoActivity.start(context, title, url, poster, position)
}

View File

@ -108,11 +108,11 @@ class NewQuestionDetailFragment :
mAdapter?.questionDetailVH?.run {
if (questionImgUrlList.size > 0) {
if (imageSet.size == questionImgUrlList.size) {
binding.richEditor.replaceAllDfImage()
richEditor.replaceAllDfImage()
} else {
for (i in imageSet) {
val url = questionImgUrlList[i.toInt()]
binding.richEditor.replaceDfImageByUrl(url)
richEditor.replaceDfImageByUrl(url)
}
}
}
@ -742,6 +742,11 @@ class NewQuestionDetailFragment :
mBinding.root.setBackgroundColor(Color.TRANSPARENT)
}
override fun onDestroyView() {
super.onDestroyView()
mAdapter?.questionDetailVH?.destroy()
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
mAdapter?.let { it.notifyItemRangeChanged(0, it.itemCount) }

View File

@ -5,8 +5,6 @@ import android.app.Activity
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.view.View
import android.webkit.JavascriptInterface
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
@ -27,7 +25,7 @@ import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.databinding.ItemArticleDetailContentBinding
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.qa.article.detail.ArticleWebCacheManager
import com.gh.gamecenter.qa.article.detail.ArticleDetailWebCacheManager
import com.gh.gamecenter.qa.article.detail.EditHistoryDialog
import com.gh.gamecenter.qa.editor.OnLinkClickListener
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
@ -38,43 +36,30 @@ class QuestionDetailContentViewHolder(
var viewModel: NewQuestionDetailViewModel
) : RecyclerView.ViewHolder(binding.root) {
val richEditor = ArticleDetailWebCacheManager
.attachToRichEditor(binding.richEditorContainer)
.apply {
setInputEnabled(false)
setPadding(16, 4, 16, 4)
enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
setTransparentBackground()
setLayoutCallback { viewModel.questionRenderedLiveData.postValue(true) }
setPageFinishedListener { viewModel.questionPageFinishedLiveData.postValue(true) }
setChromeClientListener(object : RichEditor.WebChromeClientListener {
override fun onPageFinished(view: WebView?, url: String?) {}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "问题详情")
}
})
}
private var mEntrance = ""
val questionImgUrlList = ArrayList<String>()
@SuppressLint("AddJavascriptInterface")
fun bindView(question: QuestionsDetailEntity) {
binding.run {
richEditor.setInputEnabled(false)
richEditor.enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
richEditor.setTransparentBackground()
richEditor.setPadding(16, 4, 16, 4)
richEditor.addJavascriptInterface(JsInterface(question.status), "imagelistener")
richEditor.addJavascriptInterface(
OnLinkClickListener(
root.context, question.title
?: "", question.status, mEntrance, "问题详情"
), "OnLinkClickListener"
)
richEditor.setLayoutCallback(object : EmptyCallback {
override fun onCallback() {
viewModel.questionRenderedLiveData.postValue(true)
}
})
richEditor.setPageFinishedListener {
viewModel.questionPageFinishedLiveData.postValue(true)
}
richEditor.setChromeClientListener(object : RichEditor.WebChromeClientListener {
override fun onPageFinished(view: WebView?, url: String?) {
}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "问题详情")
}
})
richEditor.setWebResourceRequestInterceptor { view, url ->
ArticleWebCacheManager.shouldInterceptRequest(view, url)
}
approvalStatusTv.goneIf(question.status == "pass")
statusContainer.goneIf(question.status == "pass")
when (question.status) {
@ -156,6 +141,16 @@ class QuestionDetailContentViewHolder(
userNameTv.text = question.user.name
userIconIv.display(question.user.border, question.user.icon, question.user.auth?.icon)
richEditor.setContentOwner(question.me.isContentOwner)
richEditor.setImageListener(ImageListener(question.status))
richEditor.setOnLinkClickListener(
OnLinkClickListener(
root.context,
question.title ?: "",
question.status,
mEntrance,
"问题详情"
)
)
// 避免列表频繁刷新
if (richEditor.currentContent != question.description) {
richEditor.setHtml(question.description, true)
@ -280,15 +275,15 @@ class QuestionDetailContentViewHolder(
* 回调列表视频播放结束时的时间
*/
fun onVideoPlayedCallback(url: String, position: Int) {
binding.richEditor.onVideoPlayedCallback(url, position)
richEditor.onVideoPlayedCallback(url, position)
}
inner class JsInterface(val status: String) {
@JavascriptInterface
fun imageClick(url: String) {
inner class ImageListener(val status: String) : RichEditor.ImageListener {
override fun onImageClick(url: String) {
when {
url.contains("web_load_dfimg_icon.png") -> {
runOnUiThread { binding.richEditor.replaceAllDfImageExcludeGif() }
runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
}
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
@ -317,12 +312,21 @@ class QuestionDetailContentViewHolder(
}
}
@JavascriptInterface
fun imageArr(url: String) {
override fun onImageLoad(url: String) {
val defUrl = url.split("\\?".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
if (!questionImgUrlList.contains(defUrl) && !url.contains("web_load_dfimg_icon.png")) {
questionImgUrlList.add(defUrl)
}
}
}
fun destroy() {
richEditor.setLayoutCallback(null)
richEditor.setImageListener(null)
richEditor.setOnLinkClickListener(null)
richEditor.setChromeClientListener(null)
richEditor.setContentOwner(false)
richEditor.setPageFinishedListener(null)
ArticleDetailWebCacheManager.detachFromRichEditor(binding.richEditorContainer)
}
}