国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android自定義View實現水面上漲效果

2019-12-12 05:14:36
字體:
來源:轉載
供稿:網友

實現效果如下:

實現思路:

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

2、如何水波紋效果:利用貝塞爾曲線,動態改變波峰值,實現“隨著進度的增加,水波紋逐漸變小的效果”

話不多說,看代碼。

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

圓的背景顏色:circle_color,進度的顏色:progress_color,進度顯示文字的顏色:text_color,進度文字的大小:text_size,還有最后一個:波紋最大高度:ripple_topheight

<declare-styleable name="WaterProgressView">  <attr name="circle_color" format="color"/><!--圓的顏色-->  <attr name="progress_color" format="color"/><!--進度的顏色-->  <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 { //默認圓的背景色 public static final int DEFAULT_CIRCLE_COLOR = 0xff00cccc; //默認進度的顏色 public static final int DEFAULT_PROGRESS_COLOR = 0xff00CC66; //默認文字的顏色 public static final int DEFAULT_TEXT_COLOR = 0xffffffff; //默認文字的大小 public static final int DEFAULT_TEXT_SIZE = 18; //默認的波峰最高點 public static final int DEFAULT_RIPPLE_TOPHEIGHT = 10; private Context mContext; private Canvas mPaintCanvas; private Bitmap mBitmap; //畫圓的畫筆 private Paint mCirclePaint; //畫圓的畫筆的顏色 private int mCircleColor; //畫進度的畫筆 private Paint mProgressPaint; //畫進度的畫筆的顏色 private int mProgressColor ; //畫進度的path private Path mProgressPath; //貝塞爾曲線波峰最大值 private int mRippleTop = 10; //進度文字的畫筆 private Paint mTextPaint; //進度文字的顏色 private int mTextColor; private int mTextSize = 18; //目標進度,也就是雙擊時處理任務的進度,會影響曲線的振幅 private int mTargetProgress = 50; //監聽雙擊和單擊事件 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();}

定義構造函數,注意 mProgressPaint.setXfermode

//當new該類時調用此構造函數public WaterProgressView(Context context) {  this(context,null);}//當xml文件中定義該自定義View時調用此構造函數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);  //初始化畫筆的相關屬性  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);  //初始化畫進度的paint mProgressPaint = new Paint();  mProgressPaint.setColor(mProgressColor);  mProgressPaint.setAntiAlias(true);  mProgressPaint.setDither(true);  mProgressPaint.setStyle(Paint.Style.FILL);  //其實mProgressPaint畫的也是矩形,當設置xfermode為PorterDuff.Mode.SRC_IN后則顯示的為圓與進度矩形的交集,則為半圓 mProgressPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));  //初始化畫進度文字的畫筆 mTextPaint = new Paint();  mTextPaint.setColor(mTextColor);  mTextPaint.setStyle(Paint.Style.FILL);  mTextPaint.setAntiAlias(true);  mTextPaint.setDither(true);  mTextPaint.setTextSize(mTextSize);}

onMeasure()方法代碼:

@Overrideprotected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  //使用時,需要明確定義該View的尺寸,即用測量模式為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,進度條,進度文字draw到自定義canvas的bitmap上,再將此bitmap draw到onDraw方法中的canvas上。drawCircle與drawText應該沒什么難度,關鍵點就在于畫進度條,怎么畫呢?既然有水波紋效果,有曲線,就用drawPath了。

drawPath的流程如下:


其中ratio的代碼如下,即ratio為當前進度占總進度的百分比

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

因為坐標是從B點向下和向右正向延伸的,則A點的坐標為(width,(1-ratio)*height),其中width為bitmap的寬,height為bitmap的高。我們先將mProgressPath.moveTo到A點,然后從A點順時針方向確定path的各個關鍵點,如圖,則代碼如下:

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

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


我們設定波峰最高點為10,則一段波長為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);


這樣就能畫出水面上漲且有波紋效果的進度條了。但我們還要實現隨著水面上漲,越接近目標進度,水面波紋應該越來越小,則應該把10抽出為變量定義為mRippleTop等初始時波峰最大值,然后定義top為隨著進度不斷接近目標進度時曲線的實時波峰值 ,其中mTargetProgress為目標progress,因為有一個目標進度才能實現當前進度不斷接近目標進度的過程中,水面漸趨于平面的效果:

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); }

