文档章节

Android Browser学习九 快捷菜单模块: PieControl的架构

SuShine
 SuShine
发布于 2014/03/26 22:06
字数 1164
阅读 829
收藏 2

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



这里我们看到 了BrowserSettings的用法, 不过是读取了Sharepreference的数据而已.  但这样做实现全局的setting控制还是很值得学习的.

然后就是添加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);//添加到整个浏览器的顶层: }



那块看一下Piemenu的初始化, 是把PieMenu的画笔, 颜色. 大小等都初始化了出来:
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);
    }



前面说过, PieMenu的填充items是PieContrl来控制的, 也就是populateMenu这个函数来实现的填充扇子,

这个操作在不同的设备上展示是不同的, 我们只看在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);
    }



真正的制造Item是在makeItem等函数中:
//生成各种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