今天介绍一下浏览器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