如此就能真正實現水面上漲的進度條了。

但如何實現圖中雙擊時水面從0%上漲到目標進度,單擊時水面在目標進度不斷涌動的效果呢?
先說說雙擊效果的實現:這個簡單,定義一個Handler,,當雙擊時,handler.postDelayed(runnable,time) ,每隔一段時間progress+1,在runnable中invalidate()不斷更新進度,直到當前progress到達mTargetProgress。

代碼如下

/** * 實現雙擊動畫 */private void startDoubleTapAnimation() {  setProgress(0);  doubleTapHandler.postDelayed(doubleTapRunnable,60);}private Handler doubleTapHandler = new Handler(){  @Override  public void handleMessage(Message msg) {   super.handleMessage(msg);  }};//雙擊處理線程,隔60ms發送一次數據private Runnable doubleTapRunnable = new Runnable() {  @Override  public void run() {   if(getProgress() < mTargetProgress) {     invalidate();     setProgress(getProgress()+1);     doubleTapHandler.postDelayed(doubleTapRunnable,60);   } else {     doubleTapHandler.removeCallbacks(doubleTapRunnable);   }  }};

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

核心代碼如下:

private void startSingleTapAnimation() {  isSingleTapAnimation = true;  singleTapHandler.postDelayed(singleTapRunnable,200);}private Handler singleTapHandler = new Handler(){  @Override  public void handleMessage(Message msg) {   super.handleMessage(msg);  }};//單擊處理線程,隔200ms發送一次數據private Runnable singleTapRunnable = new Runnable() {  @Override  public void run() {   if(mSingleTapAnimationCount > 0) {     invalidate();     mSingleTapAnimationCount--;     singleTapHandler.postDelayed(singleTapRunnable,200);   } else {     singleTapHandler.removeCallbacks(singleTapRunnable);   //是否正在進行單擊動畫    isSingleTapAnimation = false;    //重置單擊動畫運行次數為50次  mSingleTapAnimationCount = 50;   }  }};

onDraw中的代碼作相應的更改,因單擊與雙擊時drawPath中曲線部分的繪制邏輯不一樣,則我們定義一個變量isSingleTapAnimation 區別是正在進行單擊動畫還是在進行雙擊動畫。

更改后的代碼如下:

//畫進度mProgressPath.reset();//從右上邊開始draw pathint 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狀態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狀態,為了將效果放大,將mRippleTop放大2倍 //同時偶數時曲線走向如圖所示,奇數時則曲線剛好相反  float top = (mSingleTapAnimationCount*1.0f/50)*10;  //奇偶數時曲線切換  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);

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

注意點:

1、當drawCircle時要考慮到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、當drawText時,不是從text的height的中間開始draw的,而是從baseline開始draw的

那如何獲取baseline的height坐標呢

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

drawText的全部代碼如下:

//畫進度文字String text = ((int)(ratio*100))+"%";//獲得文字的寬度float textWidth = mTextPaint.measureText(text);Paint.FontMetrics metrics = mTextPaint.getFontMetrics();//descent+ascent為負數,所以是減而不是加float baseLine = height*1.0f/2 - (metrics.descent+metrics.ascent)/2;mPaintCanvas.drawText(text,width/2-textWidth/2,baseLine,mTextPaint);

3、因為要顧及到padding,記得將onDraw中的canvas translate到(getPaddingLeft(),getPaddingTop())處。

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

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

總結

以上就是這篇文章的全部內容,希望本文的內容對大家的學習或者工作帶來一定的幫助,如果有疑問大家可以留言交流。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 芮城县| 绥阳县| 成安县| 青海省| 靖边县| 梅河口市| 会理县| 安龙县| 丰镇市| 苗栗县| 沁水县| 滦平县| 图木舒克市| 乾安县| 巴南区| 乐昌市| 乌拉特后旗| 诸城市| 台东县| 都匀市| 松阳县| 阳东县| 江都市| 娱乐| 泰和县| 民乐县| 翁牛特旗| 太保市| 潼关县| 柯坪县| 麦盖提县| 永善县| 浠水县| 巴林左旗| 竹北市| 沙雅县| 铁岭市| 定日县| 富锦市| 石狮市| 上饶市|