Android Browser PieMenu移植

不知道有人发现没Android 3.0开始 浏览器多了个控件

Settings-> Lab ->QuickControls如下图所示


然后回到浏览器触摸两边就有所谓的QuickControl控件可以使用如下图


然后我想能不能把这个控件移植出来在我们自己的程序中可以使用呢。还好Android是开源的

说干就干打开AndroidBrowser源代码分析了下大概是个MVC的架构V中的Control部分去掉就好了剩下的就是我们需要的了

google程序员写的代码那是相当的有条理读起来还是比较方便的 

PieMenu(我喜欢叫它扇形控件)是个FrameLayout  至于什么叫FrameLayout 不懂的可以查看SDK

然后里面的一个个元素叫PieItem ,然后PieItem是个view 可以写字放图片都可以的,里面还是很容易理解的google针对padphone设计了两个布局具体可以查看源代码

搞了大半天  其实只要认真分析下,改点代码还是不难得。终于搞定移植具体效果如下



以后使用呢 就使用如下方法就可以了  事件相应可以写成回调函数的样式,我使用的是Handler的方式

        PieMenu mPie = new PieMenu(this,mHandler);
        FrameLayout mContentView = (FrameLayout) findViewById(R.id.main_content);
        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        mPie.setLayoutParams(lp);
        mContentView.addView(mPie);


/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.kael.peimenudemo;



import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewGroup;

import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;







public class PieMenu extends FrameLayout {

    private static final int MAX_LEVELS = 5;

//    public interface PieController {
//        /**
//         * called before menu opens to customize menu
//         * returns if pie state has been changed
//         */
//        public boolean onOpen();
//    }

    /**
     * A view like object that lives off of the pie menu
     */
    public interface PieView {

        public interface OnLayoutListener {
            public void onLayout(int ax, int ay, boolean left);
        }

        public void setLayoutListener(OnLayoutListener l);

        public void layout(int anchorX, int anchorY, boolean onleft, float angle);

        public void draw(Canvas c);

        public boolean onTouchEvent(MotionEvent evt);

    }

    private Point mCenter;
    private int mRadius;
    private int mRadiusInc;
    private int mSlop;
    private int mTouchOffset;

    private boolean mOpen;
//    private PieController mController;

    private List<PieItem> mItems;
    private int mLevels;
    private int[] mCounts;
    private PieView mPieView = null;

    private Drawable mBackground;
    private Paint mNormalPaint;
    private Paint mSelectedPaint;

    // touch handling
    PieItem mCurrentItem;

    private boolean mUseBackground = false;
    int mItemSize ;
    private Context ct;
    private PieItem mNewTab;
    private PieItem mBookmarks;
    private PieItem mBookmarks_2;
    private PieItem mBookmarks_3;
    private PieItem mBookmarks_4;
    private PieItem mBookmarks_5;
    private PieItem mBookmarks_6;
 //   private PopupMenu mPopup;
    /**
     * @param context
     * @param attrs
     * @param defStyle
     */
    public PieMenu(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    /**
     * @param context
     * @param attrs
     */
    public PieMenu(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    /**
     * @param context
     */
    public PieMenu(Context context,final Handler mhandler) {
        super(context);
        init(context);
        mItemSize = (int) context.getResources().getDimension(R.dimen.qc_item_size);
        this.ct = context;
        mNewTab = makeItem(R.drawable.ic_new_window_holo_dark, 1);
        mBookmarks = makeItem(R.drawable.ic_bookmarks_holo_dark, 1);
        mNewTab.getView().setOnClickListener(new OnClickListener(){

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
          //      Toast.makeText(ct, "hello", Toast.LENGTH_LONG).show();
                Message msg = mhandler.obtainMessage();
                msg.what = 1;
                mhandler.sendMessage(msg);
//                mPopup = new PopupMenu(ct,mNewTab.getView());
//                Menu menu = mPopup.getMenu();
//                mPopup.getMenuInflater().inflate(R.menu.browser, menu);
//                
//              //  mPopup.setOnMenuItemClickListener(this);
//                mPopup.show();
            }});
        mBookmarks_2 = makeItem(R.drawable.ic_bookmarks_holo_dark, 1);
        mBookmarks_3 = makeItem(R.drawable.ic_bookmarks_holo_dark, 2);
        mBookmarks_4 = makeItem(R.drawable.ic_bookmarks_holo_dark, 2);
        mBookmarks_5 = makeItem(R.drawable.ic_bookmarks_holo_dark, 2);
        mBookmarks_6 = makeTextItem(R.drawable.ic_bookmarks_holo_dark, 2);
//level 1
        this.addItem(mNewTab);
        this.addItem(mBookmarks);
        this.addItem(mBookmarks_2);
//level 2        
        this.addItem(mBookmarks_3);
        this.addItem(mBookmarks_4);
        this.addItem(mBookmarks_5);
        this.addItem(mBookmarks_6);
        
    }

    private void init(Context ctx) {
        mItems = new ArrayList<PieItem>();
        mLevels = 1;
        mCounts = new int[MAX_LEVELS];
        Resources res = ctx.getResources();
        mRadius = (int) res.getDimension(R.dimen.qc_radius_start);//60dip
        mRadiusInc = (int) res.getDimension(R.dimen.qc_radius_increment);//70dip
        mSlop = (int) res.getDimension(R.dimen.qc_slop);//10.dip
        mTouchOffset = (int) res.getDimension(R.dimen.qc_touch_offset);//15dip
        mOpen = false;
        setWillNotDraw(false);
        setDrawingCacheEnabled(false);
        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);
    }
//
//    public void setController(PieController ctl) {
//        mController = ctl;
//    }

