Android 类似UC浏览器的效果:向上滑动地址栏隐藏功能

思路

要求

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浏览器的效果:向上滑动地址栏隐藏功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

您可能感兴趣的文章:

  • Android自定义FloatingActionButton滑动行为只隐藏不出现的问题小结
  • Android 中实现ListView滑动隐藏标题栏的代码
  • Android 顶部标题栏随滑动时的渐变隐藏和渐变显示效果
  • Android用Scroller实现一个可向上滑动的底部导航栏
(0)

相关推荐

  • Android自定义FloatingActionButton滑动行为只隐藏不出现的问题小结

    先来段Behavior代码,网上关于FloatingActionButton(以下简称FAB)滑动的代码很多了,参考一下. public class FabBehavior extends FloatingActionButton.Behavior{ private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator(); private boolean mIsAnimatingOut = false; p

  • Android用Scroller实现一个可向上滑动的底部导航栏

    静静等了5分钟竟不知道如何写我这第一篇文章.每次都想好好的学习学习,有时间多敲敲代码,写几篇自己的文章.今天终于开始实行了,还是有点小激动的.哈哈! 好了废话就不多收了.我今天想实现的一个功能就是一个可以上滑底部菜单栏.为什么我会想搞这么个东西呢, 还是源于一年前,我们app 有这么个需求,当时百度也好谷歌也好,都没有找到想要的效果,其实很简单的一个效果.但是当时我也是真的太菜了,所有有关自定义的控件真的是不会,看别人的代码还好,真要是自己写,一点头绪都没有.因为我试着写了,真的不行啊.当时觉得

  • Android 中实现ListView滑动隐藏标题栏的代码

    布局中listview要覆盖标题栏 int mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop(); //滑动监听 showHideTitleBar(true); ListView standby_lv = (ListView) findViewById(R.id.standby_lv); standby_lv.setOnTouchListener(new View.OnTouchListener() { @Override p

  • Android 顶部标题栏随滑动时的渐变隐藏和渐变显示效果

    各位早上好,话不多说,先上效果图: 注意顶部:首页TextView的变化(显示和隐藏)! 首先分析下:UI状态,其是由RecyclerView添加头部组成+RecyclerView 头部添加和RecyclerView分别引用如下:具体的分装数据的过程这里就不在说明,下篇博客会更加深入的写关于 RecyclerView总添加多种不同type类型 compile 'com.bartoszlipinski.recyclerviewheader:library:1.2.1' compile 'com.a

  • Android 类似UC浏览器的效果:向上滑动地址栏隐藏功能

    思路 要求 ScrollView 嵌套 地址栏 和 WebView 手指滑屏向下滚动(网页向上),如果网页有滚动条,首先把 地址栏 滚动到消失,然后 WebView 才开始滚动: 手指滑屏向上滚动(网页向下),如果地址栏隐藏,那么 地址栏 首先慢慢显示,然后 WebView 才开始滚动. 实现方案 根据 View 的 onInterceptTouchEvent 和 onTouchEvent 原理.把 ScrollView 设置为 WebView 的一个变量,在 WebView的 onInterc

  • Android仿UC浏览器左右上下滚动功能

    本文要解决在侧滑菜单右边加个文本框,并能实现文本的上下滑动和菜单的左右滚动.这里推荐可以好好看看android的触摸事件的分发机制,这里我就不详细讲了,我只讲讲这个应用.要实现的功能就像UC浏览器(或其它手机浏览器)的左右滚动,切换网页,上下滚动,拖动内容. 本文的效果: 一.功能要求与实现 1.功能要求: (1)手指一开始按着屏幕左右移动时,只能左右滚动菜单,如果这时手指一直按着,而且上下移动了,那么菜单显示部分保持不变,但文本框也不上下移动!                       (2

  • Android 类似微信登录输入框效果

    微信的登录输入框效果如下 进入自动打开自动启动软键盘 点击下一个输入框,下划线颜色改变 怎么实现这样的效果呢,其实非常简单! 简单的布局我就不说了,直接上干货. 1.实现进入自动弹出软键盘,在根文件中的Activity中设置 windowSoftInputMode 属性为 stateVisible|adjustResize 例如 <activity android:name=".SetLoginPasswordActivity" android:windowSoftInputMo

  • 最好用的Android省市区三级联动选择效果

    Android省市区选择三级联动效果,一个不大不小的功能,就算你做过,但是没有相关的代码直接写,也要花掉你至少半天时间. 下面我写出我的实现过程(思路绝对清晰). 先上效果图 一.准备数据 我是用的本地的json数据(走网络的话太慢,每次都要请求),放在asserts中.格式如下: [{ "name": "河北省", "city": [ { "name": "石家庄市", "area":

  • Android自定义控件实现UC浏览器语音搜索效果

    最近项目上要实现语音搜索功能,界面样式要模仿一下UC浏览器的样式,UC浏览器中有一个控件,会随着声音大小浮动,然后寻思偷个懒,百度一下,结果也没有找到类似的,只能自己动手了. 先上图看我实现的效果: 这是自定义控件的代码,里面注释也很明白,就不费话了 public class CustomCircleView extends View{ private Paint mPaint; private int strokeWidth = 0; //圆环的宽度 private Bitmap bitmap

  • Android ScrollView实现向上滑动控件顶部悬浮效果

    本文参考了: <上滑停靠顶端的悬浮框>的代码,在此表示感谢.[上滑停靠顶端的悬浮框]里的实现方法是使用两个控件,滑动时,监听ScrollView的滚动Y值,从而通过对两个控件的显示隐藏来实现控件的顶部悬浮.但是实际应用场景中,有可能需要悬浮的控件里面的内容是比较多的,如果通过显示隐藏的方式来实现的话,操作控件里的内容时,需要重复定义两套变量,对控件里的内容进行修改时也是要操作再次,非常麻烦. 本文的方法是通过addView和removeView来实现的. 一.首先让ScrollView实现滚动

  • Android项目仿UC浏览器和360手机卫士消息常驻栏(通知栏)

    之前网上看了下自定义消息栏,通知栏,了解到了Notification这个控件,发现UC浏览器等都是这种类型,今天写个demo实现下,如图: 其中每个按钮都有不同的功能,代码如下: package com.example.textwsjdemo; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.Pendin

  • JS实现仿UC浏览器前进后退效果的实例代码

    测试浏览器为谷歌浏览器(谷歌toggle device toolbar) var startx, starty, endx, endy, moveX, moveY, seatX, seatY; var clickState = false; //获取输入框dom元素 var text = document.forms["form"]; //设置样式 function setCss(obj) { var cssStr = "z-index:5;width:37px;height

  • 微信浏览器弹出框滑动时页面跟着滑动的实现代码(兼容Android和IOS端)

    在做微信开发的时候遇到这个问题:微信浏览器弹出框滑动时页面跟着滑动. 我觉得这个问题用的是下面这几行代码: var $body = $('body'), dialogIsInView = !1,//当前是不是对话框 lastContentContainerScrollTop = -1,//用于弹出框禁止内容滚动 $contentContainer = $('#content-container');//内容容器 //阻止Window滚动 function stopWindowScroll() {

随机推荐