使用自定义控件让图片转起来

1. 在res/values下新建属性文件attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <declare-styleable name="RotateCircle">
        <attr name="r" format="dimension"/>
    </declare-styleable>
</resources>

2. 在布局文件中对属性r初始化

    <com.lyl.rotatecircle.RotateCircle
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        app:r="100dp"
        android:id="@+id/rc"/>

3. 实现RotateCircle类

public class RotateCircle extends View implements Runnable{
    private Context mContext;

    //最终显示的圆的半径
    private float mR;
    //最终显示的圆心位置
    private float mX;
    private float mY;

    //圆所在屏幕中的宽高 -- 变量能定义成float,就不定义为int,在最后再类型转换
    private float realW;
    private float realH;

    //画圆的画笔
    private Paint mPaint = new Paint();
    private float mAngle;//旋转角度
    //自定义控件 默认找两个参数的构造
    public RotateCircle(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RotateCircle(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;

        //获取控件属性的值
        obtainStyledAttributes(attrs);
    }

    private void obtainStyledAttributes(AttributeSet attrs){
        TypedArray ta = mContext.obtainStyledAttributes(attrs, R.styleable.RotateCircle);
        mR = ta.getDimension(R.styleable.RotateCircle_r, 0);
        ta.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        realW = 2*mR;//getMeasuredWidth();//20200627
        realH = 2*mR;
        setMeasuredDimension((int)realW, (int)realH);
    }

    /**缩放bp
     * x/y bp的中心坐标
     * w/h bp的最终宽高
     * paint 画bp的画笔
     * */
    private void setBitmap(Bitmap bp, float x, float y, float w, float h, Paint paint){
        if (null == bp ){
            return;
        }

        float oriW = bp.getWidth();
        float oriH = bp.getHeight();
        if (oriW <= 0.0f || oriH <= 0.0f){
            return;
        }
        float scaleX = w/oriW;
        float scaleY = h/oriH;
        float scale = Math.max(scaleX, scaleY);//20200627

        Matrix matrix = new Matrix();
        matrix.reset();//重置矩阵为单位矩阵!不可少
        //先缩放,再平移
        matrix.postScale(scale, scale);
        //matrix.postTranslate(x - w/2, y - h/2);
        matrix.postTranslate(x - oriW*scale/2f, y - oriH*scale/2f);//20200627

        /**
         平铺模式有三种:
         Shader.TileMode.CLAMP:如果着色器超出原始边界范围,会复制边缘颜色。
         Shader.TileMode.MIRROR:图像不停翻转来平铺,直到平铺完毕。
         Shader.TileMode.REPEAT:横向和纵向的重复着色器的图像。
         一般来说,当Canvas的宽度(高度)小于等于BitmapShader中Bitmap的宽度(高度),我们会使用Shader.TileMode.CLAMP模式,
         否则我们会使用Shader.TileMode.MIRROR或者Shader.TileMode.REPEAT模式。
         **/
        BitmapShader shader = new BitmapShader(bp, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        shader.setLocalMatrix(matrix);
        paint.setAntiAlias(true);
        paint.setShader(shader);
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        //有些变量初始化用到的值,要在测量以后才知道,这些变量可在布局方法中初始化
        //测量 - 布局 - 绘画:只有绘画会走多次,尽量少的在onDraw中计算/初始化变量等等
        mX = realW - mR;
        mY = realH - mR;

        Bitmap bp = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.circle);
        setBitmap(bp, mX, mY, realW, realH, mPaint);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //1 使用图片
        canvas.save();//将已经绘制的图像保存起来,让后续的操作就好像在一个新图层上操作一样。
        //从自定义视图区域的左顶点(0, 0)到视图的(mX, mY),但是圆的圆心在视图区域的边界处转
        canvas.translate(mX, mY);
        canvas.rotate(mAngle);//画布旋转 逆向为正值
        canvas.translate(-mX, -mY);
        canvas.drawCircle(mX, mY, mR, mPaint);
        canvas.restore();//合并图层的操作,作用是将save之后绘制的图像和save之前的图像进行合并。save/restore成对出现
    }

    @Override
    public void run() {
        while (true) {
            try {
                mAngle += 2;
                postInvalidate();// 刷新view
                Thread.sleep(10);// 每10ms刷新一次
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

4. 使用RotateCircle,让图片动起来。

        rc = findViewById(R.id.rc);
        new Thread(rc).start();

5. 以上是使用给定的图片,下面自己画空心圆,然后使用LinearGradient给图片上色,让圆环转动起来。

同样,要设置圆环的半径和厚度,定义初值,并获取这些值。

public class RotateCircle extends View implements Runnable{
    private Context mContext;

    //最终显示的圆的半径
    private float mR;
    //最终显示的圆的厚度
    private float mThickness;
    //最终显示的圆心位置
    private float mX;
    private float mY;

    //圆所在屏幕中的宽高 -- 变量能定义成float,就不定义为int,在最后再类型转换
    private float realW;
    private float realH;

    //画圆的画笔
    private Paint mPaint = new Paint();
    private float mAngle;//旋转角度
    //自定义控件 默认找两个参数的构造
    public RotateCircle(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RotateCircle(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;

        //获取控件属性的值
        obtainStyledAttributes(attrs);
    }

    private void obtainStyledAttributes(AttributeSet attrs){
        TypedArray ta = mContext.obtainStyledAttributes(attrs, R.styleable.RotateCircle);
        mR = ta.getDimension(R.styleable.RotateCircle_r, 0);
        mThickness = ta.getDimension(R.styleable.RotateCircle_thickness, 0);
        ta.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        realW = getMeasuredWidth();
        realH = 2*(mR + mThickness);//2*mR;
        setMeasuredDimension((int)realW, (int)realH);
    }

    /** 缩放bp
     * x/y bp的中心坐标
     * w/h bp的最终宽高
     * paint 画bp的画笔
     * */
    private void setBitmap(Bitmap bp, float x, float y, float w, float h, Paint paint){
        if (null == bp ){
            return;
        }

        float oriW = bp.getWidth();
        float oriH = bp.getHeight();
        if (oriW <= 0.0f || oriH <= 0.0f){
            return;
        }
        float scaleX = w/oriW;
        float scaleY = h/oriH;
        float scale = Math.min(scaleX, scaleY);

        Matrix matrix = new Matrix();
        matrix.reset();//重置矩阵为单位矩阵!不可少
        matrix.postScale(scale, scale);
        matrix.postTranslate(x - w/2, y - h/2);

        /**
         平铺模式有三种:
         Shader.TileMode.CLAMP:如果着色器超出原始边界范围,会复制边缘颜色。
         Shader.TileMode.MIRROR:图像不停翻转来平铺,直到平铺完毕。
         Shader.TileMode.REPEAT:横向和纵向的重复着色器的图像。
         一般来说,当Canvas的宽度(高度)小于等于BitmapShader中Bitmap的宽度(高度),我们会使用Shader.TileMode.CLAMP模式,
         否则我们会使用Shader.TileMode.MIRROR或者Shader.TileMode.REPEAT模式。
         **/
        BitmapShader shader = new BitmapShader(bp, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        shader.setLocalMatrix(matrix);
        paint.setAntiAlias(true);
        paint.setShader(shader);
    }

    private void setPaint(float x0, float y0, float x1, float y1, Paint paint){
        paint.reset();
        paint.setAntiAlias(true);
        paint.setStrokeWidth(mThickness); //设置宽度
        paint.setStyle(Paint.Style.STROKE);  //设置空心
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        /**
         paint 默认画笔为矩形,即如果要用圆形画笔,则在paint定义里面加上
         mArcPaint.setStrokeJoin(Paint.Join.ROUND);
         mArcPaint.setStrokeCap(Paint.Cap.ROUND);
         如果想要从圆形切换成矩形需要
         paint.setStrokeJoin(Paint.Join.MITER);
         paint.setStrokeCap(Paint.Cap.SQUARE);
         **/
        int[] colors = new int[]{0XFF7DE2F1, 0XFF1F69C6, 0XFF1F69C6};
        float[] positions = new float[]{0,0.5f,1.0f};

        LinearGradient lg =new LinearGradient(
                x0, y0, x1, y1,
                colors, positions, Shader.TileMode.CLAMP);
        paint.setShader(lg);
    }
    @SuppressLint("DrawAllocation")
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //有些变量初始化用到的值,要在测量以后才知道,这些变量可在布局方法中初始化
        //测量 - 布局 - 绘画:只有绘画会走多次,尽量少的在onDraw中计算/初始化变量等等

        //1 使用图片
//        mX = realW - mR;
//        mY = realH - mR;
//        Bitmap bp = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.circle);
//        setBitmap(bp, mX, mY, realW, realH, mPaint);

        //2. 自己画圆
        mX = realW - mR - mThickness;
        mY = realH - mR - mThickness;
        setPaint(0, 0, realW, 0, mPaint);//水平方向上渐变
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //1 使用图片
//        canvas.save();//将已经绘制的图像保存起来,让后续的操作就好像在一个新图层上操作一样。
//        //从自定义视图区域的左顶点(0, 0)到视图的(mX, mY),但是圆的圆心在视图区域的边界处转
//        canvas.translate(mX, mY);
//        canvas.rotate(mAngle);//画布旋转 逆向为正值
//        canvas.translate(-mX, -mY);
//        canvas.drawCircle(mX, mY, mR, mPaint);
//        canvas.restore();//合并图层的操作,作用是将save之后绘制的图像和save之前的图像进行合并。save/restore成对出现

        //2. 自己画圆
        canvas.save();
        canvas.translate(mX, mY);
        canvas.rotate(mAngle);
        canvas.drawCircle(0, 0, mR, mPaint);//此处画的是空心圆,半径为mR,圆的厚度为mThickness
        canvas.restore();
    }

    @Override
    public void run() {
        while (true) {
            try {
                mAngle += 2;
                postInvalidate();// 刷新view
                Thread.sleep(10);// 每10ms刷新一次
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

源码下载:RotateCircle

参考:

图片的旋转平移

Android绘图之Shader
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值