標籤:android browser 瀏覽器 ui
我們先看一張瀏覽器的主介面,上面標示瀏覽器介面各部分對應的類,這裡是以平板上的介面為例。給張圖是為了給大家一個直觀的感覺。
BrowserActivity是整個應用的主介面,在onCreate中建立了Controller對象,Controller對象是整個應用最重要的管理類,這個後面再說。
@Override public void onCreate(Bundle icicle) { mController = createController();}
Controller的建立中建立了UI類,UI類是最主要的視圖類,它雖然不是View類的子類,只是一個包含很多抽象方法的介面,但是它的實作類別包含了重要的View視圖成員。後面將通過UI的實作類別BaseUi將這些視圖成員和BrowserActivity中布局檔案中視圖ID一一對應起來,關於這點後面描述。
private Controller createController() { Controller controller = new Controller(this); boolean xlarge = isTablet(this); UI ui = null; if (xlarge) { ui = new XLargeUi(this, controller); } else { ui = new PhoneUi(this, controller); } controller.setUi(ui); return controller;}
由上,我們看到根據isTablet() 方法擷取的值,將會建立不同的UI類。
看一下isTablet()方法:
public static boolean isTablet(Context context) { return context.getResources().getBoolean(R.bool.isTablet); }
可以看出,這裡是通過一個資源檔的值來確定的,實際上這裡是用來區分這個是手機應用還是平板應用的。取值為true的時候擷取的是XLargeUi對象,取值為false的時候,擷取的是PhoneUi對象。由於我的項目是平板的,就以XLargeUi 為例進行分析。
在此,我們把這幾個類的繼承關係理一理:
public interface UI {//....} public abstract class BaseUi implements UI {//...} public class XLargeUi extends BaseUi {//...} public class PhoneUi extends BaseUi {//...}
我們現在來看看XLargeUi 的定義:
public class XLargeUi extends BaseUi { private ActionBar mActionBar; private TabBar mTabBar; private NavigationBarTablet mNavBar; /** * @param browser * @param controller */ public XLargeUi(Activity browser, UiController controller) { super(browser, controller); //other code mNavBar = (NavigationBarTablet) mTitleBar.getNavigationBar(); mTabBar = new TabBar(mActivity, mUiController, this); mActionBar = mActivity.getActionBar(); setupActionBar(); } private void setupActionBar() { mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); mActionBar.setCustomView(mTabBar);} //other code}
構造方法中傳入了兩個參數,第一個是應用的主介面BrowserActivity,第二個是UiController 對象,該對象主要做Ui進行控制,如對選項卡的操作,載入URL等。
建構函式中主要做了下面的事情:
1、通過TitleBar類型成員變數mTitleBar擷取NavigationBarTablet類型的對象mNavBar ,這個對象即是導航工具列。就是瀏覽器介面的如下的工具列
該對象主要用於更新導覽列的狀態,即對前進後退鍵、URL輸入框、URL表徵圖進行操作。
成員變數mTitleBar是從BaseUi繼承而來的。
2、新建立一個TabBar類型的對象,這個TabBar對象是只有平板才有的。建立時傳入主介面BrowserActivity、UiController 對象、XLargeUi自身。建立的對象即選項卡欄
該對象將用來進行選項卡的相關操作,增加、刪除、更新選項卡,改變收藏夾表徵圖favicon,修改URL標題等。
3、通過主介面BrowserActivity擷取ActionBar對象。
4、設定ActionBar的樣式,並將選項卡欄TabBar對象設定為ActionBar的自訂視圖。
關於BaseUi
BaseUi是平板介面XLargeUi和手機介面PhoneUi共有的父類。
public abstract class BaseUi implements UI { Activity mActivity; UiController mUiController; TabControl mTabControl; private UrlBarAutoShowManager mUrlBarAutoShowManager; protected TitleBar mTitleBar; private NavigationBarBase mNavigationBar; protected PieControl mPieControl; public BaseUi(Activity browser, UiController controller) { mActivity = browser; mUiController = controller; mTabControl = controller.getTabControl(); FrameLayout frameLayout = (FrameLayout) mActivity.getWindow() .getDecorView().findViewById(android.R.id.content); LayoutInflater.from(mActivity).inflate(R.layout.custom_screen, frameLayout); //... setFullscreen(BrowserSettings.getInstance().useFullscreen()); mTitleBar = new TitleBar(mActivity, mUiController, this, mContentView); mTitleBar.setProgress(100); mNavigationBar = mTitleBar.getNavigationBar(); mUrlBarAutoShowManager = new UrlBarAutoShowManager(this);}}
先從構造方法來看:
構造方法傳入了兩個參數:第一個是應用的主介面BrowserActivity,第二個是UiController 對象,也就是建立XLargeUi時傳入的兩個參數。
構造方法中主要完成了如下的事情:
1、通過UiController 對象擷取TabControl類型的對象mTabControl 。
2、為BrowserActivity設定視圖。查看BrowserActivity的代碼,通篇沒有找到setContentView的影子,那麼它是怎麼為activity設定視圖的呢?原來是在這裡。
FrameLayout frameLayout = (FrameLayout) mActivity.getWindow() .getDecorView().findViewById(android.R.id.content); LayoutInflater.from(mActivity).inflate(R.layout.custom_screen, frameLayout);
這裡是將資源檔對應的視圖加入到android.R.id.content定義的FrameLayout中。這是怎麼回事呢?
原來activity中的setContentView如下:
public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); //... }
Activity中:
public Window getWindow() { return mWindow;}mWindow = PolicyManager.makeNewWindow(this);
PolicyManager中:
public final class PolicyManager {private static final String POLICY_IMPL_CLASS_NAME = "com.android.internal.policy.impl.Policy"; private static final IPolicy sPolicy; static { try { Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME); sPolicy = (IPolicy)policyClass.newInstance(); } catch (InstantiationException ex) { throw new RuntimeException( "exception", ex); } }public static Window makeNewWindow(Context context) { return sPolicy.makeNewWindow(context); }}
IPolicy 中:
public interface IPolicy { public Window makeNewWindow(Context context);}
Policy中
public class Policy implements IPolicy//...public Window makeNewWindow(Context context) { return new PhoneWindow(context); }}
所以Activity的getWindow()擷取的是PhoneWindow對象。
而PhoneWindow繼承了Window,並覆寫了setContentView,PhoneWindow中setContentView(int layoutResID)方法如下:
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
就是將該布局資源檔填入mContentParent,那麼mContentParent是什麼呢?看下面
private void installDecor() {if (mContentParent == null) { mContentParent = generateLayout(mDecor);//....}}protected ViewGroup generateLayout(DecorView decor) {//...ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//...return contentParent;}
由上可知,是由ID為ID_ANDROID_CONTENT的資源檔定義的。
該值由Window類繼承而來,看看定義
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
mActivity.getWindow()獲得一個Window對象,該對象調用getDecorView()找到的android.R.id.content,正是此處的com.android.internal.R.id.content,所以activity的setContentView實際就是將view加入到android.R.id.content定義的ViewGroup中,上面的第二步操作就等價於在BrowserActivity中setContentView。
3、根據BrowserSettings中的配置值設定是否全屏。
4、建立一個TitleBar對象。需要傳入的參數是BrowserActivity,UiController,BaseUi,FrameLayout,前兩個參數是作為BaseUi構造方法的參數傳進來的,第三個是BaseUi本身,第四個參數是布局中的一個FrameLayout。TitleBar對象是手機和平板共有的,而TabBar是平板特有的,故有這樣的設計。
5、設定TitleBar中的ProgressBar的最大值為100。這個ProgressBar也就是顯示載入網頁的進度的。載入時顯現,載入完畢時消失。
6、獲得NavigationBarBase對象,在平板中獲得的是NavigationBarTablet,在手機中獲得的是NavigationBarPhone.即導覽列。
7、建立一個UrlBarAutoShowManager對象,該對象用來控制網頁滾動過程中顯示和隱藏標題列TitleBar.
回過頭來看一下XLargeUi ,我們提到了NavigationBarTablet類型的對象mNavBar和TabBar類型的對象,為什麼這兩個不在BaseUi裡面定義呢?
這是因為這兩個是平板介面中特有的,手機介面中不存在。mNavBar在手機介面中是轉為NavigationBarPhone類型的,而TabBar是選項卡欄,手機螢幕小,所有沒有選項卡欄。