欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android自定義view實(shí)現(xiàn)水波紋進(jìn)度球效果

 更新時(shí)間:2016年08月19日 16:19:45   作者:顧修忠  
在我們的日常開發(fā)中自定義控件還是用的挺多的,設(shè)計(jì)師或者產(chǎn)品為了更好的漂亮,美觀,交互都會(huì)做一些牛逼的ui效果圖,但是最后實(shí)現(xiàn)的還是我們程序員啊。所以說(shuō) 自定義view你還是得會(huì)的。

今天我們要實(shí)現(xiàn)的這個(gè)view沒有太多交互性的view,所以就繼承view。

自定義view的套路,套路很深

      1、獲取我們自定義屬性attrs(可省略)

      2、重寫onMeasure方法,計(jì)算控件的寬和高

      3、重寫onDraw方法,繪制我們的控件

這么看來(lái),自定義view的套路很清晰嘛。

我們看下今天的效果圖,其中一個(gè)是放慢的效果(時(shí)間調(diào)的長(zhǎng))


我們按照套路來(lái)。

一.自定義屬性

 <declare-styleable name="WaveProgressView">
  <attr name="radius" format="dimension|reference" />
  <attr name="radius_color" format="color|reference" />
  <attr name="progress_text_color" format="color|reference" />
  <attr name="progress_text_size" format="dimension|reference" />
  <attr name="progress_color" format="color|reference" />
  <attr name="progress" format="float" />
  <attr name="maxProgress" format="float" />
 </declare-styleable>

看下效果圖我們就知道因該需要哪些屬性。就不說(shuō)了。

然后就是獲取我們的這些屬性,就是用TypedArray來(lái)獲取。當(dāng)然是在構(gòu)造中獲取,一般我們會(huì)復(fù)寫構(gòu)造方法,少參數(shù)調(diào)用參數(shù)多的,然后走到參數(shù)最多的那個(gè)。

TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.WaveProgressView, defStyleAttr, R.style.WaveProgressViewDefault);
  radius = (int) a.getDimension(R.styleable.WaveProgressView_radius, radius);
  textColor = a.getColor(R.styleable.WaveProgressView_progress_text_color, 0);
  textSize = a.getDimensionPixelSize(R.styleable.WaveProgressView_progress_text_size, 0);
  progressColor = a.getColor(R.styleable.WaveProgressView_progress_color, 0);
  radiusColor = a.getColor(R.styleable.WaveProgressView_radius_color, 0);
  progress = a.getFloat(R.styleable.WaveProgressView_progress, 0);
  maxProgress = a.getFloat(R.styleable.WaveProgressView_maxProgress, 100);
  a.recycle();

注: R.style.WaveProgressViewDefault是這個(gè)控件的默認(rèn)樣式。

二.onMeasure測(cè)量

我們重寫這個(gè)方法主要是更具父看見的寬和高來(lái)設(shè)置自己的寬和高。

 @Override 
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  //計(jì)算寬和高
  int exceptW = getPaddingLeft() + getPaddingRight() + 2 * radius;
  int exceptH = getPaddingTop() + getPaddingBottom() + 2 * radius;
  int width = resolveSize(exceptW, widthMeasureSpec);
  int height = resolveSize(exceptH, heightMeasureSpec);
  int min = Math.min(width, height);

  this.width = this.height = min;

  //計(jì)算半徑,減去padding的最小值
  int minLR = Math.min(getPaddingLeft(), getPaddingRight());
  int minTB = Math.min(getPaddingTop(), getPaddingBottom());
  minPadding = Math.min(minLR, minTB);
  radius = (min - minPadding * 2) / 2;

  setMeasuredDimension(min, min);
 }

首先該控件的寬和高肯定是一樣的,因?yàn)槭莻€(gè)圓嘛。其實(shí)是寬和高與半徑和內(nèi)邊距有關(guān),這里的內(nèi)邊距,我們?nèi)∩舷伦笥易钚〉囊粋€(gè)。寬和高也選擇取最小的。

this.width = this.height = min; 包含左右邊距。

