Compare commits

...

2 Commits

Author SHA1 Message Date
49a610deee feat: 动态java方法注册 2023-10-11 19:41:28 +08:00
61f43d53b2 feat: 文章详情WebView预加载 2023-10-11 15:52:35 +08:00
10 changed files with 182 additions and 73 deletions

View File

@ -352,9 +352,9 @@ RE.ImageClickListener = function() {
var img = imgs[i]; var img = imgs[i];
var imageClassName = img.className; var imageClassName = img.className;
if (imageClassName == "image-link"|| img.className == "poster") continue; if (imageClassName == "image-link"|| img.className == "poster") continue;
window.imagelistener.imageArr(img.src); window.NativeCallBack.invokeMethod("imageArr", img.src);
img.onclick = function() { img.onclick = function() {
window.imagelistener.imageClick(this.src); window.NativeCallBack.invokeMethod("imageClick", this.src);
} }
} }
} }

View File

@ -43,8 +43,10 @@ import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
/** /**
* Copyright (C) 2017 Wasabeef * Copyright (C) 2017 Wasabeef
@ -77,6 +79,8 @@ public class RichEditor extends WebView {
private EmptyCallback mInitialLayoutCallback; private EmptyCallback mInitialLayoutCallback;
private Map<String, DynamicJsInterface> mDynamicJsInterfaces = new HashMap<>();
private String mCurrentContent = ""; private String mCurrentContent = "";
public enum Type { public enum Type {
@ -605,6 +609,14 @@ public class RichEditor extends WebView {
exec("javascript:RE.formatBlock();"); exec("javascript:RE.formatBlock();");
} }
public void registerDynamicJsInterface(String method, DynamicJsInterface jsInterface) {
mDynamicJsInterfaces.put(method, jsInterface);
}
public void unregisterDynamicJsInterface(String method) {
mDynamicJsInterfaces.remove(method);
}
/** /**
* 调用 JS 方法,告诉网页端该 url 对应视频播放的进度 * 调用 JS 方法,告诉网页端该 url 对应视频播放的进度
*/ */
@ -862,6 +874,14 @@ public class RichEditor extends WebView {
public void logMtaEvent(String event) { public void logMtaEvent(String event) {
// do nothing, mta is deprecated // do nothing, mta is deprecated
} }
@JavascriptInterface
public void invokeMethod(String method, String data) {
DynamicJsInterface jsInterface = mDynamicJsInterfaces.get(method);
if (jsInterface != null) {
jsInterface.invoke(data);
}
}
} }
@Override @Override
@ -874,4 +894,8 @@ public class RichEditor extends WebView {
mInitialLayoutCallback = null; mInitialLayoutCallback = null;
} }
} }
public interface DynamicJsInterface {
void invoke(String data);
}
} }

View File

