前面说了这么多却发现我们的Tab还没有介绍, Tab到底是个什么东西呢?
其实是一个含有两个WebView 成员一个WebViewController成员的类: 其中PageState用来真正存储这个tab网页的一些信息,包括url 标题 图标等
1
2
3
4
5
6
7
8
|
// Main WebView wrapper tab的容器
private
View mContainer;
// Main WebView 显示网页的webview
private
WebView mMainView;
// Subwindow container
private
View mSubViewContainer;
// Subwindow WebView在一个tab可能会弹出 另一个WebView的dialog 使用subwebview实现 (True if the new window should be a dialog, rather than a full-size window.)
private
WebView mSubView;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<
LinearLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
android:orientation
=
"vertical"
android:fitsSystemWindows
=
"true"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
>
<!-- Wrapper layout for the WebView, which must be in a FrameLayout. -->
<
FrameLayout
android:id
=
"@+id/webview_wrapper"
android:layout_width
=
"match_parent"
android:layout_height
=
"0dip"
android:layout_weight
=
"1"
/>
<!-- Geolocation permissions prompt -->
<
ViewStub
android:id
=
"@+id/geolocation_permissions_prompt"
android:layout
=
"@layout/geolocation_permissions_prompt"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
/>
</
LinearLayout
>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@Override
public
void
onSetWebView(Tab tab, WebView webView) {
View container = tab.getViewContainer();
if
(container ==
null
) {
// The tab consists of a container view, which contains the main
// WebView, as well as any other UI elements associated with the tab.
container = mActivity.getLayoutInflat().inflate(R.layout.tab,
mContentView,
false
);
//把tab的ContainerView Attach到Activity
tab.setViewContainer(container);
}
if
(tab.getWebView() != webView) {
//如果tab当前的 mMainWebview 和 以前的webview不一样就把以前的那个webview 从 Container 移除掉
// Just remove the old one.
FrameLayout wrapper =
(FrameLayout) container.findViewById(R.id.webview_wrapper);
wrapper.removeView(tab.getWebView());
}
}
|
这个函数是谁调用的呢? 终归肯定是Controller进行的, 但是这里设计就有点乱了:
顺序是这样: Controller::setActiveTab -> TabControl::setCurrentTab -> Tab::setWebView -> Controler ::onSetWebView -> BaseUI::onSetWebView
Controller调用 BaseUI 的setActiveTab函数 (其实最后是attachTabToContentView)把Tab的mWebView和container 关联起来:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
protected
void
attachTabToContentView(Tab tab) {
if
((tab ==
null
) || (tab.getWebView() ==
null
)) {
return
;
}
View container = tab.getViewContainer();
//对应tab的layout
WebView mainView = tab.getWebView();
// Attach the WebView to the container and then attach the
// container to the content view.
//把Tab的container添加到mContentView
FrameLayout wrapper =
(FrameLayout) container.findViewById(R.id.webview_wrapper);
ViewGroup parent = (ViewGroup) mainView.getParent();
if
(parent != wrapper) {
if
(parent !=
null
) {
Log.w(LOGTAG,
"mMainView already has a parent in"
+
" attachTabToContentView!"
);
parent.removeView(mainView);
}
wrapper.addView(mainView);
}
else
{
Log.w(LOGTAG,
"mMainView is already attached to wrapper in"
+
" attachTabToContentView!"
);
}
parent = (ViewGroup) container.getParent();
if
(parent != mContentView) {
if
(parent !=
null
) {
Log.w(LOGTAG,
"mContainer already has a parent in"
+
" attachTabToContentView!"
);
parent.removeView(container);
}
mContentView.addView(container, COVER_SCREEN_PARAMS);
}
else
{
Log.w(LOGTAG,
"mContainer is already attached to content in"
+
" attachTabToContentView!"
);
}
mUiController.attachSubWindow(tab);
}
|
这样我们就明白了, Activity只是一个容器, 当哪个Tab放到前台, BaseUI就拿到对应Tab的Container和Webview , 把这两个空间attach到Activity的ContentView中去
之所以这样做, 可能是因为作者想让 BaseUI来进行把View attach到Activity上的操作 , Tab只做控制Webview load网页的操作; 他们之间的交互由Controller来控制.但是感觉这个设计可能有些乱了.
Tab获取当前快照的函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
protected
void
capture() {
if
(mMainView ==
null
|| mCapture ==
null
)
return
;
if
(mMainView.getContentWidth() <=
0
|| mMainView.getContentHeight() <=
0
) {
return
;
}
Canvas c =
new
Canvas(mCapture);
final
int
left = mMainView.getScrollX();
//快照抓取的是tab的顶部
final
int
top = mMainView.getScrollY() + mMainView.getVisibleTitleHeight();
int
state = c.save();
c.translate(-left, -top);
float
scale = mCaptureWidth / (
float
) mMainView.getWidth();
c.scale(scale, scale, left, top);
if
(mMainView
instanceof
BrowserWebView) {
((BrowserWebView)mMainView).drawContent(c);
}
else
{
mMainView.draw(c);
}
c.restoreToCount(state);
// manually anti-alias the edges for the tilt
c.drawRect(
0
,
0
,
1
, mCapture.getHeight(), sAlphaPaint);
c.drawRect(mCapture.getWidth() -
1
,
0
, mCapture.getWidth(),
mCapture.getHeight(), sAlphaPaint);
c.drawRect(
0
,
0
, mCapture.getWidth(),
1
, sAlphaPaint);
c.drawRect(
0
, mCapture.getHeight() -
1
, mCapture.getWidth(),
mCapture.getHeight(), sAlphaPaint);
c.setBitmap(
null
);
//释放canvas绘制的bitmap
mHandler.removeMessages(MSG_CAPTURE);
persistThumbnail();
TabControl tc = mWebViewController.getTabControl();
if
(tc !=
null
) {
OnThumbnailUpdatedListener updateListener
= tc.getOnThumbnailUpdatedListener();
if
(updateListener !=
null
) {
//通知更新了缩略图
updateListener.onThumbnailUpdated(
this
);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
/**
* 保存离线阅读的一些数据
* @return
*/
public
ContentValues createSnapshotValues() {
if
(mMainView ==
null
)
return
null
;
SnapshotByteArrayOutputStream bos =
new
SnapshotByteArrayOutputStream();
try
{
GZIPOutputStream stream =
new
GZIPOutputStream(bos);
if
(!mMainView.saveViewState(stream)) {
return
null
;
}
stream.flush();
stream.close();
}
catch
(Exception e) {
Log.w(LOGTAG,
"Failed to save view state"
, e);
return
null
;
}
byte
[] data = bos.toByteArray();
ContentValues values =
new
ContentValues();
values.put(Snapshots.TITLE, mCurrentState.mTitle);
//标题
values.put(Snapshots.URL, mCurrentState.mUrl);
//url
values.put(Snapshots.VIEWSTATE, data);
values.put(Snapshots.BACKGROUND, mMainView.getPageBackgroundColor());
//背景
values.put(Snapshots.DATE_CREATED, System.currentTimeMillis());
//时间
values.put(Snapshots.FAVICON, compressBitmap(getFavicon()));
//网址图标
Bitmap screenshot = Controller.createScreenshot(mMainView,
Controller.getDesiredThumbnailWidth(mContext),
Controller.getDesiredThumbnailHeight(mContext));
values.put(Snapshots.THUMBNAIL, compressBitmap(screenshot));
return
values;
}
public
byte
[] compressBitmap(Bitmap bitmap) {
if
(bitmap ==
null
) {
return
null
;
}
ByteArrayOutputStream stream =
new
ByteArrayOutputStream();
bitmap.compress(CompressFormat.PNG,
100
, stream);
return
stream.toByteArray();
}
|
1
2
3
4
5
6
7
8
9
10
11
|
private
void
handleProceededAfterSslError(SslError error) {
if
(error.getUrl().equals(mCurrentState.mUrl)) {
// The security state should currently be SECURITY_STATE_SECURE.
setSecurityState(SecurityState.SECURITY_STATE_BAD_CERTIFICATE);
mCurrentState.mSslCertificateError = error;
}
else
if
(getSecurityState() == SecurityState.SECURITY_STATE_SECURE) {
// The page's main resource is secure and this error is for a
// sub-resource.
setSecurityState(SecurityState.SECURITY_STATE_MIXED);
}
}
|
1
2
3
4
5
6
7
8
9
10
|
/**
* Called when an SSL error occurred while loading a resource, but the
* WebView but chose to proceed anyway based on a decision retained
* from a previous response to onReceivedSslError(). We update our
* security state to reflect this.
*/
@Override
public
void
onProceededAfterSslError(WebView view, SslError error) {
handleProceededAfterSslError(error);
}
|
原文地址:http://my.oschina.net/sfshine/blog/198022