resolveSize這個(gè)方法很好的為我們實(shí)現(xiàn)了我們想要的寬和高我慢看下源碼。

 public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
  final int specMode = MeasureSpec.getMode(measureSpec);
  final int specSize = MeasureSpec.getSize(measureSpec);
  final int result;
  switch (specMode) {
   case MeasureSpec.AT_MOST:
    if (specSize < size) {
     result = specSize | MEASURED_STATE_TOO_SMALL;
    } else {
     result = size;
    }
    break;
   case MeasureSpec.EXACTLY:
    result = specSize;
    break;
   case MeasureSpec.UNSPECIFIED:
   default:
    result = size;
  }
  return result | (childMeasuredState & MEASURED_STATE_MASK);
 }

如果我們自己寫也是這樣寫。

最后通過(guò)setMeasuredDimension設(shè)置寬和高。

三.onDraw繪制

關(guān)于繪制有很多android 提供了很多API,這里就不多說(shuō)了。

繪制首先就是一些畫筆的初始化。

需要提一下繪制path路徑的畫筆設(shè)置為PorterDuff.Mode.SRC_IN模式,這個(gè)模式只顯示重疊的部分。

 pathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  pathPaint.setColor(progressColor);
  pathPaint.setDither(true);
  pathPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

我們要將所有的繪制 繪制到一個(gè)透明的bitmap上,然后將這個(gè)bitmap繪制到canvas上。

if (bitmap == null) {
   bitmap = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888);
   bitmapCanvas = new Canvas(bitmap);
  }

為了方便計(jì)算和繪制,我將坐標(biāo)系平移padding的距離

 bitmapCanvas.save();
  //移動(dòng)坐標(biāo)系
  bitmapCanvas.translate(minPadding, minPadding);
 // .... some thing
 bitmapCanvas.restore();

3.1繪制圓

  bitmapCanvas.drawCircle(radius, radius, radius, circlePaint);

3.2繪制PATH 路徑.

一是要實(shí)現(xiàn)波紋的左右飄,和上下的振幅慢慢的減小

繪制這個(gè)之前我們需要知道二階貝塞爾曲線的大致原理。

簡(jiǎn)單的說(shuō)就是知道:P1起始點(diǎn),P2是終點(diǎn),P1是控制點(diǎn).利用塞爾曲線的公式就可以得道沿途的一些點(diǎn),最后把點(diǎn)連起來(lái)就是嘍。

下面這個(gè)圖片來(lái)于網(wǎng)絡(luò):


二階貝塞爾曲線

在android-sdk里提供了繪制貝塞爾曲線的函數(shù)rQuadTo方法

public void rQuadTo(float dx1, float dy1, float dx2, float dy2)

      dx1:控制點(diǎn)X坐標(biāo),表示相對(duì)上一個(gè)終點(diǎn)X坐標(biāo)的位移坐標(biāo),可為負(fù)值,正值表示相加,負(fù)值表示相減;

      dy1:控制點(diǎn)Y坐標(biāo),相對(duì)上一個(gè)終點(diǎn)Y坐標(biāo)的位移坐標(biāo)。同樣可為負(fù)值,正值表示相加,負(fù)值表示相減;

      dx2:終點(diǎn)X坐標(biāo),同樣是一個(gè)相對(duì)坐標(biāo),相對(duì)上一個(gè)終點(diǎn)X坐標(biāo)的位移值,可為負(fù)值,正值表示相加,負(fù)值表示相減;

      dy2:終點(diǎn)Y坐標(biāo),同樣是一個(gè)相對(duì),相對(duì)上一個(gè)終點(diǎn)Y坐標(biāo)的位移值??蔀樨?fù)值,正值表示相加,負(fù)值表示相減;

這四個(gè)參數(shù)都是傳遞的都是相對(duì)值,相對(duì)上一個(gè)終點(diǎn)的位移值。

要實(shí)現(xiàn)振幅慢慢的減小我們可以調(diào)節(jié)控制點(diǎn)的y坐標(biāo)即可,即:

float percent=progress * 1.0f / maxProgress;

就可以得到[0,1]的

一個(gè)閉區(qū)間,[0,1]這貨好啊,我喜歡,可以來(lái)做很多事情。

這樣我們就可以根據(jù)percent來(lái)調(diào)節(jié)控制點(diǎn)的y坐標(biāo)了。

//根據(jù)直徑計(jì)算繪制貝賽爾曲線的次數(shù)
   int count = radius * 4 / 60;
   //控制-控制點(diǎn)y的坐標(biāo)
   float point = (1 - percent) * 15;
   for (int i = 0; i < count; i++) {
    path.rQuadTo(15, -point, 30, 0);
    path.rQuadTo(15, point, 30, 0);
   }

