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

来源:互联网 发布:流氓推广软件是什么 编辑:程序博客网 时间:2024/05/01 16:37

作者:jason.chen


  从状态栏往下拉的时候,就会出现“快速设置”界面,如下图所示。快速启动界面可以让用户快速设置和操作。对于急需或频繁使用的控件和操作,保留“快速设置”图块,且不应将其用作启动应用的快捷方式。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
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 毕业了要搬宿舍怎么办 中专学历认证已停止怎么办 中专不做学历认证考试怎么办 大学生欠学费被扣毕业证怎么办 考警校体检没过怎么办 美国签证申请预约名字写错怎么办 当兵不从学校走怎么办 门牙崩了一小块怎么办 遇到很难过的事情怎么办 小孩子上课精力不集中怎么办 每天工作都很累压力大怎么办 重体力活搬不动怎么办 大学没参加体测怎么办 英文写的很丑怎么办 患有勃起障碍应该怎么办较好 运动过度小腿肌肉酸痛怎么办 高考有纹身是字怎么办 新生儿测听力没过关怎么办 色弱高考体检时没查出来怎么办 公司福利体检查二对半怎么办 高考体检表复印件丢了怎么办 高考体检表身高填错了怎么办 大学档案高考体检表丢了怎么办 工厂组织体检我有乙肝怎么办 我有乙肝单位组织体检怎么办? 矮腰袜子老掉怎么办 短腰袜子老下滑怎么办 中考体检结果丢了怎么办 咳嗽左胸围一处刺痛怎么办? 阴茎小父母催婚怎么办 头发扎进指甲缝怎么办 指甲缝扎流血了怎么办 中考考差了高中怎么办 骨折后我抽烟了怎么办 五年级科学考不好怎么办 考试连续考差了怎么办 客户说没考虑好怎么办 小孩生殖器痒经常用手抓怎么办 孩子在幼儿园生殖器官受伤怎么办? 被骗了3000块钱怎么办 小说 月经来了想体检怎么办