    public void setUseBackground(boolean useBackground) {
        mUseBackground = useBackground;
    }

    public void addItem(PieItem item) {
        // add the item to the pie itself
        mItems.add(item);
        int l = item.getLevel();
    //    System.out.println("l=="+l);
        mLevels = Math.max(mLevels, l);
        mCounts[l]++;
    }

    public void removeItem(PieItem item) {
        mItems.remove(item);
    }

    public void clearItems() {
        mItems.clear();
    }

    private boolean onTheLeft() {
        return mCenter.x < mSlop;
    }

    /**
     * guaranteed has center set
     * @param show
     */
    private void show(boolean show) {
        mOpen = show;
        if (mOpen) {
//            if (mController != null) {
//                boolean changed = mController.onOpen();
//            }
            layoutPie();
        }
        if (!show) {
            mCurrentItem = null;
            mPieView = null;
        }
        invalidate();
    }

    private void setCenter(int x, int y) {
        if (x < mSlop) {
            mCenter.x = 0;
        } else {
            mCenter.x = getWidth();
        }
        mCenter.y = y;
    }

    private void layoutPie() {
        
        float emptyangle = (float) Math.PI / 16;
        int rgap = 2;
        int inner = mRadius + rgap;
        int outer = mRadius + mRadiusInc - rgap;
 //       int radius = mRadius;
        int gap = 1;
        for (int i = 0; i < mLevels; i++) {
            int level = i + 1;
    //        System.out.println("layoutpie=mCount="+mCounts[level]);
            float sweep = (float) (Math.PI - 2 * emptyangle) / mCounts[level];
            float angle = emptyangle + sweep / 2;
            for (PieItem item : mItems) {
                if (item.getLevel() == level) {
                    View view = item.getView();
                    view.measure(view.getLayoutParams().width,
                            view.getLayoutParams().height);
                    int w = view.getMeasuredWidth();
                    int h = view.getMeasuredHeight();
                    int r = inner + (outer - inner) * 2 / 3;
                    int x = (int) (r * Math.sin(angle));
                    int y = mCenter.y - (int) (r * Math.cos(angle)) - h / 2;
                    if (onTheLeft()) {
                        x = mCenter.x + x - w / 2;
                    } else {
                        x = mCenter.x - x - w / 2;
                    }
                    view.layout(x, y, x + w, y + h);
                    float itemstart = angle - sweep / 2;
           //         System.out.println("draw cicle");
                    Path slice = makeSlice(getDegrees(itemstart) - gap,
                            getDegrees(itemstart + sweep) + gap,
                            outer, inner, mCenter);
          //          System.out.println("itemstart="+itemstart+"sweep"+sweep+"inner"+inner+"outer"+outer);
                    item.setGeometry(itemstart, sweep, inner, outer, slice);
                    angle += sweep;
                }
            }
            inner += mRadiusInc;
            outer += mRadiusInc;
        }
    }


