Canvas畫(huà)布的使用
默認(rèn)坐標(biāo):Canvas類(lèi)相當(dāng)于一個(gè)矩形畫(huà)布辰如,默認(rèn)0,0坐標(biāo)是左上角怎栽。用到的坐標(biāo)都是畫(huà)布上的(即視圖坐標(biāo)系)
其常用方法有颊亮,draw( )方法最后一個(gè)參數(shù)都為Paint 對(duì)象:
- drawRect(RectF rect,Paint paint);//在畫(huà)布上繪制一個(gè)矩形;
- react 對(duì)象為描述需要在Canvas上區(qū)域拂铡;
參數(shù)一表示矩形左邊的X坐標(biāo)壹无,余下三個(gè)同理。
- react 對(duì)象為描述需要在Canvas上區(qū)域拂铡;
save( );
//此方法用于保存當(dāng)前畫(huà)布的狀態(tài)感帅,一般多用于旋轉(zhuǎn)畫(huà)布的時(shí)候
restore()斗锭;
///此方法用于恢復(fù)畫(huà)布,即恢復(fù)到save( )方法存入時(shí)的狀態(tài)留瞳,但在畫(huà)布上畫(huà)的東西不會(huì)被撤銷(xiāo)拒迅。
rotate(float degrees, float px, float py);
//此方法用于旋轉(zhuǎn)畫(huà)布她倘,參數(shù)一為旋轉(zhuǎn)的角度(正數(shù)為順時(shí)針?lè)较蜣D(zhuǎn)璧微,負(fù)數(shù)為逆時(shí)針),參數(shù)二硬梁,三畫(huà)布旋轉(zhuǎn)的軸點(diǎn). 當(dāng)畫(huà)布旋轉(zhuǎn)時(shí)呢前硫,其的坐標(biāo)也是跟著一起轉(zhuǎn)動(dòng)了,所以就意味著一定要此Canvas的中心坐標(biāo)點(diǎn)來(lái)旋轉(zhuǎn),不然轉(zhuǎn)了后內(nèi)容會(huì)丟失荧止。當(dāng)確定點(diǎn)后屹电,是以此點(diǎn)的上直線(xiàn)為O度. 當(dāng)轉(zhuǎn)動(dòng)到相應(yīng)的角度后阶剑,又以那個(gè)角度會(huì)0度。
一般來(lái)說(shuō)危号,一次轉(zhuǎn)動(dòng) 對(duì)應(yīng)一個(gè)內(nèi)容繪制牧愁。下次轉(zhuǎn)動(dòng)并不會(huì)把上次繪制的內(nèi)容也轉(zhuǎn)動(dòng)了.
多用于圓盤(pán)類(lèi)的自定義View,確定一個(gè)地方的內(nèi)容就可以轉(zhuǎn)動(dòng)繪制,省去多次確定坐標(biāo).
RectF rectf =new RectF(float left,float top,float reight,float bottom);
drawPath(Path path,Paint paint);
//在畫(huà)布上繪制一個(gè)Path圖形(即圖像的輪廓)外莲,圖形復(fù)雜時(shí)就用這個(gè)猪半。
- Path類(lèi)常用方法用:
moveTo(float x,float y); //設(shè)置下次連接操作的坐標(biāo)(移動(dòng)的起點(diǎn))。如不設(shè)置呢為Canvas的0,0 坐標(biāo)偷线。
lineTo(float x,float y);//設(shè)置上個(gè)點(diǎn)到此點(diǎn)用直線(xiàn)連接磨确。
close();連接第一點(diǎn)到最后一個(gè)點(diǎn),形成閉合声邦。
更多內(nèi)容查看此
drawBitmap(Bitmap bitmap,float left , float top , Paint paint) ;
//其實(shí)呢就是貼圖乏奥。
因?yàn)橐粡垐D片長(zhǎng)寬肯定是確定的,所以確定左上角的坐標(biāo)就OK 了亥曹。
drawLine(float startx , float starty , float stopx , float stopy , Paint paint) ;
//畫(huà)線(xiàn)邓了,當(dāng)x坐標(biāo)的起始和終止的坐標(biāo)不變時(shí),變化的為Y時(shí)歇式,畫(huà)出來(lái)的時(shí)豎線(xiàn)驶悟。反之橫線(xiàn)胡野。
當(dāng)x和y都是變的材失,畫(huà)出來(lái)的就是斜線(xiàn)。
drawPoint(float x, float y, Paint paint)硫豆;
//表示畫(huà)點(diǎn)龙巨。
drawText(String text, float x, floaty, Paint paint) ;
//繪制文本熊响,x,y坐標(biāo)用于確定文字左上角的坐標(biāo)旨别。
drawOval(RectF oval, Paint paint);
//此為畫(huà)橢圓汗茄,參數(shù)一用于確定橢圓外接矩形(用于確定橢圓能畫(huà)多大).
drawCircle(float cx, float cy, float radius,Paint paint);
//此為畫(huà)園秸弛,參數(shù)一二為確定圓的中心坐標(biāo)。參數(shù)三為半徑洪碳。
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 递览;
//畫(huà)弧,參數(shù)一用于確定弧能畫(huà)多大瞳腌,參數(shù)二為弧的起始角度绞铃,參數(shù)三為繪制的角度,(弧會(huì)順時(shí)針繪制)嫂侍。參數(shù)四為是否把所畫(huà)弧的開(kāi)始點(diǎn)結(jié)束點(diǎn)連接到弧心儿捧,為false弧線(xiàn)荚坞,true就封。
Paint 畫(huà)筆的使用
Paint對(duì)象呢為用于在Canvas上畫(huà)東西的筆菲盾;
常用方法:
setAntiAlias(boolean aa);
此用于用于設(shè)置是否開(kāi)啟抗鋸齒颓影,為true呢為開(kāi)啟,false為不開(kāi)啟懒鉴。
setStyle(Paint.Style style);
此用于設(shè)置畫(huà)筆的風(fēng)格瞭空。默認(rèn)為 FILL(填滿(mǎn)) ;
STROKE表示筆為空心疗我,僅僅畫(huà)邊咆畏。FILL_AND_STROKE表示中心部分為空,外圍填滿(mǎn)吴裤。
setStrokeWidth(float width)旧找;
此方法用于設(shè)置當(dāng)畫(huà)筆為STROKE時(shí),延伸出去的寬度。
setStrokeCap(Paint.Cap cap);
此方法用于設(shè)置一般用于弧線(xiàn)麦牺,設(shè)置頭部和尾部是圓角钮蛛,默認(rèn)為直角.
setColor(int color);
此方法用于設(shè)置畫(huà)筆的顏色.
setTextSize(float textSize) ;
滑動(dòng)移動(dòng)距離的計(jì)算
@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; //獲取當(dāng)前移動(dòng)了多少距離;
break;
case MotionEvent.ACTION_UP:
//處理輸入的離開(kāi)的事件.
break;
}
return true;
}
如果在ACTION_MOVE中有重繪視圖時(shí),一定要最后重新獲取當(dāng)前的坐標(biāo)剖膳,下次再執(zhí)行到ACTION_MOVE狀態(tài)才能正確計(jì)算偏移量魏颓,不然lastx一直是DOWN狀態(tài)是的坐標(biāo).
X軸: 如果計(jì)算出移動(dòng)的值為-值,那么就是向左滑,為正值吱晒,就是向右滑甸饱。
Y軸:如果計(jì)算出移動(dòng)的值為-值,那么就是向上滑仑濒,為正值叹话,就是向下滑。
自定義View 三大流程
通常要寫(xiě)自定義View呢
onMeasure( int widthMeasureSpec, int heightMeasureSpec) ; //測(cè)量墩瞳;
onLayout(boolean changed, int left, int top, int right, int bottom) ; //布局驼壶;
onDraw(Canvas canvas);//繪制
onMeasure( )方法都得重寫(xiě)喉酌,因?yàn)橄到y(tǒng)默認(rèn)對(duì)wrap_content的屬性值處理是match_parent.所以想支持此屬性就必須復(fù)寫(xiě)此方法來(lái)指定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類(lèi),為幫助測(cè)量View的, onMeasure( )方法中傳遞進(jìn)入的值泪电,是一個(gè)32位的int 值.解析出這個(gè)值就能獲得此View的模式 和 大小.一般用到的兩種模式
EXACTLY:即精確模式,View 的layout_height 和layout_width屬性指定為精確數(shù)字或者match_parent時(shí)般妙,View使用的就是EXACTLY模式。
AT_MOST: 即最大值模式歪架,當(dāng) View 的layout_height 和 layout_width 屬性指定為wrap_content.就是此模式 意指 控件的大小隨內(nèi)容變化股冗,但是不能超過(guò)父View
一般來(lái)說(shuō)的話(huà),不寫(xiě)ViewGroup的話(huà)onLayout是不用i復(fù)寫(xiě)的和蚪,沒(méi)有包含其他View所以也就不用計(jì)算其子View 的布局坐標(biāo)止状。
onDraw( ) ,此方法就是最終顯示到屏幕上的內(nèi)容. 在繪制自定義View時(shí)呢烹棉,記得一定要把其拆分開(kāi),然后一個(gè)個(gè)的繪制 .
自定義屬性
首先values文件下怯疤,新建attrs.xml的xml文件浆洗。聲明資源類(lèi)型為子標(biāo)簽為
<?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屬性表示支持的類(lèi)型 .
要在布局文件中使用自定義屬性呢集峦,首先要要為這些自定義屬性導(dǎo)入一個(gè)命名空間.
xmlns:app="http://schemas.android.com/apk/res-auto"
然后在具體的控件里伏社,就能通過(guò)app:屬性名 找到自定義屬性了.
解析具體設(shè)置那些自定義屬性,如下所示:
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);
}
通過(guò)構(gòu)造函數(shù)提供的AttributeSet類(lèi)的對(duì)象塔淤,getContext().obtainStyledAttributes(AttributeSet set, int[] attrs); 把AttributeSet對(duì)象解析到自定義屬性 容器里(即對(duì)應(yīng)R.styleable.TopBar) 摘昌,獲得一個(gè)TypedArray類(lèi)的對(duì)象,調(diào)用getString(R.styleable.TopBar_title, 0 ) ,其他屬性獲取方式類(lèi)似高蜂,參數(shù)一為索引名聪黎,傳入自定義屬性中的名字就好(一定要以-連接),參數(shù)二為沒(méi)有獲取到值時(shí)的默認(rèn)值。獲取到了之后呢就是到需要的對(duì)象處set 就好备恤。
慣性滑動(dòng)的處理
要處理慣性滑動(dòng)呢稿饰,至少需要兩個(gè)類(lèi) Scroller類(lèi)對(duì)象和VelocityTracker類(lèi)對(duì)象。其中VelocityTracker對(duì)象 為獲取速度露泊,Scroller 對(duì)象為滾動(dòng)動(dòng)畫(huà)與移動(dòng).
Scroll 對(duì)象正常構(gòu)造就好喉镰。VelocityTracker 對(duì)象呢 在 View 回調(diào) onTouchEvent( ) 方法時(shí),在其方法開(kāi)始出調(diào)用abtion( ) 獲得VelocityTracker 的實(shí)例惭笑。再調(diào)用VelocityTracker 的addMovement( MotionEvent event) 添加 移動(dòng)事件 對(duì)象侣姆。
@Override
public boolean onTouchEvent(MotionEvent event) {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain(); //創(chuàng)建速度追蹤的對(duì)象。
}
velocityTracker.addMovement(event); //添加移動(dòng)事件的對(duì)象脖咐。
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);//設(shè)置計(jì)量單位铺敌,毫秒. 就是多少毫秒收集一次移動(dòng)的像素。
float currentVelocityx = velocityTracker.getXVelocity(); //分別獲取X 屁擅,Y 軸的速度
float currentVelocityy = velocityTracker.getYVelocity();
if ( Math.abs(currentVelocityx) < 800) { //設(shè)置不隨便一滑就滾動(dòng)了。
return true;
}
//此處把X的距離取反是為了方便統(tǒng)一产弹。fling()方法就是慣性滑動(dòng)的處理和動(dòng)畫(huà)派歌。
scroller.fling(130,starty,(int)(-Math.abs(currentVelocityx)),(int)(Math.abs(currentVelocityy)),0,1080,0,1920);
velocityTracker.recycle(); //回收對(duì)象
velocityTracker = null ;
break;
}
return true;
}
@Override
public void computeScroll() { //計(jì)算滾動(dòng)的距離
if (scroller.computeScrollOffset()) {
int currX = scroller.getCurrX(); // 獲得當(dāng)前X軸 的滾動(dòng)偏移量。
invalidate(); //沒(méi)有滾動(dòng)完痰哨,所以還得調(diào)用重繪函數(shù).
}
}
scroller.fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) 胶果,此方法是關(guān)鍵。 startScroll() 對(duì)應(yīng)的是平滑移動(dòng)斤斧,而fling( ) 方法對(duì)應(yīng)得就是 急速滾動(dòng). 需要注意的就是 起始的 X,Y軸位置 是不能小于 參數(shù)六 和 參數(shù)七 對(duì)應(yīng)的最小滑動(dòng)到的位置早抠,不然滾動(dòng)的動(dòng)畫(huà)效果不會(huì)觸發(fā),只是滾動(dòng)了。 其中呢computeScroll( )方法是在onDraw( )中回調(diào)的 .需要的是當(dāng)使用的是startScroll( )方法呢撬讽,其后一定要調(diào)用invalidate() (重繪) . 而fling( )方法不需要 蕊连,可能其方法內(nèi)部已經(jīng)調(diào)用了悬垃。 computeScrollOffset( ) 用于判斷整個(gè)滾動(dòng)事件是否已經(jīng)結(jié)束,true 表示沒(méi)有甘苍,false就是滾動(dòng)完了.