Android 开发之 SimpleTagImageView自定义ImageView

这两天看了ImageView相关的开源项目:https://github.com/wujingchao/SimpleTagImageView,对我来说比较有用,刚好要用到这个,在电商美食相关的app开发中经常遇到新品热销、火爆、消费状态的状态标示,这里一个imageview就解决了这个布局问题,都不用考虑适配问题。废话不多说了,下面看SimpleTagImageView效果图以及构造方法:

public class SimpleTagImageView extends ImageView {
  public SimpleTagImageView(Context context) {
        this(context, null);
    }

  public SimpleTagImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

  public SimpleTagImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDensity = context.getResources().getDisplayMetrics().density;
        TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.SimpleTagImageView,defStyleAttr,0);
        mTagOrientation = a.getInteger(R.styleable.SimpleTagImageView_simple_tag_orientation,0);
        mTagWidth = a.getDimensionPixelSize(R.styleable.SimpleTagImageView_simple_tag_width, dip2px(DEFAULT_TAG_WIDTH));
        mCornerDistance = a.getDimensionPixelSize(R.styleable.SimpleTagImageView_simple_corner_distance,dip2px(DEFAULT_CORNER_DISTANCE));
        mTagBackgroundColor = a.getColor(R.styleable.SimpleTagImageView_simple_tag_background_color,DEFAULT_TAG_BACKGROUND_COLOR);
        mTagText = a.getString(R.styleable.SimpleTagImageView_simple_tag_text);
        mTagTextSize = a.getDimensionPixelSize(R.styleable.SimpleTagImageView_simple_tag_textSize, dip2px(DEFAULT_TAG_TEXT_SIZE));
        mTagTextColor = a.getColor(R.styleable.SimpleTagImageView_simple_tag_textColor, DEFAULT_TAG_TEXT_COLOR);
        mTagEnable = a.getBoolean(R.styleable.SimpleTagImageView_simple_tag_enable,true);
        mRoundRadius = a.getDimensionPixelSize(R.styleable.SimpleTagImageView_simple_tag_round_radius,0);
        a.recycle();
        if(TextUtils.isEmpty(mTagText))mTagText = "";
        mPaint = new Paint();
        mPath = new Path();
        mTextPaint = new Paint();
        mTagTextBound = new Rect();
        startPoint = new MyPoint();
        endPoint = new MyPoint();
        mRoundRect = new RectF();
    }
}

这里获取自定义属性值,并且初始化画笔以及rect等参数,自定义属性如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
      <declare-styleable name="SimpleTagImageView">
        <attr name="simple_corner_distance" format="dimension" />
        <attr name="simple_tag_width" format="dimension"/>
        <attr name="simple_tag_background_color" format="color"/>
        <attr name="simple_tag_text" format="string"/>
        <attr name="simple_tag_textSize" format="dimension" />
        <attr name="simple_tag_textColor" format="color"/>
        <attr name="simple_tag_orientation" format="enum">
            <enum name="left_top" value="0"/>
            <enum name="right_top" value="1"/>
            <enum name="left_bottom" value="2"/>
            <enum name="right_bottom" value="3"/>
        </attr>
        <attr name="simple_tag_enable" format="boolean"/>
        <attr name="simple_tag_round_radius" format="dimension"/>
    </declare-styleable>
</resources>

下面来看onDraw代码块

  @Override
    protected void onDraw(Canvas mCanvas) {
        if(mRoundRadius == 0) {
            super.onDraw(mCanvas);
        }else {
            Drawable d = getDrawable();
            if(d == null) return;
            if(d.getIntrinsicWidth() == 0 || d.getIntrinsicHeight() == 0) return;
            setupBitmapPaint();
            mRoundRect.set(getPaddingLeft(),getPaddingTop(),getMeasuredWidth() - getPaddingRight(),getMeasuredHeight() - getPaddingBottom());
            mCanvas.drawRoundRect(mRoundRect, mRoundRadius, mRoundRadius, mBitmapPaint);
        }
        ................
        }

