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

Android自定義view實(shí)現(xiàn)阻尼效果的加載動(dòng)畫

 更新時(shí)間:2016年11月08日 14:44:05   作者:安兒IT  
這篇文章主要介紹了Android自定義view實(shí)現(xiàn)阻尼效果的加載動(dòng)畫的相關(guān)資料,非常不錯(cuò),具有一定的參考借鑒加載,需要的朋友可以參考下

效果:

這里寫圖片描述

需要知識(shí):

1. 二次貝塞爾曲線

2. 動(dòng)畫知識(shí)

3. 基礎(chǔ)自定義view知識(shí)

先來解釋下什么叫阻尼運(yùn)動(dòng)

阻尼振動(dòng)是指,由于振動(dòng)系統(tǒng)受到摩擦和介質(zhì)阻力或其他能耗而使振幅隨時(shí)間逐漸衰減的振動(dòng),又稱減幅振動(dòng)、衰減振動(dòng)。[1] 不論是彈簧振子還是單擺由于外界的摩擦和介質(zhì)阻力總是存在,在振動(dòng)過程中要不斷克服外界阻力做功,消耗能量,振幅就會(huì)逐漸減小,經(jīng)過一段時(shí)間,振動(dòng)就會(huì)完全停下來。這種振幅隨時(shí)間減小的振動(dòng)稱為阻尼振動(dòng).因?yàn)檎穹c振動(dòng)的能量有關(guān),阻尼振動(dòng)也就是能量不斷減少的振動(dòng).阻尼振動(dòng)是非簡(jiǎn)諧運(yùn)動(dòng).阻尼振動(dòng)系統(tǒng)屬于耗散系統(tǒng)。這里的阻尼是指任何振動(dòng)系統(tǒng)在振動(dòng)中,由于外界作用或系統(tǒng)本身固有的原因引起的振動(dòng)幅度逐漸下降的特性,以及此一特性的量化表征。

這里寫圖片描述

本例中文字部分凹陷就是這種效果,當(dāng)然這篇文章知識(shí)帶你簡(jiǎn)單的使用.

跳動(dòng)的水果效果實(shí)現(xiàn)

剖析:從上面的效果圖中很面就是從頂端向下掉落然后再向上 期間旋轉(zhuǎn)即可.

那么我們首先自定義一個(gè)view繼承FrameLayout

public class My extends FrameLayout {
public My(Context context) {
super(context);
}
public My(Context context, AttributeSet attrs) {
super(context, attrs);
}
}

需要素材如下三張圖片:

這里寫圖片描述
這里寫圖片描述
這里寫圖片描述

也許有人會(huì)問我看到你效果圖到頂部或者底部就變成向上或者向下了.你三張夠嗎?

答:到頂部或者底部旋轉(zhuǎn)180度即可

我們現(xiàn)在自定義中定義幾個(gè)變量

//用于記錄當(dāng)前圖片使用數(shù)組中的哪張
int indexImgFlag = 0;
//下沉圖片 前面三個(gè)圖片的id
int allImgDown [] = {R.mipmap.p2,R.mipmap.p4,R.mipmap.p6,R.mipmap.p8};
//動(dòng)畫效果一次下沉或上彈的時(shí)間 animationDuration*2=一次完整動(dòng)畫時(shí)間
int animationDuration = 1000;
//彈起來的圖片
ImageView iv;
//圖片下沉高度(即從最高點(diǎn)到最低點(diǎn)的距離)
int downHeight = 2;
//掉下去的動(dòng)畫
private Animation translateDown;
//彈起動(dòng)畫
private Animation translateUp;
//旋轉(zhuǎn)動(dòng)畫
private ObjectAnimator rotation;

我們?cè)賮砜纯闯跏蓟瘎?dòng)畫的方法(此方法使用了遞歸思想,實(shí)現(xiàn)無限播放動(dòng)畫,大家可以看看哪里不理解)

