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就是滾動完了.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一:canvas簡介 1.1什么是canvas? ①:canvas是HTML5提供的一種新標簽 ②:HTML5 ...
    GreenHand1閱讀 4,719評論 2 32
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,337評論 25 708
  • HTML5 Canvas HTML5 元素用于圖形的繪制,通過腳本 (通常是JavaScript)來完成. ...
    靜候那一米陽光閱讀 343評論 0 1
  • 下午三四點左右稱重 53.7kg 從今天開始放暑假 決定想要減肥和練好身材 運動軟件上的記錄都是以前的算上今天的 ...
    七筒妹妹閱讀 299評論 0 0
  • 氣溫驟然降下 猶如我突然暗淡的心 明天,是每個人都期待的 可是明天就真的很美好嗎 那些讓人猝不及防的意外 怎可知道...
    春無憂閱讀 364評論 0 0