如果背景图片需要圆角,并且图片的宽高不等0,就先调用setupBitmapPaint()方法初始化mBitmapPaint画笔,并配置画笔缩放模式和shader,接着rect边界赋值,最后绘制到画布。

    @Override
    protected void onDraw(Canvas mCanvas) {
         ...................................
        if(mTagWidth > 0 && mTagEnable) {
            float rDistance = mCornerDistance + mTagWidth/2;
            chooseTagOrientation(rDistance);
            mTextPaint.setTextSize(mTagTextSize);
            mTextPaint.getTextBounds(mTagText,0,mTagText.length(),mTagTextBound);
            mPaint.setDither(true);
            mPaint.setAntiAlias(true);
            mPaint.setColor(mTagBackgroundColor);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeJoin(Paint.Join.ROUND);
            mPaint.setStrokeCap(Paint.Cap.SQUARE);
            mPaint.setStrokeWidth(mTagWidth);
            mPath.reset();
            mPath.moveTo(startPoint.x, startPoint.y);
            mPath.lineTo(endPoint.x, endPoint.y);
            mCanvas.drawPath(mPath, mPaint);
            mTextPaint.setColor(mTagTextColor);
            mTextPaint.setTextSize(mTagTextSize);
            mTextPaint.setAntiAlias(true);
//          斜边长度
            float hypotenuse = THE_SQUARE_ROOT_OF_2 * rDistance;
            mCanvas.drawTextOnPath(mTagText, mPath, hypotenuse / 2 - mTagTextBound.width() / 2,
                    mTagTextBound.height() / 2, mTextPaint);
        }
       }

绘制圆角背景图后,根据Tag方向检测起始点,然后通过贝塞尔曲线Path绘制到画布,控件公开了自定义属性的set 、get方法,例如:

   /**
     *
     * @param tagOrientation {@link #LEFT_TOP} or
     *                       {@link #LEFT_BOTTOM} or
     *                       {@link #RIGHT_TOP} or
     *                       {@link #RIGHT_BOTTOM}
     */
    public void setTagOrientation(int tagOrientation) {
        if(tagOrientation == this.mTagOrientation)return;
        this.mTagOrientation = tagOrientation;
        invalidate();
    }

Eclipse开发者项目中引用只需要把自定义属性文件和自定义控件导入即可,自定义控件完整代码如下:


/**
 * @author wujingchao  2015-02-20 email:wujingchao@aliyun.com
 */
public class SimpleTagImageView extends ImageView {

    public static final String TAG = "SimpleTagImageView";

    public  static final byte LEFT_TOP = 0x00;

    public  static final byte RIGHT_TOP = 0x01;

    public  static final byte LEFT_BOTTOM = 0x02;

    public  static final byte RIGHT_BOTTOM = 0x03;

    private static final float THE_SQUARE_ROOT_OF_2 = (float) Math.sqrt(2);

    private static final int DEFAULT_TAG_WIDTH = 20;

    private static final int DEFAULT_CORNER_DISTANCE = 20;

    private static final int DEFAULT_TAG_BACKGROUND_COLOR = 0x9F27CDC0;

    private static final int DEFAULT_TAG_TEXT_SIZE = 15;

    private static final int DEFAULT_TAG_TEXT_COLOR = 0xFFFFFFFF;

    private float mCornerDistance;

    private float mTagWidth;

    private int mTagBackgroundColor;

    private Path mPath;

    private Paint mPaint;

    private String mTagText;

    private int mTagTextSize;

    private Paint mTextPaint;

    private Rect mTagTextBound;

    private int mTagTextColor;

    private float mDensity;

    private int mTagOrientation;

    private MyPoint startPoint;

    private MyPoint endPoint;

    private Paint mBitmapPaint;

    private RectF mRoundRect;

    private boolean mTagEnable;

    private int mRoundRadius;

    public SimpleTagImageView(Context context) {
        this(context, null);
    }