//初始化彈跳動(dòng)畫
public void MyAnmation(){
//下沉效果動(dòng)畫
translateDown = new TranslateAnimation(
Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,0,
Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,downHeight);
translateDown.setDuration(animationDuration);
//設(shè)置一個(gè)插值器 動(dòng)畫將會(huì)播放越來越快 模擬重力
translateDown.setInterpolator(new AccelerateInterpolator());
//上彈動(dòng)畫
translateUp = new TranslateAnimation(
Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,0,
Animation.RELATIVE_TO_SELF,downHeight,Animation.RELATIVE_TO_SELF,0
);
translateUp.setDuration(animationDuration);
////設(shè)置一個(gè)插值器 動(dòng)畫將會(huì)播放越來越慢 模擬反重力
translateUp.setInterpolator(new DecelerateInterpolator());
//當(dāng)下沉動(dòng)畫完成時(shí)播放啟動(dòng)上彈
translateDown.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
iv.setImageResource(allImgDown[indexImgFlag]);
rotation = ObjectAnimator.ofFloat(iv, "rotation", 180f, 360f);
rotation.setDuration(1000);
rotation.start();
}
@Override
public void onAnimationEnd(Animation animation) {
iv.startAnimation(translateUp);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
//當(dāng)上移動(dòng)畫完成時(shí) 播放下移動(dòng)畫
translateUp.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
indexImgFlag = 1+indexImgFlag>=allImgDown.length?0:1+indexImgFlag;
iv.setImageResource(allImgDown[indexImgFlag]);
rotation = ObjectAnimator.ofFloat(iv, "rotation", 0.0f, 180f);
rotation.setDuration(1000);
rotation.start();
}
@Override
public void onAnimationEnd(Animation animation) {
//遞歸
iv.startAnimation(translateDown);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}

以上代碼知識(shí):

插值器:會(huì)讓一個(gè)動(dòng)畫在播放時(shí)在某一時(shí)間段加快動(dòng)畫或者減慢

//設(shè)置一個(gè)插值器 動(dòng)畫將會(huì)播放越來越快 模擬重力

1.translateDown.setInterpolator(new AccelerateInterpolator());

這個(gè)插值器 速率表示圖:

這里寫圖片描述

可以從斜率看到使用此插值器 動(dòng)畫將越來越快.意義在于模仿下落時(shí)重力的影響

////設(shè)置一個(gè)插值器 動(dòng)畫將會(huì)播放越來越慢 模擬反重力

2. translateUp.setInterpolator(new DecelerateInterpolator());

速率圖:

這里寫圖片描述

最后我們初始化下圖片控件到我們的自定義view

private void init() {
//初始化彈跳圖片 控件
iv = new ImageView(getContext());
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
iv.setLayoutParams(params);
iv.setImageResource(allImgDown[0]);
this.addView(iv);
iv.measure(0,0);
iv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (!flagMeure)
{
flagMeure =true;
//由于畫文字是由基準(zhǔn)線開始
path.moveTo(iv.getX()-textWidth/2+iv.getWidth()/2, textHeight+iv.getHeight()+downHeight*iv.getHeight());
//計(jì)算最大彈力
maxElasticFactor = (float) (textHeight / elastic);
//初始化貝塞爾曲線
path.rQuadTo(textWidth / 2, 0, textWidth, 0);
//初始化上彈和下沉動(dòng)畫
MyAnmation();
iv.startAnimation(translateDown);
}
}
});
}

上面的知識(shí):

1. iv.measure(0,0);主動(dòng)通知系統(tǒng)去測(cè)量此控件 不然iv.getwidth = 0;
//下面這個(gè)是同理 等iv測(cè)量完時(shí)回調(diào) 不然iv.getwidth = 0;
2. iv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){ 
… 
}

原因:TextView tv = new TextView() 或者 LayoutInflat 填充布局都是

異步所以你在new出來或者填充時(shí)直接獲取自然返回0

到現(xiàn)在為止你只需要在自定義view 的onSizeChanged回調(diào)方法中調(diào)用init()即可看到動(dòng)畫的彈動(dòng)

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
init();
}

此方法會(huì)在onmesure方法執(zhí)行完成后回調(diào) 這樣你就可以在此方法獲得自定義view的寬高了

效果圖:

這里寫圖片描述

畫文字成u形

首先你得知道如下知識(shí)

貝塞爾曲線:具體學(xué)習(xí)

這里我們用到2此貝塞爾曲線

我們看看大概是什么叫2次貝塞爾曲線

