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

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是选项卡栏,手机屏幕小,所有没有选项卡栏。

android6.0原生brower_android原生browser分析(二)--界面篇相关推荐

  1. Android原生(Native)C开发之二 framebuffer篇

    为什么80%的码农都做不了架构师?>>>    Android原生(Native)C开发之二 framebuffer篇 如对Android原生(Natvie)C开发还任何疑问,请参阅 ...

  2. android6.0中app crash流程分析

    要根据这个流程分析一下如何在应用中截获系统的app crash弹框,然后做到更人性化 基于Android 6.0的源码剖析, 分析Android应用Crash是如何处理的. /frameworks/b ...

  3. Android 系统(41)---Android7.0 PowerManagerService亮灭屏分析(二)

    Android7.0 PowerManagerService亮灭屏分析(二) 3029 在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerReque ...

  4. Android6.0 Sensor架构和问题分析

    本文在借鉴网友的资料后再重新梳理了一遍,都是站在前人的基础.巨人的肩膀上再次总结分析出来的,仅供大家参考! 本文主要描述了在Android 6.0系统.MTK6755平台上sensor相关软硬件的体系 ...

  5. Android6.0的Looper源码分析(1)

    1      Looper简介 Android在Java标准线程模型的基础上,提供了消息驱动机制,用于多线程之间的通信.而其具体实现就是Looper. Android Looper的实现主要包括了3个 ...

  6. 值得你关注的Android6.0上的重要变化(二)

    十.Android KeyStore变化   此版本上Android Keystore provider不再支持DSA,仍旧支持ECDSA.   锁屏密码在(如用户或设备管理器)禁用或重置的情况下,不 ...

  7. Android6.0 mtk去除原生相机设置中的选项

    Android去除原生相机设置中的录像中的一些选项,其中代码路径是: 6753_M\alps\vendor\mediatek\proprietary\packages\apps\Camera\src\ ...

  8. Android7.0 PowerManagerService亮灭屏分析(二)

    在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerRequest中传入DisplayPowerController中进一步处理.在亮屏过程中Disp ...

  9. [Android6.0][RK3399] PWM Backlight 驱动分析

    DTS 分析 backlight: backlight {status = "disabled";compatible = "pwm-backlight";pw ...

最新文章

  1. python列表遍历 空列表_Python list列表执行reversed()后执行pop()返回迭代对象遍历为空问题...
  2. 最短路径——Floyd算法HDU Today(hdu2112)
  3. ROS笔记(13) 记录与回放数据
  4. gradle 不支持多级子模块_解决gradle多模块依赖在Idea中能运行,gradle build失败的问题。...
  5. lda 协方差矩阵_数据降维算法总结(LDAamp;PCA)
  6. react ssr php,一文吃透 React SSR 服务端渲染和同构原理
  7. 〖Demo〗-- 多级评论
  8. Java面试题:程序计数器为什么是私有的?
  9. Socket TCP UDP
  10. 从超大规模云服务提供商处学习效率
  11. Multisim14.1中/英文版软件下载和安装教程|兼容WIN10
  12. Linux redis常用命令
  13. 关于彻底卸载手心输入法的终极操作
  14. Nginx-RTMP功能调研
  15. Unity内动态影子的各种做法
  16. Aspose.Words 将Word(DOC / DOCX)转换为HTML教程
  17. telegraf 使用 inputs.exec插件收集监控数据
  18. Spring Boot(二): 集成Mybatis
  19. 2021年全球与中国重型泥浆泵行业市场规模及发展前景分析
  20. Python全栈面试题

热门文章

  1. java+lodop+vue+热敏打印机,打印图片
  2. spring boot初步
  3. 前后端分离的思考与实践(六)
  4. ORACLE 建库过程总结
  5. 分布式事务一致性方案
  6. PADS 9.5封装向导 多一个管脚
  7. Extjs的数据读取器store和后台返回类型简单解析
  8. 用来向登录页面输出验证码图片的一般处理程序页面
  9. Delphi 与 DirectX 之 DelphiX(60): TDIB.DoTrace();
  10. IDC:无线数字化转型持续进行 第二季度全球企业WLAN市场强劲增长