Android控件CircleLayout

来源:互联网 发布:合并pdf软件 编辑:程序博客网 时间:2024/06/06 01:17
public class CircleLayout extends ViewGroup {    public static final int LAYOUT_NORMAL = 1;    public static final int LAYOUT_PIE = 2;    private int mLayoutMode = LAYOUT_NORMAL;    private Drawable mInnerCircle;    private float mAngleOffset;    private float mAngleRange;    private float mDividerWidth;    private int mInnerRadius;    private Paint mDividerPaint;    private Paint mCirclePaint;    private RectF mBounds = new RectF();    private Bitmap mDst;    private Bitmap mSrc;    private Canvas mSrcCanvas;    private Canvas mDstCanvas;    private Xfermode mXfer;    private Paint mXferPaint;    private View mMotionTarget;    private Bitmap mDrawingCache;    private Canvas mCachedCanvas;    private Set<View> mDirtyViews = new HashSet<View>();    private boolean mCached = false;    public CircleLayout(Context context) {        this(context, null);    }    @SuppressLint("NewApi")    public CircleLayout(Context context, AttributeSet attrs) {        super(context, attrs);        mDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);        TypedArray a = context.getTheme().obtainStyledAttributes(attrs,                R.styleable.CircleLayout, 0, 0);        try {            int dividerColor = a.getColor(                    R.styleable.CircleLayout_sliceDivider,                    getResources().getColor(android.R.color.darker_gray));            mInnerCircle = a.getDrawable(R.styleable.CircleLayout_innerCircle);            if (mInnerCircle instanceof ColorDrawable) {                int innerColor = a.getColor(                        R.styleable.CircleLayout_innerCircle,                        getResources().getColor(android.R.color.white));                mCirclePaint.setColor(innerColor);            }            mDividerPaint.setColor(dividerColor);            mAngleOffset = a                    .getFloat(R.styleable.CircleLayout_angleOffset, 90f);            mAngleRange = a.getFloat(R.styleable.CircleLayout_angleRange, 360f);            mDividerWidth = a.getDimensionPixelSize(                    R.styleable.CircleLayout_dividerWidth, 1);            mInnerRadius = a.getDimensionPixelSize(                    R.styleable.CircleLayout_innerRadius, 80);            mLayoutMode = a.getColor(R.styleable.CircleLayout_layoutMode,                    LAYOUT_NORMAL);        } finally {            a.recycle();        }        mDividerPaint.setStrokeWidth(mDividerWidth);        mXfer = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);        mXferPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        // Turn off hardware acceleration if possible        if (Build.VERSION.SDK_INT >= 11) {            setLayerType(LAYER_TYPE_SOFTWARE, null);        }    }    public void setLayoutMode(int mode) {        mLayoutMode = mode;        requestLayout();        invalidate();    }    public int getLayoutMode() {        return mLayoutMode;    }    public int getRadius() {        final int width = getWidth();        final int height = getHeight();        final float minDimen = width > height ? height : width;        float radius = (minDimen - mInnerRadius) / 2f;        return (int) radius;    }    public void getCenter(PointF p) {        p.set(getWidth() / 2f, getHeight() / 2);    }    public void setAngleOffset(float offset) {        mAngleOffset = offset;        requestLayout();        invalidate();    }    public float getAngleOffset() {        return mAngleOffset;    }    public void setInnerRadius(int radius) {        mInnerRadius = radius;        requestLayout();        invalidate();    }    public int getInnerRadius() {        return mInnerRadius;    }    public void setInnerCircle(Drawable d) {        mInnerCircle = d;        requestLayout();        invalidate();    }    public void setInnerCircle(int res) {        mInnerCircle = getContext().getResources().getDrawable(res);        requestLayout();        invalidate();    }    public void setInnerCircleColor(int color) {        mInnerCircle = new ColorDrawable(color);        requestLayout();        invalidate();    }    public Drawable getInnerCircle() {        return mInnerCircle;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        final int count = getChildCount();        int maxHeight = 0;        int maxWidth = 0;        // Find rightmost and bottommost child        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (child.getVisibility() != GONE) {                measureChild(child, widthMeasureSpec, heightMeasureSpec);                maxWidth = Math.max(maxWidth, child.getMeasuredWidth());                maxHeight = Math.max(maxHeight, child.getMeasuredHeight());            }        }        // Check against our minimum height and width        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());        int width = resolveSize(maxWidth, widthMeasureSpec);        int height = resolveSize(maxHeight, heightMeasureSpec);        setMeasuredDimension(width, height);        if (mSrc != null                && (mSrc.getWidth() != width || mSrc.getHeight() != height)) {            mDst.recycle();            mSrc.recycle();            mDrawingCache.recycle();            mDst = null;            mSrc = null;            mDrawingCache = null;        }        if (mSrc == null) {            mSrc = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);            mDst = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);            mDrawingCache = Bitmap.createBitmap(width, height,                    Bitmap.Config.ARGB_8888);            mSrcCanvas = new Canvas(mSrc);            mDstCanvas = new Canvas(mDst);            mCachedCanvas = new Canvas(mDrawingCache);        }    }    private LayoutParams layoutParams(View child) {        return (LayoutParams) child.getLayoutParams();    }    @Override    @SuppressWarnings("deprecation")    protected void onLayout(boolean changed, int l, int t, int r, int b) {        final int childs = getChildCount();        float totalWeight = 0f;        for (int i = 0; i < childs; i++) {            final View child = getChildAt(i);            LayoutParams lp = layoutParams(child);            totalWeight += lp.weight;        }        final int width = getWidth();        final int height = getHeight();        final float minDimen = width > height ? height : width;        final float radius = (minDimen - mInnerRadius) / 2f;        mBounds.set(width / 2 - minDimen / 2, height / 2 - minDimen / 2, width                / 2 + minDimen / 2, height / 2 + minDimen / 2);        float startAngle = mAngleOffset;        for (int i = 0; i < childs; i++) {            final View child = getChildAt(i);            final LayoutParams lp = layoutParams(child);            final float angle = mAngleRange / totalWeight * lp.weight;            final float centerAngle = startAngle + angle / 2f;            final int x;            final int y;            if (childs > 1) {                x = (int) (radius * Math.cos(Math.toRadians(centerAngle)))                        + width / 2;                y = (int) (radius * Math.sin(Math.toRadians(centerAngle)))                        + height / 2;            } else {                x = width / 2;                y = height / 2;            }            final int halfChildWidth = child.getMeasuredWidth() / 2;            final int halfChildHeight = child.getMeasuredHeight() / 2;            final int left = lp.width != LayoutParams.FILL_PARENT ? x                    - halfChildWidth : 0;            final int top = lp.height != LayoutParams.FILL_PARENT ? y                    - halfChildHeight : 0;            final int right = lp.width != LayoutParams.FILL_PARENT ? x                    + halfChildWidth : width;            final int bottom = lp.height != LayoutParams.FILL_PARENT ? y                    + halfChildHeight : height;            child.layout(left, top, right, bottom);            if (left != child.getLeft() || top != child.getTop()                    || right != child.getRight() || bottom != child.getBottom()                    || lp.startAngle != startAngle                    || lp.endAngle != startAngle + angle) {                mCached = false;            }            lp.startAngle = startAngle;            startAngle += angle;            lp.endAngle = startAngle;        }        invalidate();    }    @Override    protected LayoutParams generateDefaultLayoutParams() {        return new LayoutParams(LayoutParams.WRAP_CONTENT,                LayoutParams.WRAP_CONTENT);    }    @Override    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {        LayoutParams lp = new LayoutParams(p.width, p.height);        if (p instanceof LinearLayout.LayoutParams) {            lp.weight = ((LinearLayout.LayoutParams) p).weight;        }        return lp;    }    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new LayoutParams(getContext(), attrs);    }    @Override    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {        return p instanceof LayoutParams;    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        if (mLayoutMode == LAYOUT_NORMAL) {            return super.dispatchTouchEvent(ev);        }        final int action = ev.getAction();        final float x = ev.getX() - getWidth() / 2f;        final float y = ev.getY() - getHeight() / 2f;        if (action == MotionEvent.ACTION_DOWN) {            if (mMotionTarget != null) {                MotionEvent cancelEvent = MotionEvent.obtain(ev);                cancelEvent.setAction(MotionEvent.ACTION_CANCEL);                cancelEvent.offsetLocation(-mMotionTarget.getLeft(),                        -mMotionTarget.getTop());                mMotionTarget.dispatchTouchEvent(cancelEvent);                cancelEvent.recycle();                mMotionTarget = null;            }            final float radius = (float) Math.sqrt(x * x + y * y);            if (radius < mInnerRadius || radius > getWidth() / 2f                    || radius > getHeight() / 2f) {                return false;            }            float angle = (float) Math.toDegrees(Math.atan2(y, x));            if (angle < 0)                angle += mAngleRange;            final int childs = getChildCount();            for (int i = 0; i < childs; i++) {                final View child = getChildAt(i);                final LayoutParams lp = layoutParams(child);                float startAngle = lp.startAngle % mAngleRange;                float endAngle = lp.endAngle % mAngleRange;                float touchAngle = angle;                if (startAngle > endAngle) {                    if (touchAngle < startAngle && touchAngle < endAngle) {                        touchAngle += mAngleRange;                    }                    endAngle += mAngleRange;                }                if (startAngle <= touchAngle && endAngle >= touchAngle) {                    ev.offsetLocation(-child.getLeft(), -child.getTop());                    boolean dispatched = child.dispatchTouchEvent(ev);                    if (dispatched) {                        mMotionTarget = child;                        return true;                    } else {                        ev.setLocation(0f, 0f);                        return onTouchEvent(ev);                    }                }            }        } else if (mMotionTarget != null) {            ev.offsetLocation(-mMotionTarget.getLeft(), -mMotionTarget.getTop());            mMotionTarget.dispatchTouchEvent(ev);            if (action == MotionEvent.ACTION_UP                    || action == MotionEvent.ACTION_CANCEL) {                mMotionTarget = null;            }        }        return onTouchEvent(ev);    }    private void drawChild(Canvas canvas, View child, LayoutParams lp) {        mSrcCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);        mDstCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);        mSrcCanvas.save();        int childLeft = child.getLeft();        int childTop = child.getTop();        int childRight = child.getRight();        int childBottom = child.getBottom();        mSrcCanvas.clipRect(childLeft, childTop, childRight, childBottom,                Op.REPLACE);        mSrcCanvas.translate(childLeft, childTop);        child.draw(mSrcCanvas);        mSrcCanvas.restore();        mXferPaint.setXfermode(null);        mXferPaint.setColor(Color.BLACK);        float sweepAngle = (lp.endAngle - lp.startAngle) % 361;        mDstCanvas                .drawArc(mBounds, lp.startAngle, sweepAngle, true, mXferPaint);        mXferPaint.setXfermode(mXfer);        mDstCanvas.drawBitmap(mSrc, 0f, 0f, mXferPaint);        canvas.drawBitmap(mDst, 0f, 0f, null);    }    private void redrawDirty(Canvas canvas) {        for (View child : mDirtyViews) {            drawChild(canvas, child, layoutParams(child));        }        if (mMotionTarget != null) {            drawChild(canvas, mMotionTarget, layoutParams(mMotionTarget));        }    }    private void drawDividers(Canvas canvas, float halfWidth, float halfHeight,                              float radius) {        final int childs = getChildCount();        if (childs < 2) {            return;        }        for (int i = 0; i < childs; i++) {            final View child = getChildAt(i);            LayoutParams lp = layoutParams(child);            canvas.drawLine(halfWidth, halfHeight,                    radius * (float) Math.cos(Math.toRadians(lp.startAngle))                            + halfWidth,                    radius * (float) Math.sin(Math.toRadians(lp.startAngle))                            + halfHeight, mDividerPaint);            if (i == childs - 1) {                canvas.drawLine(halfWidth, halfHeight,                        radius * (float) Math.cos(Math.toRadians(lp.endAngle))                                + halfWidth,                        radius * (float) Math.sin(Math.toRadians(lp.endAngle))                                + halfHeight, mDividerPaint);            }        }    }    private void drawInnerCircle(Canvas canvas, float halfWidth,                                 float halfHeight) {        if (mInnerCircle != null) {            if (!(mInnerCircle instanceof ColorDrawable)) {                mInnerCircle                        .setBounds((int) halfWidth - mInnerRadius,                                (int) halfHeight - mInnerRadius,                                (int) halfWidth + mInnerRadius,                                (int) halfHeight + mInnerRadius);                mInnerCircle.draw(canvas);            } else {                canvas.drawCircle(halfWidth, halfHeight, mInnerRadius,                        mCirclePaint);            }        }    }    @Override    protected void dispatchDraw(Canvas canvas) {        if (mLayoutMode == LAYOUT_NORMAL) {            super.dispatchDraw(canvas);            return;        }        if (mSrc == null || mDst == null || mSrc.isRecycled()                || mDst.isRecycled()) {            return;        }        final int childs = getChildCount();        final float halfWidth = getWidth() / 2f;        final float halfHeight = getHeight() / 2f;        final float radius = halfWidth > halfHeight ? halfHeight : halfWidth;        if (mCached && mDrawingCache != null && !mDrawingCache.isRecycled()                && mDirtyViews.size() < childs / 2) {            canvas.drawBitmap(mDrawingCache, 0f, 0f, null);            redrawDirty(canvas);            drawDividers(canvas, halfWidth, halfHeight, radius);            drawInnerCircle(canvas, halfWidth, halfHeight);            return;        } else {            mCached = false;        }        Canvas sCanvas = null;        if (mCachedCanvas != null) {            sCanvas = canvas;            canvas = mCachedCanvas;        }        Drawable bkg = getBackground();        if (bkg != null) {            bkg.draw(canvas);        }        for (int i = 0; i < childs; i++) {            final View child = getChildAt(i);            LayoutParams lp = layoutParams(child);            drawChild(canvas, child, lp);        }        drawDividers(canvas, halfWidth, halfHeight, radius);        drawInnerCircle(canvas, halfWidth, halfHeight);        if (mCachedCanvas != null) {            sCanvas.drawBitmap(mDrawingCache, 0f, 0f, null);            mDirtyViews.clear();            mCached = true;        }    }    public static class LayoutParams extends ViewGroup.LayoutParams {        private float startAngle;        private float endAngle;        public float weight = 1f;        public LayoutParams(int width, int height) {            super(width, height);        }        public LayoutParams(Context context, AttributeSet attrs) {            super(context, attrs);        }    }}


<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="CircleLayout">        <attr name="innerRadius" format="dimension" />        <attr name="sliceDivider" format="reference|color" />        <attr name="innerCircle" format="reference|color" />        <attr name="angleOffset" format="float" />        <attr name="angleRange" format="float" />        <attr name="layoutMode">            <enum name="normal" value="1" />            <enum name="pie" value="2" />        </attr>        <attr name="dividerWidth" format="dimension" />    </declare-styleable></resources>




原创粉丝点击