Canvas笔记

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文件。声明资源类型为子标签为

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="TopBar">
        <attr name="title" format="string"/>
        <attr name="titleTextSize" format="dimension"/>
        <attr name="titleTextColor" format="color"/>
        <attr name="leftTextColor" format="color"/>
        <attr name="leftText" format="string"/>
        <attr name="leftBackground" format="reference|color"/>
        <attr name="rightTextColor" format="color"/>
        <attr name="rightText" format="string"/>
        <attr name="rightBackground" format="reference|color"/>
    </declare-styleable>
</resources>

//其中<declare-styleable>元素的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就是滚动完了.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容

  • 一:canvas简介 1.1什么是canvas? ①:canvas是HTML5提供的一种新标签 ②:HTML5 ...
    GreenHand1阅读 4,683评论 2 32
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,116评论 25 707
  • HTML5 Canvas HTML5 元素用于图形的绘制,通过脚本 (通常是JavaScript)来完成. ...
    静候那一米阳光阅读 332评论 0 1
  • 下午三四点左右称重 53.7kg 从今天开始放暑假 决定想要减肥和练好身材 运动软件上的记录都是以前的算上今天的 ...
    七筒妹妹阅读 287评论 0 0
  • 气温骤然降下 犹如我突然暗淡的心 明天,是每个人都期待的 可是明天就真的很美好吗 那些让人猝不及防的意外 怎可知道...
    春无忧阅读 350评论 0 0