Canvas笔记

Canvas画布的使用

默认坐标:
Canvas

Canvas类相当于一个矩形画布,默认0,0坐标是左上角。用到的坐标都是画布上的(即视图坐标系)
其常用方法有,draw( )方法最后一个参数都为Paint 对象:

  • drawRect(RectF rect,Paint paint);//在画布上绘制一个矩形;
    • react 对象为描述需要在Canvas上区域;
      参数一表示矩形左边的X坐标,余下三个同理。
save( );             

//此方法用于保存当前画布的状态,一般多用于旋转画布的时候

restore();

///此方法用于恢复画布,即恢复到save( )方法存入时的状态,但在画布上画的东西不会被撤销。

rotate(float degrees, float px, float py);

//此方法用于旋转画布,参数一为旋转的角度(正数为顺时针方向转,负数为逆时针),参数二,三画布旋转的轴点. 当画布旋转时呢,其的坐标也是跟着一起转动了,所以就意味着一定要此Canvas的中心坐标点来旋转,不然转了后内容会丢失。当确定点后,是以此点的上直线为O度. 当转动到相应的角度后,又以那个角度会0度。

一般来说,一次转动 对应一个内容绘制。下次转动并不会把上次绘制的内容也转动了.
多用于圆盘类的自定义View,确定一个地方的内容就可以转动绘制,省去多次确定坐标.

 RectF rectf =new RectF(float left,float top,float reight,float bottom);                
drawPath(Path path,Paint paint);

//在画布上绘制一个Path图形(即图像的轮廓),图形复杂时就用这个。

  • Path类常用方法用:

moveTo(float x,float y); //设置下次连接操作的坐标(移动的起点)。如不设置呢为Canvas的0,0 坐标。
lineTo(float x,float y);//设置上个点到此点用直线连接。
close();连接第一点到最后一个点,形成闭合。
更多内容查看此

drawBitmap(Bitmap bitmap,float left , float top , Paint paint) ;

//其实呢就是贴图。
因为一张图片长宽肯定是确定的,所以确定左上角的坐标就OK 了。

 drawLine(float startx , float starty , float stopx , float stopy , Paint paint) ;                               

//画线,当x坐标的起始和终止的坐标不变时,变化的为Y时,画出来的时竖线。反之横线。
当x和y都是变的,画出来的就是斜线。

drawPoint(float x, float y, Paint paint); 

//表示画点。

drawText(String text, float x, floaty, Paint paint) ;

//绘制文本,x,y坐标用于确定文字左上角的坐标。

drawOval(RectF oval, Paint paint);

//此为画椭圆,参数一用于确定椭圆外接矩形(用于确定椭圆能画多大).

drawCircle(float cx, float cy, float radius,Paint paint);

//此为画园,参数一二为确定圆的中心坐标。参数三为半径。

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) ;

//画弧,参数一用于确定弧能画多大,参数二为弧的起始角度,参数三为绘制的角度,(弧会顺时针绘制)。参数四为是否把所画弧的开始点结束点连接到弧心,为false弧线,true就封。

Paint 画笔的使用

Paint对象呢为用于在Canvas上画东西的笔;

常用方法:

setAntiAlias(boolean aa);

此用于用于设置是否开启抗锯齿,为true呢为开启,false为不开启。

setStyle(Paint.Style style);

此用于设置画笔的风格。默认为 FILL(填满) ;
STROKE表示笔为空心,仅仅画边。FILL_AND_STROKE表示中心部分为空,外围填满。

setStrokeWidth(float width);

此方法用于设置当画笔为STROKE时,延伸出去的宽度。

setStrokeCap(Paint.Cap cap);

此方法用于设置一般用于弧线,设置头部和尾部是圆角,默认为直角.

setColor(int color);

此方法用于设置画笔的颜色.

setTextSize(float textSize) ;

滑动移动距离的计算

@Override 
public boolean onTouchEvent(MotionEvent event) {
        switch(event.getAction( ) ) {
                case MotionEvent.ACTION_DOWN:
                 lastx = (int) event.getX( );
                break;
                
                case MotionEvent.ACTION_MOVE:
               movex = (int) event.getX()-lastx;  //获取当前移动了多少距离;
                break;
                
                case MotionEvent.ACTION_UP:
                //处理输入的离开的事件.
                break;
        }
        return true;
}

