Android 类似UC浏览器的效果:向上滑动地址栏隐藏功能
作者:stwstw0123 发布时间:2023-01-29 05:01:41
思路
要求
ScrollView 嵌套 地址栏 和 WebView
手指滑屏向下滚动(网页向上),如果网页有滚动条,首先把 地址栏 滚动到消失,然后 WebView 才开始滚动;
手指滑屏向上滚动(网页向下),如果地址栏隐藏,那么 地址栏 首先慢慢显示,然后 WebView 才开始滚动。
实现方案
根据 View 的 onInterceptTouchEvent 和 onTouchEvent 原理。把 ScrollView 设置为 WebView 的一个变量,在 WebView的 onInterceptTouchEvent 方法里检测到 MotionEvent.ACTION_DOWN 事件后中断事件,在 WebView 的 onTouchEvent 事件中根据具体情况决定是把 MotionEvent.ACTION_MOVE 事件传送给 ScrollView 还是留给自己
由于MotionEvent.ACTION_MOVE 事件传送给 ScrollView 后无法在一次 Touch 事件中再接收,所以会导致如果有地址栏,向下滑动第一次只能滑动到 ScrollView 消失
+
Hack网页,加入JS脚本,前行让网页顶部空出来一段空白,空白处覆盖地址栏
优点是WebView大小不变化,容易控制
缺点是比较复杂要处理各种网页元素,各种 position 情况,实现复杂,效率低
由手势接管所有触发操作,再由它分发给需要滚动的控件
本文方法
资源
SrollView下面包含节点地址栏,WebView控件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<samples.zjc.com.testbrowserfeature.MyScrollView
android:id="@+id/scrollView"
android:scrollbars="none"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/toolBar"
android:background="#5ff0"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/urlEdit"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/goButton"
android:text="Go"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<samples.zjc.com.testbrowserfeature.MyWebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="100dp" />
</LinearLayout>
</samples.zjc.com.testbrowserfeature.MyScrollView>
</RelativeLayout>
ScrollView继承自 ScrollView
onTouchEvent 中阻止 MotionEvent.ACTION_MOVE 事件
public class MyScrollView extends ScrollView {
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_MOVE) {
return true;
}
return super.onTouchEvent(ev);
}
}
MyWebView继承自 WebView
onTouchEvent 中阻止 MotionEvent.ACTION_MOVE 事件
onDrawListner
计算竖直滚动范围
public class MyWebView extends WebView {
public interface MyWebViewListener {
void afterDraw(WebView webView);
}
private MyWebViewListener mListener;
private int mMoveCheckedCnt;
public MyWebView(Context context) {
super(context);
}
public MyWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MyWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public MyWebView(Context context, AttributeSet attrs, int defStyleAttr, boolean privateBrowsing) {
super(context, attrs, defStyleAttr, privateBrowsing);
}
public void setListener(MyWebViewListener listener) {
mListener = listener;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mMoveCheckedCnt = 0;
flingScroll(0, 0);
break;
case MotionEvent.ACTION_MOVE:
mMoveCheckedCnt++;
return false;
case MotionEvent.ACTION_UP:
if(mMoveCheckedCnt >= 2) {
event.setAction(MotionEvent.ACTION_CANCEL);
mMoveCheckedCnt = 0;
}
break;
}
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
MyWebViewListener listener = mListener;
if(listener != null) {
listener.afterDraw(this);
}
}
public int getVScrollRange() {
int v = computeVerticalScrollRange() - computeVerticalScrollExtent();
if(v < 0) {
v = 0;
}
return v;
}
}
主窗口
GlobalLayoutListener 获取地址栏和滚动视图高度
GestureDetector 逻辑分发 - 决定是滑动webview还是改变webview高度从而改变ScrollView滚动范围(ScrollView总是滚动到最底)
WebView 重画之后检测当前地址栏偏移
public class MainActivity extends AppCompatActivity implements MyWebView.MyWebViewListener {
MyWebView mWebView;
GestureDetector mGesture = null;
View mToolBar;
int mToolBarHeight;
MyScrollView mScrollView;
int mScrollViewHeight;
int mScrollOffset;
EditText mUrlEdit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (MyWebView) findViewById(R.id.webView);
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
});
mWebView.setListener(this);
mWebView.loadUrl("http://www.sohu.com");
mUrlEdit = (EditText) findViewById(R.id.urlEdit);
findViewById(R.id.goButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String url = mUrlEdit.getText().toString();
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "http://" + url;
}
mWebView.loadUrl(url);
}
});
mToolBar = findViewById(R.id.toolBar);
mScrollView = (MyScrollView)findViewById(R.id.scrollView);
ScrollView scrollView = (ScrollView) mScrollView;
findViewById(R.id.root).getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mToolBarHeight = mToolBar.getHeight();
mScrollViewHeight = mScrollView.getHeight();
adjustScrollView();
}
});
mGesture = new GestureDetector(this, new GestureListener());
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
mGesture.onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
@Override
public void afterDraw(WebView webView) {
if (mWebView.getVScrollRange() < mScrollOffset) {
mScrollOffset = mWebView.getVScrollRange();
adjustScrollView();
}
}
class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDoubleTap(MotionEvent e) {
Log.e("Temp", "onDoubleTap");
return super.onDoubleTap(e);
}
@Override
public boolean onDown(MotionEvent e) {
Log.e("Temp", "onDown");
return super.onDown(e);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
Log.e("Temp", "onFling:velocityX = " + velocityX + " velocityY" + velocityY);
int effectX = (int) velocityX;
int effectY = (int) velocityY;
if (effectOnScrollViewByScroll((velocityY < 0 ? 1 : -1) * 8000)) {
effectY = 0;
}
mWebView.flingScroll(-effectX, -effectY);
return super.onFling(e1, e2, velocityX, velocityY);
}
@Override
public void onLongPress(MotionEvent e) {
Log.e("Temp", "onLongPress");
super.onLongPress(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
Log.e("Temp", "onScroll:distanceX = " + distanceX + " distanceY = " + distanceY);
int effectX = (int) distanceX;
int effectY = (int) distanceY;
if (effectOnScrollViewByScroll((int) distanceY)) {
effectY = 0;
}
mWebView.scrollBy(effectX, effectY);
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
Log.e("Temp", "onSingleTapUp");
return super.onSingleTapUp(e);
}
}
private boolean effectOnScrollViewByScroll(int distanceY) {
if (distanceY > 0) {
// scroll up, the web will scroll down
if (mScrollOffset >= mToolBarHeight || mScrollOffset >= mWebView.getVScrollRange()) {
return false;
}
mScrollOffset += distanceY;
if (mScrollOffset > mToolBarHeight) {
mScrollOffset = mToolBarHeight;
}
if (mScrollOffset > mWebView.getVScrollRange()) {
mScrollOffset = mWebView.getVScrollRange();
}
} else {
if (mScrollOffset <= 0) {
return false;
}
mScrollOffset += distanceY;
if (mScrollOffset <= 0) {
mScrollOffset = 0;
}
}
adjustScrollView();
return true;
}
private void adjustScrollView() {
Log.e("Temp", "offset is " + mScrollOffset);
ViewGroup.LayoutParams layoutParams = mWebView.getLayoutParams();
int newHeight = (mScrollViewHeight - mToolBarHeight) + mScrollOffset;
Log.e("Temp", "newHeight is " + newHeight + ", layoutParams.height" + layoutParams.height);
if (newHeight != layoutParams.height) {
layoutParams.height = newHeight;
mWebView.setLayoutParams(layoutParams);
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
}
}
}
总结
以上所述是小编给大家介绍的Android 类似UC浏览器的效果:向上滑动地址栏隐藏功能网站的支持!
来源:http://blog.csdn.net/stwstw0123/article/details/49335303


