android 7.0 system UI之快速启动栏的分析(一)

来源:互联网 发布:软件项目介绍 编辑:程序博客网 时间:2024/05/21 22:44

转自:http://blog.csdn.net/maetelibom/article/details/53896076


  从状态栏往下拉的时候,就会出现“快速设置”界面,如下图所示。快速启动界面可以让用户快速设置和操作。对于急需或频繁使用的控件和操作,保留“快速设置”图块,且不应将其用作启动应用的快捷方式。android7.0添加了新的API,让开发者可以为自己的应用添加快速设置图标。这篇文件将要分析快速启动的设计。当然,只是个人见解。
快速设置界面     这里写图片描述
  快速设置图标有两种类型,一种是system UI预置的(QSTile),另一种是第三方应用添加的(CustiomTile)。system UI使用Tile来代表一个快速设置。本文只介绍system UI预置的QSTile,关于第三方应用的Tile后续再分析。
  

一、Tile数据加载

1、主要类介绍

  Tile数据相关类图
  QSTile:快速设置Tile的基类,快速设置都继承自这个类。它使用Host提供的Looper, 管理着快速设置(以下文章都用Tile来指代快速设置)的状态(State)变化。Tile通过重载handleUpdateState方法来更新状态。如果监听到状态变化,或者点击事件需要更新状态,使用refreshState来更新State。
  QSTileHost: Host的实现,管理Tile 状态的变化。它包含各种Tile的Control类,用来设置和监听Tile。
  State: Tile的状态,包含图标,名称等信息。
  H:继承自Handler, 通过looper跑在QSTileHost的线程里。用来处理Tile的各种事件。
  Tunable: 接口,配置变化的回调。QSTileHost实现这个接口。
  TunerSercice:用来监听各种配置变化。

2、Tile加载流程

这里从创建View开始跟踪(之前的流程可以参考http://blog.csdn.net/zhudaozhuan/article/details/50817180),过程如下图所示:
这里写图片描述
在TunerService的addTunable的方法,会监听ContentProvider中的Tile的变化(一般是增加或减少Tile),监听的函数是registerContentObserver。Tile是存储在Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser),相关代码如下:

    private void addTunable(Tunable tunable, String key) {        if (!mTunableLookup.containsKey(key)) {            mTunableLookup.put(key, new ArraySet<Tunable>());        }        mTunableLookup.get(key).add(tunable);        Uri uri = Settings.Secure.getUriFor(key);        if (!mListeningUris.containsKey(uri)) {            mListeningUris.put(uri, key);            //listening for the tile change            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);        }        // Send the first state.        String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);        tunable.onTuningChanged(key, value);    }

  当Tile发生变化或第一次添加的时候,会执行QSTileHost的onTuningChanged的方法,在这个方法里会创建所有需要显示的Tile。部分代码如下:
  

       for (String tileSpec : tileSpecs) {            QSTile<?> tile = mTiles.get(tileSpec);            if (tile != null && (!(tile instanceof CustomTile)                    || ((CustomTile) tile).getUser() == currentUser)) {                if (DEBUG) Log.d(TAG, "Adding " + tile);                tile.removeCallbacks();                newTiles.put(tileSpec, tile);            } else {                if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);                try {                    tile = createTile(tileSpec);                    if (tile != null && tile.isAvailable()) {                        tile.setTileSpec(tileSpec);                        newTiles.put(tileSpec, tile);                    }                } catch (Throwable t) {                    Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);                }            }        }        mCurrentUser = currentUser;        mTileSpecs.clear();        mTileSpecs.addAll(tileSpecs);        mTiles.clear();        mTiles.putAll(newTiles);        for (int i = 0; i < mCallbacks.size(); i++) {            mCallbacks.get(i).onTilesChanged();        }

  createTile方法,根据tileSpec创建响应的Tile,使用的是工厂方法。最终会把创建的Tile都存储在QSTileHost的mTiles,如果有注册QSTile.Host.Callback的监听,也会回调响应的onTilesChanged方法。
  自此,QSTileHost已经获取到了Tile的数据。

二、未展开的界面显示

  显示Tile的地方有两个,请看前面的两张图,一个是未展开的状态,只显示前面几个Tile;另一个是全部展开的状态,可以显示所有的Tile,还有其他的设置功能。这里先介绍未展开的Tile界面。
  这里写图片描述

1.主要类介绍

这里写图片描述
  QSPanel: 用来显示快速设置的界面。
  QuickQSPanel: QSPanel的子类,里面包含一个HeaderTileLayout,用来显示少数几个Tile的界面。在快速设置界面的顶部。
  HeaderTileLayout: QuickQSPanel创建的View,用来显示少数几个Tile的界面。
  TileRecord: 包含Tile数据,Tile界面(QSTileBaseView)等数据。
  QSTileBaseView: 继承自LinearLayout,用来显示Tile。不显示标题。
  QSTileView: 继承自QSTileBaseView, 用来显示Tile,会显示标题。HeaderTileLayout不使用这个。
  

