亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android自定義View實(shí)現(xiàn)水面上漲效果

 更新時(shí)間:2016年09月19日 08:50:41   作者:copor  
這篇文章給大家介紹了利用Android自定義View實(shí)現(xiàn)水面上漲效果,對(duì)大家日常開發(fā)很有幫助,有需要的朋友們可以參考借鑒。

實(shí)現(xiàn)效果如下:

實(shí)現(xiàn)思路:

1、如何實(shí)現(xiàn)圓中水面上漲效果:利用Paint的setXfermode屬性為PorterDuff.Mode.SRC_IN畫出進(jìn)度所在的矩形與圓的交集實(shí)現(xiàn)

2、如何水波紋效果:利用貝塞爾曲線,動(dòng)態(tài)改變波峰值,實(shí)現(xiàn)“隨著進(jìn)度的增加,水波紋逐漸變小的效果”

話不多說,看代碼。

首先是自定義屬性值,有哪些可自定義屬性值呢?

圓的背景顏色:circle_color,進(jìn)度的顏色:progress_color,進(jìn)度顯示文字的顏色:text_color,進(jìn)度文字的大?。簍ext_size,還有最后一個(gè):波紋最大高度:ripple_topheight

<declare-styleable name="WaterProgressView"> 
 <attr name="circle_color" format="color"/><!--圓的顏色--> 
 <attr name="progress_color" format="color"/><!--進(jìn)度的顏色--> 
 <attr name="text_color" format="color"/><!--文字的顏色--> 
 <attr name="text_size" format="dimension"/><!--文字大小--> 
 <attr name="ripple_topheight" format="dimension"/><!--水頁漣漪最大高度-->
</declare-styleable>

下面是自定義View:WaterProgressView的部份代碼:

成員變量

public class WaterProgressView extends ProgressBar {
 //默認(rèn)圓的背景色
 public static final int DEFAULT_CIRCLE_COLOR = 0xff00cccc;
 //默認(rèn)進(jìn)度的顏色
 public static final int DEFAULT_PROGRESS_COLOR = 0xff00CC66;
 //默認(rèn)文字的顏色
 public static final int DEFAULT_TEXT_COLOR = 0xffffffff;
 //默認(rèn)文字的大小
 public static final int DEFAULT_TEXT_SIZE = 18;
 //默認(rèn)的波峰最高點(diǎn)
 public static final int DEFAULT_RIPPLE_TOPHEIGHT = 10;

 private Context mContext;
 private Canvas mPaintCanvas;
 private Bitmap mBitmap;

 //畫圓的畫筆
 private Paint mCirclePaint;
 //畫圓的畫筆的顏色
 private int mCircleColor;

 //畫進(jìn)度的畫筆
 private Paint mProgressPaint;
 //畫進(jìn)度的畫筆的顏色
 private int mProgressColor ;
 //畫進(jìn)度的path
 private Path mProgressPath;
 //貝塞爾曲線波峰最大值
 private int mRippleTop = 10;

 //進(jìn)度文字的畫筆
 private Paint mTextPaint;
 //進(jìn)度文字的顏色
 private int mTextColor;
 private int mTextSize = 18;
 //目標(biāo)進(jìn)度,也就是雙擊時(shí)處理任務(wù)的進(jìn)度,會(huì)影響曲線的振幅
 private int mTargetProgress = 50;

 //監(jiān)聽雙擊和單擊事件
 private GestureDetector mGestureDetector;
}

獲取自定義屬性值:

private void getAttrValue(AttributeSet attrs) { 

 TypedArray ta = mContext.obtainStyledAttributes(attrs, R.styleable.WaterProgressView); 
 mCircleColor = ta.getColor(R.styleable.WaterProgressView_circle_color,DEFAULT_CIRCLE_COLOR);    
 mProgressColor = ta.getColor(R.styleable.WaterProgressView_progress_color,DEFAULT_PROGRESS_COLOR); 
 mTextColor = ta.getColor(R.styleable.WaterProgressView_text_color,DEFAULT_TEXT_COLOR);  
 mTextSize = (int) ta.getDimension(R.styleable.WaterProgressView_text_size, DesityUtils.sp2px(mContext,DEFAULT_TEXT_SIZE)); 
 mRippleTop = (int)ta.getDimension(R.styleable.WaterProgressView_ripple_topheight,DesityUtils.dp2px(mContext,DEFAULT_RIPPLE_TOPHEIGHT)); 
 ta.recycle();

}

定義構(gòu)造函數(shù),注意 mProgressPaint.setXfermode