如果在ACTION_MOVE中有重绘视图时,一定要最后重新获取当前的坐标,下次再执行到ACTION_MOVE状态才能正确计算偏移量,不然lastx一直是DOWN状态是的坐标.

X轴: 如果计算出移动的值为-值,那么就是向左滑,为正值,就是向右滑。

Y轴:如果计算出移动的值为-值,那么就是向上滑,为正值,就是向下滑。

自定义View 三大流程

通常要写自定义View呢

onMeasure( int widthMeasureSpec, int heightMeasureSpec) ; //测量;
onLayout(boolean changed, int left, int top, int right, int bottom) ; //布局;
onDraw(Canvas canvas);//绘制

onMeasure( )方法都得重写,因为系统默认对wrap_content的属性值处理是match_parent.所以想支持此属性就必须复写此方法来指定wrap_content的值。

@Override
protected  void  onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
   setMeasuredDimension(measureHW(widthMeasureSpec,150) , measureHW(heightMeasureSpec,200));
}

private  int  widthMeasure(int  measureSpec , int defultV) {
   int result = defultV ;
   int mode = MeasureSpec.getMode(measureSpec);
   int size = MeasureSpec.getSize(measureSpec);
        
   if (mode == MeasureSpec.EXACTLY) {
         result = size;
   }else if (mode == MeasureSpec.AT_MOST) {
         result = Math.min(result , specSize);
   } 
   return result;
}
   

其中使用到的MeasurSpec类,为帮助测量View的, onMeasure( )方法中传递进入的值,是一个32位的int 值.解析出这个值就能获得此View的模式 和 大小.一般用到的两种模式

EXACTLY:即精确模式,View 的layout_height 和layout_width属性指定为精确数字或者match_parent时,View使用的就是EXACTLY模式。

AT_MOST: 即最大值模式,当 View 的layout_height 和 layout_width 属性指定为wrap_content.就是此模式 意指 控件的大小随内容变化,但是不能超过父View

一般来说的话,不写ViewGroup的话onLayout是不用i复写的,没有包含其他View所以也就不用计算其子View 的布局坐标。

onDraw( ) ,此方法就是最终显示到屏幕上的内容. 在绘制自定义View时呢,记得一定要把其拆分开,然后一个个的绘制 .

自定义属性

首先values文件下,新建attrs.xml的xml文件。声明资源类型为子标签为



    
        
        
        
        
        
        
        
        
        
    

//其中元素的name属性的值,就是此组自定义属性的容器名.

而 < attr > 元素表示的name属性表示View 控件的属性名,format属性表示支持的类型 .

要在布局文件中使用自定义属性呢,首先要要为这些自定义属性导入一个命名空间.

xmlns:app="http://schemas.android.com/apk/res-auto"

然后在具体的控件里,就能通过app:属性名 找到自定义属性了.

解析具体设置那些自定义属性,如下所示:

public TopBar(Context context, AttributeSet attrs) {
  super(context, attrs);
  getAttrs(attrs);
  setView(context);
}
 private void getAttrs(AttributeSet attrs) {
   TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.TopBar);
   mTitleText = ta.getString(R.styleable.TopBar_title);
   mTitleTextSize = ta.getDimension(R.styleable.TopBar_titleTextSize,10);
   mTitleTextColor = ta.getColor(R.styleable.TopBar_titleTextColor,0);
   mleftBackgound = ta.getDrawable(R.styleable.TopBar_leftBackground);
   mleftColor = ta.getColor(R.styleable.TopBar_leftTextColor,0);
   mleftText = ta.getString(R.styleable.TopBar_leftText);
   mRightColor = ta.getColor(R.styleable.TopBar_rightTextColor,0);
   mRightText = ta.getString(R.styleable.TopBar_rightText);
   mRightBackgound = ta.getDrawable(R.styleable.TopBar_rightBackground);
   ta.recycle();
}
 public void setView(Context context) {
    mleftButton = new Button(context);
    mRightButton = new Button(context);
    mTitle = new TextView(context);

    mTitle.setText(mTitleText);
    mTitle.setTextColor(mTitleTextColor);
    mTitle.setTextSize(mTitleTextSize);
    mTitle.setGravity(Gravity.CENTER);
    mTitleLP = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.MATCH_PARENT);
    mTitleLP.addRule(RelativeLayout.CENTER_IN_PARENT);

    mTitle.setLayoutParams(mTitleLP);

    mleftLP = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.MATCH_PARENT);
    mleftLP.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
    mleftButton.setText(mleftText);
    mleftButton.setTextColor(mleftColor);
    mleftButton.setBackground(mleftBackgound);
    mleftButton.setLayoutParams(mleftLP);

    mRightLp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.MATCH_PARENT);
    mRightLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
    mRightButton.setText(mRightText);
    mRightButton.setTextColor(mRightColor);
    mRightButton.setBackground(mRightBackgound);
    mRightButton.setLayoutParams(mRightLp);


     addView(mTitle);
     addView(mRightButton);
     addView(mleftButton);
     setOnclik(mRightButton,mleftButton);
 }