2. 界面显示流程

    未展开的Tile显示在QuickQSPanel的HeaderTileLayout里。可以查看QuickStatusBarHeader的xml文件:quick_status_bar_expanded_header.xml。在QuickQSPanel的构造函数中,创建并加入HeaderTileLayout:

    public QuickQSPanel(Context context, AttributeSet attrs) {        super(context, attrs);        if (mTileLayout != null) {            for (int i = 0; i < mRecords.size(); i++) {                mTileLayout.removeTile(mRecords.get(i));            }            removeView((View) mTileLayout);        }        mTileLayout = new HeaderTileLayout(context);        mTileLayout.setListening(mListening);        addView((View) mTileLayout, 1 /* Between brightness and footer */);    }

  它的父类QSPanel的构造函数会加入一个Brightness View和Footer View, 这里并不需要显示。加载Tile的时序图如下所示:这里写图片描述
  在QSPanel的addTile中,会去创建TileRecord。TileRecord包含Tile的界面和tile的数据,同时还包含一个callback,这在处理事件的时候,会用到。然后在HeaderTileLayout的add Tile方法,会把这个QSTileBaseView添加进去。相关代码代码如下:

    protected void addTile(final QSTile<?> tile, boolean collapsedView) {        final TileRecord r = new TileRecord();        r.tile = tile;        r.tileView = createTileView(tile, collapsedView);        final QSTile.Callback callback = new QSTile.Callback() {            @Override            public void onStateChanged(QSTile.State state) {                drawTile(r, state);            }            @Override            public void onShowDetail(boolean show) {                // Both the collapsed and full QS panels get this callback, this check determines                // which one should handle showing the detail.                if (shouldShowDetail()) {                    QSPanel.this.showDetail(show, r);                }            }            @Override            public void onToggleStateChanged(boolean state) {                if (mDetailRecord == r) {                    fireToggleStateChanged(state);                }            }            @Override            public void onScanStateChanged(boolean state) {                r.scanState = state;                if (mDetailRecord == r) {                    fireScanStateChanged(r.scanState);                }            }            @Override            public void onAnnouncementRequested(CharSequence announcement) {                announceForAccessibility(announcement);            }        };        r.tile.addCallback(callback);        r.callback = callback;        final View.OnClickListener click = new View.OnClickListener() {            @Override            public void onClick(View v) {                onTileClick(r.tile);            }        };        final View.OnLongClickListener longClick = new View.OnLongClickListener() {            @Override            public boolean onLongClick(View v) {                r.tile.longClick();                return true;            }        };        r.tileView.init(click, longClick);        r.tile.refreshState();        mRecords.add(r);        if (mTileLayout != null) {            mTileLayout.addTile(r);        }    }

三、未展开界面事件处理

主要有点击事件,长按事件等。

1.点击事件

  onClick的事件,参考前面addTile的代码,里面有创建一个Click类,用来监听onClick事件。时序图如下:
这里写图片描述
  1)QuickQSPanel有重载了QSPanle的onTileClick事件,会执行handleSecondaryClick。从上图可以看出,它默认的行为也是执行QSTile的handleClick事件。每个QSTile的子类都可以重载handleSecondaryClick方法,来区别未展开和展开的Tile点击事件。所以,点击QuickQSPanel的Tile界面,执行的是handleSecondaryClick
  2)QSPanel的点击事件流程。触发点击事件后,执行QSTile的handleClick方法。这是一个虚方法,各个QSTile的子类需要重载这个方法。有些Tile会在这里执行刷新状态refreshState, 有些Tile会执行showDetail。
  3)refreshState的流程,QSTile的子类,要在这里更新Tile的State
  4)showDetail的流程。这里有一个要注意的地方是,在QSTile.Callback的onShowDetail方法中,会判断shouldShowDetail的方法。这个方法在未展开的panel里,返回的是!mExpanded, 而在展开的Panel里,返回的是
  QuickQSPanle是QSPanel的子类,可能会重载QSPanel的方法。跟踪代码的时候,需要注意是否有重载。像onTileClick就有重载。
  

2.longClick事件

  长按事件也是在QSPanl的addTile方法里创建的,最后会执行到handleLongClick的方法。在这里会从getLongClickIntent里面,获取一个Intent, 启动该Intent的activity。getLongClickIntent是一个虚方法,每个QSTile的子类, 需要重载该方法。
  

0 0
原创粉丝点击