android原生browser分析(二)--界面篇

来源:互联网 发布:域名注册不用实名 编辑:程序博客网 时间:2024/06/03 14:35

本文转载,感觉写的很详细,原文地址:http://blog.csdn.net/android_hasen/article/details/32910627


我们先看一张浏览器的主界面,上面标示浏览器界面各部分对应的类,这里是以平板上的界面为例。给张图是为了给大家一个直观的感觉。

 

  

BrowserActivity是整个应用的主界面,在onCreate中创建了Controller对象,Controller对象是整个应用最重要的管理类,这个后面再说。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.     public void onCreate(Bundle icicle) {  
  3.        mController = createController();  
  4. }  

Controller的创建中新建了UI类,UI类是最主要的视图类,它虽然不是View类的子类,只是一个包含很多抽象方法的接口,但是它的实现类包含了重要的View视图成员。后面将通过UI的实现类BaseUi将这些视图成员和BrowserActivity中布局文件中视图ID一一对应起来,关于这点后面描述。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1.    
  2. private Controller createController() {  
  3.         Controller controller = new Controller(this);  
  4.         boolean xlarge = isTablet(this);  
  5.         UI ui = null;  
  6.         if (xlarge) {  
  7.             ui = new XLargeUi(this, controller);  
  8.         } else {  
  9.             ui = new PhoneUi(this, controller);  
  10.         }  
  11.         controller.setUi(ui);  
  12.         return controller;  
  13. }  
  14.    

由上,我们看到根据isTablet() 方法获取的值,将会创建不同的UI类。

看一下isTablet()方法:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static boolean isTablet(Context context) {  
  2.             return context.getResources().getBoolean(R.bool.isTablet);  
  3.    }  

可以看出,这里是通过一个资源文件的值来确定的,实际上这里是用来区分这个是手机应用还是平板应用的。取值为true的时候获取的是XLargeUi对象,取值为false的时候,获取的是PhoneUi对象。由于我的项目是平板的,就以XLargeUi 为例进行分析。

 

在此,我们把这几个类的继承关系理一理:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public interface UI {  
  2. //....  
  3. }  
  4.    
  5. public abstract class BaseUi implements UI {  
  6. //...  
  7. }  
  8.    
  9. public class XLargeUi extends BaseUi {  
  10. //...  
  11. }  
  12.    
  13. public class PhoneUi extends BaseUi {  
  14. //...  
  15. }  
  16.    

我们现在来看看XLargeUi 的定义:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class XLargeUi extends BaseUi {  
  2.    
  3.     private ActionBar mActionBar;  
  4.     private TabBar mTabBar;  
  5.    
  6.     private NavigationBarTablet mNavBar;  
  7.    
  8.     /** 
  9.      * @param browser 
  10.      * @param controller 
  11.      */  
  12.     public XLargeUi(Activity browser, UiController controller) {  
  13.         super(browser, controller);  
  14.         //other code  
  15.         mNavBar = (NavigationBarTablet) mTitleBar.getNavigationBar();  
  16.         mTabBar = new TabBar(mActivity, mUiController, this);  
  17.         mActionBar = mActivity.getActionBar();  
  18.         setupActionBar();  
  19.     }  
  20.    
  21.     private void setupActionBar() {          mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);  
  22.         mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);  
  23.         mActionBar.setCustomView(mTabBar);  
  24. }  
  25.    
  26. //other code  
  27. }  

构造方法中传入了两个参数,第一个是应用的主界面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共有的父类。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public abstract class BaseUi implements UI {  
  2.    
  3.     Activity mActivity;  
  4.     UiController mUiController;  
  5.     TabControl mTabControl;  
  6.    
  7.     private UrlBarAutoShowManager mUrlBarAutoShowManager;  
  8.    
  9.     protected TitleBar mTitleBar;  
  10.     private NavigationBarBase mNavigationBar;  
  11.     protected PieControl mPieControl;  
  12.    
  13.     public BaseUi(Activity browser, UiController controller) {  
  14.         mActivity = browser;  
  15.         mUiController = controller;  
  16.         mTabControl = controller.getTabControl();  
  17.      
  18.         FrameLayout frameLayout = (FrameLayout) mActivity.getWindow()  
  19.                 .getDecorView().findViewById(android.R.id.content);  
  20.         LayoutInflater.from(mActivity)  
  21. .inflate(R.layout.custom_screen, frameLayout);  
  22.         //...  
  23.         setFullscreen(BrowserSettings.getInstance().useFullscreen());  
  24.         mTitleBar = new TitleBar(mActivity, mUiController, this,  
  25.                 mContentView);  
  26.         mTitleBar.setProgress(100);  
  27.         mNavigationBar = mTitleBar.getNavigationBar();  
  28.         mUrlBarAutoShowManager = new UrlBarAutoShowManager(this);  
  29. }  
  30. }  

