前面說了這麼多卻發現咱們的Tab尚未介紹, Tab究竟是個什麼東西呢? java
實際上是一個含有兩個WebView 成員一個WebViewController成員的類: 其中PageState用來真正存儲這個tab網頁的一些信息,包括url 標題 圖標等 android
// 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;
<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>
@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進行的, 可是這裏設計就有點亂了: web
順序是這樣: Controller::setActiveTab -> TabControl::setCurrentTab -> Tab::setWebView -> Controler ::onSetWebView -> BaseUI::onSetWebView canvas
Controller調用 BaseUI 的setActiveTab函數 (其實最後是attachTabToContentView)把Tab的mWebView和container 關聯起來: app
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中去 ide
之因此這樣作, 多是由於做者想讓 BaseUI來進行把View attach到Activity上的操做 , Tab只作控制Webview load網頁的操做; 他們之間的交互由Controller來控制.可是感受這個設計可能有些亂了. 函數
Tab獲取當前快照的函數: 佈局
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); } } }
/** * 保存離線閱讀的一些數據 * @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(); }
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); } }
/** * 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); }