    /**
     * converts a
     *
     * @param angle from 0..PI to Android degrees (clockwise starting at 3
     *        o'clock)
     * @return skia angle
     */
    private float getDegrees(double angle) {
        return (float) (270 - 180 * angle / Math.PI);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mOpen) {
            int state;
            if (mUseBackground) {
                int w = mBackground.getIntrinsicWidth();
                int h = mBackground.getIntrinsicHeight();
                int left = mCenter.x - w;
                int top = mCenter.y - h / 2;
                mBackground.setBounds(left, top, left + w, top + h);
                state = canvas.save();
                if (onTheLeft()) {
                    canvas.scale(-1, 1);
                }
                mBackground.draw(canvas);
                canvas.restoreToCount(state);
            }
            for (PieItem item : mItems) {
                Paint p = item.isSelected() ? mSelectedPaint : mNormalPaint;
                state = canvas.save();
                if (onTheLeft()) {
                    canvas.scale(-1, 1);
                }
                drawPath(canvas, item.getPath(), p);
                canvas.restoreToCount(state);
                drawItem(canvas, item);
            }
            if (mPieView != null) {
    //            System.out.println("ddddrawwww");
                mPieView.draw(canvas);
            }
        }
    }

    private void drawItem(Canvas canvas, PieItem item) {
  //      System.out.println("draw Item");
//        int outer = item.getOuterRadius();
//        int left = mCenter.x - outer;
//        int top = mCenter.y - outer;
        // draw the item view
        View view = item.getView();
        int state = canvas.save();
        canvas.translate(view.getX(), view.getY());
  //      System.out.println("getx="+view.getX()+"gety="+view.getY());
      //  canvas.translate(100,100);
        view.draw(canvas);
        canvas.restoreToCount(state);
    }

    private void drawPath(Canvas canvas, Path path, Paint paint) {
        canvas.drawPath(path, paint);
    }

    private Path makeSlice(float start, float end, int outer, int inner, Point center) {
        RectF bb =
                new RectF(center.x - outer, center.y - outer, center.x + outer,
                        center.y + outer);
        RectF bbi =
                new RectF(center.x - inner, center.y - inner, center.x + inner,
                        center.y + inner);
        Path path = new Path();
        path.arcTo(bb, start, end - start, true);
        path.arcTo(bbi, end, start - end);
        path.close();
        return path;
    }

    // touch handling for pie

    @Override
    public boolean onTouchEvent(MotionEvent evt) {
        float x = evt.getX();
        float y = evt.getY();
   //     System.out.println("x="+x+"  y="+y);
        int action = evt.getActionMasked();
        if (MotionEvent.ACTION_DOWN == action) {
            if ((x > getWidth() - mSlop) || (x < mSlop)) {
                setCenter((int) x, (int) y);
                show(true);
                return true;
            }
        } else if (MotionEvent.ACTION_UP == action) {
            if (mOpen) {
                boolean handled = false;
                if (mPieView != null) {
                    handled = mPieView.onTouchEvent(evt);
                }
                PieItem item = mCurrentItem;
                deselect();
                show(false);
                if (!handled && (item != null)) {
                    item.getView().performClick();
                }
                return true;
            }
        } else if (MotionEvent.ACTION_CANCEL == action) {
            if (mOpen) {
                show(false);
            }
            deselect();
            return false;
        } else if (MotionEvent.ACTION_MOVE == action) {
            boolean handled = false;
            PointF polar = getPolar(x, y);
            int maxr = mRadius + mLevels * mRadiusInc + 50;
            if (mPieView != null) {
                handled = mPieView.onTouchEvent(evt);
            }
            if (handled) {
                invalidate();
                return false;
            }
            if (polar.y > maxr) {
                deselect();
                show(false);
                evt.setAction(MotionEvent.ACTION_DOWN);
                if (getParent() != null) {
                    ((ViewGroup) getParent()).dispatchTouchEvent(evt);
                }
                return false;
            }
            PieItem item = findItem(polar);
            if (mCurrentItem != item) {
                onEnter(item);
                if ((item != null) && item.isPieView()) {
                    int cx = item.getView().getLeft() + (onTheLeft()
                            ? item.getView().getWidth() : 0);
                    int cy = item.getView().getTop();
                    mPieView = item.getPieView();
                    layoutPieView(mPieView, cx, cy,
                            (item.getStartAngle() + item.getSweep()) / 2);
                }
                invalidate();
            }
        }
        // always re-dispatch event
        return false;
    }

    private void layoutPieView(PieView pv, int x, int y, float angle) {
        pv.layout(x, y, onTheLeft(), angle);
    }

    /**
     * enter a slice for a view
     * updates model only
     * @param item
     */
    private void onEnter(PieItem item) {
        // deselect
        if (mCurrentItem != null) {
            mCurrentItem.setSelected(false);
        }
        if (item != null) {
            // clear up stack
            playSoundEffect(SoundEffectConstants.CLICK);
            item.setSelected(true);
            mPieView = null;
        }
        mCurrentItem = item;
    }

    private void deselect() {
        if (mCurrentItem != null) {
            mCurrentItem.setSelected(false);
        }
        mCurrentItem = null;
        mPieView = null;
    }

    private PointF getPolar(float x, float y) {
        PointF res = new PointF();
        // get angle and radius from x/y
        res.x = (float) Math.PI / 2;
        x = mCenter.x - x;
        if (mCenter.x < mSlop) {
            x = -x;
        }
        y = mCenter.y - y;
        res.y = (float) Math.sqrt(x * x + y * y);
        if (y > 0) {
            res.x = (float) Math.asin(x / res.y);
        } else if (y < 0) {
            res.x = (float) (Math.PI - Math.asin(x / res.y ));
        }
        return res;
    }

    /**
     *
     * @param polar x: angle, y: dist
     * @return the item at angle/dist or null
     */
    private PieItem findItem(PointF polar) {
        // find the matching item:
        for (PieItem item : mItems) {
            if ((item.getInnerRadius() - mTouchOffset < polar.y)
                    && (item.getOuterRadius() - mTouchOffset > polar.y)
                    && (item.getStartAngle() < polar.x)
                    && (item.getStartAngle() + item.getSweep() > polar.x)) {
                return item;
            }
        }
        return null;
    }
    protected PieItem makeItem(int image, int l) {
        ImageView view = new ImageView(ct);
        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);
    }
    protected PieItem makeTextItem(int image, int l) {
        TextView view = new TextView(ct);
        view.setText("Hello");
        view.setMinimumWidth(mItemSize);
        view.setMinimumHeight(mItemSize);
        //view.setScaleType(ScaleType.CENTER);
        LayoutParams lp = new LayoutParams(mItemSize, mItemSize);
        view.setLayoutParams(lp);
        return new PieItem(view, l);
    }
}

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.kael.peimenudemo;



