精华内容
下载资源
问答
  • 2016-11-15 16:57:07

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53173288
    本文出自【DylanAndroid的博客】


    Android自定义View和属性动画完美结合,创造出酷炫圆环动画,带标尺和进度

    无意中,在看了【Android自定义View实战】之仿QQ运动步数圆弧及动画,Dylan计步中的控件StepArcView 这个之后,突然发奇想,想做这么一个图。在原来的基础上增加一些东西,这样会更好一点。内容更丰富。

    主要是在原来的基础上添加了如下功能

    • 1.进度圆环的颜色是渐变。
    • 2.添加一个进度标尺,类似与钟表表盘的样子,用来显示刻度。
    • 3.添加一个进度指示器,三角形的样子,用来显示进度。

      效果图如下

    效果图

    下面,就针对这三个变化来说明一下:

    一.渐变的圆环颜色

            /**
             * 设置圆形渐变
             * 【第一个参数】:中心点x坐标
             * 【第二个参数】:中心点y坐标
             * 【第三个参数】:渐变的颜色数组
             * 【第四个参数】:渐变的颜色数组对应的相对位置
             */
            paintCurrent.setShader(new SweepGradient(centerX, centerX, new int[]{getResources().getColor(R.color.start_color), getResources().getColor(R.color.end_color)}, null));
    

    二.画三角形进度指示器

    • 1.拿到一个资源图片对象
            /**
             * 通过这个拿到一个资源图片对象
             */
            bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.arrow);
    • 2.计算三角形移动轨迹的坐标

       /**
        * 为进度设置动画
        * ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,
        * 而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。
        * 它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,
        * 我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,
        * 那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。
        *
        * @param last
        * @param current
        */
       @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
       private void setAnimation(float last, float current, int length) {
           ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, current);
           progressAnimator.setDuration(length);
           progressAnimator.setTarget(currentAngleLength);
           progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
               @Override
               public void onAnimationUpdate(ValueAnimator animation) {
                   /**每次要绘制的圆弧角度**/
                   currentAngleLength = (float) animation.getAnimatedValue();
      
                   /**要绘制的三角形指示器的半径**/
                   float radius=centerX - borderWidth-bitmap.getHeight();
                   /**要绘制的三角形指示器的x坐标**/
                   point.x = (float) (centerX +radius * Math.cos((startAngle + currentAngleLength) * Math.PI / 180));
                   /**要绘制的三角形指示器的y坐标**/
                   point.y = (float) (centerX + radius* Math.sin((startAngle + currentAngleLength) * Math.PI / 180));
                   Log.d("stepView", point + "");
      
                   /**要绘制的圆弧多绘制的部分减掉**/
                   double subtractionScale = borderWidth/2/(centerX*2*Math.PI);
                   double subtractionAngle=subtractionScale*angleLength;
                   if(currentAngleLength>subtractionAngle){
                       currentAngleLength-=subtractionAngle;
                   }
                   invalidate();
               }
           });
           progressAnimator.start();
       }
    • 3.调整旋转的角度后绘制三角形指示器

      /**
        * 5.画三角形
        *
        * @param canvas
        */
       private void drawBitmap(Canvas canvas) {
           // 定义矩阵对象
           Matrix matrix = new Matrix();
           // 参数为正则向右旋转
           matrix.postRotate(startAngle + currentAngleLength + 90);
           Bitmap dstbmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
                   matrix, true);
           Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);//这里不管怎么设置都不影响最终图像
           canvas.drawBitmap(dstbmp, point.x - dstbmp.getWidth() / 2, point.y - dstbmp.getHeight() / 2, mBitmapPaint);
       }

      三.绘制进度表尺的钟表仪表盘

       /**
        * 6. 画钟表线
        *
        * @param canvas
        */
       private void drawLine(Canvas canvas) {
           Paint mPaint = new Paint();
           mPaint.setStrokeWidth(5);
           mPaint.setColor(getResources().getColor(R.color.start_color));
           /**要绘制的表盘线的总数**/
           int count = 60;
           /**要绘制的表盘每个间隔线条之间的夹角**/
           int avgAngle = (360 / (count - 1));
           /**要绘制的表盘的最长的半径**/
           float radius = centerX - borderWidth - bitmap.getHeight() - 20;
           /**要绘制的表盘线条长度**/
           int lineLength = 25;
           /**起始点**/
           PointF point1 = new PointF();
           /**终止点**/
           PointF point2 = new PointF();
           for (int i = 0; i < count; i++) {
               int angle = avgAngle * i;
               /**起始点坐标**/
               point1.x = centerX + (float) Math.cos(angle * (Math.PI / 180)) * radius;
               point1.y = centerX + (float) Math.sin(angle * (Math.PI / 180)) * radius;
      
               /**终止点坐标**/
               point2.x = centerX + (float) Math.cos(angle * (Math.PI / 180)) * (radius - lineLength);
               point2.y = centerX + (float) Math.sin(angle * (Math.PI / 180)) * (radius - lineLength);
      
               /**画线**/
               canvas.drawLine(point1.x, point1.y, point2.x, point2.y, mPaint);
           }
       }
      

      四.完整代码

      package cn.bluemobi.dylan.stepcirclestaffview;
      
      /**
      * Created by yuandl on 2016-11-08.
      */
      
      import android.animation.ValueAnimator;
      import android.content.Context;
      import android.graphics.Bitmap;
      import android.graphics.BitmapFactory;
      import android.graphics.Canvas;
      import android.graphics.Color;
      import android.graphics.LinearGradient;
      import android.graphics.Matrix;
      import android.graphics.Paint;
      import android.graphics.PointF;
      import android.graphics.RadialGradient;
      import android.graphics.Rect;
      import android.graphics.RectF;
      import android.graphics.Shader;
      import android.graphics.SweepGradient;
      import android.graphics.Typeface;
      import android.os.Build;
      import android.support.annotation.RequiresApi;
      import android.util.AttributeSet;
      import android.util.Log;
      import android.view.View;
      
      import java.util.logging.Logger;
      
      
      /**
      * Created by DylanAndroid on 2016/5/26.
      * 显示步数的圆弧
      */
      public class StepArcView extends View {
      
       /**
        * 圆弧的宽度
        */
       private float borderWidth = 38f;
       /**
        * 画步数的数值的字体大小
        */
       private float numberTextSize = 0;
       /**
        * 步数
        */
       private String stepNumber = "0";
       /**
        * 开始绘制圆弧的角度
        */
       private float startAngle = 90;
       /**
        * 终点对应的角度和起始点对应的角度的夹角
        */
       private float angleLength = 360;
       /**
        * 所要绘制的当前步数的红色圆弧终点到起点的夹角
        */
       private float currentAngleLength = 0;
       /**
        * 动画时长
        */
       private int animationLength = 3000;
      
       private PointF point;
       private float centerX;
       private Bitmap bitmap;
       private int totalStepNum;
      
       private void init() {
           point = new PointF();
           /**
            * 通过这个拿到一个资源图片对象
            */
           bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.arrow);
      
      
       }
      
       public StepArcView(Context context) {
           super(context);
           init();
      
       }
      
      
       public StepArcView(Context context, AttributeSet attrs) {
           super(context, attrs);
           init();
       }
      
       public StepArcView(Context context, AttributeSet attrs, int defStyleAttr) {
           super(context, attrs, defStyleAttr);
           init();
       }
      
      
       @Override
       protected void onDraw(Canvas canvas) {
           super.onDraw(canvas);
           /**中心点的x坐标*/
           centerX = (getWidth()) / 2;
           /**指定圆弧的外轮廓矩形区域*/
           RectF rectF = new RectF(0 + borderWidth, borderWidth, 2 * centerX - borderWidth, 2 * centerX - borderWidth);
      
           /**【第一步】绘制整体的黄色圆弧*/
           drawArcYellow(canvas, rectF);
           /**【第二步】绘制当前进度的红色圆弧*/
           drawArcRed(canvas, rectF);
           /**【第三步】绘制当前进度的红色数字*/
           drawTextNumber(canvas, centerX);
           /**【第四步】绘制"步数"的红色数字*/
           drawTextStepString(canvas, centerX);
           /**【第五步】绘制"步数"进度标尺的三角形*/
           drawBitmap(canvas);
           /**【第六步】绘制"步数"进度标尺类似于钟表线隔*/
           drawLine(canvas);
       }
      
      
       /**
        * 1.绘制总步数的黄色圆弧
        *
        * @param canvas 画笔
        * @param rectF  参考的矩形
        */
       private void drawArcYellow(Canvas canvas, RectF rectF) {
           Paint paint = new Paint();
           /** 默认画笔颜色,黄色 */
           paint.setColor(getResources().getColor(R.color.default_color));
           /** 结合处为圆弧*/
           paint.setStrokeJoin(Paint.Join.ROUND);
           /** 设置画笔的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形*/
           paint.setStrokeCap(Paint.Cap.ROUND);
           /** 设置画笔的填充样式 Paint.Style.FILL  :填充内部;Paint.Style.FILL_AND_STROKE  :填充内部和描边;  Paint.Style.STROKE  :仅描边*/
           paint.setStyle(Paint.Style.STROKE);
           /**抗锯齿功能*/
           paint.setAntiAlias(true);
           /**设置画笔宽度*/
           paint.setStrokeWidth(borderWidth);
      
           /**绘制圆弧的方法
            * drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧,
            参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,
            参数二是起始角(度)在电弧的开始,圆弧起始角度,单位为度。
            参数三圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。
            参数四是如果这是true(真)的话,在绘制圆弧时将圆心包括在内,通常用来绘制扇形;如果它是false(假)这将是一个弧线,
            参数五是Paint对象;
            */
           canvas.drawArc(rectF, startAngle, angleLength, false, paint);
      
       }
      
       /**
        * 2.绘制当前步数的红色圆弧
        */
       private void drawArcRed(Canvas canvas, RectF rectF) {
           Paint paintCurrent = new Paint();
           paintCurrent.setStrokeJoin(Paint.Join.ROUND);
           paintCurrent.setStrokeCap(Paint.Cap.SQUARE);//圆角弧度
           paintCurrent.setStyle(Paint.Style.STROKE);//设置填充样式
           paintCurrent.setAntiAlias(true);//抗锯齿功能
           paintCurrent.setStrokeWidth(borderWidth);//设置画笔宽度
           /**
            * 设置圆形渐变
            * 【第一个参数】:中心点x坐标
            * 【第二个参数】:中心点y坐标
            * 【第三个参数】:渐变的颜色数组
            * 【第四个参数】:渐变的颜色数组对应的相对位置
            */
           paintCurrent.setShader(new SweepGradient(centerX, centerX, new int[]{getResources().getColor(R.color.start_color), getResources().getColor(R.color.end_color)}, null));
           canvas.drawArc(rectF, startAngle, currentAngleLength, false, paintCurrent);
       }
      
       /**
        * 3.圆环中心的步数
        */
       private void drawTextNumber(Canvas canvas, float centerX) {
           Paint vTextPaint = new Paint();
           vTextPaint.setTextAlign(Paint.Align.CENTER);
           vTextPaint.setAntiAlias(true);//抗锯齿功能
           vTextPaint.setTextSize(numberTextSize);
           Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
           vTextPaint.setTypeface(font);//字体风格
           vTextPaint.setColor(getResources().getColor(R.color.center_text_color));
           Rect bounds_Number = new Rect();
           vTextPaint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
           canvas.drawText(stepNumber, centerX, getHeight() / 2 + bounds_Number.height() / 2, vTextPaint);
      
       }
      
       /**
        * 4.圆环中心[步数]的文字
        */
       private void drawTextStepString(Canvas canvas, float centerX) {
           Paint vTextPaint = new Paint();
           vTextPaint.setTextSize(dipToPx(13));
           vTextPaint.setTextAlign(Paint.Align.CENTER);
           vTextPaint.setAntiAlias(true);//抗锯齿功能
           vTextPaint.setColor(getResources().getColor(R.color.other_text_color));
           String stepString = "目标 "+totalStepNum;
           Rect bounds = new Rect();
           vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);
           canvas.drawText(stepString, centerX, getHeight() / 2 + bounds.height() + getFontHeight(numberTextSize), vTextPaint);
           canvas.save();
            stepString = "今天步数";
            bounds = new Rect();
           vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);
           canvas.drawText(stepString, centerX, getHeight() / 2  - getFontHeight(numberTextSize), vTextPaint);
      
       }
      
       /**
        * 5.画三角形
        *
        * @param canvas
        */
       private void drawBitmap(Canvas canvas) {
           // 定义矩阵对象
           Matrix matrix = new Matrix();
           // 参数为正则向右旋转
           matrix.postRotate(startAngle + currentAngleLength + 90);
           Bitmap dstbmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
                   matrix, true);
           Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);//这里不管怎么设置都不影响最终图像
           canvas.drawBitmap(dstbmp, point.x - dstbmp.getWidth() / 2, point.y - dstbmp.getHeight() / 2, mBitmapPaint);
       }
      
       /**
        * 6. 画钟表线
        *
        * @param canvas
        */
       private void drawLine(Canvas canvas) {
           Paint mPaint = new Paint();
           mPaint.setStrokeWidth(5);
           mPaint.setColor(getResources().getColor(R.color.start_color));
           /**要绘制的表盘线的总数**/
           int count = 60;
           /**要绘制的表盘每个间隔线条之间的夹角**/
           int avgAngle = (360 / (count - 1));
           /**要绘制的表盘的最长的半径**/
           float radius = centerX - borderWidth - bitmap.getHeight() - 20;
           /**要绘制的表盘线条长度**/
           int lineLength = 25;
           /**起始点**/
           PointF point1 = new PointF();
           /**终止点**/
           PointF point2 = new PointF();
           for (int i = 0; i < count; i++) {
               int angle = avgAngle * i;
               /**起始点坐标**/
               point1.x = centerX + (float) Math.cos(angle * (Math.PI / 180)) * radius;
               point1.y = centerX + (float) Math.sin(angle * (Math.PI / 180)) * radius;
      
               /**终止点坐标**/
               point2.x = centerX + (float) Math.cos(angle * (Math.PI / 180)) * (radius - lineLength);
               point2.y = centerX + (float) Math.sin(angle * (Math.PI / 180)) * (radius - lineLength);
      
               /**画线**/
               canvas.drawLine(point1.x, point1.y, point2.x, point2.y, mPaint);
           }
       }
      
       /**
        * 获取当前步数的数字的高度
        *
        * @param fontSize 字体大小
        * @return 字体高度
        */
       public int getFontHeight(float fontSize) {
           Paint paint = new Paint();
           paint.setTextSize(fontSize);
           Rect bounds_Number = new Rect();
           paint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
           return bounds_Number.height();
       }
      
       /**
        * dip 转换成px
        *
        * @param dip
        * @return
        */
      
       private int dipToPx(float dip) {
           float density = getContext().getResources().getDisplayMetrics().density;
           return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
       }
      
       /**
        * 所走的步数进度
        *
        * @param totalStepNum  设置的步数
        * @param currentCounts 所走步数
        */
       @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
       public void setCurrentCount(int totalStepNum, int currentCounts) {
           this.totalStepNum = totalStepNum;
           stepNumber = currentCounts + "";
           setTextSize(currentCounts);
           /**如果当前走的步数超过总步数则圆弧还是270度,不能成为园*/
           if (currentCounts > totalStepNum) {
               currentCounts = totalStepNum;
           }
      
           /**所走步数占用总共步数的百分比*/
           float scale = (float) currentCounts / totalStepNum;
           /**换算成弧度最后要到达的角度的长度-->弧长*/
           float currentAngleLength = scale * angleLength;
           /**开始执行动画*/
           setAnimation(0, currentAngleLength, animationLength);
       }
      
       /**
        * 为进度设置动画
        * ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,
        * 而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。
        * 它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,
        * 我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,
        * 那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。
        *
        * @param last
        * @param current
        */
       @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
       private void setAnimation(float last, float current, int length) {
           ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, current);
           progressAnimator.setDuration(length);
           progressAnimator.setTarget(currentAngleLength);
           progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
               @Override
               public void onAnimationUpdate(ValueAnimator animation) {
                   /**每次要绘制的圆弧角度**/
                   currentAngleLength = (float) animation.getAnimatedValue();
      
                   /**要绘制的三角形指示器的半径**/
                   float radius=centerX - borderWidth-bitmap.getHeight();
                   /**要绘制的三角形指示器的x坐标**/
                   point.x = (float) (centerX +radius * Math.cos((startAngle + currentAngleLength) * Math.PI / 180));
                   /**要绘制的三角形指示器的y坐标**/
                   point.y = (float) (centerX + radius* Math.sin((startAngle + currentAngleLength) * Math.PI / 180));
                   Log.d("stepView", point + "");
      
                   /**要绘制的圆弧多绘制的部分减掉**/
                   double subtractionScale = borderWidth/2/(centerX*2*Math.PI);
                   double subtractionAngle=subtractionScale*angleLength;
                   if(currentAngleLength>subtractionAngle){
                       currentAngleLength-=subtractionAngle;
                   }
                   invalidate();
               }
           });
           progressAnimator.start();
       }
      
       /**
        * 设置文本大小,防止步数特别大之后放不下,将字体大小动态设置
        *
        * @param num
        */
       public void setTextSize(int num) {
           String s = String.valueOf(num);
           int length = s.length();
           if (length <= 4) {
               numberTextSize = dipToPx(40);
           } else if (length > 4 && length <= 6) {
               numberTextSize = dipToPx(30);
           } else if (length > 6 && length <= 8) {
               numberTextSize = dipToPx(25);
           } else if (length > 8) {
               numberTextSize = dipToPx(20);
           }
       }
      
      }
      

      五.GitHub

    更多相关内容
  • Android6.0源码分析之View(一)

    万次阅读 2016-12-19 14:33:27
    目前对于view还处于学习阶段,本来打算学习结束之后再写一篇进行总结,但是发现自己自制力太差,学习效率太低,所以在此,边学边写博客,不仅督促自己完成对view的学习,而且还可以看看大家对于view有什么想知道的,...

    目前对于view还处于学习阶段,本来打算学习结束之后再写一篇进行总结,但是发现自己自制力太差,学习效率太低,所以在此,边学边写博客,不仅督促自己完成对view的学习,而且还可以看看大家对于view有什么想知道的,顺便来看看自己需要研究些什么。


    请尊重技术原创,转载请注明出处 ,本文出自

    fang_fang_story

    接下来就是对view的学习。对于view我的学习思路是先要对view相关的知识进行一个整体的系统的了解,然后在对view中各个知识模块进行详细的研究,如果有正在研究view并且遇到问题的可以留言,大家一起讨论讨论。想要对view先有个整体的了解,我建议大家参考view.java中的类的注释,注释中交代了view所涉及到的所有知识。


    Chapter One ,View概念介绍

    View是基本的用户界面组件,一个view拥有屏幕上的一块儿矩形区域,可以进行绘制以及事件events的处理。View类是各种控件widget类的基类,控件widget是那些用户交互UI组件,比如,button,textview,imageview等等。View的子类ViewGroup是界面布局layout的基类,layout是指linearlayout,relativelayout等等,这些layout是一些不可见但占有space的invisible的布局容器,可以包括layout或者widget,并且可以声明他们自己的layout属性。(笔者注:invisible是说控件存在屏幕上已经布局了该view但是不可见此时控件可以有事件处理逻辑,gone是指控件不存在屏幕上现在不存在该view);


    Chapter Two,Using View(view如何使用)


    一个Window窗口中所有的view都在同一个tree即树形结构中。往一个树形结构中添加view有两种方法

    1,可以通过代码添加(笔者注:调用viewgroup的addview方法 ,如下代码),

    RelativeLayout mRl = (RelativeLayout) findViewById(R.id.rl);
    TextView tv = new TextView(this);
    tv.setText("这是在代码中加载的view");
    tv.setTextColor(Color.RED);
    mRl.addView(tv);

    2,也可以在view树的layout文件中添加。

    一旦你创建了view树,你可能想做以下一些操作。
    1>,设置属性,properties,不同的view子类会有一些不同的属性,在编译期就确定的属性可以在xml文件中规定,如果在运行时期才能确定的属性则在代码中规定
    2>,设置焦点,focus,framework层会处理焦点的移动来响应用户的操作,如果想要强制view获取焦点可以调用requestfocus方法
    3>,监听事件,listener,view允许客户端设置监听器,当所监听的事件发生时会进行notify。
    example setOnFocusChangeListener 监听view的焦点的获取和丢失。其他view的子类也会提供一些特殊的监听器,比如button有点击监听

    4>,设置可见性,visibility,可以设置view的隐藏和显示(笔者注:对于可见性的参数有三个选择,visible,gone,invisible。对于visible,是view对用户可见,占据一定的space。对于gone,是对用户不可见,且不占据space。但对于invisible,虽然对于用户不可见,但仍旧占有space。)


    framework负责measuring laying out and drawing views,(测量,布局,以及绘制)(笔者注:也就是说,对于加载view,先是测量一下view的大小,然后布局一下view的xy坐标,即在画布的位置,然后就在某个位置绘制已经能measure的view。很符合逻辑,就像比如想要画一个圆,先要确定圆心位置和圆半径,然后才能画圆)。除非你是自定义的viewgroup,否则你自己不能调用这些方法进行相关的操作

    Chapter Three,View的生命周期--LifeCycle

    要想自定义view,你可以覆写一些framework层使用view时调用的一些标准方法,不需要覆写所有的,事实上,可以覆写onDraw,如下表所示

    CategoryMethodsDescription
    CreationConstructors(构造器方法)当view在代码中被创建时会调用该方法,填充xml文件时也会调用该方法
     onFinishInflate()当view以及他所有的子view都被填充时会调用该方法
    LayoutonMeasure(int ,int )决定view与子view所要求的大小时调用
     onLayout(boolean,int,int,int,int)当view给子view分配size和position,大小和位置的时候会调用
     onSizeChanged(int,int,int,int)当view的size发生改变时会调用
    DrawingonDraw当view需要提供content内容时调用
    Event Processing(事件处理)onKeyDown(int,KeyEvent)当有物理按键被按下时会调用
     onKeyUp(int,KeyEvent)当物理按键抬起时会调用
     onTrackBallEvent(MotionEvent)???Called when a trackball motion event occurs(笔者注:何时触发,view轨迹移动)
     onTouchEvent(MotionEvent)当有触摸事件发生时会触发
    FocusonFocusChanged(boolean,int,android.graphics.Rect)当view的焦点发生改变时(失去或者获取焦点)会调用该方法
     onWindowFocusChanged(boolean)当包含这个view的window获取或者失去焦点时(activity的生命周期有关)
    AttachingonAttachedToWindow()当与window绑定时会调用该方法
     onDetachedFromWindow()当与window解除绑定时会调用该方法
     onWindowVisibilityChanged(int)当所绑定的window的可见性发生改变时会调用






















    各方法详细阐述:
    1>,构造方法,view的构造方法有四个
    第一,最简单的(其实还有构造器,但是不是public的,只是测试),只有一个context参数,View(Context),这个构造方法适合在代码中创建view中使用(笔者注:如果在自定义view时只覆写了该方法,在xml文件中添加view就会报inflateException.)
    这里的context上下文参数使用的是正在运行的,用来获取theme,resource等等
    第二,View(Context,AttributSet),当在xml使用view时会调用该方法,所以如果你想要在xml中使用自定义的view,必须覆写该构造方法。当所有子类被添加成功后会调用onFinishInflate 方法;AttributSet参数是view在xml文件中添加的属性标签(笔者注:在xml文件中使用veiw时所添加的各种标签属性都是通过传入的attrs来被解析的,如果在自定义view时构造方法中传入的attrs为null,则在xml文件中规定的各种属性均不会被解析。example,如下图,在xml中添加了我自定义的button,但是因为在自定义button时我的构造方法中传入的为null,则导致button的xml中定义的属性没有被解析)


    第三,View(Context,AttributSet,int),当在xml中使用view时会调用该方法,第三个参数是defStyleAttr,这个参数的值与当前activity的theme主题有关,是引用的当前activity的一个主题属性item,默认的为0
    第四,View(Context,AttributSet,int,int),xml使用时调用,又多了第四个参数:defStyleRes,当且仅当第三个参数为0或者第三个参数没有在主题属性中定义时该参数才会起作用。是一个style的id。
    笔者总结:通过这么分析,总结一下view的构造方法以及构造参数。
    view的public构造方法共4个,涉及到的构造参数共四个
    • Context context:上下文对象,用于获取theme,resource资源等等。(获取当前activity的theme用this,获取整个应用可以用getApplicationContext)
    • AttributSet attrs:属性集合,有了这个参数,才可以解析在xml中添加的view的各种属性。(width,height...etc)
    • int defStyleAttr:主题属性(item)比如buttonstyle等等,R.attrs....,是在context所获取的theme中寻找view所对应的一个item属性,用来规定view的样式资源style
    • int defStyleRes:样式资源style的id,可以使用自己自定义的style来给view,自定义的style可以通过context获取到resource资源获取,但是这个只有在主题中没有定义样式资源这个属性或者样式资源这个属性为0时才会起作用
    添加view的方法
    • 可以在代码中动态添加方法(在运行时期添加),借助viewgroup特有的addView(View)方法添加view,添加view可借助只含有Context构造参数的构造方法
    • 可以在xml中静态添加(在编译期添加),在xml中添加view要注意两个问题,一个是一定要覆写带有AttributSet参数的构造方法,二是这个参数一定不能为null,因为这个参数会传给framework层让其去解析xml文件中对应的属性

    在笔者刚开始自定义view时问题很多,比如在xml文件中使用时会报出填充异常等等,很多都是因为对view的构造方法不熟悉导致的。只有了解了基础,才能进行举一反三,毕竟关于view的这些都是依靠这些基础而来的,不论再多变,不变的是基础!!!

    2>,onFinishInflate()
    该方法是针对用layout文件填充view来说的,当view以及它的子view全都被添加之后会调用该方法,在填充布局的最后一个阶段别调用。即使自定义view覆写了该方法,一定要记得调用父类的方法,以保证framework层可以调用到。(笔者注:但是对于这个方法view是究竟怎么被调用的,没有找到Java中的,也许涉及到view的加载过程)。在该方法中可以对所填充的layout文件中的控件进行初始化。

    对于onMeasure,onLayout,onDraw以及事件的处理,需要仔细分析。对于这些方法的分析先留着以后进行,接下来继续对view的介绍


    Chapter Four ,IDS

    对于view的id应该没什么好说的了,经常使用。view需要一个整形的id与其对应,在你所查找的view树范围(一个window窗口)内,ID具有唯一性。所以在view树中可以通过ID来寻找view


    Chapter Five, Position

    view的几何形状是个矩形(笔者注:自定义selector改变形状除外),left和top的值决定了view的location(位置),宽和高的大小决定了view的size(尺寸大小),不论是尺寸还是位置单位都是像素。笔者注:所以可以知道通过getLeft(view的X坐标)和getTop(view的Y坐标)就可以获取到view的位置(但是所获取到的getLeft和getTop都是相对于它的父view来说的)


    还提供了一些类似的方法,比如,getRight/getBottom,来避免不必要的计算。
    这里的getLeft说view的左边界距离他直接父类的左边界的距离,getRight说的是view的右边界距离他直接父类的左边界的距离。关系如下图所示


    Chapter Six,SizePaddingMargins

    Size:View的大小通过宽和高两个值来体现,事实上一个view拥有两对宽和高的值

    • the first第一对,measured width/height,测量宽高(getMeasuredHeight/width):这组宽高定义了view在它的父view中所想要的宽高
    • the Sec第二对,width/height有时也叫drawingwidth/drawingHeight,绘制的宽高(getWidth/height):定义了在layout结束后在draw绘制时view在屏幕上所显示的真是的宽高,这组值有可能与第一组值不同。

    Padding : 为了测量view的尺寸,需要将Padding考虑在内,View的padding包括left、top、right、bottom。padding用来表示view的内容的偏移量,(这个不再多说,目前还没看到需要有什么注意的地方,如果以后有遇到再说)

    Margins:margin说的是view与view之间的距离,所以对于单个view来说没有margin的概念,margin是针对viewgroup来说的。


    Chapter Seven,EventHandlingThreading(事件处理线程)

    view的事件处理周期如下:
    事件触发(event trigger)---->分发给合适的view(dispatch event )--->view处理event并且通知listener(handler&notify)
    如果在事件处理的过程中view的边界需要修改,view会调用invalidate方法,requestLayoiut或者invalidate方法会导致framework层会以合适的方式进行measure测量,laying out布局,drawing绘制view树

    注,整个view树是一个单独的线程,你只能在UI线程中调用view的方法,如果你想在别的线程中更新UI,可以借助handler进行线程间通信

    Chapter Eight,FocusHandling(焦点处理)


    framework层会处理普通的焦点移动事件来响应用户的输入,包括view焦点的移动和隐藏,或者是另一个新的veiw获取到焦点。

    • isFocusable()/setFocusable(boolean) :View是否想要获取焦点
    • isFocusableInTouchMode/setFocusableInTouchMode:在触摸模式下,view是否想要获取焦点
    • requestFocus():让view获取焦点

    View焦点的移动基于一定的运算规则----总是在给定方向上去寻找离他最近的下一个view。在极少数情况下,默认的焦点移动的顺序可能无法匹配开发者的预期的行为,可以在xml文件中显式的去指定下一个要获取焦点的id(nextFocusDown/left/right/up)

    Chapter Nine Touch Mode (触摸模式)


    当用户通过键盘上的方向键来导航用户界面时,给那些可操作的item(比如button)焦点是很有必要的以便于用户可以看到谁会处理用户输入。
    如果设备有触摸的功能,但是用户通过触摸界面来进行交互的话没必要让view高亮或者获取焦点,这就是触摸模式。


    对于一个可触摸的设备,一旦用户触摸了设备屏幕,就会进入到触摸模式,从这一点开始,只有isFocusInTouchMode为true的view才可以获取焦点,比如编辑框,其他的可触摸的view在被触摸时不会再获取焦点,这类view只会响应点击事件,不会去响应触摸事件


    任何时候只要用户点击了按键,会立刻退出触摸模式,并且找到一个view让其获取焦点或者高亮,以便用户在不触摸的情况下可以继续对用户界面进行交互。

    触摸模式的状态时activity保存的 ,通过activity调用isInTouchMode方法,可以看看当前是否处于触摸模式下。

    Chapter Ten Tags(标签)

    与id不同,tags不是用来标识view的,tags本质上是与view相关联的一些额外的信息,tags通常来用存储一些与view相关的一些数据,而不是把他们放在单独的结构中。标签是个很方便的记忆的东西,我一般在adapter刷新数据中用的较多,用法其实也很简单。
    首先你需要在string.xml文件中定义一个标签,格式如下

    <resources>
    
    。。。。。
        <item name="R.tag.view" type="id"/>
    </resources>

    然后你可以在Java中传递view的信息



    可以看到有两种方式,第一是setTag(要保存的信息),第二种是setTag(key,value);如果你只需要保存一个view相关的信息,完全可以使用setTag(value)的方式,但如果要保存的view的信息有多个,那就要使用setTag(key,value)来保存,然后通过对应的tag的key来获取所保存的信息。


    其他与measure,layout,draw以及触摸或者按键事件的处理过程需要详细研究


    【S】【actor added 】【2016-12-29】

    Chapter Eleven,Layout

    Layout包含两个过程:measure测量过程和layout定位布局的过程。

    通过measure(int,int)来实现测量过程,测量是对view树从上到下进行递归测量的一个过程。在递归测量的过程中每一个view把自己的测量数据交给view树。在测量过程结束后每一个view存储了自己的测量值。

    layout定位布局的过程发生在layout(int,int,int,int),也是沿着view树从上到下的过程,在layout过程中每一个父view有责任根据measure过程中所计算的子view的大小去定位子view的位置。


    当一个view的measure的方法返回时,该view的measuredWIdth和measuredHeight必须被设置,也就是说,如果调用了measure方法对view进行了测量,那就必须调用setMeasuredDimensionRaw方法将view包括该view的子view的measuredHeight和measuredWidth保存起来。view的measuredHeight/measuredWidth大小必须遵循父view对子view的限制。只有这样才能保证在测量过程结束后父view能够接受子view的尺寸,进行更好的布局。父view可能对子view多次调用measure方法。例如,父view去测量未指明大小的view所想要的size,又或者所有view的大小加起来过大或者过小时就会重新进行measure。

    measure的过程涉及到两个类。一个是MeasureSpec(测量规范)用来让子view告诉父view他们想要怎样被测量。还有一个LayoutParams类用来描述一个view想要多大,对于view的尺寸可能是以下三种的一个值

    • an exact number:一个确定的值
    • match_parent:和父类一样大
    • wrap_parent:包裹内容.

    不同的viewgroup有不同 的layoutparams的子类,例如,Absolutelayout的layoutparams的子类可以添加X和Y的值。

    测量规范是父view对子view的一个测量规范,一个测量规范有三种模式

    • UNSPECIFIED:未指明的,父view不对子view的测量做任何要求,子view想要多大就多大
    • EXACTLY:准确的值,父view给子view一个确定值,不论子view想要多大,只能使用父view给子view规定的值
    • AT_MOST:至多,父view会规定一个最大值,子view的大小不能超过这个值

    通过调用requestlayout()方法可以初始化layout,当一个view察觉到当前的边界已经不再适合时会调用。


    Chapter Twelve,Drawing


    Drawing用来遍历整个view树并且渲染无效区域的view,因为drawing是沿着view进行遍历的,这也就意味着父view优先于子view的绘制,同一级的view是按照他们view树中出现的顺序进行绘制,如果你给一个view设置了背景图片,那么在回调onDraw方法之前会优先绘制背景图片。

    framework不会绘制不处于无效区域(无效区域是指还未被使用的一片区域)的view。

    通过调用invaliate()方法来强制进行drawing。


    Chapter Thirteen,Animation

    可以调用setAnimation(animation)或者startAnimation(animation)来给view绑定一个动画。动画可以是view进行伸缩,旋转,平移和透明度渐变的动画。如果一个有子view的view添加了动画,那么这个动画会影响以该view为一个根节点沿着view树向下的所有view。当开始动画后,framework会按照动画要求对view进行重新绘制,直到动画结束。


    Chapter Fourteen,嵌套类

    • BaseSavedState:基类,通过继承该类并借助onSaveInstanceState保存自己状态
    • MeasureSpec:封装了父view对子view的测量要求
    • onClickListener:当view被点击时调用
    • onCreateContextMenuListener:当view创建了上下文菜单时触发
    • onFocusChangedListener:当view的焦点发生改变时触发
    • onKeyListener:当有按键事件会触发
    • onLongClickListener:长按时触发
    • onTouchListener:触摸时触发


    展开全文
  • Qt之QGraphicsView入门篇

    万次阅读 多人点赞 2020-04-05 20:28:40
    作者:billy 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 简介 在Qt界面库中,对于图形的绘制,可以使用 ... } 更请参考 Qt之QGraphicsView进阶篇 Qt之QGraphicsView实战篇

    作者:billy
    版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

    简介

    在Qt界面库中,对于图形的绘制,可以使用 QPainter 实现普通二维图形的绘制,该方法在 paintEvent 事件里编写绘图程序,其本质绘制的图形是位图,这种方法更适合于绘制复杂度不高的固定图形,并且不能实现图项的选择、编辑、拖放、修改等交互功能

    对于需要绘制大量的、需要交互的图形,可使用Graphics View绘图架构,它是一种基于图形项(Graphics Item)的模型/视图模式,这种方式可以在一个场景中绘制大量图元项,且每个图元项都是可选择、可交互的。

    从QT4.2开始引入了Graphics View框架用来取代QT3中的Canvas模块,并作出了改进,Graphics View框架实现了模型-视图结构的图形管理,能对大量图元进行管理,支持碰撞检测,坐标变换和图元组等多种方便的功能

    QPainter采用面向过程的描述方式绘图;GraphicsView采用面向对象的描述方式绘图。对于复杂的图像来说,如果图像包含大量的直线、曲线、多边形等图元对象,管理图元对象比管理QPainter的绘制过程语句要容易,并且图元对象更符合面向对象的思想,图形的可复用性更好。

    GraphicsView 框架

    在Graphics View绘图架构中,主要涉及到下面三个类的使用:

    1. 场景类(QGraphicsScene):该类提供绘图场景(Scene),场景是不可见的,是一个抽象的管理图形项的容器,可向场景中添加图形项,获取场景中的某个图形项等,主要功能如下:
    • 提供管理大量图元的快速接口;
    • 传播事件到场景中的每个图元;
    • 提供未经变换的渲染功能,主要用于打印;
    • 管理图元状态,如图元选择和焦点处理;
    1. 视图类(QGraphicsView):该类提供绘图的视图(View)组件,用于显示场景中的内容。可以为一个场景设置几个视图,用于对同一个数据集提供不同的观察方式;

    2. 图形项类(QGraphicsItem):该类提供了一些基本的图形元件,也可在此基础上自定义图形项,它支持各种事件的响应,如鼠标事件、键盘事件、拖放事件等,以实现图形的交互功能,下面列举一些标准图元:

    • QGraphicsEllipseItem 椭圆
    • QGraphicsLineItem 直线
    • QGraphicsPathItem 路径
    • QGraphicsPixmapItem 图像
    • QGraphicsPolygonItem 多边形
    • QGraphicsRectItem 矩形
    • QGraphicsSimpleTextItem 简单文本
    • QGraphicsTextItem 文本浏览
    • QGraphicsItem是图元的基类,用户可以继承QGraphicsItem实现自定义的图元;

    GraphicsView 坐标系

    Graphics View基于笛卡尔坐标系。item在场景中的位置与几何形状通过x,y坐标来表示。当使用未经变形的视图来观察场景时,场景中的一个单位等于屏幕上的一个像素。在Graphics View绘图架构中涉及到了3个坐标系,即场景坐标、视图坐标及图形项坐标。

    1. 场景坐标:对应QPainter的逻辑坐标,以场景的中心为原点,X轴正方向向右,Y轴正方向向下
      在这里插入图片描述
    2. 视图坐标:即窗口界面的物理坐标,单位是像素,其左上角为原点坐标,所有鼠标事件、拖拽事件最开始都使用视图坐标,为了和图元交互,需要转换坐标为场景坐标
    3. 图形项坐标:图元存在于自己的本地坐标上,通常以图元的中心为原点,图元中心也是所有坐标变换的原点,图元坐标方向是X轴正方向向右,Y轴正方向向下(同上图)

    示例

    #include <QApplication>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QGraphicsRectItem>
    #include "math.h"
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        QGraphicsScene scene;   // 定义一个场景,设置背景色为红色
        scene.setBackgroundBrush(Qt::red);
    
        QPen pen;   // 定义一个画笔,设置画笔颜色和宽度
        pen.setColor(QColor(0, 160, 230));
        pen.setWidth(10);
    
        QGraphicsRectItem *m_rectItem = new QGraphicsRectItem();   // 定义一个矩形图元
        m_rectItem->setRect(0, 0, 80, 80);
        m_rectItem->setPen(pen);
        m_rectItem->setBrush(QBrush(QColor(255, 0, 255)));
        m_rectItem->setFlag(QGraphicsItem::ItemIsMovable);
    
        QGraphicsLineItem *m_lineItem = new QGraphicsLineItem();    // 定义一个直线图元
        m_lineItem->setLine(QLineF(0, 0, 100, 100));
        m_lineItem->setPen(pen);
        m_lineItem->setFlag(QGraphicsItem::ItemIsMovable);
    
        QGraphicsPathItem *m_pathItem = new QGraphicsPathItem();    // 定义一个路径图元
        QPainterPath path;
        path.moveTo(90, 50);
        for (int i = 1; i < 5; ++i) {
            path.lineTo(50 + 40 * cos(0.8 * i * M_PI), 50 + 40 * sin(0.8 * i * M_PI));
        }
        path.closeSubpath();
        m_pathItem->setPath(path);
        m_pathItem->setPen(pen);
        m_pathItem->setFlag(QGraphicsItem::ItemIsMovable);
    
        QGraphicsPolygonItem *m_polygonItem = new QGraphicsPolygonItem();   // 定义一个多边形图元
        QPolygonF polygon;
        polygon << QPointF(-100.0, -150.0) << QPointF(-120.0, 150.0)
                << QPointF(320.0, 160.0) << QPointF(220.0, -140.0);
        m_polygonItem->setPolygon(polygon);
        m_polygonItem->setPen(pen);
        m_polygonItem->setFlag(QGraphicsItem::ItemIsMovable);
    
        scene.addItem(m_rectItem);      // 把矩形图元添加到场景
        scene.addItem(m_lineItem);      // 把直线图元添加到场景
        scene.addItem(m_pathItem);      // 把路径图元添加到场景
        scene.addItem(m_polygonItem);   // 把多边形图元添加到场景
    
        QGraphicsView view(&scene); // 定义一个视图,并把场景添加到视图
        view.resize(1024, 768);
        view.show();
    
        return a.exec();
    }
    

    在这里插入图片描述

    更多请参考

    展开全文
  • Qt之QGraphicsView进阶篇

    万次阅读 多人点赞 2020-04-06 15:50:35
    作者:billy 版权声明:著作权归作者所有,商业转载请联系作者... 示例 拖拽和碰撞检测示例 github 源代码下载 网上的一个3D模型示例 github 源代码下载 更请参考 Qt之QGraphicsView入门篇 Qt之QGraphicsView实战篇

    作者:billy
    版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

    前言

    上一章节介绍了 QGraphicsView 中的基础内容,具体请参考 Qt之QGraphicsView入门篇。这一章节我们来具体了解一下 GraphicsView 框架中有哪些特性。

    缩放与旋转

    QGraphicsView 通过 QGraphicsView::setMatrix() 支持和 QPainter 一样的仿射变换,通过对一个视图应用变换,你可以很容易地支持普通的导航特性如缩放与旋转。

    示例:

    #include <QApplication>
    #include <QGraphicsView>
    #include <QGraphicsScene>
    #include <QGraphicsRectItem>
    #include <QDialog>
    #include <QVBoxLayout>
    #include <QPushButton>
    
    class MyGraphicsView : public QGraphicsView	// 自定义视图类,实现对视图的缩放和旋转
    {
    public:
        MyGraphicsView() {}
    
    public slots:
        void zoomIn() { scale(1.2, 1.2); }
        void zoomOut() { scale(1/1.2, 1/1.2); }
        void rotateLeft() { rotate(-30); }
        void rotateRight() { rotate(30); }
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        QGraphicsScene scene;   // 定义一个场景,设置背景色为红色
        scene.setBackgroundBrush(Qt::red);
    
        QPen pen;   // 定义一个画笔,设置画笔颜色和宽度
        pen.setColor(QColor(0, 160, 230));
        pen.setWidth(10);
    
        QGraphicsRectItem *m_rectItem = new QGraphicsRectItem();   // 定义一个矩形图元
        m_rectItem->setRect(0, 0, 80, 80);
        m_rectItem->setPen(pen);
        m_rectItem->setBrush(QBrush(QColor(255, 255, 255)));
        m_rectItem->setFlag(QGraphicsItem::ItemIsMovable);
        scene.addItem(m_rectItem);      // 把矩形图元添加到场景
    
        QDialog *dialog = new QDialog(nullptr, Qt::CustomizeWindowHint | Qt::WindowTitleHint);
        dialog->setWindowOpacity(0.8);
        dialog->setWindowTitle("operation");
        dialog->setLayout(new QVBoxLayout);
        QPushButton *m_zoomInBtn = new QPushButton("zoomIn");
        QPushButton *m_zoomOutBtn = new QPushButton("zoomOut");
        QPushButton *m_rotateLeftBtn = new QPushButton("rotateLeft");
        QPushButton *m_rotateRightBtn = new QPushButton("rotateRight");
        dialog->layout()->addWidget(m_zoomInBtn);
        dialog->layout()->addWidget(m_zoomOutBtn);
        dialog->layout()->addWidget(m_rotateLeftBtn);
        dialog->layout()->addWidget(m_rotateRightBtn);
        scene.addWidget(dialog);
    
        MyGraphicsView view; // 定义一个视图,并把场景添加到视图
        view.setScene(&scene);
        view.resize(1024, 768);
        view.show();
    
        // 点击按钮实现视图的缩放和旋转
        QObject::connect(m_zoomInBtn, &QPushButton::clicked, [&]() { view.zoomIn(); });
        QObject::connect(m_zoomOutBtn, &QPushButton::clicked, [&]() { view.zoomOut(); });
        QObject::connect(m_rotateLeftBtn, &QPushButton::clicked, [&]() { view.rotateLeft(); });
        QObject::connect(m_rotateRightBtn, &QPushButton::clicked, [&]() { view.rotateRight(); });
    
        return a.exec();
    }
    

    在这里插入图片描述

    打印

    Qt中对打印的支持是有一个独立的 printsupport 模块来完成的,所以要想在程序中使用Qt的打印功能,必须先在pro文件中添加:
    QT += printsupport

    图形视图通过它的展示函数:QGraphicsScene::render() 和 QGraphicsView::render() 提供单线(single-line)打印。这些函数提供相同的API,通过将 QPainter 传递给展示函数,你可以打印场景、视图的全部或部分内容。

    场景和视图的渲染函数的不同在于 QGraphicsScene::render() 使用场景坐标,而 QGraphicsView::render() 使用视图坐标。QGraphicsScene::render() 经常用于打印未变换场景中的整块,例如一块图形数据或是打印一个文本文档。 QGraphicsView::render() 适合用于打印屏幕快照(screenshots),缺省情况下它展示视图端口中的当前内容。

    官方示例:在这里插入图片描述在这里插入图片描述

    拖拽和碰撞检测

    由于 QGraphicsView 继承自 QWidget,所以 GraphicsView 同样提供了拖拽功能。此外,GraphicsView 框架也为场景、图元提供拖拽支持。当视图接收到拖拽事件,GraphicsView 框架会将拖拽事件翻译为 QGraphicsSceneDragDropEvent 事件,再发送到场景,场景接管事件,再把事件发送到光标下接受拖拽的第一个图元

    为了在场景中获取拖拽事件,你需要重新实现QGraphicsScene::dragEnterEvent() 和在 QGraphicsItem 的子类里任何与你特定场景需要的事件处理器。items 也可以通过调用 QGraphicsItem::setAcceptDrops() 获得拖拽支持,为了处理将要进行的拖拽,你需要重新实现 dropEvent、dragdragEnterEvent、dragLeaveEvent、dropMoveEvent、mousePressEvent、mouseReleaseEvent 和 mouseMoveEvent

    为了处理碰撞时的行为,我们需要创建 QGrahicsItem 的一个子类,然后重新实现 boundingRect 和 paint。boundingRect 用于返回图元绘制所需要的估测区域,而 paint 实现实际的绘图操纵

    这里展示一个小例子,外面一圈是 colorItem 实现了拖拽,中间是个 dropItem 实现了碰撞检测。当我把某个 colorItem 拖拽并触碰到 dropItem 时,dropItem 的颜色就会变成 colorItem 的颜色。代码太多就不放了,感兴趣的小伙伴可以在最下面示例中下载源代码。

    在这里插入图片描述

    光标与工具提示

    像QWidget一样,QGraphicsItem也支持光标 QgraphicsItem::setCursor() 与工具提示 QGraphicsItem::setToolTip()。当光标进入到item的区域,光标与工具提示被 QGraphicsView 激活,通过调用 QGraphicsItem::contains()来检测。你也可以直接在视图上设置一个缺省光标 QGraphicsView::setCursor()。

    动画

    GraphicsView 框架支持多种层次的动画,使用动画框架可以很容易制作出动画。GraphicsView 框架支持的动画实现种类如下:

    • 图元需要继承自 QGraphicsObject,并且需要联结 QPropertyAnimation属性
    • 创建继承自 QObject 和 QGraphicsItem 的图元,图元可以设置自己的定时器,通过在QObject::timeEvent() 中增加步进的方式来控制动画
    • 通过调用 QGraphicsScene::advance() 来推进场景,依次调用QGraphicsItem::advance()

    OpenGL渲染

    为了使用 OpenGL 渲染,你要设置一个新的 QGLWidget 作为QGraphicsView 的视口 QGraphicsView::setViewPort()。假如你让 OpenGL 提供反锯齿功能,你需要 OpenGL 采样缓冲支持。

    QGraphicsView view;
    view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
    view.setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
    view.setScene(new OpenGLScene);
    view.show();
    

    Item组

    通过把一个图元做为另一个图元的孩子,可以得到图元组的大多数本质特性:所有图元会一起移动,所有变换会从父到子传递

    另外,QGraphicsItemGroup 是一个特殊的图元。为了增加和删除图元,它使用一个有用接口合并了子图元的事件处理。把一个图元加到QGraphicsItemGroup 仍会保留图元的原始位置与变换,而给一个图元重新指定父图元则会让图元根据其新的父亲重新定位。可以用QGraphicsScene::createItemGroup() 创建图元组。

    图形组件和布局

    QT4.4 通过 QGraphicsWidget 支持图形和图元布局。QGraphicsWidget 类似于 QWidget,但 QGraphicsWidget 并不从 QPaintDevice 继承,而是继承自 QGraphicsItem。QGraphicsWidget 支持事件、信号与槽、大小和策略。通过 QGraphicsLinearLayout、QGraphicsGridLayout 可以对图形组件进行布局管理。

    QGraphicsWidget 继承了 QWidget 和 QGraphicsItem 的优点,如 QWidget的样式、字体、调色板、布局方向以及 QGraphicsItem 的图形、独立精度和变换支持。

    QGraphicsLayout 是专为 QGraphicsWidget 特殊设计的第二代布局框架。QGraphicsLayout 的API类似于 QLayout。通过 QGraphicsLinearLayout 和QGraphicsGridLayout 可以管理组件与子布局。

    嵌入组件

    图形视图框架为嵌入任何组件到场景提供了无缝支持。可以嵌入简单的组件,如 QLineEdit、QPushButton,或是复杂的组件如 QTableWidget,甚至是主窗口。

    要嵌入组件到场景,只需要调用 QGraphicsScene::addWidget(),或是创建一个 QGraphicsProxyWidget 实例,手动嵌入组件。通过 QGraphicsProxyWidget,图形视图框架可以深度整合客户组件特性,如光标、工具提示、鼠标、平板和键盘事件、子组件、动画、弹拉框、组件输入焦点和激活。QGraphicsProxyWidget 甚至整合了嵌入组件的 tab 顺序,可以通过 tab 选择嵌入的组件。甚至可以嵌入一个 QGraphicsView 到场景。

    示例

    1. 拖拽和碰撞检测示例
      github 源代码下载

    2. 网上的一个3D模型示例
      github 源代码下载

    在这里插入图片描述

    更多请参考

    展开全文
  • 近期有一个自定义View功能,类似于仪表的模型,可以将指针动态指定到某一个刻度上,话不说,先上图 先说下思路 1.先获取自定义的一些属性,初始化一些资源 2.在onMeasure中测量控件的具体大小 3.然后...
  • QGraphicsView使用详解

    万次阅读 2018-11-16 15:57:27
    QQ:609162385 ...QT4.2开始引入了Graphics View框架用来取代QT3中的Canvas模块,并作出了改进,Graphics View框架实现了模型-视图结构的图形管理,能对大量图元进行管理,支持碰撞检测,坐标变换和图元...
  • 基于QGraphicsView的简易画板EasyCanvas -- 第二版

    千次阅读 热门讨论 2020-09-05 18:30:14
    EasyCanvas 第二版,第一版的功能可参见: 基于QGraphicsView的简易画板EasyCanvas – 第一版 第二版 在第一版的基础上增加如下功能: 节点树 撤销/重做 Python脚本动态修改节点属性 方案管理 1. 节点树 具有同步...
  • QT学习笔记: QGraphicsView的使用

    千次阅读 2020-01-09 16:54:20
    QGraphicsView提供一个可视的窗口,用于显示场景中的图元,一个场景中可以有个视图。 QPainter采用面向过程的描述方式绘图;GraphicsView采用的是面向对象的描述方式绘图。 GraphicsView绘图时首先创建一个场景...
  • QT中,GraphicsView

    万次阅读 2019-04-24 14:02:54
    这个框架是一个相对成熟的渲染引擎的上层框架,通常也可以会叫做 Scene - View。 在QT中,GraphicsView是一个与QWdiget系列一点点不一样的系统。这个系统主要由下面几个框架类构成:QGraphicsView, QGraphicsScene,...
  • QGraphicsScene 提供了图形视图框架的场景,相当于一块画布,并具有以下功能。 1.一个管理大量图形项的快速接口。 2.向每个图形项传播事件 3.管理图形项的状态,比如选择,焦点处理等 4.提供无转换的渲染功能,...
  • Android自定义View使用总结

    千次阅读 2018-09-21 10:29:31
    2016年11月29日 16:49:19 峥嵘life 阅读数:3148 标签: android widget 自定义View 个人分类: Andr图形和动画 android 版权声明:本文为博主原创文章。只要评论中留言就可以转载。 ...
  • GraphicsView框架

    千次阅读 2018-11-09 16:38:57
    QT4.2开始引入了Graphics View框架用来取代QT3中的Canvas模块,并作出了改进,Graphics View框架实现了模型-视图结构的图形管理,能对大量图元进行管理,支持碰撞检测,坐标变换和图元组等多种方便的功能。...
  • Android SurfaceView实战 打造抽奖转盘

    万次阅读 多人点赞 2014-12-04 14:21:04
    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41722441 ,本文出自:【张鸿洋的博客】1、概述今天给大家带来SurfaceView的一个...可以看到SurfaceView也是继承了View,但是我们并不需要去实现
  • Android开发笔记(九十九)圆形转盘

    千次阅读 热门讨论 2016-05-11 11:08:21
    cubicTo与quadTo的区别在于:quadTo要与moveTo配合使用才能实现平滑效果,而cubicTo了两个参数,相当于内部已经包含了moveTo的功能。 arcTo : 定义弧线 addRect : 添加矩形路径 addOval : 添加椭圆路径 ...
  • 文章目录功能介绍代码介绍myitem.h/cpp 图形项myview.h/cpp场景主函数结果展示代码下载参考 功能介绍 点击左键、按 Shift 键可以单选,按下 Ctrl 可进行多选。 选中时候点击右键,可以选择隐藏物体,归位 移动:...
  • 牛逼!Java 从入门到精通,超全汇总版

    万次阅读 多人点赞 2021-05-06 19:40:33
    Java 设计模式Java 进阶Java 并发编程实战Java 并发编程艺术Java 并发编程之美图解Java线程设计模式JVM深入理解 Java 虚拟机Java 虚拟机规范HotSpot 实战自己动手写 Java 虚拟机MySQLMySQL 基础教程SQL 基础教程...
  • 基于QGraphicsView实现绘画曲线注意点

    千次阅读 2019-05-08 23:26:15
    对于刚了解QGraphics体系来说,基于QGraphicsView实现白板绘画曲线实现矢量绘画其实不难,然后基于非矢量绘画(通过绘画图片QImage)时,看了源代码后,结合经理所说方法,遇到两个问题,并被这两个问题弄蒙了,特意...
  • 微信小程序 自定义组件之《转盘》

    万次阅读 多人点赞 2018-07-29 22:12:58
    开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。 这篇博文主要就是分享个自己...
  • 如果您不介意使用结构有点非常规但功能卓越的键盘,那么您也应该朝着这个方向发展。 8. Azio Vision Backlit USB Keyboard with Large Print keys and 5 Interchangeable Backlight Colors (KB506) - Wired 8. 具有...
  • Qt Model/View编程介绍

    千次阅读 2017-03-28 07:52:29
    Qt中包含了一系列的项视图类,它们使用model/view的...这种由这种架构引进的功能分离特性给了开发者很大的灵活性去自定义自己的展示方式,并且提供了一个编制的模型接口以使很种的数据源都可以和现存的视图相结合。
  • 金融类自定义View(二)–股票分时图 本节主要介绍股票分时图的详细实现思路与遇到的各种问题。该分时图主要包括以下功能:数据的适配、x/y背景轴的绘制、x/轴文字的绘制、长按十字的处理、实时折线的处理、分时...
  • Android性能优化(4):UI渲染机制以及优化

    千次阅读 多人点赞 2019-11-20 09:38:34
    文章目录1. 渲染机制分析1.1 渲染机制1.2 卡顿现象1.3 内存抖动2. 渲染优化方式2.1 过度绘制优化2.1.1 Show GPU ...在从Android 6.0源码的角度剖析View的绘制原理一文中,我们了解到View的绘制流程有三个步骤,即m...
  • class View : public QGraphicsView   {   Q_OBJECT   ...   public slots:   void zoomIn() { scale(1.2, 1.2); }   void zoomOut() { scale(1 / 1.2, 1 / 1.2); }   void rotateLeft...
  • Android知识梳理之自定义View

    千次阅读 2016-04-28 15:29:28
    我们可能就需要自定义一些View来获得比较好的效果,自定义View是android开发者走向高级开发工程师必须要走的一关。自定义View主要包含三块自定义Viewgroup,自绘View,组合View。本文我们主要讲自绘View。 ...
  • 今天早上上班,看见有个技术群里面在讨论自定义软件的问题,我之前也没有接触过,所以在这里学习一下,做个记录,软件我们自己定义的简洁方法 在Android中,Android 提供了一个KeyboardView 类,这是用于处理...
  • Android View 属性大全

    千次阅读 2018-04-04 18:49:09
    (1)视图透明度,值在0-1之间,0为完全透明,1为完全不透明 android:alpha setAlpha(float) (2)视图背景 android:background setBackgroundResource...(4)设置view的备注说明,作为一种辅助功能提供,为一些...
  • Android7.0 窗口你值得拥有

    万次阅读 2016-09-23 16:52:29
    窗口分屏其实在国内并不陌生,已经有一些手机和平板搭载了“分屏任务”和”APP窗口化”功能,但这些都是手机厂商自主定制系统中添加的功能,并非安卓原生所有,因此他们的实现方式可能会不一样,但是现在google原始...
  • Android自定义键盘之汉字键盘

    千次阅读 2016-07-16 22:19:46
    isModifier:指定按键是否为功能键( modifier key ) ,例如 Alt 或者 Shift ,取值为true或false。 keyOutputText:指定按键输出的文本内容,取值为字符串。 isRepeatable:指定按键是否是可重复的,如果长按该...
  • 当然如果你需要更功能,可以写那么一点点的代码,就可以让你的应用程序高大上啊高大上... 主要特点 1.除了想要特殊的效果外,你不需要写一行代码2.完全的类库自动工作模式3.没有那么的UIScrollView,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,663
精华内容 10,665
关键字:

多功能方向盘view