Canvas畫布的使用
默認坐標:Canvas類相當于一個矩形畫布,默認0,0坐標是左上角。用到的坐標都是畫布上的(即視圖坐標系)
其常用方法有,draw( )方法最后一個參數都為Paint 對象:
- drawRect(RectF rect,Paint paint);//在畫布上繪制一個矩形;
- react 對象為描述需要在Canvas上區域;
參數一表示矩形左邊的X坐標,余下三個同理。
- react 對象為描述需要在Canvas上區域;
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就是滾動完了.