总所周知,如果在Android原生提供的webview中,如果我们要实现自定义的长按复制选择弹窗
需要重写Webview的这两个方法
具体的实现思路网上有很多参考文章
今天说一下如果你的项目引用了X5内核的webview,如果想要实现自定义长按复制弹窗会发现,这样实现就无效了
貌似是因为X5内核自己实现了长按点击事件,因为我在使用布局分析工具中发现,当使用X5内核的Webview
显示的自定义弹窗变成了FramLayout布局,如果你使用原生Webview会发现,长安复制弹窗是三个PopupWindow
那接下来的事儿就好办了,我如果拿到X5内核承载复制的FramLayout控件,那么直接替换成我自己的布局不就好了么
那么问题变成如何找到那个FramLayout
在自定义的X5Webview子类中
1。设置长按点击事件
/** * 开启长按文本自定义弹窗 */
public void openCustomLongTextPop(){
setOnLongClickListener(new OnLongClickListener() {
@Override public boolean onLongClick(View v) { getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override public void onGlobalLayout() { getViewTreeObserver().removeOnGlobalLayoutListener(this); foreachView(X5WebView.this);
}
});
return false;
}
});
}
2.遍历找到最上面的FramLayout,就是我们想要找到FramLayout
public void foreachView(View view) {
if (view instanceof ViewGroup) {
if(((ViewGroup) view).getChildCount()<=0)return;
View childAt1 = ((ViewGroup) view).getChildAt(((ViewGroup)view).getChildCount()-1);
if (view instanceof FrameLayout && !(view instanceof X5WebView)) { Log.e("X5Webview", "childCount:" + frameLayout.getChildCount()); repleaceCopyPopView(frameLayout); return; } foreachView(childAt1); }
foreachView(childAt1);
}
}
3.替换我们自己的布局
/** * 替换复制弹窗的方法 * @param frameLayout */private void repleaceCopyPopView(FrameLayout frameLayout) {
View childAt = frameLayout.getChildAt(0);
frameLayout.removeView(childAt);
View customCopyLayout = createCustomCopyLayout();frameLayout.postDelayed(new Runnable() {
@Overridepublic void run() {
frameLayout.addView(customCopyLayout);
}
},500);
}
4.我们项目中的自定义布局,仅供参考
private View createCustomCopyLayout() {
LinearLayout linearLayout = new LinearLayout(this.getContext()); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams((int) Utils.dp2px(200f), (int) Utils.dp2px(40f)); linearLayout.setLayoutParams(layoutParams); linearLayout.setBackgroundResource(R.drawable.shap_corner2_white_stroke1_ededed); LinearLayout.LayoutParams childLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1);// View actionSelectView = LayoutInflater.from(this.getContext()).inflate(R.layout.layout_longpress_copywindow,null); TextView textView = new TextView(this.getContext()); textView.setText("复制"); textView.setTextColor(Color.BLACK); textView.setGravity(Gravity.CENTER); textView.setLayoutParams(childLayoutParams); TextView textView1 = new TextView(this.getContext()); textView1.setText("笔记"); textView1.setTextColor(Color.BLACK); textView1.setGravity(Gravity.CENTER); textView1.setLayoutParams(childLayoutParams); TextView textView2 = new TextView(this.getContext()); textView2.setText("分享"); textView2.setTextColor(Color.BLACK); textView2.setGravity(Gravity.CENTER); textView2.setLayoutParams(childLayoutParams); linearLayout.addView(textView); linearLayout.addView(textView1); linearLayout.addView(textView2); textView.setOnClickListener(v -> { getSelectedData("复制"); performClickEvent(); }); textView1.setOnClickListener(v -> { performClickEvent(); getSelectedData("笔记"); }); textView2.setOnClickListener(v -> { getSelectedData("分享"); performClickEvent(); }); return linearLayout;
}
5.最后一步,当你点击了自己的按钮后,还要模拟一下点击事件,让弹窗消失
/** * 模拟点击事件,达到复制弹窗消失目的 */
public void performClickEvent() {
MotionEvent me = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);// onTouch(mPageWidget, me); dispatchTouchEvent(me);
}
6.补充两个小细节
当我这么实现后,发现点击html中的视频横屏的时候,发现他在顶部又创建了一个framlayout,那么这时候当我们再点击长按的时候,找第一个就不对了,会在视频framlayout中又创建了一个,所以我要将如果之前找到过的复制弹窗进行下标识,更改查找方法为
/** * 寻找长按复制弹窗,并替换自己的定义的View * * 当视频容器未加入视图时,Webview中只有一个承载复制弹窗的容器, * 如果点击播放了视频,这时候Webview中有两个Framlayout,而且这两个FragmLayout的顺序跟出现顺序有关系 * 一步小心可能把视频播放控件remove掉, * 所以当第一次找到复制弹窗的时候要加一个Tag标记 * 如果找到了标记位,直接替换标记位里的view * @param view */
public static final String CopyFramTag = "myCustomCopyPopTag";
public void foreachView(View view) {
if (view instanceof ViewGroup) {
if(((ViewGroup) view).getChildCount()<=0)return;
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
View childAt = ((ViewGroup) view).getChildAt(i); if(CopyFramTag.equals(childAt.getTag())){
repleaceCopyPopView((FrameLayout) childAt); return;
}
}
View childAt1 = ((ViewGroup) view).getChildAt(((ViewGroup) view).getChildCount()-1);
if (view instanceof FrameLayout && !(view instanceof X5WebView)) { view.setTag(CopyFramTag);
FrameLayout frameLayout = (FrameLayout) view;
Log.e("X5Webview", "childCount:" + frameLayout.getChildCount());
repleaceCopyPopView(frameLayout); return;
}
foreachView(childAt1);
}
}
第二个细节:
当点击的位置比较考右边这时候,弹窗会有一半显示在屏幕外,这时候需要我们在替换自己布局后,判断下布局的位置,做一个平移操作,补充替换代码为
/** * 替换复制弹窗的方法 * @param frameLayout */
private void repleaceCopyPopView(FrameLayout frameLayout) {
View childAt = frameLayout.getChildAt(0); frameLayout.removeView(childAt); View customCopyLayout = createCustomCopyLayout();
frameLayout.postDelayed(new Runnable() {
@Override public void run() {
// frameLayout.setLayoutParams(layoutParams); frameLayout.addView(customCopyLayout); //监听布局变化 frameLayout.addOnLayoutChangeListener(new OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
float x = frameLayout.getX(); float v1 = VideoPlayUtils.getScreenWidth(v.getContext()) - Utils.dp2px(200f);
//最小距离 //如果弹窗位置,超出了屏幕,将弹窗平移进来, if(x > VideoPlayUtils.getScreenWidth(v.getContext()) - v1){ Log.e("X5Webview","x: " + x + " v1: " + v1 + " tranns: " + (x-v1)); frameLayout.animate().translationX(v1-x-Utils.dp2px(10)).start();
}
}
});
}
}, 500);
}
至此应该可以完美实现,自定义弹窗更能,
提示:使用原生方法的js注入同样要写。和原生方案一样
代码不知道怎么粘贴,格式有点乱,见谅。
网友评论