先从构造方法来看:

构造方法传入了两个参数:第一个是应用的主界面BrowserActivity,第二个是UiController 对象,也就是创建XLargeUi时传入的两个参数。

 

构造方法中主要完成了如下的事情:

1、通过UiController 对象获取TabControl类型的对象mTabControl 

2、为BrowserActivity设置视图。查看BrowserActivity的代码,通篇没有找到setContentView的影子,那么它是怎么为activity设置视图的呢?原来是在这里。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. FrameLayout frameLayout = (FrameLayout) mActivity.getWindow()  
  2.                 .getDecorView().findViewById(android.R.id.content);  
  3.     LayoutInflater.from(mActivity)  
  4. .inflate(R.layout.custom_screen, frameLayout);  

这里是将资源文件对应的视图加入到android.R.id.content定义的FrameLayout中。这是怎么回事呢?

原来activity中的setContentView如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public void setContentView(int layoutResID) {  
  2.         getWindow().setContentView(layoutResID);  
  3.         //...  
  4.     }  

Activity中:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public Window getWindow() {  
  2.         return mWindow;  
  3. }  
  4. mWindow = PolicyManager.makeNewWindow(this);  

PolicyManager中:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public final class PolicyManager {  
  2. private static final String POLICY_IMPL_CLASS_NAME =  
  3.         "com.android.internal.policy.impl.Policy";  
  4.    
  5.     private static final IPolicy sPolicy;  
  6.    
  7.     static {  
  8.         try {  
  9.             Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);  
  10.             sPolicy = (IPolicy)policyClass.newInstance();  
  11.         } catch (InstantiationException ex) {  
  12.             throw new RuntimeException( "exception", ex);  
  13.         }   
  14.     }  
  15. public static Window makeNewWindow(Context context) {  
  16.         return sPolicy.makeNewWindow(context);  
  17.     }  
  18. }  

IPolicy 中:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public interface IPolicy {  
  2.     public Window makeNewWindow(Context context);  
  3. }  

Policy

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class Policy implements IPolicy  
  2. //...  
  3. public Window makeNewWindow(Context context) {  
  4.         return new PhoneWindow(context);  
  5.     }  
  6. }  

所以ActivitygetWindow()获取的是PhoneWindow对象。

PhoneWindow继承了Window,并覆写了setContentViewPhoneWindowsetContentView(int layoutResID)方法如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.     public void setContentView(int layoutResID) {  
  3.         if (mContentParent == null) {  
  4.             installDecor();  
  5.         } else {  
  6.             mContentParent.removeAllViews();  
  7.         }  
  8.         mLayoutInflater.inflate(layoutResID, mContentParent);  
  9.         final Callback cb = getCallback();  
  10.         if (cb != null && !isDestroyed()) {  
  11.             cb.onContentChanged();  
  12.         }  
  13.     }  

就是将该布局资源文件填入mContentParent,那么mContentParent是什么呢?看下面

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private void installDecor() {  
  2. if (mContentParent == null) {  
  3.             mContentParent = generateLayout(mDecor);  
  4. //....  
  5. }  
  6. }  
  7. protected ViewGroup generateLayout(DecorView decor) {  
  8. //...  
  9. ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  
  10. //...  
  11. return contentParent;  
  12. }  

由上可知,是由IDID_ANDROID_CONTENT的资源文件定义的。

该值由Window类继承而来,看看定义

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. 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,所以activitysetContentView实际就是将view加入到android.R.id.content定义的ViewGroup中,上面的第二步操作就等价于在BrowserActivity中setContentView

 

3、根据BrowserSettings中的配置值设置是否全屏。

4、新建一个TitleBar对象。需要传入的参数是BrowserActivity,UiController,BaseUiFrameLayout,前两个参数是作为BaseUi构造方法的参数传进来的,第三个是BaseUi本身,第四个参数是布局中的一个FrameLayoutTitleBar对象是手机和平板共有的,而TabBar是平板特有的,故有这样的设计。

5、设置TitleBar中的ProgressBar的最大值为100。这个ProgressBar也就是显示加载网页的进度的。加载时显现,加载完毕时消失。

6、获得NavigationBarBase对象,在平板中获得的是NavigationBarTablet,在手机中获得的是NavigationBarPhone.即导航栏。

7、创建一个UrlBarAutoShowManager对象,该对象用来控制网页滚动过程中显示和隐藏标题栏TitleBar.

 

回过头来看一下XLargeUi ,我们提到了NavigationBarTablet类型的对象mNavBar和TabBar类型的对象,为什么这两个不在BaseUi里面定义呢?

这是因为这两个是平板界面中特有的,手机界面中不存在。mNavBar在手机界面中是转为NavigationBarPhone类型的,而TabBar是选项卡栏,手机屏幕小,所有没有选项卡栏。


0 0