中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

自定義View實現(xiàn)鐘擺效果進度條PendulumView

在網(wǎng)上看到了一個IOS組件PendulumView,實現(xiàn)了鐘擺的動畫效果。由于原生的進度條確實是不好看,所以想可以自定義View實現(xiàn)這樣的效果,以后也可以用于加載頁面的進度條。

公司主營業(yè)務(wù):成都做網(wǎng)站、網(wǎng)站建設(shè)、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)推出永順免費做網(wǎng)站回饋大家。

廢話不多說,先上效果圖

底部黑邊是錄制時不小心錄上的,可以忽略。

既然是自定義View我們就按標準的流程來,第一步,自定義屬性自定義屬性

建立屬性文件

在Android項目的res->values目錄下新建一個attrs.xml文件,文件內(nèi)容如下:

[html] view plain copy

<?xml version="1.0" encoding="utf-8"?>

<resources>

<declare-styleable name="PendulumView">

<attr name="globeNum" format="integer"/>

<attr name="globeColor" format="color"/>

<attr name="globeRadius" format="dimension"/>

<attr name="swingRadius" format="dimension"/>

</declare-styleable>

</resources>

其中declare-styleable的name屬性用于在代碼中引用該屬性文件。name屬性,一般情況下寫的都是我們自定義View的類名,較為直觀。

使用styleale,系統(tǒng)可以為我們完成很多常量(int[]數(shù)組,下標常量)等的編寫,簡化我們的開發(fā)工作,例如下面代碼中用到的R.styleable.PendulumView_golbeNum等就是系統(tǒng)為我們自動生成的。

globeNum屬性表示小球數(shù)量,globeColor表示小球顏色,globeRadius表示小球半徑,swingRadius表示擺動半徑讀取屬性值

在自定view的構(gòu)造方法中通過TypedArray讀取屬性值通過AttributeSet同樣可以獲取屬性值,但是如果屬性值是引用類型,則得到的只是ID,仍需繼續(xù)通過解析ID獲取真正的屬性值,而TypedArray直接幫助我們完成了上述工作。

[java] view plain copy

public PendulumView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);

//使用TypedArray讀取自定義的屬性值

TypedArray ta = context.getResources().obtainAttributes(attrs, R.styleable.PendulumView);int count = ta.getIndexCount();

for (int i = 0; i < count; i++) {

int attr = ta.getIndex(i);

switch (attr) {

case R.styleable.PendulumView_globeNum:

mGlobeNum = ta.getInt(attr, 5);

break;

case R.styleable.PendulumView_globeRadius:

mGlobeRadius = ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 16, getResources().getDisplayMetrics()));break;

case R.styleable.PendulumView_globeColor:

mGlobeColor = ta.getColor(attr, Color.BLUE);break;

case R.styleable.PendulumView_swingRadius:

mSwingRadius = ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 16, getResources().getDisplayMetrics()));break;

}

}

ta.recycle();  //避免下次讀取時出現(xiàn)問題

mPaint = new Paint();

mPaint.setColor(mGlobeColor);

}

重寫OnMeasure()方法

[java] view plain copy

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);//高度為小球半徑+擺動半徑

int height = mGlobeRadius + mSwingRadius;//寬度為2*擺動半徑+(小球數(shù)量-1)*小球直徑int width = mSwingRadius + mGlobeRadius * 2 * (mGlobeNum - 1) + mSwingRadius;//如果測量模式為EXACTLY,則直接使用推薦值,如不為EXACTLY(一般處理wrap_content情況),使用自己計算的寬高setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? widthSize : width, (heightMode == MeasureSpec.EXACTLY) ? heightSize : height);}

其中

[java] view plain copy

int height = mGlobeRadius + mSwingRadius;<pre name="code" class="java">int width = mSwingRadius + mGlobeRadius * 2 * (mGlobeNum - 1) + mSwingRadius;用于處理測量模式為AT_MOST的情況,一般是自定義View的寬高設(shè)置為了wrap_content,此時通過小球的數(shù)量,半徑,擺動的半徑等計算View的寬高,如下圖:

以小球個數(shù)5為例,View的大小為下圖紅色矩形區(qū)域重寫onDraw()方法

自定義View實現(xiàn)鐘擺效果進度條PendulumView

[java] view plain copy

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//繪制除左右兩個小球外的其他小球