這里寫圖片描述 

我們看看 三個(gè)點(diǎn) p0 p1 p2 我們 把p0 稱為 開始點(diǎn) p1 為控制點(diǎn) p2 結(jié)束點(diǎn),那么可以用貝塞爾公式畫出如圖曲線

這里大家沒必要深究怎么畫出來. 也不需要你懂 這個(gè)要數(shù)學(xué)基礎(chǔ)的

那么我們?cè)诎沧恐性趺串嬆?#63;

Path path = new Path();
//p0的x y坐標(biāo)
path.moveTo(p0.x,y);
path.rQuadTo(p1.x,p1.y,p2.x,p2.y);

這就是API調(diào)用方法是不是很簡(jiǎn)單?那么你又會(huì)問那么怎么畫出來呢?
很簡(jiǎn)單在 dispatchDraw方法 或者onDraw中 調(diào)用

@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
canvas.drawPath(path,paint);
}

那么你畫出來的效果應(yīng)該和在Ps用鋼筆畫出來的差不多 ps中鋼筆工具就是二次貝塞爾曲線

這里寫圖片描述 

(借用下圖片)

如果你的三個(gè)點(diǎn)的位置如剛開的圖片 p0 p1 p2 (p1在p0右上方并且 p1在p2左上方)一樣那么在屏幕中的顯示效果如下

這里寫圖片描述 

這里隨擴(kuò)張下dispatchDraw和ondraw的區(qū)別

如果你的自定義view 是繼承view 那么 會(huì)先調(diào)用 ondraw->>dispatchDraw

如果你的自定義view 是繼承viewgroup那么會(huì)跳過ondraw方法直接調(diào)用dispathchDraw這里特別注意!!我們這個(gè)案例中繼承的是FrameLayout, 而frameLayout又是繼承自viewgroup所以….

那么我們回到主題 如何畫一個(gè)U形文字?簡(jiǎn)單就是說按照我們畫出來的曲線在上面寫字 如: 文字是”CSDN開源中國(guó)” 如何讓這幾個(gè)字貼著我們的曲線寫出來?

這里介紹一個(gè)API

canvas.drawTextOnPath()

這里寫圖片描述 

第一個(gè)參數(shù):文字 類型為字符串

第二個(gè)參數(shù):路徑 也就是我們前面的二次貝塞爾曲線

第三個(gè)參數(shù):沿著路徑文字開始的位置 說白了偏移量

第四個(gè)參數(shù):貼著路徑的高度的偏移量

hOffset:

The distance along the path to add to the text's starting position

vOffset:

The distance above(-) or below(+) the path to position the text
//ok我們看看他可以畫出什么樣子的文字

這里寫圖片描述

這種看大家對(duì)貝塞爾曲線的理解,你理解的越深那么你可以畫出的圖像越多,當(dāng)然不一定要用貝塞爾曲線

確定貝塞爾曲線的起點(diǎn)

我們?cè)诨剡^頭來看看我們的效果圖

這里寫圖片描述

我們可以看到文字應(yīng)該是在iv(彈跳的圖片中央位置且正好在 iv彈到底部的位置)

這里我們先補(bǔ)充知識(shí)在考慮計(jì)算

我們來學(xué)習(xí)一下文字的測(cè)量我們來看幅圖

這里寫圖片描述

我們調(diào)用畫文字的API時(shí)

canvas.drawTextOnPath或者canvas.drawText 是從基準(zhǔn)線開始畫的也就是說途中的baseline開始畫.

如:

canvas.drawText(“FMY”,0,0,paint);

那么你將看不到文字 只能在屏幕看到文字底部如下圖:

這里寫圖片描述

另一個(gè)同理API drawTextOnPath 也是

再看看幾個(gè)簡(jiǎn)單的API

1 . paint.measureText(“FMY”);返回在此畫筆paint下寫FMY文字的寬度

下面的API會(huì)把文字的距離左邊left 上邊top 右邊right 底部的bottom的值寫入此矩形 那么

rect.right-rect.left=文字寬度 
rect.bottom-rect.top=文字高度
//矩形
Rect rect = new Rect();
//將文字畫入矩形目的是為了測(cè)量高度
paint.getTextBounds(printText, 0, printText.length(), rect);

