前言
最近项目中有个需求是对后台返回的Html数据显示并且能够进行标记,点击标记还要能显示对应的笔记数据,那么这样的需求,对于同类型的小说来说是我们力所能及First想到的,但是你看遍所有的博客和Demo之后你会发现,他们使用的返回数据是完全不一样的,那么对于网页数据怎么来实现标记呢,首先选择获取文字就是一个问题,怎么获取到选中的文字,那么就只有重写WebView,对WebView的菜单栏进行编辑添加自己的菜单栏-添加笔记。
知识点
(1).WebView长按显示对应的添加笔记菜单==》GitHub跳转
(2).WebView网页数据标记显示
WebView自定义
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import java.util.ArrayList;
import java.util.List;
/**
* Created by guoshuyu on 2017/6/16.
*/
public class SectionActionWebView extends WebView {
static String TAG = "CustomActionWebView";
ActionMode mActionMode;
List<String> mActionList = new ArrayList<>();
OnActionSelectListener mActionSelectListener;
public SectionActionWebView(Context context) {
super(context);
}
public SectionActionWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SectionActionWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 处理item,处理点击
*
* @param actionMode
*/
private ActionMode resolveActionMode(ActionMode actionMode) {
if (actionMode != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
actionMode.setType(ActionMode.TYPE_FLOATING);
}
final Menu menu = actionMode.getMenu();
mActionMode = actionMode;
menu.clear();
for (int i = 0; i < mActionList.size(); i++) {
menu.add(mActionList.get(i));
}
for (int i = 0; i < menu.size(); i++) {
MenuItem menuItem = menu.getItem(i);
menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
getSelectedData((String) item.getTitle());
releaseAction();
return true;
}
});
}
}
mActionMode = actionMode;
return actionMode;
}
@Override
public ActionMode startActionMode(ActionMode.Callback callback) {
ActionMode actionMode = super.startActionMode(callback);
return resolveActionMode(actionMode);
}
@Override
public ActionMode startActionMode(ActionMode.Callback callback, int type) {
ActionMode actionMode = super.startActionMode(callback, type);
return resolveActionMode(actionMode);
}
private void releaseAction() {
if (mActionMode != null) {
mActionMode.finish();
mActionMode = null;
}
}
/**
* 点击的时候,获取网页中选择的文本,回掉到原生中的js接口
*
* @param title 传入点击的item文本,一起通过js返回给原生接口
*/
private void getSelectedData(String title) {
String js = "(function getSelectedText() {" +
"var txt;" +
"var title = \"" + title + "\";" +
"if (window.getSelection) {" +
"txt = window.getSelection().toString();" +
"} else if (window.document.getSelection) {" +
"txt = window.document.getSelection().toString();" +
"} else if (window.document.selection) {" +
"txt = window.document.selection.createRange().text;" +
"}" +
"JSInterface.callback(txt,title);" +
"})()";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
evaluateJavascript("javascript:" + js, null);
} else {
loadUrl("javascript:" + js);
}
}
public void linkJSInterface() {
addJavascriptInterface(new ActionSelectInterface(this), "JSInterface");
}
/**
* 设置弹出action列表
*
* @param actionList
*/
public void setActionList(List<String> actionList) {
mActionList = actionList;
}
/**
* 设置点击回掉
*
* @param actionSelectListener
*/
public void setActionSelectListener(OnActionSelectListener actionSelectListener) {
this.mActionSelectListener = actionSelectListener;
}
/**
* 隐藏消失Action
*/
public void dismissAction() {
releaseAction();
}
/**
* js选中的回掉接口
*/
private class ActionSelectInterface {
SectionActionWebView mContext;
ActionSelectInterface(SectionActionWebView c) {
mContext = c;
}
@JavascriptInterface
public void callback(final String value, final String title) {
if (mActionSelectListener != null) {
mActionSelectListener.onClick(title, value);
}
}
}
}
/**
* Created by guoshuyu on 2017/6/17.
*/
public interface OnActionSelectListener {
void onClick(String title, String selectText);
}
重点是对WebView进行标记,这个逻辑就需要我们来写了。当我们拿到文字之后需要做的操作是,对文字进行处理标记。其他很多人在看我博客之前已经尝试了很多方式或者说是百度了很多了方法了。此处我采用的是将服务器拉取的数据进行打散操作,然后将打散之后的数据进行重新拼接处理显示,利用如此方案来实现标记。
Html标签处理工具类
import android.text.TextUtils;
import com.android.utils.ListUtils;
import com.yunxiang.social.app.Constants;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by Relin
* on 2018-12-28.
* 笔记标注
*/
public class HtmlUtils {
/**
* 开始标签
*/
public static final String TAG_START = "start";
/**
* 标签内容
*/
public static final String TAG_CONTENT = "content";
public static final String IS_MARK = "IS_MARK";
public static final String MARK = "1";
public static final String UNMARK = "0";
/**
* 结束标签
*/
public static final String TAG_END = "end";
/**
* 删除网页所有标签
*
* @param content 网页内容
* @return
*/
public static String deleteTag(String content) {
content = deleteTag(content, "<[^>]+>");
content = deleteTag(content, "\\s*|\t|\n|\n");
return content;
}
/**
* 删除网页标签
*
* @param content 网页内容
* @param regex 正则
* @return
*/
public static String deleteTag(String content, String regex) {
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(content);
content = matcher.replaceAll("").trim();
return content;
}
/**
* 拆分所有标签
*
* @param content
* @return
*/
public static List<Map<String, String>> splitTag(String content) {
if (TextUtils.isEmpty(content)) {
return null;
}
int tagIndex = 0;
StringBuffer contentBuffer = new StringBuffer();
StringBuffer tagBuffer = new StringBuffer();
Map<String, String> tagMap = new HashMap<>();
List<Map<String, String>> tagList = new ArrayList<>();
char[] chars = content.toCharArray();
for (int i = 0; i < chars.length; i++) {
//获取前标签内容
if ((chars[i] + "").equals("<") && tagIndex == 0) {
tagBuffer = new StringBuffer("");
tagBuffer.append(chars[i]);
tagIndex = 1;
}
if (!(chars[i] + "").equals("<") && !(chars[i] + "").equals(">") && tagIndex == 1) {
tagBuffer.append(chars[i] + "");
}
if ((chars[i] + "").equals(">") && tagIndex == 1) {
tagBuffer.append(chars[i] + "");
tagMap = new HashMap<>();
tagMap.put(TAG_START, tagBuffer.toString());
tagMap.put(TAG_CONTENT, "");
tagIndex = 2;
}
//获取内容
if (tagIndex == 2 && !(chars[i] + "").equals("<") && !(chars[i] + "").equals(">")) {
contentBuffer.append(chars[i] + "");
tagMap.put(TAG_CONTENT, contentBuffer.toString());
}
//获取后标签内容
if (tagIndex == 2 && (chars[i] + "").equals("<")) {
tagBuffer = new StringBuffer("");
tagBuffer.append(chars[i]);
tagIndex = 3;
}
if (tagIndex == 3 && !(chars[i] + "").equals("<") && !(chars[i] + "").equals(">")) {
tagBuffer.append(chars[i]);
}
if (tagIndex == 3 && (chars[i] + "").equals(">")) {
if (tagMap.get(HtmlUtils.TAG_START).startsWith(tagBuffer.toString().replace("/", "").replace(">", ""))) {
tagBuffer.append(chars[i]);
tagMap.put(TAG_END, tagBuffer.toString());
tagMap.put(IS_MARK, UNMARK);
tagList.add(tagMap);
contentBuffer = new StringBuffer("");
tagIndex = 0;
} else {
tagIndex = 2;
}
}
}
return tagList;
}
/**
* 添加标记
*
* @param selectText 选中的文字
* @param tagList 标签数据
*/
public static void addRemark(String selectText, List<Map<String, String>> tagList) {
for (int i = 0; i < ListUtils.getSize(tagList); i++) {
Map<String, String> item = tagList.get(i);
String start = item.get(HtmlUtils.TAG_START);
String middle = item.get(HtmlUtils.TAG_CONTENT);
String end = item.get(HtmlUtils.TAG_END);
if (selectText.contains(middle)) {
tagList.get(i).put(HtmlUtils.TAG_START, "<a href=\"" + Constants.BASE_URL + "\" style='color:#00CCCC'>" + start);
tagList.get(i).put(HtmlUtils.TAG_END, end + "</a>");
}
if (middle.contains(selectText)) {
int index = middle.indexOf(selectText);
String equalText = middle.substring(index, selectText.length() + index);
middle = middle.replace(equalText, "<a href=\"" + Constants.BASE_URL + "\" style='color:#00CCCC'>" + equalText + "</a>");
tagList.get(i).put(HtmlUtils.TAG_CONTENT, middle);
}
}
}
/**
* 组合标记
*
* @param cacheList
* @return
*/
public static StringBuffer combinationRemark(List<Map<String, String>> cacheList) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < ListUtils.getSize(cacheList); i++) {
Map<String, String> item = cacheList.get(i);
String start = item.get(HtmlUtils.TAG_START);
String middle = item.get(HtmlUtils.TAG_CONTENT);
String end = item.get(HtmlUtils.TAG_END);
sb.append(start + middle + end);
}
return sb;
}
}
WebView加载Html数据
public void loadLinkWebData(WebView webView, String data) {
WebSettings settings = webView.getSettings();
settings.setSupportZoom(true);
settings.setTextZoom(85);
settings.setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient(){
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
//点击事件拦截接收
Log.e("RRL","url==>"+request.getUrl().toString());
return super.shouldInterceptRequest(view, request);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//点击事件拦截接收
Log.e("RRL","url==>"+url);
return true;
}
});
webView.setWebChromeClient(new WebChromeClient());
webView.loadDataWithBaseURL(null, data, "text/html", "utf-8", null);
}
例子
HtmlUtils.addRemark(selectText, tagList);//selectText为WebView选中的文字
StringBuffer completeBuffer = HtmlUtils.combinationRemark(tagList);
loadLinkWebData(webView, completeBuffer.toString());