猜你喜欢
- Session具有以下特点: (1)Session中的数据保存在服务器端; (2)Session中可以保存任意类型的数据; (2)Sessi
- 大家好,今天我们继续来学习Android 8.0系统的适配。之前我们已经讲到了,Android 8.0系统最主要需要进行适配的地方有两处:应
- 先来看看效果:一、添加依赖库的步骤1.项目的gradle文件内的做以下改动allprojects { repositories
- 通常用java来打包文件生成压缩文件后,有如下两个地方会出现乱码 :1、内容的中文乱码问题,这个问题网上很多人给出了解决方法,主要有两种方法
- 1.JVM Heap(堆)溢出:java.lang.OutOfMemoryError: Java heap spaceJVM在启动的时候会自
- JoinPoint的getSignature方法在使用springboot写aop的时候,有个JoinPoint类,用来获取代理类和被代理类
- 1、这个方法在GpsNET.JT809Core里来源:https://www.cnblogs.com/cnsend/p/12197982.h
- 本文实例为大家分享了Android简单使用PopupWindow的的具体代码,供大家参考,具体内容如下思路1.在res下面创建一个menu文
- 前言从windows窗口的概念开始,通过对比去理解Android窗口体系,本文没有深入源码,重在理解概念代码都是抄来抄去,概念也是互相借鉴
- 本文介绍了Android Studio 超级简单的打包生成apk,分享给大家,也给自己留个笔记。为什么要打包:apk文件就是一个包,打包就是
- 前言:在实际的应用开发中,很多时候往往因为一些不可控的因素导致程序出现一些错误,这个时候就要及时把异常信息反馈给客户端,便于客户端能够及时地
- 方法一:需要调用win32api,winform、wpf通用[DllImport("user32.dll")]publi
- 本文实例为大家分享了Unity实现VR中在黑板上写字的具体代码,供大家参考,具体内容如下一、工具1.开发用的是Unity 5.6.2版本2.
- 介绍在 C/C++ 中,程序员负责对象的创建和销毁。通常程序员会忽略无用对象的销毁。由于这种疏忽,在某些时候,为了创建新对象,可能没有足够的
- 在项目中常常常使用到DataTable,假设DataTable使用得当,不仅能使程序简洁有用,并且可以提高性能,达到事半功倍的效果,List
- 这几天恰好和朋友谈起了递归,忽然发现不少朋友对于“尾递归”的概念比较模糊,网上搜索一番也没有发现讲解地完整详细的资料,于是写了这么一篇文章,
- 一、创建maven项目我使用的是汉化的idea可以选择原型,我这里没有选择输入项目名称,完成创建二、配置tomcat选择运行编辑配置点加号找
- 一、创建 Android Project在新建对话框中输入 App 属性,SDK版本全部选最新的,不作版本兼容。主题选择 Holo Dark
- 加载网络图片Image.network()是Flutter提供的一种从网络上加载图片的方法,它可以从指定的URL加载图片,并在加载完成后将其
- 介绍该系统有三个角色,分别是:普通用户、房屋中介、管理员。普通用户的功能:浏览房屋信息、预约看房、和中介聊天、申请成为中介等等。房屋中介的功