Android 類似UC瀏覽器的效果:向上滑動(dòng)地址欄隱藏功能
思路
要求
ScrollView 嵌套 地址欄 和 WebView
手指滑屏向下滾動(dòng)(網(wǎng)頁(yè)向上),如果網(wǎng)頁(yè)有滾動(dòng)條,首先把 地址欄 滾動(dòng)到消失,然后 WebView 才開始滾動(dòng);
手指滑屏向上滾動(dòng)(網(wǎng)頁(yè)向下),如果地址欄隱藏,那么 地址欄 首先慢慢顯示,然后 WebView 才開始滾動(dòng)。
實(shí)現(xiàn)方案
- 根據(jù) View 的 onInterceptTouchEvent 和 onTouchEvent 原理。把 ScrollView 設(shè)置為 WebView 的一個(gè)變量,在 WebView的 onInterceptTouchEvent 方法里檢測(cè)到 MotionEvent.ACTION_DOWN 事件后中斷事件,在 WebView 的 onTouchEvent 事件中根據(jù)具體情況決定是把 MotionEvent.ACTION_MOVE 事件傳送給 ScrollView 還是留給自己
- 由于MotionEvent.ACTION_MOVE 事件傳送給 ScrollView 后無(wú)法在一次 Touch 事件中再接收,所以會(huì)導(dǎo)致如果有地址欄,向下滑動(dòng)第一次只能滑動(dòng)到 ScrollView 消失
- +
- Hack網(wǎng)頁(yè),加入JS腳本,前行讓網(wǎng)頁(yè)頂部空出來(lái)一段空白,空白處覆蓋地址欄
- 優(yōu)點(diǎn)是WebView大小不變化,容易控制
- 缺點(diǎn)是比較復(fù)雜要處理各種網(wǎng)頁(yè)元素,各種 position 情況,實(shí)現(xiàn)復(fù)雜,效率低
- 由手勢(shì)接管所有觸發(fā)操作,再由它分發(fā)給需要滾動(dòng)的控件
本文方法
資源
SrollView下面包含節(jié)點(diǎn)地址欄,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
計(jì)算豎直滾動(dòng)范圍
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 獲取地址欄和滾動(dòng)視圖高度
GestureDetector 邏輯分發(fā) - 決定是滑動(dòng)webview還是改變webview高度從而改變ScrollView滾動(dòng)范圍(ScrollView總是滾動(dòng)到最底)
WebView 重畫之后檢測(cè)當(dāng)前地址欄偏移
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);
}
});
}
}
}
總結(jié)
以上所述是小編給大家介紹的Android 類似UC瀏覽器的效果:向上滑動(dòng)地址欄隱藏功能,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Android實(shí)現(xiàn)View滑動(dòng)效果的6種方法
這篇文章主要介紹了Android實(shí)現(xiàn)View滑動(dòng)的6種方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03
Android GridView不改變背景色實(shí)現(xiàn)網(wǎng)格線效果
這篇文章主要介紹了Android GridView不改變背景色實(shí)現(xiàn)網(wǎng)格線效果,需要的朋友可以參考下2016-03-03
activity全屏實(shí)現(xiàn)沉浸式效果,并且單獨(dú)觸摸不會(huì)彈出虛擬按鍵的方法
今天小編就為大家分享一篇activity全屏實(shí)現(xiàn)沉浸式效果,并且單獨(dú)觸摸不會(huì)彈出虛擬按鍵的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2018-07-07
Android開發(fā)實(shí)現(xiàn)廣告無(wú)限循環(huán)功能示例
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)廣告無(wú)限循環(huán)功能,結(jié)合完整實(shí)例形式分析了Android廣告圖片輪播功能的具體實(shí)現(xiàn)步驟與相關(guān)功能、布局等操作技巧,需要的朋友可以參考下2017-11-11
Android創(chuàng)建與解析XML(三)——詳解Sax方式
Android中SurfaceTexture TextureView SurfaceView GLSurfaceView
Flutter源碼分析之自定義控件(RenderBox)指南