@ -62,7 +62,7 @@ class CommunityHomeFragment : LazyFragment() {
override fun onFragmentFirstVisible() { override fun onFragmentFirstVisible() {
ArticleWebCacheManager.init(requireContext()) ArticleWebCacheManager.init(requireContext().applicationContext)
mViewModel = viewModelProvider() mViewModel = viewModelProvider()

View File

@ -8,14 +8,12 @@ import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager.LayoutParams
import androidx.viewpager.widget.ViewPager.OnPageChangeListener import androidx.viewpager.widget.ViewPager.OnPageChangeListener
import com.gh.common.util.PackageUtils import com.gh.common.util.PackageUtils
import com.gh.gamecenter.R import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.UnAvaliableWebviewViewHolder import com.gh.gamecenter.adapter.viewholder.UnAvaliableWebviewViewHolder
import com.gh.gamecenter.common.baselist.LoadStatus import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.databinding.TabItemBinding 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.toBinding
import com.gh.gamecenter.common.utils.visibleIf import com.gh.gamecenter.common.utils.visibleIf
import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.core.utils.MtaHelper
@ -53,6 +51,7 @@ class ArticleDetailAdapter(
ITEM_ARTICLE_DETAIL -> { ITEM_ARTICLE_DETAIL -> {
val isWebViewInstalled = PackageUtils.checkWebViewIsAvailable(mContext) val isWebViewInstalled = PackageUtils.checkWebViewIsAvailable(mContext)
if (isWebViewInstalled) { if (isWebViewInstalled) {
ArticleDetailContentViewHolder.wvLoadTimeInMills = System.currentTimeMillis()
val binding: ItemArticleDetailContentBinding = val binding: ItemArticleDetailContentBinding =
ItemArticleDetailContentBinding.inflate(mLayoutInflater, parent, false) ItemArticleDetailContentBinding.inflate(mLayoutInflater, parent, false)
ArticleDetailContentViewHolder(binding, mViewModel).apply { articleDetailVH = this } ArticleDetailContentViewHolder(binding, mViewModel).apply { articleDetailVH = this }

View File

@ -5,10 +5,10 @@ import android.app.Activity
import android.graphics.Bitmap import android.graphics.Bitmap
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.Spanned import android.text.Spanned
import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.webkit.JavascriptInterface import android.webkit.JavascriptInterface
import android.webkit.WebResourceResponse
import android.webkit.WebView import android.webkit.WebView
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
@ -42,6 +42,33 @@ class ArticleDetailContentViewHolder(
var viewModel: ArticleDetailViewModel var viewModel: ArticleDetailViewModel
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
companion object {
var wvLoadTimeInMills = 0L
}
val richEditor = ArticleWebCacheManager
.attachToRichEditor(binding.richEditorContainer)
.apply {
setInputEnabled(false)
setPadding(16, 4, 16, 4)
enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
setTransparentBackground()
setLayoutCallback { viewModel.articleRenderedLiveData.postValue(true) }
setPageFinishedListener {
Log.d("ArticleDetail", "pageFinished->${System.currentTimeMillis() - wvLoadTimeInMills}")
viewModel.articlePageFinishedLiveData.postValue(true)
}
setChromeClientListener(object : RichEditor.WebChromeClientListener {
override fun onPageFinished(view: WebView?, url: String?) {
Log.d("ArticleDetail", "onPageFinished: $url->${System.currentTimeMillis() - wvLoadTimeInMills}")
}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "帖子详情")
}
})
}
private var mEntrance = "" private var mEntrance = ""
val articleImgUrlList = ArrayList<String>() val articleImgUrlList = ArrayList<String>()
@ -59,11 +86,41 @@ class ArticleDetailContentViewHolder(
titleTv.setTextColor(R.color.text_title.toColor(binding.root.context)) titleTv.setTextColor(R.color.text_title.toColor(binding.root.context))
gameName.setTextColor(R.color.text_subtitleDesc.toColor(binding.root.context)) gameName.setTextColor(R.color.text_subtitleDesc.toColor(binding.root.context))
richEditor.enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context)) richEditor.registerDynamicJsInterface("imageArr") { url ->
richEditor.setTransparentBackground() val defUrl = url.split("\\?".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
richEditor.setInputEnabled(false) if (!articleImgUrlList.contains(defUrl) && !url.contains("web_load_dfimg_icon.png")) {
richEditor.setPadding(16, 4, 16, 4) articleImgUrlList.add(defUrl)
richEditor.addJavascriptInterface(JsInterface(article.status ?: ""), "imagelistener") }
}
richEditor.registerDynamicJsInterface("imageClick") { url ->
when {
url.contains("web_load_dfimg_icon.png") -> {
runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
}
else -> {
val status = article.status ?: ""
clickToastByStatus(status) {
var current = 0
var i = 0
val size = articleImgUrlList.size
while (i < size) {
if (url.contains(articleImgUrlList.get(i))) {
current = i
}
i++
}
val intent = ImageViewerActivity.getIntent(
binding.root.context, articleImgUrlList, current,
mEntrance + "+(帖子详情[" + binding.titleTv.text.toString() + "])"
)
(binding.root.context as Activity).startActivityForResult(
intent,
ImageViewerActivity.REQUEST_FOR_VIEWED_IMAGE
)
}
}
}
}
richEditor.addJavascriptInterface( richEditor.addJavascriptInterface(
OnLinkClickListener( OnLinkClickListener(
root.context, root.context,
@ -73,26 +130,6 @@ class ArticleDetailContentViewHolder(
"帖子详情" "帖子详情"
), "OnLinkClickListener" ), "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") approvalStatusTv.goneIf(article.status == "pass")
statusContainer.goneIf(article.status == "pass") statusContainer.goneIf(article.status == "pass")
@ -325,7 +362,7 @@ class ArticleDetailContentViewHolder(
* 回调列表视频播放结束时的时间 * 回调列表视频播放结束时的时间
*/ */
fun onVideoPlayedCallback(url: String, position: Int) { fun onVideoPlayedCallback(url: String, position: Int) {
binding.richEditor.onVideoPlayedCallback(url, position) richEditor.onVideoPlayedCallback(url, position)
} }
fun updateFollowBtn(isFollowed: Boolean) { fun updateFollowBtn(isFollowed: Boolean) {
@ -349,7 +386,7 @@ class ArticleDetailContentViewHolder(
fun imageClick(url: String) { fun imageClick(url: String) {
when { when {
url.contains("web_load_dfimg_icon.png") -> { url.contains("web_load_dfimg_icon.png") -> {
runOnUiThread { binding.richEditor.replaceAllDfImageExcludeGif() } runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
} }
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> { // url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) } // runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
@ -386,4 +423,14 @@ class ArticleDetailContentViewHolder(
} }
} }
} }
fun destroy() {
richEditor.setLayoutCallback(null)
richEditor.removeJavascriptInterface("imagelistener")
richEditor.removeJavascriptInterface("OnLinkClickListener")
richEditor.setChromeClientListener(null)
richEditor.setContentOwner(false)
richEditor.setPageFinishedListener(null)
ArticleWebCacheManager.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.Constants
import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.AdditionalParamsEntity 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.CommunityEntity
import com.gh.gamecenter.common.entity.NormalShareEntity import com.gh.gamecenter.common.entity.NormalShareEntity
import com.gh.gamecenter.common.eventbus.EBReuse 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.EBDeleteDetail
import com.gh.gamecenter.eventbus.EBTopCommunityChanged import com.gh.gamecenter.eventbus.EBTopCommunityChanged
import com.gh.gamecenter.feature.entity.ArticleDraftEntity 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.feature.entity.Permissions
import com.gh.gamecenter.login.user.UserManager import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
@ -119,11 +119,11 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
mAdapter?.articleDetailVH?.run { mAdapter?.articleDetailVH?.run {
if (articleImgUrlList.size > 0) { if (articleImgUrlList.size > 0) {
if (imageSet.size == articleImgUrlList.size) { if (imageSet.size == articleImgUrlList.size) {
binding.richEditor.replaceAllDfImage() richEditor.replaceAllDfImage()
} else { } else {
for (i in imageSet) { for (i in imageSet) {
val url = articleImgUrlList[i.toInt()] val url = articleImgUrlList[i.toInt()]
binding.richEditor.replaceDfImageByUrl(url) richEditor.replaceDfImageByUrl(url)
} }
} }
} }
@ -165,6 +165,9 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
mAdapter?.articleDetailVH?.destroy()
if (mViewModel.detailEntity != null) { if (mViewModel.detailEntity != null) {
HistoryHelper.insertArticleEntity(mViewModel.detailEntity!!) HistoryHelper.insertArticleEntity(mViewModel.detailEntity!!)

View File

@ -2,8 +2,11 @@ package com.gh.gamecenter.qa.article.detail
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.MutableContextWrapper
import android.view.ViewGroup
import android.webkit.WebResourceResponse import android.webkit.WebResourceResponse
import android.webkit.WebView import android.webkit.WebView
import com.gh.common.view.RichEditor
import com.gh.gamecenter.common.utils.EnvHelper import com.gh.gamecenter.common.utils.EnvHelper
import com.gh.gamecenter.core.utils.MD5Utils import com.gh.gamecenter.core.utils.MD5Utils
import io.reactivex.Single import io.reactivex.Single
@ -18,6 +21,7 @@ import retrofit2.http.GET
import retrofit2.http.Query import retrofit2.http.Query
import retrofit2.http.Streaming import retrofit2.http.Streaming
import retrofit2.http.Url import retrofit2.http.Url
import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -25,23 +29,26 @@ import java.util.concurrent.TimeUnit
/** /**
* 社区文章详情-Web资源缓存管理类 * 社区文章详情-Web资源缓存管理类
*/ */
@SuppressLint("StaticFieldLeak")
object ArticleWebCacheManager { object ArticleWebCacheManager {
private lateinit var cacheDir: File private lateinit var cacheDir: File
private lateinit var cacheRichEditor: RichEditor
private var isInit = false private var isInit = false
fun init(context: Context) {
init(File(context.cacheDir, "article/web"))
}
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
fun init(cacheDir: File) { fun init(applicationContext: Context) {
if (isInit) return if (isInit) return
isInit = true isInit = true
this.cacheDir = cacheDir.apply { this.cacheRichEditor = RichEditor(
MutableContextWrapper(applicationContext)
)
this.cacheDir = File(applicationContext.cacheDir, "article/web").apply {
if (!exists()) mkdirs() if (!exists()) mkdirs()
} }
@ -92,6 +99,25 @@ object ArticleWebCacheManager {
.subscribe({}, {}) .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("", false)
this.cacheRichEditor.setWebResourceRequestInterceptor(null)
(this.cacheRichEditor.context as MutableContextWrapper).baseContext = parent.context.applicationContext
parent.removeView(this.cacheRichEditor)
}
/** /**
* 拦截WebView的资源请求并返回URL对应的缓存数据 * 拦截WebView的资源请求并返回URL对应的缓存数据
*/ */

View File

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

View File

@ -6,7 +6,6 @@ import android.text.SpannableStringBuilder
import android.text.Spanned import android.text.Spanned
import android.view.View import android.view.View
import android.webkit.JavascriptInterface import android.webkit.JavascriptInterface
import android.webkit.WebResourceResponse
import android.webkit.WebView import android.webkit.WebView
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -38,43 +37,39 @@ class QuestionDetailContentViewHolder(
var viewModel: NewQuestionDetailViewModel var viewModel: NewQuestionDetailViewModel
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
val richEditor = ArticleWebCacheManager
.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 = "" private var mEntrance = ""
val questionImgUrlList = ArrayList<String>() val questionImgUrlList = ArrayList<String>()
@SuppressLint("AddJavascriptInterface") @SuppressLint("AddJavascriptInterface")
fun bindView(question: QuestionsDetailEntity) { fun bindView(question: QuestionsDetailEntity) {
binding.run { binding.run {
richEditor.setInputEnabled(false) richEditor.removeJavascriptInterface("imagelistener")
richEditor.enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
richEditor.setTransparentBackground()
richEditor.setPadding(16, 4, 16, 4)
richEditor.addJavascriptInterface(JsInterface(question.status), "imagelistener") richEditor.addJavascriptInterface(JsInterface(question.status), "imagelistener")
richEditor.removeJavascriptInterface("OnLinkClickListener")
richEditor.addJavascriptInterface( richEditor.addJavascriptInterface(
OnLinkClickListener( OnLinkClickListener(
root.context, question.title root.context, question.title
?: "", question.status, mEntrance, "问题详情" ?: "", question.status, mEntrance, "问题详情"
), "OnLinkClickListener" ), "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") approvalStatusTv.goneIf(question.status == "pass")
statusContainer.goneIf(question.status == "pass") statusContainer.goneIf(question.status == "pass")
when (question.status) { when (question.status) {
@ -280,7 +275,7 @@ class QuestionDetailContentViewHolder(
* 回调列表视频播放结束时的时间 * 回调列表视频播放结束时的时间
*/ */
fun onVideoPlayedCallback(url: String, position: Int) { fun onVideoPlayedCallback(url: String, position: Int) {
binding.richEditor.onVideoPlayedCallback(url, position) richEditor.onVideoPlayedCallback(url, position)
} }
inner class JsInterface(val status: String) { inner class JsInterface(val status: String) {
@ -288,7 +283,7 @@ class QuestionDetailContentViewHolder(
fun imageClick(url: String) { fun imageClick(url: String) {
when { when {
url.contains("web_load_dfimg_icon.png") -> { url.contains("web_load_dfimg_icon.png") -> {
runOnUiThread { binding.richEditor.replaceAllDfImageExcludeGif() } runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
} }
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> { // url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) } // runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
@ -325,4 +320,14 @@ class QuestionDetailContentViewHolder(
} }
} }
} }
fun destroy() {
richEditor.setLayoutCallback(null)
richEditor.removeJavascriptInterface("imagelistener")
richEditor.removeJavascriptInterface("OnLinkClickListener")
richEditor.setChromeClientListener(null)
richEditor.setContentOwner(false)
richEditor.setPageFinishedListener(null)
ArticleWebCacheManager.detachFromRichEditor(binding.richEditorContainer)
}
} }

View File

@ -217,8 +217,8 @@
android:textStyle="bold" android:textStyle="bold"
tools:text="这是一个很长很长很长很长很长很长很长很长很长很长的标题" /> tools:text="这是一个很长很长很长很长很长很长很长很长很长很长的标题" />
<com.gh.common.view.RichEditor <FrameLayout
android:id="@+id/richEditor" android:id="@+id/richEditorContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="13dp" /> android:layout_marginBottom="13dp" />