Android Browser學習五 多窗口: Tab 總體結構

前面說了這麼多卻發現咱們的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;



其中mContainer就是tab的主Ui 了 他通常是一個形如"


<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>



這樣的佈局, 經過 BaseUI的onSetWebView設置:



@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);
            }
        }
    }



Tab 保存 當前網頁的函數


/**
     * 保存離線閱讀的一些數據
     * @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();
    }



處理SSLError的函數


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);
        }
相關文章
相關標籤/搜索