要實(shí)現(xiàn)左右波紋只需要控制閉合路徑的左上角的x坐標(biāo)即可,當(dāng)然也是根據(jù)percent嘍。

大家可以結(jié)合下面這個(gè)圖來(lái)理解下上面的話。

path繪制的完整代碼片段。

 //繪制PATH
  //重置繪制路線
  path.reset();
  float percent=progress * 1.0f / maxProgress;
  float y = (1 - percent) * radius * 2;
  //移動(dòng)到右上邊
  path.moveTo(radius * 2, y);
  //移動(dòng)到最右下方
  path.lineTo(radius * 2, radius * 2);
  //移動(dòng)到最左下邊
  path.lineTo(0, radius * 2);
  //移動(dòng)到左上邊
  // path.lineTo(0, y);
  //實(shí)現(xiàn)左右波動(dòng),根據(jù)progress來(lái)平移
  path.lineTo(-(1 -percent) * radius*2, y);
  if (progress != 0.0f) {
   //根據(jù)直徑計(jì)算繪制貝賽爾曲線的次數(shù)
   int count = radius * 4 / 60;
   //控制-控制點(diǎn)y的坐標(biāo)
   float point = (1 - percent) * 15;
   for (int i = 0; i < count; i++) {
    path.rQuadTo(15, -point, 30, 0);
    path.rQuadTo(15, point, 30, 0);
   }
  }
  //閉合
  path.close();
  bitmapCanvas.drawPath(path, pathPaint);

3.3繪制進(jìn)度的文字

這個(gè)就比較簡(jiǎn)單了,繪制在控件的中間即可。關(guān)于文字的坐標(biāo)計(jì)算還是很好理解的。

 //繪制文字
  String text = progress + "%";
  float textW = textPaint.measureText(text);
  Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
  float baseLine = radius - (fontMetrics.ascent + fontMetrics.descent) / 2;
  bitmapCanvas.drawText(text, radius - textW / 2, baseLine, textPaint);

最后別忘了把我們的bitmap繪制到canvas上。

canvas.drawBitmap(bitmap, 0, 0, null);

哦,最后是實(shí)用方法,這里我們不用thread+handler,我們用屬性動(dòng)畫。

你懂的?。?!,like

 ObjectAnimator objectAnimator0 = ObjectAnimator.ofFloat(waveProgressView_0, "progress", 0f, 100f);
  objectAnimator0.setDuration(3300);
  objectAnimator0.setInterpolator(new LinearInterpolator());
  objectAnimator0.start();

結(jié)束語(yǔ)

至此,也就實(shí)現(xiàn)了我們的效果。以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家開發(fā)Android能有所幫助。

相關(guān)文章

  • 谷歌被屏蔽后如何搭建安卓環(huán)境

    谷歌被屏蔽后如何搭建安卓環(huán)境

    從5月27日開始,谷歌(Google)在華的幾乎所有的服務(wù)都處于無(wú)法使用的狀態(tài),除了搜索引擎遭到屏蔽之外,谷歌的郵箱(Gmail)、日歷(Calendar)、翻譯(Translate)、地圖(Maps)、分析(Analytics)和Google AdSense等產(chǎn)品也受到了影響。同時(shí)安裝安卓環(huán)境的時(shí)候同樣容易出現(xiàn)問題
    2014-06-06
  • Android 中 Tweened animation的實(shí)例詳解

    Android 中 Tweened animation的實(shí)例詳解

    這篇文章主要介紹了Android 中 Tweened animation的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家理解掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-09-09
  • Android HTTP網(wǎng)絡(luò)請(qǐng)求的異步實(shí)現(xiàn)

    Android HTTP網(wǎng)絡(luò)請(qǐng)求的異步實(shí)現(xiàn)

    這篇文章主要介紹了Android HTTP網(wǎng)絡(luò)請(qǐng)求的異步實(shí)現(xiàn),感興趣的小伙伴們可以參考一下
    2016-07-07
  • android圖像繪制(五)畫布保存為指定格式/大小的圖片

    android圖像繪制(五)畫布保存為指定格式/大小的圖片

    將圖片進(jìn)行編輯(放縮,涂鴉等),最后保存成指定格式、大小的圖片,接下來(lái)將介紹保存方法,感興趣的朋友可以了解下啊
    2013-01-01
  • 最新評(píng)論