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

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

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

android原生browser分析(二)--界面篇,布布扣,bubuko.com

时间: 2024-10-24 04:44:22

android原生browser分析(二)--界面篇的相关文章

android原生browser分析(一)--Application

类Browser.java是整个应用的Application.其代码如下: public class Browser extends Application { @Override public void onCreate() { super.onCreate(); // create CookieSyncManager with current Context CookieSyncManager.createInstance(this); BrowserSettings.initialize(

Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

1 背景 还记得前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事件疑惑吗?当时说了,在那一篇咱们只讨论View的触摸事件派发机制,这个疑惑留在了这一篇解释,也就是ViewGroup的事件派发机制. PS:阅读本篇前建议先查看前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>,这一篇承接上一篇. 关于View与ViewGroup的区别在前一篇的A

CM android的CMUpdater分析(二)

至于为何要在这里讲解android系统源码中的系统更新,我已经在上一篇< CM android的CMUpdater分析(一)>中介绍了.在上一篇中,主要讲解了在eclipse中如何搭建系统应用的开发环境,现在我们就使用eclipse来分析CMUpdater源码.该系统更新是CM修改原生android的基础上实现的.通过分析android系统的应用源码,可以学到一些很好的思想和编程方法学.好了,废话少说,现在就开始我们的学习之旅. 首先,在开始介绍之前,我先把之前根据CMUpdater源码分析来

Android Init进程分析番外篇:9.0的init进程

1 前言 在之前的几篇文章中,我们基于Android 4.4源码分析了init进程的启动及事件处理逻辑.目前Android版本已然更新到Android 10了,很多代码及服务架构都做了大范围的调整重构,这其中也包括init相关的代码. 但是,万变不离其宗,很多核心思想和处理逻辑Android还是都保留了下来,只是在写作上做了更好的功能封装和逻辑分割. 接下来笔者就非常简略的说一下Android 9.0 的init进程. 2 Android 9.0 init 分析 对于9.0的init笔者不想做过

(转)Android项目重构之路:界面篇

在前一篇文章<Android项目重构之路:架构篇>中已经简单说明了项目的架构,将项目分为了四个层级:模型层.接口层.核心层.界面层.其中,最上层的界面,是变化最频繁的一个层面,也是最复杂最容易出问题的一个层面,如果规划不好,很容易做着做着,又乱成一团了. 要规划好界面层,至少应该遵循几条基本的原则: 保持规范性:定义好开发规范,包括书写规范.命名规范.注释规范等,并按照规范严格执行: 保持单一性:布局就只做布局,内容就只做内容,各自分离好:每个方法.每个类,也只做一件事情: 保持简洁性:保持代

Android启动流程分析(二) init进程的启动

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# 分析init进程,首先要分析init进程是如何启动的, init的源码位于(system/core/init),我们先来看看init进程的android.mk 下面是从system/core/init模块拿出来的一段code: LOCAL_MODULE:= init LOCAL_

[Android]Fragment源代码分析(二) 状态

我们上一讲,抛出来一个问题,就是当Activity的onCreateView的时候,是怎样构造Fragment中的View參数.要回答这个问题我们先要了解Fragment的状态,这是Fragment管理中很重要的一环.我们先来看一下FragmentActivity提供的一些核心回调: @Override protected void onCreate(Bundle savedInstanceState) { mFragments.attachActivity(this, mContainer,

Android触摸屏事件派发机制详解与源码分析三(Activity篇)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 该篇承接上一篇<Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)>,阅读本篇之前建议先阅读. 1 背景 还记得前面两篇从Android的基础最小元素控件(View)到ViewGroup控件的触摸屏事件分发机制分析吗?你可能看完会有疑惑,View的事件是ViewGro

(转)Android项目重构之路:实现篇

前两篇文章Android项目重构之路:架构篇和Android项目重构之路:界面篇已经讲了我的项目开始搭建时的架构设计和界面设计,这篇就讲讲具体怎么实现的,以实现最小化可用产品(MVP)的目标,用最简单的方式来搭建架构和实现代码. IDE采用Android Studio,Demo实现的功能为用户注册.登录和展示一个券列表,数据采用我们现有项目的测试数据,接口也是我们项目中的测试接口. 项目搭建 根据架构篇所讲的,将项目分为了四个层级:模型层.接口层.核心层.界面层.四个层级之间的关系如下图所示: