• 圆角布局


    原文:https://github.com/GcsSloop/rclayout

     https://www.gcssloop.com/gebug/rclayout

    代码:

    RCRelativeLayout.java
    package com.example.m_evolution.View;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.widget.Checkable;
    import android.widget.RelativeLayout;
    
    import com.example.m_evolution.Helper.RCAttrs;
    import com.example.m_evolution.Helper.RCHelper;
    
    
    /**
     * 作用:圆角相对布局
     * 作者:GcsSloop
     */
    public class RCRelativeLayout extends RelativeLayout implements Checkable, RCAttrs {
        RCHelper mRCHelper;
    
        public RCRelativeLayout(Context context) {
            this(context, null);
        }
    
        public RCRelativeLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public RCRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mRCHelper = new RCHelper();
            mRCHelper.initAttrs(context, attrs);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mRCHelper.onSizeChanged(this, w, h);
        }
    
        @Override
        protected void dispatchDraw(Canvas canvas) {
            canvas.saveLayer(mRCHelper.mLayer, null, Canvas.ALL_SAVE_FLAG);
            super.dispatchDraw(canvas);
            mRCHelper.onClipDraw(canvas);
            canvas.restore();
        }
    
        @Override
        public void draw(Canvas canvas) {
            if (mRCHelper.mClipBackground) {
                canvas.save();
                canvas.clipPath(mRCHelper.mClipPath);
                super.draw(canvas);
                canvas.restore();
            } else {
                super.draw(canvas);
            }
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            int action = ev.getAction();
            if (action == MotionEvent.ACTION_DOWN && !mRCHelper.mAreaRegion.contains((int) ev.getX(), (int) ev.getY())) {
                return false;
            }
            if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP) {
                refreshDrawableState();
            } else if (action == MotionEvent.ACTION_CANCEL) {
                setPressed(false);
                refreshDrawableState();
            }
            return super.dispatchTouchEvent(ev);
        }
    
        //--- 公开接口 ----------------------------------------------------------------------------------
    
        public void setClipBackground(boolean clipBackground) {
            mRCHelper.mClipBackground = clipBackground;
            invalidate();
        }
    
        public void setRoundAsCircle(boolean roundAsCircle) {
            mRCHelper.mRoundAsCircle = roundAsCircle;
            invalidate();
        }
    
        public void setRadius(int radius) {
            for (int i = 0; i < mRCHelper.radii.length; i++) {
                mRCHelper.radii[i] = radius;
            }
            invalidate();
        }
    
        public void setTopLeftRadius(int topLeftRadius) {
            mRCHelper.radii[0] = topLeftRadius;
            mRCHelper.radii[1] = topLeftRadius;
            invalidate();
        }
    
        public void setTopRightRadius(int topRightRadius) {
            mRCHelper.radii[2] = topRightRadius;
            mRCHelper.radii[3] = topRightRadius;
            invalidate();
        }
    
        public void setBottomLeftRadius(int bottomLeftRadius) {
            mRCHelper.radii[6] = bottomLeftRadius;
            mRCHelper.radii[7] = bottomLeftRadius;
            invalidate();
        }
    
        public void setBottomRightRadius(int bottomRightRadius) {
            mRCHelper.radii[4] = bottomRightRadius;
            mRCHelper.radii[5] = bottomRightRadius;
            invalidate();
        }
    
        public void setStrokeWidth(int strokeWidth) {
            mRCHelper.mStrokeWidth = strokeWidth;
            invalidate();
        }
    
        public void setStrokeColor(int strokeColor) {
            mRCHelper.mStrokeColor = strokeColor;
            invalidate();
        }
    
        @Override
        public void invalidate() {
            if (null != mRCHelper)
                mRCHelper.refreshRegion(this);
            super.invalidate();
        }
    
        public boolean isClipBackground() {
            return mRCHelper.mClipBackground;
        }
    
        public boolean isRoundAsCircle() {
            return mRCHelper.mRoundAsCircle;
        }
    
        public float getTopLeftRadius() {
            return mRCHelper.radii[0];
        }
    
        public float getTopRightRadius() {
            return mRCHelper.radii[2];
        }
    
        public float getBottomLeftRadius() {
            return mRCHelper.radii[4];
        }
    
        public float getBottomRightRadius() {
            return mRCHelper.radii[6];
        }
    
        public int getStrokeWidth() {
            return mRCHelper.mStrokeWidth;
        }
    
        public int getStrokeColor() {
            return mRCHelper.mStrokeColor;
        }
    
    
        //--- Selector 支持 ----------------------------------------------------------------------------
    
        @Override
        protected void drawableStateChanged() {
            super.drawableStateChanged();
            mRCHelper.drawableStateChanged(this);
        }
    
        @Override
        public void setChecked(boolean checked) {
            if (mRCHelper.mChecked != checked) {
                mRCHelper.mChecked = checked;
                refreshDrawableState();
                if (mRCHelper.mOnCheckedChangeListener != null) {
                    mRCHelper.mOnCheckedChangeListener.onCheckedChanged(this, mRCHelper.mChecked);
                }
            }
        }
    
        @Override
        public boolean isChecked() {
            return mRCHelper.mChecked;
        }
    
        @Override
        public void toggle() {
            setChecked(!mRCHelper.mChecked);
        }
    
        public void setOnCheckedChangeListener(RCHelper.OnCheckedChangeListener listener) {
            mRCHelper.mOnCheckedChangeListener = listener;
        }
    }
    RCHelper.java
    package com.example.m_evolution.Helper;
    
    import android.content.Context;
    import android.content.res.ColorStateList;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.PointF;
    import android.graphics.PorterDuff;
    import android.graphics.PorterDuffXfermode;
    import android.graphics.RectF;
    import android.graphics.Region;
    import android.os.Build;
    import android.util.AttributeSet;
    import android.view.View;
    import android.widget.Checkable;
    
    
    import com.example.m_evolution.R;
    
    import java.util.ArrayList;
    
    /**
     * 作用:圆角辅助工具
     * 作者:GcsSloop
     */
    public class RCHelper {
        public float[] radii = new float[8];   // top-left, top-right, bottom-right, bottom-left
        public Path mClipPath;                 // 剪裁区域路径
        public Paint mPaint;                   // 画笔
        public boolean mRoundAsCircle = false; // 圆形
        public int mDefaultStrokeColor;        // 默认描边颜色
        public int mStrokeColor;               // 描边颜色
        public ColorStateList mStrokeColorStateList;// 描边颜色的状态
        public int mStrokeWidth;               // 描边半径
        public boolean mClipBackground;        // 是否剪裁背景
        public Region mAreaRegion;             // 内容区域
        public RectF mLayer;                   // 画布图层大小
    
        public void initAttrs(Context context, AttributeSet attrs) {
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RCAttrs);
            mRoundAsCircle = ta.getBoolean(R.styleable.RCAttrs_round_as_circle, false);
            mStrokeColorStateList = ta.getColorStateList(R.styleable.RCAttrs_stroke_color);
            if (null != mStrokeColorStateList) {
                mStrokeColor = mStrokeColorStateList.getDefaultColor();
                mDefaultStrokeColor = mStrokeColorStateList.getDefaultColor();
            } else {
                mStrokeColor = Color.WHITE;
                mDefaultStrokeColor = Color.WHITE;
            }
            mStrokeWidth = ta.getDimensionPixelSize(R.styleable.RCAttrs_stroke_width, 0);
            mClipBackground = ta.getBoolean(R.styleable.RCAttrs_clip_background, false);
            int roundCorner = ta.getDimensionPixelSize(R.styleable.RCAttrs_round_corner, 0);
            int roundCornerTopLeft = ta.getDimensionPixelSize(
                    R.styleable.RCAttrs_round_corner_top_left, roundCorner);
            int roundCornerTopRight = ta.getDimensionPixelSize(
                    R.styleable.RCAttrs_round_corner_top_right, roundCorner);
            int roundCornerBottomLeft = ta.getDimensionPixelSize(
                    R.styleable.RCAttrs_round_corner_bottom_left, roundCorner);
            int roundCornerBottomRight = ta.getDimensionPixelSize(
                    R.styleable.RCAttrs_round_corner_bottom_right, roundCorner);
            ta.recycle();
    
            radii[0] = roundCornerTopLeft;
            radii[1] = roundCornerTopLeft;
    
            radii[2] = roundCornerTopRight;
            radii[3] = roundCornerTopRight;
    
            radii[4] = roundCornerBottomRight;
            radii[5] = roundCornerBottomRight;
    
            radii[6] = roundCornerBottomLeft;
            radii[7] = roundCornerBottomLeft;
    
            mLayer = new RectF();
            mClipPath = new Path();
            mAreaRegion = new Region();
            mPaint = new Paint();
            mPaint.setColor(Color.WHITE);
            mPaint.setAntiAlias(true);
        }
    
        public void onSizeChanged(View view, int w, int h) {
            mLayer.set(0, 0, w, h);
            refreshRegion(view);
        }
    
        public void refreshRegion(View view) {
            int w = (int) mLayer.width();
            int h = (int) mLayer.height();
            RectF areas = new RectF();
            areas.left = view.getPaddingLeft();
            areas.top = view.getPaddingTop();
            areas.right = w - view.getPaddingRight();
            areas.bottom = h - view.getPaddingBottom();
            mClipPath.reset();
            if (mRoundAsCircle) {
                float d = areas.width() >= areas.height() ? areas.height() : areas.width();
                float r = d / 2;
                PointF center = new PointF(w / 2, h / 2);
                if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
                    mClipPath.addCircle(center.x, center.y, r, Path.Direction.CW);
    
                    mClipPath.moveTo(0, 0);  // 通过空操作让Path区域占满画布
                    mClipPath.moveTo(w, h);
                } else {
                    float y = h / 2 - r;
                    mClipPath.moveTo(areas.left, y);
                    mClipPath.addCircle(center.x, y + r, r, Path.Direction.CW);
                }
            } else {
                mClipPath.addRoundRect(areas, radii, Path.Direction.CW);
            }
            Region clip = new Region((int) areas.left, (int) areas.top,
                    (int) areas.right, (int) areas.bottom);
            mAreaRegion.setPath(mClipPath, clip);
        }
    
        public void onClipDraw(Canvas canvas) {
            if (mStrokeWidth > 0) {
                // 支持半透明描边,将与描边区域重叠的内容裁剪掉
                mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
                mPaint.setColor(Color.WHITE);
                mPaint.setStrokeWidth(mStrokeWidth * 2);
                mPaint.setStyle(Paint.Style.STROKE);
                canvas.drawPath(mClipPath, mPaint);
                // 绘制描边
                mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
                mPaint.setColor(mStrokeColor);
                mPaint.setStyle(Paint.Style.STROKE);
                canvas.drawPath(mClipPath, mPaint);
            }
            mPaint.setColor(Color.WHITE);
            mPaint.setStyle(Paint.Style.FILL);
    
            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
                mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
                canvas.drawPath(mClipPath, mPaint);
            } else {
                mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
    
                final Path path = new Path();
                path.addRect(0, 0, (int) mLayer.width(), (int) mLayer.height(), Path.Direction.CW);
                path.op(mClipPath, Path.Op.DIFFERENCE);
                canvas.drawPath(path, mPaint);
            }
        }
    
    
        //--- Selector 支持 ----------------------------------------------------------------------------
    
        public boolean mChecked;              // 是否是 check 状态
        public OnCheckedChangeListener mOnCheckedChangeListener;
    
        public void drawableStateChanged(View view) {
            if (view instanceof RCAttrs) {
                ArrayList<Integer> stateListArray = new ArrayList<>();
                if (view instanceof Checkable) {
                    stateListArray.add(android.R.attr.state_checkable);
                    if (((Checkable) view).isChecked())
                        stateListArray.add(android.R.attr.state_checked);
                }
                if (view.isEnabled()) stateListArray.add(android.R.attr.state_enabled);
                if (view.isFocused()) stateListArray.add(android.R.attr.state_focused);
                if (view.isPressed()) stateListArray.add(android.R.attr.state_pressed);
                if (view.isHovered()) stateListArray.add(android.R.attr.state_hovered);
                if (view.isSelected()) stateListArray.add(android.R.attr.state_selected);
                if (view.isActivated()) stateListArray.add(android.R.attr.state_activated);
                if (view.hasWindowFocus()) stateListArray.add(android.R.attr.state_window_focused);
    
                if (mStrokeColorStateList != null && mStrokeColorStateList.isStateful()) {
                    int[] stateList = new int[stateListArray.size()];
                    for (int i = 0; i < stateListArray.size(); i++) {
                        stateList[i] = stateListArray.get(i);
                    }
                    int stateColor = mStrokeColorStateList.getColorForState(stateList, mDefaultStrokeColor);
                    ((RCAttrs) view).setStrokeColor(stateColor);
                }
            }
        }
    
        public interface OnCheckedChangeListener {
            void onCheckedChanged(View view, boolean isChecked);
        }
    }
    RCAttrs.java
    package com.example.m_evolution.Helper;
    
    public interface RCAttrs {
        void setClipBackground(boolean clipBackground);
    
        void setRoundAsCircle(boolean roundAsCircle);
    
        void setRadius(int radius);
    
        void setTopLeftRadius(int topLeftRadius);
    
        void setTopRightRadius(int topRightRadius);
    
        void setBottomLeftRadius(int bottomLeftRadius);
    
        void setBottomRightRadius(int bottomRightRadius);
    
        void setStrokeWidth(int strokeWidth);
    
        void setStrokeColor(int strokeColor);
    
        boolean isClipBackground();
    
        boolean isRoundAsCircle();
    
        float getTopLeftRadius();
    
        float getTopRightRadius();
    
        float getBottomLeftRadius();
    
        float getBottomRightRadius();
    
        int getStrokeWidth();
    
        int getStrokeColor();
    }

    attrs.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <!-- *公共属性* -->
        <!--圆形-->
        <attr name="round_as_circle" format="boolean" />
        <!--全部圆角半径-->
        <attr name="round_corner" format="integer|dimension" />
        <!--针对各个角的半径-->
        <attr name="round_corner_top_left" format="integer|dimension" />
        <attr name="round_corner_top_right" format="integer|dimension" />
        <attr name="round_corner_bottom_left" format="integer|dimension" />
        <attr name="round_corner_bottom_right" format="integer|dimension" />
    
        <!--描边颜色/半径-->
        <attr name="stroke_color" format="color|reference" />
        <attr name="stroke_width" format="integer|dimension" />
    
        <!-- 是否剪裁 RCLayout 的背景 -->
        <attr name="clip_background" format="boolean" />
    
        <!--真正用于解析的属性-->
        <declare-styleable name="RCAttrs">
            <attr name="round_as_circle" />
            <attr name="round_corner" />
            <attr name="round_corner_top_left" />
            <attr name="round_corner_top_right" />
            <attr name="round_corner_bottom_left" />
            <attr name="round_corner_bottom_right" />
            <attr name="stroke_color" />
            <attr name="stroke_width" />
            <attr name="clip_background" />
        </declare-styleable>
    
        <!--假体:用于提示-->
        <declare-styleable name="RCRelativeLayout">
            <attr name="round_as_circle" />
            <attr name="round_corner" />
            <attr name="round_corner_top_left" />
            <attr name="round_corner_top_right" />
            <attr name="round_corner_bottom_left" />
            <attr name="round_corner_bottom_right" />
            <attr name="stroke_color" />
            <attr name="stroke_width" />
            <attr name="clip_background" />
        </declare-styleable>
    
        <!--假体:用于提示-->
        <declare-styleable name="RCImageView">
            <attr name="round_as_circle" />
            <attr name="round_corner" />
            <attr name="round_corner_top_left" />
            <attr name="round_corner_top_right" />
            <attr name="round_corner_bottom_left" />
            <attr name="round_corner_bottom_right" />
            <attr name="stroke_color" />
            <attr name="stroke_width" />
            <attr name="clip_background" />
        </declare-styleable>
    </resources>
  • 相关阅读:
    写日志文件
    内存文件映射应用举例『转』
    HOOK编程
    获取当前进程的名称
    C++实现FTP文件传输
    C/C++ 实现windows进程/线程/模块 遍历
    Unicode下的CString与char *转换
    MFC常用基本数据类型
    geos 3.6.1编译 win7 vs2015
    JQuery Mobile iscroll插件使用教程及注意事项
  • 原文地址:https://www.cnblogs.com/zhaozilongcjiajia/p/11348875.html
Copyright © 2020-2023  润新知