那么請(qǐng)看:

private void init() {
//初始化彈跳圖片 控件
iv = new ImageView(getContext());
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
iv.setLayoutParams(params);
iv.setImageResource(allImgDown[0]);
this.addView(iv);
//畫筆的初始化
paint = new Paint();
paint.setStrokeWidth(1);
paint.setColor(Color.CYAN);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(50);
paint.setAntiAlias(true);
//矩形
Rect rect = new Rect();
//將文字畫入矩形目的是為了測(cè)量高度
paint.getTextBounds(printText, 0, printText.length(), rect);
//文本寬度
textWidth = paint.measureText(printText);
//獲得文字高度
textHeight = rect.bottom - rect.top;
//初始化路徑
path = new Path();
iv.setX(getWidth()/2);
iv.measure(0,0);
iv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (!flagMeure)
{
flagMeure =true;
//由于畫文字是由基準(zhǔn)線開始
path.moveTo(iv.getX()-textWidth/2+iv.getWidth()/2, textHeight+iv.getHeight()+downHeight*iv.getHeight());
//計(jì)算最大彈力
maxElasticFactor = (float) (textHeight / elastic);
//初始化貝塞爾曲線
path.rQuadTo(textWidth / 2, 0, textWidth, 0);
//初始化上彈和下沉動(dòng)畫
MyAnmation();
iv.startAnimation(translateDown);
}
}
});
}

我們現(xiàn)在寫一個(gè)類當(dāng)iv圖片(彈跳圖)碰到文字頂部時(shí)設(shè)置一個(gè)監(jiān)聽器 時(shí)間正好是彈圖向上到頂部的時(shí)間 期間不斷讓文字凹陷在恢復(fù)正常

//用于播放文字下沉和上浮動(dòng)畫傳入的數(shù)值必須是 圖片下沉和上升的一次時(shí)間
public void initAnimation(int duration){
//這里為什maxElasticFactor/4 好看...另一個(gè)同理 這個(gè)數(shù)值大家自行調(diào)整
ValueAnimator animator = ValueAnimator.ofFloat(maxElasticFactor/4, (float) (maxElasticFactor / 1.5),0);
animator.setDuration(duration/2);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
calc();//重新畫路徑
nowElasticFactor= (float) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}

再來一個(gè)重新繪畫路徑計(jì)算的方法

public void calc(){
//重置路徑
path.reset();
//由于畫文字是由基準(zhǔn)線開始
path.moveTo(iv.getX()-textWidth/2+iv.getWidth()/2, textHeight+iv.getHeight()+downHeight*iv.getHeight());
//畫二次貝塞爾曲線
path.rQuadTo(textWidth / 2, nowElasticFactor, textWidth, 0);
}

好了到這里我們看看完整源碼吧:

package com.example.administrator.myapplication;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;
public class My extends FrameLayout {
//畫筆
private Paint paint;
//路徑
private Path path;
//要輸入的文本
private String printText = "正在加載";
//文本寬
private float textWidth;
//文本高
private float textHeight;
//測(cè)量文字寬高的時(shí)候使用的矩形
private Rect rect;
//最大彈力系數(shù)
private float elastic = 1.5f;
//最大彈力
private float maxElasticFactor;
//當(dāng)前彈力
private float nowElasticFactor;
//用于記錄當(dāng)前圖片使用數(shù)組中的哪張
int indexImgFlag = 0;
//下沉圖片
int allImgDown [] = {R.mipmap.p2,R.mipmap.p4,R.mipmap.p6,R.mipmap.p8};
//動(dòng)畫效果一次下沉或上彈的時(shí)間 animationDuration*2=一次完整動(dòng)畫時(shí)間
int animationDuration = 1000;
//彈起來的圖片
ImageView iv;
//圖片下沉高度(即從最高點(diǎn)到最低點(diǎn)的距離)
int downHeight = 2;
private Animation translateDown;
private Animation translateUp;
private ObjectAnimator rotation;
public My(Context context) {
super(context);
}
public My(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
canvas.drawTextOnPath(printText, path, 0, 0, paint);
}
//用于播放文字下沉和上浮動(dòng)畫傳入的數(shù)值必須是 圖片下沉和上升的一次時(shí)間
public void initAnimation(int duration){
//這里為什maxElasticFactor/4為什么
ValueAnimator animator = ValueAnimator.ofFloat(maxElasticFactor/4, (float) (maxElasticFactor / 1.5),0);
animator.setDuration(duration/2);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
calc();
nowElasticFactor= (float) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
public void calc(){
//重置路徑
path.reset();
//由于畫文字是由基準(zhǔn)線開始
path.moveTo(iv.getX()-textWidth/2+iv.getWidth()/2, textHeight+iv.getHeight()+downHeight*iv.getHeight());
//畫二次貝塞爾曲線
path.rQuadTo(textWidth / 2, nowElasticFactor, textWidth, 0);
}
//初始化彈跳動(dòng)畫
public void MyAnmation(){
//下沉效果動(dòng)畫
translateDown = new TranslateAnimation(
Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,0,
Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,downHeight);
translateDown.setDuration(animationDuration);
//設(shè)置一個(gè)插值器 動(dòng)畫將會(huì)播放越來越快 模擬重力
translateDown.setInterpolator(new AccelerateInterpolator());
//上彈動(dòng)畫
translateUp = new TranslateAnimation(
Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,0,
Animation.RELATIVE_TO_SELF,downHeight,Animation.RELATIVE_TO_SELF,0
);
translateUp.setDuration(animationDuration);
////設(shè)置一個(gè)插值器 動(dòng)畫將會(huì)播放越來越慢 模擬反重力
translateUp.setInterpolator(new DecelerateInterpolator());
//當(dāng)下沉動(dòng)畫完成時(shí)播放啟動(dòng)上彈
translateDown.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
iv.setImageResource(allImgDown[indexImgFlag]);
rotation = ObjectAnimator.ofFloat(iv, "rotation", 180f, 360f);
rotation.setDuration(1000);
rotation.start();
}
@Override
public void onAnimationEnd(Animation animation) {
iv.startAnimation(translateUp);
initAnimation(animationDuration);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
//當(dāng)上移動(dòng)畫完成時(shí) 播放下移動(dòng)畫
translateUp.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
indexImgFlag = 1+indexImgFlag>=allImgDown.length?0:1+indexImgFlag;
iv.setImageResource(allImgDown[indexImgFlag]);
rotation = ObjectAnimator.ofFloat(iv, "rotation", 0.0f, 180f);
rotation.setDuration(1000);
rotation.start();
}
@Override
public void onAnimationEnd(Animation animation) {
//遞歸
iv.startAnimation(translateDown);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
boolean flagMeure;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
init();
}
private void init() {
//初始化彈跳圖片 控件
iv = new ImageView(getContext());
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
iv.setLayoutParams(params);
iv.setImageResource(allImgDown[0]);
this.addView(iv);
//畫筆的初始化
paint = new Paint();
paint.setStrokeWidth(1);
paint.setColor(Color.CYAN);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(50);
paint.setAntiAlias(true);
//矩形
rect = new Rect();
//將文字畫入矩形目的是為了測(cè)量高度
paint.getTextBounds(printText, 0, printText.length(), rect);
//文本寬度
textWidth = paint.measureText(printText);
//獲得文字高度
textHeight = rect.bottom - rect.top;
//初始化路徑
path = new Path();
iv.setX(getWidth()/2);
iv.measure(0,0);
iv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (!flagMeure)
{
flagMeure =true;
//由于畫文字是由基準(zhǔn)線開始
path.moveTo(iv.getX()-textWidth/2+iv.getWidth()/2, textHeight+iv.getHeight()+downHeight*iv.getHeight());
//計(jì)算最大彈力
maxElasticFactor = (float) (textHeight / elastic);
//初始化貝塞爾曲線
path.rQuadTo(textWidth / 2, 0, textWidth, 0);
//初始化上彈和下沉動(dòng)畫
MyAnmation();
iv.startAnimation(translateDown);
}
}
});
}
}

小人奉上源碼一封供大家 參考github源碼下載地址

以上所述是小編給大家介紹的Android自定義view實(shí)現(xiàn)阻尼效果的加載動(dòng)畫,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

最新評(píng)論