for (int i = 0; i < mGlobeNum - 2; i++) {canvas.drawCircle(mSwingRadius + (i + 1) * 2 * mGlobeRadius, mSwingRadius, mGlobeRadius, mPaint);}

if (mLeftPoint == null || mRightPoint == null) {//初始化最左右兩小球坐標

mLeftPoint = new Point(mSwingRadius, mSwingRadius);mRightPoint = new Point(mSwingRadius + mGlobeRadius * 2 * (mGlobeNum - 1), mSwingRadius);//開啟擺動動畫

startPendulumAnimation();

}

//繪制左右兩小球

canvas.drawCircle(mLeftPoint.x, mLeftPoint.y, mGlobeRadius, mPaint);canvas.drawCircle(mRightPoint.x, mRightPoint.y, mGlobeRadius, mPaint);}

onDraw()方法是自定義View的關(guān)鍵所在,在該方法體內(nèi)繪制View的顯示效果。代碼首先繪制了除去最左邊最右邊小球以外的其他小球,然后對左右兩小球的坐標值進行判斷,如果是第一次繪制,坐標值均為空,則初始化兩小球坐標,并且開啟動畫。最后通過mLeftPoint,mRightPoint的x,y值,繪制左右兩個小球。

其中mLeftPoint,mRightPoint均是android.graphics.Point對象,僅是使用它們來存放左右兩小球的x,y坐標信息。

使用屬性動畫

[java] view plain copy

public void startPendulumAnimation() {

//使用屬性動畫

final ValueAnimator anim = ValueAnimator.ofObject(new TypeEvaluator() {@Override

public Object evaluate(float fraction, Object startValue, Object endValue) {//參數(shù)fraction用于表示動畫的完成度,我們根據(jù)它來計算當前的動畫值double angle = Math.toRadians(90 * fraction);int x = (int) ((mSwingRadius - mGlobeRadius) * Math.sin(angle));int y = (int) ((mSwingRadius - mGlobeRadius) * Math.cos(angle));Point point = new Point(x, y);

return point;

}

}, new Point(), new Point());

anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Override

public void onAnimationUpdate(ValueAnimator animation) {Point point = (Point) animation.getAnimatedValue();//獲得當前的fraction值

float fraction = anim.getAnimatedFraction();//判斷是否是fraction先減小后增大,即是否處于即將向上擺動狀態(tài)//在每次即將向上擺動時切換小球

if (lastSlope && fraction > mLastFraction) {isNext = !isNext;

}

//通過不斷改動左右小球的x,y坐標值實現(xiàn)動畫效果//利用isNext來判斷應(yīng)該是左邊小球動,還是右邊小球動if (isNext) {

//當左邊小球擺動時,右邊小球置于初始位置

mRightPoint.x = mSwingRadius + mGlobeRadius * 2 * (mGlobeNum - 1);mRightPoint.y = mSwingRadius;

mLeftPoint.x = mSwingRadius - point.x;

mLeftPoint.y = mGlobeRadius + point.y;

} else {

//當右邊小球擺動時,左邊小球置于初始位置

mLeftPoint.x = mSwingRadius;

mRightPoint.y = mSwingRadius;

mRightPoint.x = mSwingRadius + (mGlobeNum - 1) * mGlobeRadius * 2 + point.x;mRightPoint.y = mGlobeRadius + point.y;

}

invalidate();

lastSlope = fraction < mLastFraction;

mLastFraction = fraction;

}

});

//設(shè)置永久循環(huán)播放

anim.setRepeatCount(ValueAnimator.INFINITE);//設(shè)置循環(huán)模式為倒序播放

anim.setRepeatMode(ValueAnimator.REVERSE);anim.setDuration(200);

//設(shè)置補間器,控制動畫的變化速率

anim.setInterpolator(new DecelerateInterpolator());anim.start();

}

其中使用ValueAnimator.ofObject方法是為了可以對Point對象進行操作,更為形象具體。還有就是通過ofObject方法使用了自定義的TypeEvaluator對象,由此得到了fraction值,該值是一個從0-1變化的小數(shù)。所以該方法的后兩個參數(shù)startValue(new Point()),endValue(new Point())并沒有實際意義,也可以直接不寫,此處寫上主要是為了便于理解。同樣道理也可以直接使用ValueAnimator.ofFloat(0f, 1f)方法獲取到一個從0-1變化的小數(shù)。