//當(dāng)new該類時(shí)調(diào)用此構(gòu)造函數(shù)
public WaterProgressView(Context context) { 
 this(context,null);
}

//當(dāng)xml文件中定義該自定義View時(shí)調(diào)用此構(gòu)造函數(shù)
public WaterProgressView(Context context, AttributeSet attrs) { 
 this(context, attrs,0);
}

public WaterProgressView(Context context, AttributeSet attrs, int defStyleAttr) { 
 super(context, attrs, defStyleAttr); 
 this.mContext = context; 
 getAttrValue(attrs); 
 //初始化畫筆的相關(guān)屬性 
 initPaint(); 
 mProgressPath = new Path(); 
}

private void initPaint() { 
 //初始化畫圓的paint
 mCirclePaint = new Paint(); 
 mCirclePaint.setColor(mCircleColor); 
 mCirclePaint.setStyle(Paint.Style.FILL); 
 mCirclePaint.setAntiAlias(true); 
 mCirclePaint.setDither(true); 

 //初始化畫進(jìn)度的paint
 mProgressPaint = new Paint(); 
 mProgressPaint.setColor(mProgressColor); 
 mProgressPaint.setAntiAlias(true); 
 mProgressPaint.setDither(true); 
 mProgressPaint.setStyle(Paint.Style.FILL); 
 //其實(shí)mProgressPaint畫的也是矩形,當(dāng)設(shè)置xfermode為PorterDuff.Mode.SRC_IN后則顯示的為圓與進(jìn)度矩形的交集,則為半圓
 mProgressPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); 

 //初始化畫進(jìn)度文字的畫筆
 mTextPaint = new Paint(); 
 mTextPaint.setColor(mTextColor); 
 mTextPaint.setStyle(Paint.Style.FILL); 
 mTextPaint.setAntiAlias(true); 
 mTextPaint.setDither(true); 
 mTextPaint.setTextSize(mTextSize);

}

onMeasure()方法代碼:

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
 //使用時(shí),需要明確定義該View的尺寸,即用測(cè)量模式為MeasureSpec.EXACTLY
 int width = MeasureSpec.getSize(widthMeasureSpec); 
 int height = MeasureSpec.getSize(heightMeasureSpec); 
 setMeasuredDimension(width,height); 

 //初始化Bitmap,讓所有的drawCircle,drawPath,drawText都draw在該bitmap所在的canvas上,然后再將該bitmap 畫在onDraw方法的canvas上,
 //所以此bitmap的width,height需要減去left,top,right,bottom的padding
 mBitmap = Bitmap.createBitmap(width-getPaddingLeft()-getPaddingRight(),height- getPaddingTop()-getPaddingBottom(), Bitmap.Config.ARGB_8888); 
 mPaintCanvas = new Canvas(mBitmap);
}

接下來是核心部份,onDraw中的代碼。我們先將Circle,進(jìn)度條,進(jìn)度文字draw到自定義canvas的bitmap上,再將此bitmap draw到onDraw方法中的canvas上。drawCircle與drawText應(yīng)該沒什么難度,關(guān)鍵點(diǎn)就在于畫進(jìn)度條,怎么畫呢?既然有水波紋效果,有曲線,就用drawPath了。

drawPath的流程如下:


其中ratio的代碼如下,即ratio為當(dāng)前進(jìn)度占總進(jìn)度的百分比

float ratio = getProgress()*1.0f/getMax();

因?yàn)樽鴺?biāo)是從B點(diǎn)向下和向右正向延伸的,則A點(diǎn)的坐標(biāo)為(width,(1-ratio)*height),其中width為bitmap的寬,height為bitmap的高。我們先將mProgressPath.moveTo到A點(diǎn),然后從A點(diǎn)順時(shí)針方向確定path的各個(gè)關(guān)鍵點(diǎn),如圖,則代碼如下:

int rightTop = (int) ((1-ratio)*height);
mProgressPath.moveTo(width,rightTop);
mProgressPath.lineTo(width,height);
mProgressPath.lineTo(0,height);
mProgressPath.lineTo(0,rightTop);

如此mProgressPath已經(jīng)lineTo到了C點(diǎn),需要在A點(diǎn)與C點(diǎn)之間形成水波紋效果,則需要在A點(diǎn)與C點(diǎn)間畫貝塞爾曲線。


我們?cè)O(shè)定波峰最高點(diǎn)為10,則一段波長(zhǎng)為40,需要畫width*1.0f/40段這樣的曲線,則畫曲線的代碼如下:

int count = (int) Math.ceil(width*1.0f/(10 *4));
for(int i=0; i<count; i++) { 
 mProgressPath.rQuadTo(10,10,2* 10,0);
 mProgressPath.rQuadTo(10,-10,2* 10,0);  
}

mProgressPath.close();
mPaintCanvas.drawPath(mProgressPath,mProgressPaint);


這樣就能畫出水面上漲且有波紋效果的進(jìn)度條了。但我們還要實(shí)現(xiàn)隨著水面上漲,越接近目標(biāo)進(jìn)度,水面波紋應(yīng)該越來越小,則應(yīng)該把10抽出為變量定義為mRippleTop等初始時(shí)波峰最大值,然后定義top為隨著進(jìn)度不斷接近目標(biāo)進(jìn)度時(shí)曲線的實(shí)時(shí)波峰值 ,其中mTargetProgress為目標(biāo)progress,因?yàn)橛幸粋€(gè)目標(biāo)進(jìn)度才能實(shí)現(xiàn)當(dāng)前進(jìn)度不斷接近目標(biāo)進(jìn)度的過程中,水面漸趨于平面的效果:

float top = (mTargetProgress-getProgress())*1.0f/mTargetProgress* mRippleTop;

所以drawPath的代碼更新如下:

float top = (mTargetProgress-getProgress())*1.0f/mTargetProgress* mRippleTop;

for(int i=0; i<count; i++) { 
 mProgressPath.rQuadTo(mRippleTop,top,2* mRippleTop,0);
 mProgressPath.rQuadTo(mRippleTop,-top,2* mRippleTop,0); 
}

如此就能真正實(shí)現(xiàn)水面上漲的進(jìn)度條了。

但如何實(shí)現(xiàn)圖中雙擊時(shí)水面從0%上漲到目標(biāo)進(jìn)度,單擊時(shí)水面在目標(biāo)進(jìn)度不斷涌動(dòng)的效果呢?
先說說雙擊效果的實(shí)現(xiàn):這個(gè)簡(jiǎn)單,定義一個(gè)Handler,,當(dāng)雙擊時(shí),handler.postDelayed(runnable,time) ,每隔一段時(shí)間progress+1,在runnable中invalidate()不斷更新進(jìn)度,直到當(dāng)前progress到達(dá)mTargetProgress。

代碼如下

/**
 * 實(shí)現(xiàn)雙擊動(dòng)畫
 */
private void startDoubleTapAnimation() { 
 setProgress(0); 
 doubleTapHandler.postDelayed(doubleTapRunnable,60);
}

private Handler doubleTapHandler = new Handler(){ 
 @Override 
 public void handleMessage(Message msg) {  
 super.handleMessage(msg); 
 }
};

//雙擊處理線程,隔60ms發(fā)送一次數(shù)據(jù)
private Runnable doubleTapRunnable = new Runnable() { 
 @Override 
 public void run() {  
 if(getProgress() < mTargetProgress) {   
  invalidate();   
  setProgress(getProgress()+1);   
  doubleTapHandler.postDelayed(doubleTapRunnable,60);  
 } else {   
  doubleTapHandler.removeCallbacks(doubleTapRunnable);  
 } 
 }
};

雙擊效果實(shí)現(xiàn)了,那如何實(shí)現(xiàn)單擊效果呢?單擊時(shí)要求水面不斷涌動(dòng)一段時(shí)間,水面波紋逐漸變小,然后水面變平。我們可以定義一個(gè)mSingleTapAnimationCount變量為水面涌動(dòng)的次數(shù),然后像雙擊時(shí)的處理一樣,定義一個(gè)Handler隔一段時(shí)間發(fā)送一次更新界面的message,mSingleTapAnimationCount-- ,然后我們交替地讓初始時(shí)的波峰一次為正一次為負(fù),則能實(shí)現(xiàn)水面涌動(dòng)的效果。

核心代碼如下:

private void startSingleTapAnimation() { 
 isSingleTapAnimation = true; 
 singleTapHandler.postDelayed(singleTapRunnable,200);
}

private Handler singleTapHandler = new Handler(){ 
 @Override 
 public void handleMessage(Message msg) {  
 super.handleMessage(msg); 
 }
};

//單擊處理線程,隔200ms發(fā)送一次數(shù)據(jù)
private Runnable singleTapRunnable = new Runnable() { 
 @Override 
 public void run() {  
 if(mSingleTapAnimationCount > 0) {   
  invalidate();   
  mSingleTapAnimationCount--;   
  singleTapHandler.postDelayed(singleTapRunnable,200);  
 } else {   
  singleTapHandler.removeCallbacks(singleTapRunnable);  
 //是否正在進(jìn)行單擊動(dòng)畫  
  isSingleTapAnimation = false;   
 //重置單擊動(dòng)畫運(yùn)行次數(shù)為50次
  mSingleTapAnimationCount = 50;  
 } 
 }
};

