zoukankan      html  css  js  c++  java
  • 一个炫字都不够??!!!手把手带你打造3D自定义view

    分享一则最近流行的笑话:
    最新科学研究表明:寒冷可以使人保持年轻,楼下的王大爷表示虽然今年已经60多岁了,但是仍然冷的跟孙子一样。

    呃。好吧,这个冬天确实有点冷,在广州活生生的把我这个原生北方人,冻成一条狗。(研究表明:寒冷可以让人类基因突变。。。。)

    好了不扯了。前些日子有朋友让我写博客来分析一下这个仿MIUI的时钟,从中学到了一些炫酷效果的实现。

    本项目地址:https://github.com/githubwing/compassView

    那么是啥3D效果呢,先来看看效果图,额。。有好多个:
    这里写图片描述
    这里写图片描述
    这里写图片描述

    其实后两个都是png来的。。

    转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50590051

    那么请问,看到图形的变换,你想到了什么?
    没错!就是Matrix。
    关于Matrix你可以到爱哥的博客了解到及其详细的讲解(谢谢爱哥!)。

    下面我们就来研究一下如何用矩阵,实现这个3d的效果。

    首先新建自定义view类。

    
    public class TDView extends View {
        private Paint mPaint;
    
        private int mCenterX;
        private int mCenterY;
         public TDView(Context context, AttributeSet attrs) {
            super(context, attrs);
            mPaint = new Paint();
        }
    }

    然后在圆心画一个圆出来

     @Override
        protected void onDraw(Canvas canvas) {
    
            mCenterX = getWidth() / 2;
            mCenterY = getHeight() / 2;
            canvas.drawCircle(mCenterX,mCenterY,100,mPaint);
        }
    
    

    现在是这样的:

    这里写图片描述

    我们知道,处理一个图片的时候(切错)可以使用矩阵来处理,同时处理X,Y的话可以使用Camera类,camera可以生成一个指定效果的矩阵。直接来看用法:

    private Camera mCamera;
    private Matrix mMatrix;
    
    mMatrix = new Matrix();
    mCamera = new Camera();

    在onDraw里 把camera给旋转一下,并把生成的矩阵给一个矩阵。再把矩阵应用到canvas,看一下效果。

            mMatrix.reset();
            mCamera.save();
            mCamera.rotateX(10);
            mCamera.rotateY(20);
            mCamera.getMatrix(mMatrix);
            mCamera.restore();
            //将矩阵作用于整个canvas
            canvas.concat(mMatrix);
            ```
    这里写图片描述 
    ![这里写图片描述](http://img.blog.csdn.net/20160127001011557)
    呃。。。确实是变形了。。但是好像不是我们想要的结果? 
    这是因为,矩阵的变换坐标总从左上角(0,0)开始。所以我们要把变换的坐标改为中心点,方法如下:
        mMatrix.reset();
        mCamera.save();
        mCamera.rotateX(10);
        mCamera.rotateY(20);
        mCamera.getMatrix(mMatrix);
        mCamera.restore();
        //改变矩阵作用点
        mMatrix.preTranslate(-mCenterX, -mCenterY);
        mMatrix.postTranslate(mCenterX, mCenterY);
        canvas.concat(mMatrix);
    

    此时的效果看起来像是向左倾斜了:
    这里写图片描述
    这里写图片描述

    接下来让他跟随手指移动,重写onTouchEvent:

     @Override
        public boolean onTouchEvent(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();
    
            int action = event.getActionMasked();
            switch (action) {
                case MotionEvent.ACTION_MOVE: {
                    //这里将camera旋转的变量赋值
                    mCanvasRotateY = y;
                    mCanvasRotateX = x;
                    invalidate();
                    return true;
                }
                case MotionEvent.ACTION_UP: {
    
                    //这里将camera旋转的变量赋值
                    mCanvasRotateY = 0;
                    mCanvasRotateX = 0;
                    invalidate();
    
                    return true;
                }
            }
            return super.onTouchEvent(event);
        }
    
    哈哈。。看看是什么效果: 
    这里写图片描述
    ![这里写图片描述](http://img.blog.csdn.net/20160127002007112)
    
    什么鬼,怎么跟转硬币一样。 因为旋转的X,Y给的太大了呗。所以要约束一下。
    
    定义一个旋转最大值 

    private float mCanvasMaxRotateDegree = 20;

    再用percent思想(前面博客有提到),来处理手指触摸点和这个度数变化的关系:

    private void rotateCanvasWhenMove(float x, float y) {
    float dx = x - mCenterX;
    float dy = y - mCenterY;

        float percentX = dx / mCenterX;
        float percentY = dy /mCenterY;
    
        if (percentX > 1f) {
            percentX = 1f;
        } else if (percentX < -1f) {
            percentX = -1f;
        }
        if (percentY > 1f) {
            percentY = 1f;
        } else if (percentY < -1f) {
            percentY = -1f;
        }
    
        mCanvasRotateY = mCanvasMaxRotateDegree * percentX;
        mCanvasRotateX = -(mCanvasMaxRotateDegree * percentY);
    
    }
    

    最后将TouchEvent里面的ACTION_MOVE 调用此函数即可。
    此时,完整的代码如下:

        private int mCenterX;
        private int mCenterY;
        private float mCanvasRotateX = 0;
        private float mCanvasRotateY = 0;
        private float mCanvasMaxRotateDegree = 20;
        private Matrix mMatrix = new Matrix();
        private Camera mCamera = new Camera();
        private Paint mPaint;
        public TDView(Context context) {
            super(context);
        }
        public TDView(Context context, AttributeSet attrs) {
            super(context, attrs);
            mPaint = new Paint();
            mCanvasMaxRotateDegree = 20;
        }
    
    
    
        @Override
        protected void onDraw(Canvas canvas) {
    
            mCenterX = getWidth() / 2;
            mCenterY = getHeight() / 2;
            rotateCanvas(canvas);
            canvas.drawCircle(mCenterX, mCenterY, 100, mPaint);
    
    
    
        }
    
        private void rotateCanvas(Canvas canvas) {
            mMatrix.reset();
            mCamera.save();
            mCamera.rotateX(mCanvasRotateX);
            mCamera.rotateY(mCanvasRotateY);
            mCamera.getMatrix(mMatrix);
            mCamera.restore();
            mMatrix.preTranslate(-mCenterX, -mCenterY);
            mMatrix.postTranslate(mCenterX, mCenterY);
    
            canvas.concat(mMatrix);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();
    
            int action = event.getActionMasked();
            switch (action) {
                case MotionEvent.ACTION_DOWN: {
                    rotateCanvasWhenMove(x, y);
                    return true;
                }
                case MotionEvent.ACTION_MOVE: {
                    rotateCanvasWhenMove(x, y);
                    invalidate();
                    return true;
                }
                case MotionEvent.ACTION_UP: {
                    mCanvasRotateY = 0;
                    mCanvasRotateX = 0;
                    invalidate();
    
                    return true;
                }
            }
            return super.onTouchEvent(event);
        }
    
        private void rotateCanvasWhenMove(float x, float y) {
            float dx = x - mCenterX;
            float dy = y - mCenterY;
    
            float percentX = dx / mCenterX;
            float percentY = dy /mCenterY;
    
            if (percentX > 1f) {
                percentX = 1f;
            } else if (percentX < -1f) {
                percentX = -1f;
            }
            if (percentY > 1f) {
                percentY = 1f;
            } else if (percentY < -1f) {
                percentY = -1f;
            }
    
            mCanvasRotateY = mCanvasMaxRotateDegree * percentX;
            mCanvasRotateX = -(mCanvasMaxRotateDegree * percentY);
        }
    
    
    }

    简简单单100行代码,实现了3D效果的view:
    这里写图片描述

    接下来做什么呢?
    当然是把这个效果加到我们的自定义view里面!
    比如,把我的PanelView加上这个效果:
    这里写图片描述

    这里写图片描述
    哗!瞬间高大上!

    那么,你要不要跟我趁热来一发自定义view?
    说搞就搞!

    在原有类上进行修改
    给一个好看的底色,画一条线

            mBgColor = Color.parseColor("#227BAE");
            canvas.drawLine(mCenterX,100,mCenterX,130,mPaint);
    

    这里写图片描述
    这里写图片描述
    嗯。不错 有条线了。微调下间距,旋转画布,画出整个圆形来:

    //保存坐标系 
    canvas.save(); 
    for (int i = 0; i < 120; i++) { 
    canvas.rotate(3,mCenterX,mCenterY); 
    canvas.drawLine(mCenterX, 150, mCenterX, 170, mPaint); 
    } 
    //恢复坐标系 
    canvas.restore(); 

    这里写图片描述
    嗯。。看起来想点样子了。 接下来调整透明度。

    canvas.save();
            for (int i = 0; i < 120; i++) {
                //根据i调整透明度alpha
                mPaint.setAlpha(255-(mAlpha * i/120));
                canvas.drawLine(mCenterX, 150, mCenterX, 170, mPaint);
    
                canvas.rotate(3,mCenterX,mCenterY);
            }
            canvas.restore();

    这里写图片描述
    哈哈。。有没有点意思呢。。
    这里写图片描述
    我们画个圆球上去!画之前先ctrl + alt + m 把之前画弧的方法提出来。
    画一个紧挨着的圆

    private void drawCircle(Canvas canvas) {
    mPaint.setAlpha(255);
    canvas.drawCircle(mCenterX,213,10,mPaint);
    }
    这里写图片描述
    这里写图片描述
    不错不错,给点动态效果吧,让圆点跟着我们触摸的地方走。怎么做呢。。 当然还是旋转画布了!
    这里需要注意的是触摸点与12点钟方向形成的夹角计算。画图分析一下
    这里写图片描述
    可以看到 我们只需要调用Math.atan方法即可算出a的弧度,再将其转换为角度即可,在进行3D旋转之前,旋转画布:

    protected void onDraw(Canvas canvas) {
            canvas.drawColor(mBgColor);
            mCenterX = getWidth() / 2;
            mCenterY = getHeight() / 2;
    
            Log.e("wing",alpha+"");
            canvas.rotate((float) alpha,mCenterX,mCenterY);
    
            alpha = Math.atan((mTouchX-mCenterX)/(mCenterY-mTouchY));
            alpha = Math.toDegrees(alpha);
            if(mTouchY>mCenterY){
                alpha = alpha+180;

    现在看一下效果:
    这里写图片描述
    这里写图片描述

    效果出来了,但是还美中不足呀。 因为中间太空了,所以这个3D效果看起来有点奇怪。那就给他中间加点东西吧! 比如一个指针。

    private void drawPath(Canvas canvas) {
            mPath.moveTo(mCenterX,223);
            mPath.lineTo(mCenterX-30,mCenterY);
            mPath.lineTo(mCenterX,2*mCenterY-223);
            mPath.lineTo(mCenterX+30,mCenterY);
            mPath.lineTo(mCenterX,233);
            mPath.close();
            canvas.drawPath(mPath,mPaint);
            mPaint.setColor(Color.parseColor("#55227BAE"));
            canvas.drawCircle(mCenterX,mCenterY,20,mPaint);
            }

    最后大功告成!!! 看效果!
    这里写图片描述
    这里写图片描述

    如果你喜欢我的博客,请点击关注。欢迎评论~~

    下一篇! 手把手教你做一个qq下拉抢红包:打开链接

  • 相关阅读:
    .net core 3.1 使用Redis缓存
    JavaSE 高级 第11节 缓冲输入输出字节流
    JavaSE 高级 第10节 字节数组输出流ByteArrayOutputStream
    JavaSE 高级 第09节 字节数组输入流ByteArrayInputStream
    JavaSE 高级 第08节 文件输出流FileOutputStream
    JavaSE 高级 第07节 文件输入流FileInputStream
    JavaSE 高级 第06节 初识I、O流
    JavaSE 高级 第05节 日期类与格式化
    JavaSE 高级 第04节 StringBuffer类
    JavaSE 高级 第03节 Math类与猜数字游戏
  • 原文地址:https://www.cnblogs.com/muyuge/p/6333538.html
Copyright © 2011-2022 走看看