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下拉抢红包:打开链接

  • 相关阅读:
    JMeter之Throughput Controller详解(实验)吞吐量控制百分比
    Jmeter的场景设置方法
    Java面试题大全(2020版)
    自动化测试面试题及答案大全(5)
    自动化测试面试题及答案大全(4)
    自动化测试面试题及答案大全(3)
    自动化测试面试题及答案大全(2)
    获取当前时间前一天的随机时间
    简单的测试登录接口的脚本
    selenium无头浏览器--谷歌
  • 原文地址:https://www.cnblogs.com/muyuge/p/6333538.html
Copyright © 2011-2022 走看看