通过构造函数提供的AttributeSet类的对象,getContext().obtainStyledAttributes(AttributeSet set, int[] attrs); 把AttributeSet对象解析到自定义属性 容器里(即对应R.styleable.TopBar) ,获得一个TypedArray类的对象,调用getString(R.styleable.TopBar_title, 0 ) ,其他属性获取方式类似,参数一为索引名,传入自定义属性中的名字就好(一定要以-连接),参数二为没有获取到值时的默认值。获取到了之后呢就是到需要的对象处set 就好。

惯性滑动的处理

要处理惯性滑动呢,至少需要两个类 Scroller类对象和VelocityTracker类对象。其中VelocityTracker对象 为获取速度,Scroller 对象为滚动动画与移动.

Scroll 对象正常构造就好。VelocityTracker 对象呢 在 View 回调 onTouchEvent( ) 方法时,在其方法开始出调用abtion( ) 获得VelocityTracker 的实例。再调用VelocityTracker 的addMovement( MotionEvent event) 添加 移动事件 对象。

    @Override
 public boolean onTouchEvent(MotionEvent event) {
  if (velocityTracker == null) {
      velocityTracker  = VelocityTracker.obtain();  //创建速度追踪的对象。
   }
   velocityTracker.addMovement(event); //添加移动事件的对象。
        switch (event.getAction()) {
          case MotionEvent.ACTION_DOWN:
             lastx = (int)event.getX();
          break;
 
          case MotionEvent.ACTION_MOVE:
             movex = (int) event.getX()-lastx;
          break;

          case MotionEvent.ACTION_UP:

          velocityTracker.computeCurrentVelocity(1000);//设置计量单位,毫秒. 就是多少毫秒收集一次移动的像素。
          float currentVelocityx = velocityTracker.getXVelocity(); //分别获取X ,Y 轴的速度
          float currentVelocityy = velocityTracker.getYVelocity();

          if ( Math.abs(currentVelocityx) < 800) {  //设置不随便一滑就滚动了。
             return true;
           }
        //此处把X的距离取反是为了方便统一。fling()方法就是惯性滑动的处理和动画。
       scroller.fling(130,starty,(int)(-Math.abs(currentVelocityx)),(int)(Math.abs(currentVelocityy)),0,1080,0,1920);
       velocityTracker.recycle();   //回收对象
       velocityTracker = null ;
       break;
    }
  return true;
}

@Override
public void computeScroll() {  //计算滚动的距离
  if (scroller.computeScrollOffset()) {
      int currX = scroller.getCurrX(); //  获得当前X轴 的滚动偏移量。
      invalidate();  //没有滚动完,所以还得调用重绘函数.
     }
}

scroller.fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) ,此方法是关键。 startScroll() 对应的是平滑移动,而fling( ) 方法对应得就是 急速滚动. 需要注意的就是 起始的 X,Y轴位置 是不能小于 参数六 和 参数七 对应的最小滑动到的位置,不然滚动的动画效果不会触发,只是滚动了。 其中呢computeScroll( )方法是在onDraw( )中回调的 .需要的是当使用的是startScroll( )方法呢,其后一定要调用invalidate() (重绘) . 而fling( )方法不需要 ,可能其方法内部已经调用了。 computeScrollOffset( ) 用于判断整个滚动事件是否已经结束,true 表示没有,false就是滚动完了.

你可能感兴趣的:(Canvas笔记)