[java] view plain copy

final ValueAnimator anim = ValueAnimator.ofObject(new TypeEvaluator() {@Override

public Object evaluate(float fraction, Object startValue, Object endValue) {//參數(shù)fraction用于表示動畫的完成度,我們根據(jù)它來計算當前的動畫值double angle = Math.toRadians(90 * fraction);int x = (int) ((mSwingRadius - mGlobeRadius) * Math.sin(angle));int y = (int) ((mSwingRadius - mGlobeRadius) * Math.cos(angle));Point point = new Point(x, y);

return point;

}

}, new Point(), new Point());

通過fraction,我們計算得到小球擺動時的角度變化值,0-90度mSwingRadius-mGlobeRadius表示的值是圖中綠色直線的長度,擺動的路線,小球圓心的路線是一個以(mSwingRadius-mGlobeRadius)為半徑的弧線,變化的X值為(mSwingRadius-mGlobeRadius)*sin(angle),變化的y值為(mSwingRadius-mGlobeRadius)*cos(angle)對應(yīng)的小球?qū)嶋H的圓心坐標為(mSwingRadius-x,mGlobeRadius+y)右邊小球運動路線與左邊類似,僅僅是方向不同。右邊小球?qū)嶋H的圓心坐標(mSwingRadius + (mGlobeNum - 1) * mGlobeRadius * 2 + x,mGlobeRadius+y)可見左右兩邊小球的縱坐標是相同的,僅橫坐標不同。

[java] view plain copy

float fraction = anim.getAnimatedFraction();//判斷是否是fraction先減小后增大,即是否處于即將向上擺動狀態(tài)//在每次即將向上擺動時切換小球

if (lastSlope && fraction > mLastFraction) {isNext = !isNext;

}

//記錄上一次fraction是否不斷減小

[java] view plain copy

lastSlope = fraction < mLastFraction;

//記錄上一次的fraction

mLastFraction = fraction;

這兩段代碼用于計算何時切換運動的小球,本動畫設(shè)置了循環(huán)播放,且循環(huán)模式為倒序播放,所以動畫的一個周期即為小球拋起加上小球落下的過程。在該過程中fraction的值先有0變?yōu)?,再由1變?yōu)?。那么何時是動畫新一輪周期的開始呢?就是在小球即將拋起的時候,在這個時候切換運動的小球,即可實現(xiàn)左邊小球落下后右邊小球拋起,右邊小球落下后左邊小球拋起的動畫效果。

那么如何捕捉到這個時間點呢?

小球拋起時fraction值不斷增大,小球落下時fraction值不斷減小。小球即將拋起的時刻,就是fraction從不斷減小轉(zhuǎn)變?yōu)椴粩嘣龃蟮臅r刻。代碼中記錄上一次fraction是否在不斷減小,然后比較這一次fraction是否在不斷增大,若兩個條件均成立則切換運動的小球。

[java] view plain copy

anim.setDuration(200);

//設(shè)置補間器,控制動畫的變化速率

anim.setInterpolator(new DecelerateInterpolator());anim.start();

設(shè)置動畫的持續(xù)時間為200毫秒,讀者可以通過更改該值而達到修改小球擺動速度的目的。

設(shè)置動畫的補間器,由于小球拋起是一個逐漸減速的過程,落下是一個逐漸加速的過程,所以使用DecelerateInterpolator實現(xiàn)減速效果,在倒序播放時為加速效果。

分享名稱:自定義View實現(xiàn)鐘擺效果進度條PendulumView
文章URL:http://www.rwnh.cn/article16/ipcggg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗、移動網(wǎng)站建設(shè)手機網(wǎng)站建設(shè)、搜索引擎優(yōu)化、自適應(yīng)網(wǎng)站、品牌網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)
河源市| 申扎县| 三河市| 克拉玛依市| 广西| 北海市| 申扎县| 洞头县| 丹东市| 盐边县| 威海市| 科技| 玉田县| 泽普县| 都安| 星座| 安泽县| 交口县| 太白县| 自治县| 抚州市| 镇巴县| 正镶白旗| 衡阳县| 安平县| 泾阳县| 通许县| 平阴县| 塔城市| 乌鲁木齐县| 寿光市| 三明市| 方城县| 波密县| 荔波县| 元氏县| 高台县| 西乡县| 昭平县| 临沂市| 加查县|