今天介绍一下浏览器PieMenu的实现, Piemenu是浏览器的一个实验室功能, 但是其效果还是挺炫的如下所示:
估计很多app都的扇形菜单都参考过这个东西.而且这个东西一直起来也不难, 解耦做的还是比较好的~
今天这个文章先介绍一下Piemenu的大致架构, 其UML图如下:(使用了astah 可能有些类型是错的, ;懒得一个个改了 请谅解, 我们需要的是大体架构~)_
可以看到, Piemenu其实是一个FrameLayout, 遮罩在tab的上层, 这样用户点击这个Layout 如果是在屏幕的边缘, 就可以显示这个
PieMenu了.
这个PieMenu是完全的draw的, 给定一个点mCenter然后app就以这个点为中心, 把每个PieItem也就是扇叶绘制出来. 对于扇叶的组装, piemenu的显示等, 都是PieControl来控制的, 时序图:
按照时序图来分析一下:
添加Piemenu的最开始是在PhoneUI的构造函数中:
public PhoneUi(Activity browser, UiController controller) {
super(browser, controller);
setUseQuickControls(BrowserSettings.getInstance().useQuickControls()); //设置快速控制菜单,就是那个piemenu
mNavigationBar = (NavigationBarPhone) mTitleBar.getNavigationBar();
TypedValue heightValue = new TypedValue();
browser.getTheme().resolveAttribute(
com.android.internal.R.attr.actionBarSize, heightValue, true);
mActionBarHeight = TypedValue.complexToDimensionPixelSize(heightValue.data,
browser.getResources().getDisplayMetrics());
}
然后就是添加PieMenu到PhoneUI的顶层了:
if (useQuickControls) {
mPieControl = new PieControlPhone(mActivity, mUiController, this);
mPieControl.attachToContainer(mContentView);//把piemenu添加到contentview之上
WebView web = getWebView();
if (web != null) {
web.setEmbeddedTitleBar(null);
}
} else {//如果设置的是关闭 可能需要把之前的移除掉
if (mPieControl != null) {
mPieControl.removeFromContainer(mContentView);
}
WebView web = getWebView();
if (web != null) {
// make sure we can re-parent titlebar
if ((mTitleBar != null) && (mTitleBar.getParent() != null)) {
((ViewGroup) mTitleBar.getParent()).removeView(mTitleBar);
}
web.setEmbeddedTitleBar(mTitleBar);
}
setTitleGravity(Gravity.NO_GRAVITY);
}
attachToContainer是真正的添加到PhoneUI的逻辑, 果然是添加到整个浏览器的顶层:
protected void attachToContainer(FrameLayout container) {
if (mPie == null) {
mPie = new PieMenu(mActivity);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
mPie.setLayoutParams(lp);
populateMenu();//添加pieitem
mPie.setController(this);
}
container.addView(mPie);//添加到整个浏览器的顶层: }
public PieMenu(Context context) {
super(context);
init(context);
}
private void init(Context ctx) {
mItems = new ArrayList<PieItem>();//初始化pie中的item
mLevels = 0;//peimenu可以有几层. 用这个标志位来标志是一级菜单还是二级菜单
mCounts = new int[MAX_LEVELS];
Resources res = ctx.getResources();
mRadius = (int) res.getDimension(R.dimen.qc_radius_start); //内径
mRadiusInc = (int) res.getDimension(R.dimen.qc_radius_increment);//外径
mSlop = (int) res.getDimension(R.dimen.qc_slop); //应该是光晕, 周围有一部分光晕
mTouchOffset = (int) res.getDimension(R.dimen.qc_touch_offset); //其实这个点击的中心并不是圆形的正中
mOpen = false;
setWillNotDraw(false);//自定义了viewgroup的 ondraw 要设置这个为false这样才能调用ondraw
setDrawingCacheEnabled(false); //当调用setDrawingCacheEnabled方法设置为false, 系统会自动把原来的cache销毁。减小资源占用
mCenter = new Point(0,0);
mBackground = res.getDrawable(R.drawable.qc_background_normal);//背景
mNormalPaint = new Paint();
mNormalPaint.setColor(res.getColor(R.color.qc_normal));//默认是蓝色
mNormalPaint.setAntiAlias(true);
mSelectedPaint = new Paint();
mSelectedPaint.setColor(res.getColor(R.color.qc_selected));//选中是黄色
mSelectedPaint.setAntiAlias(true);
}
这个操作在不同的设备上展示是不同的, 我们只看在Phone上的展现也就是PieControlPhone:
protected void populateMenu() {
mUrl = makeItem(R.drawable.ic_web_holo_dark, 1);
View tabs = makeTabsView();
mShowTabs = new PieItem(tabs, 1);
mTabAdapter = new TabAdapter(mActivity, mUiController);//这是展示那个tab的小listview
PieStackView stack = new PieStackView(mActivity); //这个东西实际上是 piemenu 选择tab的时候显示的那个tab listview 后面讲解.
stack.setLayoutListener(new OnLayoutListener() {
@Override
public void onLayout(int ax, int ay, boolean left) {
buildTabs();
}
});
stack.setOnCurrentListener(mTabAdapter);
stack.setAdapter(mTabAdapter);
mShowTabs.setPieView(stack);//设置tab多窗口的列表
mOptions = makeItem(com.android.internal.R.drawable.ic_menu_moreoverflow_normal_holo_dark,
1);
// level 1
mNewTab = makeItem(R.drawable.ic_new_window_holo_dark, 1);
mBookmarks = makeItem(R.drawable.ic_bookmarks_holo_dark, 1);
mPie.addItem(mNewTab);
mPie.addItem(mShowTabs);
mPie.addItem(mUrl);
mPie.addItem(mBookmarks);
mPie.addItem(mOptions);
setClickListener(this, mUrl, mShowTabs, mOptions, mNewTab, mBookmarks);
mPopup = new PopupMenu(mActivity, mUi.getTitleBar());
Menu menu = mPopup.getMenu();//显示menu菜单
mPopup.getMenuInflater().inflate(R.menu.browser, menu);
mPopup.setOnMenuItemClickListener(this);
}
//生成各种piemenuitem的图标
protected PieItem makeItem(int image, int l) {
ImageView view = new ImageView(mActivity);
view.setImageResource(image);
view.setMinimumWidth(mItemSize);
view.setMinimumHeight(mItemSize);
view.setScaleType(ScaleType.CENTER);
LayoutParams lp = new LayoutParams(mItemSize, mItemSize);
view.setLayoutParams(lp);
return new PieItem(view, l);
}
这就是大致的PieMenu的架构了,可以看到还是谷歌常用的mvc架构, 感觉这个架构设计的挺好的, 思路很清晰以及:
统一的setting模块.
业务和展示分开,
使用继承来实现手机和平板的适配,
每个扇形都是一个PieItem类, 方便将来绘制逻辑以及事件分发逻辑的处理等,
在这个app的顶部遮罩framelayout, 从而实现点击任何屏幕编译都可以显示piemenu
这些都是值得我们学习的.
这里还有个移植的小案例:http://blog.csdn.net/libre923045/article/details/7800227