import com.kael.peimenudemo.PieMenu.PieView;

import android.graphics.Path;
import android.view.View;

/**
 * Pie menu item
 */
public class PieItem {

    private View mView;
    private PieView mPieView;
    private int level;
    private float start;
    private float sweep;
    private int inner;
    private int outer;
    private boolean mSelected;
    private Path mPath;

    public PieItem(View view, int level) {
        mView = view;
        this.level = level;
    }

    public PieItem(View view, int level, PieView sym) {
        mView = view;
        this.level = level;
        mPieView = sym;
    }

    public void setSelected(boolean s) {
        mSelected = s;
        if (mView != null) {
            mView.setSelected(s);
        }
    }

    public boolean isSelected() {
        return mSelected;
    }

    public int getLevel() {
        return level;
    }

    public void setGeometry(float st, float sw, int inside, int outside, Path p) {
        start = st;
        sweep = sw;
        inner = inside;
        outer = outside;
        mPath = p;
    }

    public float getStartAngle() {
        return start;
    }

    public float getSweep() {
        return sweep;
    }

    public int getInnerRadius() {
        return inner;
    }

    public int getOuterRadius() {
        return outer;
    }

    public boolean isPieView() {
        return (mPieView != null);
    }

    public View getView() {
        return mView;
    }

    public void setPieView(PieView sym) {
        mPieView = sym;
    }

    public PieView getPieView() {
        return mPieView;
    }

    public Path getPath() {
        return mPath;
    }

}


评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值