onDraw中的代碼作相應(yīng)的更改,因單擊與雙擊時(shí)drawPath中曲線部分的繪制邏輯不一樣,則我們定義一個(gè)變量isSingleTapAnimation 區(qū)別是正在進(jìn)行單擊動(dòng)畫還是在進(jìn)行雙擊動(dòng)畫。

更改后的代碼如下:

//畫進(jìn)度
mProgressPath.reset();
//從右上邊開始draw path
int rightTop = (int) ((1-ratio)*height);
mProgressPath.moveTo(width,rightTop);
mProgressPath.lineTo(width,height);
mProgressPath.lineTo(0,height);
mProgressPath.lineTo(0,rightTop);

//畫貝塞爾曲線,形成波浪線
int count = (int) Math.ceil(width*1.0f/(mRippleTop *4));
//不是單擊animation狀態(tài)
if(!isSingleTapAnimation&&getProgress()>0) { 
 float top = (mTargetProgress-getProgress())*1.0f/mTargetProgress* mRippleTop; 
 for(int i=0; i<count; i++) {  
 mProgressPath.rQuadTo(mRippleTop,-top,2* mRippleTop,0);   
 mProgressPath.rQuadTo(mRippleTop,top,2* mRippleTop,0); 
 }
} else { 
 //單擊animation狀態(tài),為了將效果放大,將mRippleTop放大2倍
 //同時(shí)偶數(shù)時(shí)曲線走向如圖所示,奇數(shù)時(shí)則曲線剛好相反 
 float top = (mSingleTapAnimationCount*1.0f/50)*10; 
 //奇偶數(shù)時(shí)曲線切換 
 if(mSingleTapAnimationCount%2==0) {  
  for(int i=0; i<count; i++) {   
  mProgressPath.rQuadTo(mRippleTop *2,top*2,2* mRippleTop,0);    
  mProgressPath.rQuadTo(mRippleTop *2,-top*2,2* mRippleTop,0);   
 } 
 } else {  
 for(int i=0; i<count; i++) {   
  mProgressPath.rQuadTo(mRippleTop *2,-top*2,2* mRippleTop,0);    
  mProgressPath.rQuadTo(mRippleTop *2,top*2,2* mRippleTop,0); 
 } 
 }
}
mProgressPath.close();
mPaintCanvas.drawPath(mProgressPath,mProgressPaint);

基本上重要的代碼與核心邏輯與代碼就在上面了。

注意點(diǎn):

1、當(dāng)drawCircle時(shí)要考慮到padding,則circle的寬和高為getWidth與getHeight減去padding值,代碼如下:

//自定義bitmap的寬和高
int width = getWidth()-getPaddingLeft()-getPaddingRight();
int height = getHeight()-getPaddingTop()-getPaddingBottom();

//畫圓
mPaintCanvas.drawCircle(width/2,height/2,height/2,mCirclePaint);

2、當(dāng)drawText時(shí),不是從text的height的中間開始draw的,而是從baseline開始draw的

那如何獲取baseline的height坐標(biāo)呢

Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
//因?yàn)閍scent在baseline之上,所以ascent為負(fù)數(shù)。descent+ascent為負(fù)數(shù),所以是減而不是加
float baseLine = height*1.0f/2 - (metrics.descent+metrics.ascent)/2;

drawText的全部代碼如下:

//畫進(jìn)度文字
String text = ((int)(ratio*100))+"%";

//獲得文字的寬度
float textWidth = mTextPaint.measureText(text);

Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
//descent+ascent為負(fù)數(shù),所以是減而不是加
float baseLine = height*1.0f/2 - (metrics.descent+metrics.ascent)/2;
mPaintCanvas.drawText(text,width/2-textWidth/2,baseLine,mTextPaint);

3、因?yàn)橐櫦暗絧adding,記得將onDraw中的canvas translate到(getPaddingLeft(),getPaddingTop())處。

canvas.translate(getPaddingLeft(),getPaddingTop());
canvas.drawBitmap(mBitmap,0,0,null);

最后記得將自定義的bitmap draw到onDraw中的canvas上。到這兒自定義水面上漲效果的進(jìn)度條于寫完了。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作帶來一定的幫助,如果有疑問大家可以留言交流。

相關(guān)文章

最新評(píng)論