    public SimpleTagImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SimpleTagImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDensity = context.getResources().getDisplayMetrics().density;
        TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.SimpleTagImageView,defStyleAttr,0);
        mTagOrientation = a.getInteger(R.styleable.SimpleTagImageView_simple_tag_orientation,0);
        mTagWidth = a.getDimensionPixelSize(R.styleable.SimpleTagImageView_simple_tag_width, dip2px(DEFAULT_TAG_WIDTH));
        mCornerDistance = a.getDimensionPixelSize(R.styleable.SimpleTagImageView_simple_corner_distance,dip2px(DEFAULT_CORNER_DISTANCE));
        mTagBackgroundColor = a.getColor(R.styleable.SimpleTagImageView_simple_tag_background_color,DEFAULT_TAG_BACKGROUND_COLOR);
        mTagText = a.getString(R.styleable.SimpleTagImageView_simple_tag_text);
        mTagTextSize = a.getDimensionPixelSize(R.styleable.SimpleTagImageView_simple_tag_textSize, dip2px(DEFAULT_TAG_TEXT_SIZE));
        mTagTextColor = a.getColor(R.styleable.SimpleTagImageView_simple_tag_textColor, DEFAULT_TAG_TEXT_COLOR);
        mTagEnable = a.getBoolean(R.styleable.SimpleTagImageView_simple_tag_enable,true);
        mRoundRadius = a.getDimensionPixelSize(R.styleable.SimpleTagImageView_simple_tag_round_radius,0);
        a.recycle();
        if(TextUtils.isEmpty(mTagText))mTagText = "";
        mPaint = new Paint();
        mPath = new Path();
        mTextPaint = new Paint();
        mTagTextBound = new Rect();
        startPoint = new MyPoint();
        endPoint = new MyPoint();
        mRoundRect = new RectF();
    }

    /**
     *
     * @param textSize unit:dip
     */
    public void setTagTextSize(int textSize) {
        this.mTagTextSize = dip2px(textSize);
        invalidate();
    }

    public int getTagTextSize(){
        return mTagTextSize;
    }

    /**
     *
     * @param cornerDistance unit:dip
     */
    public void setCornerDistance(int cornerDistance) {
        if(this.mCornerDistance == cornerDistance)return;
        this.mCornerDistance = dip2px(cornerDistance);
        invalidate();
    }

    /**
     *
     * @return unit:dip
     */
    public int getCornerDistance() {
        return px2dip(this.mCornerDistance);
    }

    public int getTagTextColor() {
        return this.mTagTextColor;
    }

    public void setTagTextColor(int tagTextColor) {
        if(this.mTagTextColor == tagTextColor)return;
        this.mTagTextColor = tagTextColor;
        invalidate();
    }

    public String getTagText() {
        return this.mTagText;
    }

    public void setTagText(String tagText){
        if(tagText.equals(this.mTagText))return;
        this.mTagText = tagText;
        invalidate();
    }

    public void setTagBackgroundColor(int tagBackgroundColor) {
        if(this.mTagBackgroundColor == tagBackgroundColor)return;
        this.mTagBackgroundColor = tagBackgroundColor;
        invalidate();
    }

    public int getTagBackgroundColor() {
        return this.mTagBackgroundColor;
    }

    /**
     * @return unit:dip
     */
    public int getTagWidth() {
        return px2dip(this.mTagWidth);
    }

    /**
     *
     * @param tagWidth unit:dip
     */
    public void setTagWidth(int tagWidth) {
        this.mTagWidth = dip2px(tagWidth);
        invalidate();
    }

    /**
     * @return  0 : left_top
     *          1 : right_top
     *          2 : left_bottom
     *          3 : right_bottom
     */
    public int getTagOrientation() {
        return mTagOrientation;
    }

    /**
     *
     * @param tagOrientation {@link #LEFT_TOP} or
     *                       {@link #LEFT_BOTTOM} or
     *                       {@link #RIGHT_TOP} or
     *                       {@link #RIGHT_BOTTOM}
     */
    public void setTagOrientation(int tagOrientation) {
        if(tagOrientation == this.mTagOrientation)return;
        this.mTagOrientation = tagOrientation;
        invalidate();
    }

    public void setTagEnable(boolean tagEnable) {
        if(this.mTagEnable == tagEnable) return ;
        this.mTagEnable = tagEnable;
        invalidate();
    }

    public boolean getTagEnable() {
        return this.mTagEnable;
    }

    public  int getTagRoundRadius() {
        return this.mRoundRadius;
    }

    public void setTagRoundRadius(int roundRadius) {
        if(this.mRoundRadius == roundRadius) return;
        this.mRoundRadius = roundRadius;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas mCanvas) {
        if(mRoundRadius == 0) {
            super.onDraw(mCanvas);
        }else {
            Drawable d = getDrawable();
            if(d == null) return;
            if(d.getIntrinsicWidth() == 0 || d.getIntrinsicHeight() == 0) return;
            setupBitmapPaint();
            mRoundRect.set(getPaddingLeft(),getPaddingTop(),getMeasuredWidth() - getPaddingRight(),getMeasuredHeight() - getPaddingBottom());
            mCanvas.drawRoundRect(mRoundRect, mRoundRadius, mRoundRadius, mBitmapPaint);
        }

        if(mTagWidth > 0 && mTagEnable) {
            float rDistance = mCornerDistance + mTagWidth/2;
            chooseTagOrientation(rDistance);
            mTextPaint.setTextSize(mTagTextSize);
            mTextPaint.getTextBounds(mTagText,0,mTagText.length(),mTagTextBound);
            mPaint.setDither(true);
            mPaint.setAntiAlias(true);
            mPaint.setColor(mTagBackgroundColor);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeJoin(Paint.Join.ROUND);
            mPaint.setStrokeCap(Paint.Cap.SQUARE);
            mPaint.setStrokeWidth(mTagWidth);
            mPath.reset();
            mPath.moveTo(startPoint.x, startPoint.y);
            mPath.lineTo(endPoint.x, endPoint.y);
            mCanvas.drawPath(mPath, mPaint);
            mTextPaint.setColor(mTagTextColor);
            mTextPaint.setTextSize(mTagTextSize);
            mTextPaint.setAntiAlias(true);
//          斜边长度
            float hypotenuse = THE_SQUARE_ROOT_OF_2 * rDistance;
            mCanvas.drawTextOnPath(mTagText, mPath, hypotenuse / 2 - mTagTextBound.width() / 2,
                    mTagTextBound.height() / 2, mTextPaint);
        }
    }

    private void chooseTagOrientation(float rDistance) {
        int mWidth = getMeasuredWidth();
        int mHeight = getMeasuredHeight();
        switch (mTagOrientation) {
            case 0:
                startPoint.x = 0;
                startPoint.y = rDistance;
                endPoint.x = rDistance;
                endPoint.y = 0;
                break;
            case 1:
                startPoint.x = mWidth - rDistance;
                startPoint.y = 0;
                endPoint.x = mWidth;
                endPoint.y = rDistance;
                break;
            case 2:
                startPoint.x = 0;
                startPoint.y = mHeight - rDistance;
                endPoint.x = rDistance;
                endPoint.y = mHeight;
                break;
            case 3:
                startPoint.x = mWidth - rDistance;
                startPoint.y = mHeight;
                endPoint.x = mWidth;
                endPoint.y = mHeight - rDistance;
                break;
        }
    }

    private void setupBitmapPaint() {
        Drawable drawable = getDrawable();
        if (drawable == null) {
            return;
        }
        Bitmap mBitmap = drawableToBitmap(drawable);
        BitmapShader mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        if(getScaleType() != ScaleType.FIT_XY){
            Log.w(TAG,String.format("Now scale type just support fitXY,other type invalid"));
        }
        //now scale type just support fitXY
        //todo support all scale type
        Matrix mMatrix = new Matrix();
        mMatrix.setScale(getWidth() * 1.0f / mBitmap.getWidth(), getHeight() * 1.0f / mBitmap.getHeight());
        mBitmapShader.setLocalMatrix(mMatrix);
        if(mBitmapPaint == null) {
            mBitmapPaint = new Paint();
            mBitmapPaint.setDither(false);
            mBitmapPaint.setAntiAlias(true);
            mBitmapPaint.setShader(mBitmapShader);
        }
    }

    private int dip2px(int dip) {
        return (int)(mDensity * dip + 0.5f);
    }

    private int px2dip(float px) {
        return (int)(px/mDensity + 0.5f);
    }

    private Bitmap drawableToBitmap(Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            return bitmapDrawable.getBitmap();
        }
        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();
        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        drawable.draw(canvas);
        return bitmap;
    }

    static class MyPoint {
        float x;
        float y;
    }
}

小逗逼我自定义控件不玩不溜,只有充当搬运工了,进阶路上摸索,求指导。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值