前言:
WebKit中的JavaScriptCore引擎處理的JS滑動(dòng)與原生控件滑動(dòng)沖突尚未完善,這篇文章討論下如何徹底解決H5端的滑動(dòng)控件與原生控件滑動(dòng)沖突
更多文章請(qǐng)關(guān)注:http://www.reibang.com/u/b1cff340957c
效果圖
1:點(diǎn)擊WebView中的輪播圖區(qū)域左右滑動(dòng)與上下滑動(dòng)
2:WebView非輪播區(qū)域左右翻頁(yè)ViewPager
一:WebView內(nèi)容的滑動(dòng)
首先介紹下WebView內(nèi)容的幾種滑動(dòng)方式
1:WebView在固定寬高時(shí)與自適應(yīng)屏幕時(shí)的滑動(dòng)
固定寬高時(shí),WebView加載的html頁(yè)面內(nèi)容寬度或高度大于webView的寬度或高度時(shí),滑動(dòng)的都是改變mScrollX, mScrollY的值,這種滑動(dòng)能在onScrollChanged(int l, int t, int oldl, int oldt)監(jiān)聽(tīng)到getScrollX, getScrollY值變化.
WebView自適應(yīng)屏幕寬度,高度自適應(yīng)嵌套在ScrollView中滑動(dòng)時(shí),WebView高度即為加載頁(yè)面的實(shí)際高度,此時(shí)頁(yè)面中內(nèi)容的滑動(dòng)實(shí)際就是ScrollView在控制,滑動(dòng)改變的是ScrollView中第一個(gè)子控件的scrollY值
2:H5控件通過(guò)JS控制的滑動(dòng)
這種滑動(dòng)在WebView的某個(gè)固定區(qū)域內(nèi)通過(guò)JS控制滑動(dòng),無(wú)論是水平滑動(dòng)還是垂直滑動(dòng)都不會(huì)像android原生滑動(dòng)控件有requestDisallowInterceptTouchEvent請(qǐng)求事件放行,而且又不影響WebView的scrollX,scrollY值,所以即使是(能自動(dòng)處理水平與垂直滑動(dòng)區(qū)別的)ViewPager也不能判斷它是否能夠優(yōu)先滑動(dòng)
第1個(gè)問(wèn)題只要能改變scrollX或scrollY值外層控件就能識(shí)別是否能夠優(yōu)先滑動(dòng),這里不再講述如何處理滑動(dòng)優(yōu)先
上層控件如何處理水平與垂直滑動(dòng)和多層嵌套-此處鏈接
二:解決JS與原生控件滑動(dòng)沖突
直接舉例子說(shuō)明, H5中的輪播控件被嵌套到NestedScrollView中,外層再嵌套SwipeRefreshLayout 下拉刷新,外層再嵌套ViewPager,既然外層控件無(wú)法判斷JS是否優(yōu)先滑動(dòng),那思路只能由JS絕對(duì)優(yōu)先滑動(dòng),在產(chǎn)生水平或垂直滑動(dòng)或無(wú)法滑動(dòng)時(shí)再告訴上層控件是否要放行事件
這種沖突網(wǎng)上也有一大堆的方法,有用js交互處理返回滑動(dòng)區(qū)域,在該區(qū)域內(nèi)都不攔截事件, 也有修改WebView的onTouchEvent方法,在ACTION_DOWN時(shí)請(qǐng)求.requestDisallowInterceptTouchEvent(true),在ACTION_MOVE時(shí)再根據(jù)x,y軸的差值比較來(lái)判斷是否為水平還是垂直滑動(dòng),這里分析下不完善之處
a:
如果WebView中加載的H5頁(yè)面或WebView外面嵌套的控件即有水平又有垂直滑動(dòng)時(shí),這方法就行不通了
b:
android判斷水平與垂直滑動(dòng)的標(biāo)準(zhǔn)也不是這樣子定義的,在google提供的所有的滑動(dòng)控件中都可以看到 yDiff>mTouchSlop 或 xDiff > mTouchSlop的代碼, 不僅是判斷差值還要判斷差值大于最小滑動(dòng)單位才算是水平或垂直滑動(dòng)
直接上代碼嘍,先是JS部分
(這里會(huì)用到j(luò)s交互,與js的事件監(jiān)聽(tīng),作者對(duì)這塊水平爛,如有寫(xiě)錯(cuò),多多指點(diǎn))
// 輪播滾動(dòng)
var mySwiper = $('.slidepics').swiper({
loop : true,
pagination: '.dotted p',
paginationClickable: true,
spaceBetween: 30,
autoplay:3000,
autoplayDisableOnInteraction:false,
onSliderMove:function(swiper) {
//如果光用此方法來(lái)申請(qǐng)app原生控件不攔截事件,在快速滑動(dòng)的時(shí)候可能搶不到事件
//還要配合下面的touch事件來(lái)獲取
isBeingDrag = true;
window.Android.requestEvent(true);
}
});
// 輪播滾動(dòng)事件監(jiān)聽(tīng)
$('.slidepics').on('touchstart touchmove touchend touchcancel' , function(event) {
var touch = event.originalEvent.targetTouches[0];
switch (event.type) {
case "touchstart":
mLastClientX = touch.clientX;
mLastClientY = touch.clientY;
isBeingDrag = false;
window.Android.requestEvent(true);
break;
case "touchmove":
if (!isBeingDrag) {
var xDiff = Math.abs(touch.clientX - mLastClientX);
var yDiff = Math.abs(touch.clientY - mLastClientY);
//console.log(xDiff + " " + yDiff);
if (xDiff >= touchSlop) {
isBeingDrag = true;
} else if(yDiff > touchSlop) {
//產(chǎn)生app縱向滑動(dòng),父控件不強(qiáng)制請(qǐng)求放行事件,這段邏輯主要是不影響外層垂直滑動(dòng)控件的滑動(dòng)
//js端控制滑動(dòng)與app端的標(biāo)準(zhǔn)不一樣,所以結(jié)合上面的onSliderMove:function方法來(lái)判斷
//如果H5端控件已經(jīng)產(chǎn)生滑動(dòng)時(shí)則必須請(qǐng)求父控件放行事件
//如果有些開(kāi)源控件沒(méi)有類(lèi)似onSliderMove方法時(shí),只需提供控件是否產(chǎn)生滑動(dòng)就行,原理都是一樣
isBeingDrag = true;
window.Android.requestEvent(false);
}
}
break;
case "touchend":
window.Android.requestEvent(false);
break;
}
});
//app端的滑動(dòng)值
var touchSlop = 0;
//初次按下時(shí)的x, y軸值
var mLastClientX, mLastClientY;
//是否處于滑動(dòng)中
var isBeingDrag = false;
//js附值,在web加載完成時(shí)將android的滑動(dòng)單位值傳給js
function initTouchSlop(appTouchSlop) {
touchSlop = appTouchSlop;
}
JS端事件代碼邏輯跟app端差不多,android代碼
/**
* js交互類(lèi)
*/
private class JsCallback {
@JavascriptInterface
public void requestEvent(boolean request) {
Log.i("you", "requestDisallowInterceptTouchEvent " + request+" "+Thread.currentThread().getName());
mWebView.requestDisallowInterceptTouchEvent(request);
}
}
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
int touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
StringBuilder jsSb = new StringBuilder("javascript:initTouchSlop('").append(touchSlop).append("')");
mWebView.loadUrl(jsSb.toString());
}
});
需要注意的是:js交互回調(diào)不是在主線程中執(zhí)行.如果您的界面中沒(méi)有涉及即有水平又有垂直滑動(dòng)的復(fù)雜嵌套,js代碼也可以簡(jiǎn)單化,只需要監(jiān)聽(tīng)touchstart, touchend事件即可
小細(xì)節(jié):requestDisallowInterceptTouchEvent()方法在SwipeRefreshLayout中可能會(huì)不生效,此方法在SwipeRefreshLayout中被重寫(xiě),在SDK22以下版本的V4包中,此方法什么都不執(zhí)行,google的版本問(wèn)題甚是頭疼,要解決這問(wèn)題就是下拉刷新嵌套的控件去實(shí)現(xiàn)NestedScroll,設(shè)置isNestedScrollingEnabled值為true,google也有寫(xiě)好的NestedScrollView
@Override
public void requestDisallowInterceptTouchEvent(boolean b) {
if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView)
|| (mTarget != null && !ViewCompat.isNestedScrollingEnabled(mTarget))) {
// Nope.
// 可以看出SwipeRefreshLayout的第一個(gè)child沒(méi)有實(shí)現(xiàn)NestedScroll時(shí)不能申請(qǐng)到上層控件不攔截事件,
//這也是為什么SwipeRefreshLayout嵌套很多控件下拉刷新會(huì)有沖突的原因,子控件無(wú)法傳遞requestDisallowInterceptTouchEvent
} else {
super.requestDisallowInterceptTouchEvent(b);
}
}
在使用嵌套下拉刷新SwipeRefreshLayout時(shí)注意V4包的版本,SDK22版本以下是不支持的,H5端的沖突也就沒(méi)法處理
如果您有更好的方法解決沖突,請(qǐng)多多指教
更多文章請(qǐng)關(guān)注:http://www.reibang.com/u/b1cff340957c
最后附上源碼https://github.com/youxiaochen/WebViewDemo
- 文/潘曉璐 我一進(jìn)店門(mén)璧帝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)捍岳,“玉大人,你說(shuō)我怎么就攤上這事÷嗉校” “怎么了页徐?”我有些...
- 文/不壞的土叔 我叫張陵拭嫁,是天一觀的道長(zhǎng)壶辜。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么砖顷? 我笑而不...
- 正文 為了忘掉前任,我火速辦了婚禮赃梧,結(jié)果婚禮上滤蝠,老公的妹妹穿的比我還像新娘。我一直安慰自己授嘀,他們只是感情好物咳,可當(dāng)我...
- 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蹄皱,像睡著了一般览闰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巷折,一...
- 那天压鉴,我揣著相機(jī)與錄音,去河邊找鬼锻拘。 笑死油吭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的署拟。 我是一名探鬼主播婉宰,決...
- 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼推穷!你這毒婦竟也來(lái)了心包?” 一聲冷哼從身側(cè)響起,我...
- 序言:老撾萬(wàn)榮一對(duì)情侶失蹤馒铃,失蹤者是張志新(化名)和其女友劉穎蟹腾,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體骗露,經(jīng)...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡岭佳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了萧锉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片珊随。...
- 正文 年R本政府宣布衩辟,位于F島的核電站螟炫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏艺晴。R本人自食惡果不足惜昼钻,卻給世界環(huán)境...
- 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望封寞。 院中可真熱鬧然评,春花似錦、人聲如沸狈究。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)抖锥。三九已至亿眠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間磅废,已是汗流浹背纳像。 一陣腳步聲響...
- 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像谜喊,于是被迫代替她去往敵國(guó)和親潭兽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
推薦閱讀更多精彩內(nèi)容
- Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
- 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
- 場(chǎng)景描述 最近在接觸h5與android混合開(kāi)發(fā)時(shí)遇到一個(gè)問(wèn)題斗遏,在一個(gè)activity使用ViewPager+Fr...
- 最近做了一個(gè)Android UI相關(guān)開(kāi)源項(xiàng)目庫(kù)匯總山卦,里面集合了OpenDigg 上的優(yōu)質(zhì)的Android開(kāi)源項(xiàng)目庫(kù)...
- 如果用一個(gè)關(guān)鍵詞來(lái)描述我2016,那么這個(gè)關(guān)鍵詞便是放下诵次≌巳兀回顧我的這一年,有喜有憂逾一。 總的來(lái)說(shuō)铸本,2016我過(guò)得...