一、PropertyValuesHolder
閱讀本文需要上一文Android屬性影片的基礎,這樣才可以明白接下來要講什么,
1.理解和使用
PropertyValuesHolder 是ObjectAnimation類似的一個方法,只是少了一個target,就是要執行的控制元件,看看正常的使用方法:會同時執行全部的Holder
public void doPropertyValuesHolder(){
//定義一個旋轉Holder
PropertyValuesHolder rotationHolder=
PropertyValuesHolder.ofFloat(
"rotation",
60f,40f,100f,-60f,40f,88f,77f);
//定義一個透明Holder
PropertyValuesHolder alphaHolder=
PropertyValuesHolder.ofFloat(
"alpha",
0.01f,0.5f,1.0f,0.8f,0.2f,0.0f);
//加載進ObjectAnimator
ObjectAnimator objectAnimator=ObjectAnimator.ofPropertyValuesHolder(ballImageView,rotationHolder,alphaHolder);
objectAnimator.setDuration(3000);
objectAnimator.start();
}
2.方法和引數
可以看看這個方法的引數:
ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values)
PropertyValuesHolder ofFloat(String propertyName, float... values)
Object target 是要顯示影片的控制元件
PropertyValuesHolder... values 裝載多個PropertyValuesHolder
String propertyName 代表要反射的引數,跟ObjectAnimation的引數是一樣的
float... values 代表是可變長引數
這樣的方法還有以下圖片這些:

其中ofObject()方法 ,也是跟ObjectAnimation的相似,也是要自定義TypeEvaluator,

二、Keyframe
1.理解和使用
看名字,就是理解為關鍵幀的意思,在影片中,在某幀做一些操作,從而實作對比效果比較明顯的效果,
關鍵幀表示是某個物體在哪個時間點應該在哪個位置上,
具體使用:
public void doPropertyValuesHolderKeyFrame(){
//頭keyframe1,從進度0.6開始,在進度60%的時候,數值是0.1f
Keyframe keyframe1=Keyframe.ofFloat(0.6f,0.1f);
//中間keyframe2
Keyframe keyframe2=Keyframe.ofFloat(0.1f,0.8f);
//尾部keyframe3,以50%進度作為結束,這時候的數值為0.2f
Keyframe keyframe3=Keyframe.ofFloat(0.5f,0.2f);
//裝載到Holder中,并設定要反射的方法,這是反射的是setAlpha()方法,控制透明度
PropertyValuesHolder alphaHolder=PropertyValuesHolder.ofKeyframe("alpha",keyframe1,keyframe2,keyframe3);
//把裝載到Holder中裝載到ObjectAnimator或者ValueAnimation
ObjectAnimator objectAnimator=ObjectAnimator.ofPropertyValuesHolder(ballImageView,alphaHolder);
objectAnimator.setDuration(3000);
objectAnimator.start();
}
2.方法和引數
Keyframe ofFloat(float fraction, float value)
float fraction 表示進度
float value 表示在這個進度下的數值
PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)
String propertyName 要反射的set方法
Keyframe... values 傳入Keyframe
Keyframe的方法,也是和其他的類似的,

Keyframe的set方法,設定進度,插值器,數值,
沒有設定插值器的時候,默認是線性插值器
keyframe1.setInterpolator(new LinearInterpolator()); //默認線性插值器

3.幀的操作
直接寫結論:
- 如果去掉0幀,則以第一個關鍵幀為起始位置
- 如果去掉結束幀(進度為1),則以最后一個關鍵幀為結束位置
- 使用keyframe來構建影片,至少需要2幀
三、ViewPropertyAnimator
1.理解和使用
可以通過串行的形式,快速定義影片,省去一些定義,在每次界面繪制的時候,啟動影片,比其他的更節省消耗,
比如:
ballImageView.animate().alpha(0.5f).rotation(360f).scaleX(1.5f).translationX(100f);
2.引數和方法
可以看到這些方法的回傳值,基本都是ViewPropertyAnimator


再參考一張表格:
| 函式 | 含義 |
|---|---|
| alpha(float value) | 設定透明度 |
| scaleY(float value) | 設定 Y軸方向的縮放大小 |
| scaleX(float value) | 設定X軸方向的縮放大小 |
| translationY(float value) | 設定Y軸方向的移動值 |
| translationX(float value) | 設定X軸方向的移動值 |
| rotation(float value) | 設定繞Z軸旋轉度數 |
| rotationX(float value) | 設定繞x軸旋轉度數 |
| rotationY(float value) | 設定繞 Y 軸旋轉度數 |
| x(float value) | 相對于父容器的左上角坐標在 X軸方向的最終位置 |
| y(float value) | 相對于父容器的左上角坐標在Y軸方向的最終位置 |
| alphaBy(float value) | 設定透明度增量 |
| rotationBy(float value) | 設定繞Z軸旋轉增量 |
| rotationXBy(float value) | 設定繞 X 油旋轉增量 |
| rotationYBy(float value) | 設定統Y軸旋轉增量 |
| translationXBy(float value) | 設定X軸方向的移動值增量 |
| translationYBy(float value) | 設定Y軸方向的移動值增量 |
| scaleXBy(float value) | 設定X軸方向的縮放大小增量 |
| scaleYBy(float value) | 設定 Y軸方向的縮放大小增量 |
| xBy(float value) | 相對于父容器的左上角坐標在 X軸方向的位置增量 |
| yBy(float value) | 相對于父容器的左上角坐標在 Y軸方向的位置增量 |
| setlnterpolator(Timelnterpolator interpolator) | 設定插值器 |
| setStartDelay(long startDelay) | 設定開始延時 |
| setDuration(long duration) | 設定影片時長 |
四、animateLayoutChanges
android:animateLayoutChanges="true"
在Layout加入控制元件,或者移除控制元件的時候,添加影片,但是只能使用默認影片,
<LinearLayout
android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
五、LayoutTransition
LayoutTransition可以控制ViewGroup的影片,可以使用自定義的影片,
具體使用:
public void doLayoutTransition(){
LinearLayout linearLayout=new LinearLayout(this);
//1.創建實體
LayoutTransition transition=new LayoutTransition();
//2.創建影片
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(null,"rotation",0f,90f,0f);
//3.影片出現形式進行設定
transition.setAnimator(LayoutTransition.DISAPPEARING,objectAnimator);
//4.將LayoutTransition設定到ViewGroup中
linearLayout.setLayoutTransition(transition);
//5.開源影片庫 NineOldAndroids
}
setAnimator(int transitionType, Animator animator)
這個方法中,transitionType有五個選項

CHANGE_APPEARING 由于容器中要顯示一個新的元素,其他需要變化的元素所應用的影片(問題多,不常用)
_CHANGE_DISAPPEARING_ 當個容器中某個元素要消失時,其他需要變化的元素所應用的影片(問題多,不常用)
_CHANGING_ 容器中正在更改的元素的影片變化
_APPEARING_ 元素在容器中出現時所定義的影片
_DISAPPEARING_ 元素在容器中消失時所定義的影片
六、PathMeasure
PathMeasure類似一個計算器,可以計算出目標path的坐標,長度等
1.初始化
public void doPathMeasure(){
Path path=new Path();
//初始化方法1
PathMeasure pathMeasure1=new PathMeasure();
pathMeasure1.setPath(path,true);
//初始化方法2
PathMeasure pathMeasure2=new PathMeasure(path,false);
}
setPath(Path path, boolean forceClosed)
path 就是代表要計算的目標Path,
forceClosed 是否閉合,true會計算閉合狀態下的Path,false會按照Path原來情況來計算,
2.函式呼叫
自定義一個view
public class PathView extends View {
Path mPath;
Paint mPaint;
PathMeasure mPathMeasure;
public PathView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPath=new Path();
mPaint=new Paint();
mPathMeasure=new PathMeasure();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(250,250); //畫布移動
mPaint.setColor(Color.BLUE); //畫筆顏色
mPaint.setStrokeWidth(5); //畫筆粗細
mPaint.setStyle(Paint.Style.STROKE); //畫筆風格
mPath.moveTo(0,0);
mPath.lineTo(0,100);
mPath.lineTo(100,100);
mPath.lineTo(100,0);
mPathMeasure.setPath(mPath,true);
Log.v("showLog",
"getLength()=="+mPathMeasure.getLength()
+" isClosed()=="+ mPathMeasure.isClosed()); //結果400.0 true
mPathMeasure.setPath(mPath,false);
Log.v("showLog",
"getLength()=="+mPathMeasure.getLength()
+" isClosed()=="+ mPathMeasure.isClosed()); //結果300.0 false
canvas.drawPath(mPath,mPaint); //繪制路徑
}
}
繪制效果:

2.1 PathMeasure.getLength()
PathMeasure.getLength() 函式用于測量路徑的長度
2.2 PathMeasure.isClosed()
PathMeasure.isClosed() 函式用于回傳是否測量閉合狀態
2.3 PathMeasure.nextContour()
mPath.addRect(-50, -50, 50, 50, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
mPath.addRect(-100, -100, 100, 100, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
mPath.addRect(-120, -120, 120, 120, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
mPathMeasure.setPath(mPath, false);
do {
float len = mPathMeasure.getLength();
Log.v("showLog", "len=" + len);
} while (mPathMeasure.nextContour());
效果:

列印結果:
len=400.0
len=800.0
len=960.0
PathMeasure.nextContour()得到的順序與添加的Path的順序相同
PathMeasure.getLength()只是得到當前path的長度,不是全部的長度
2.3 getSegment()
使用getSegment函式需要禁用硬體加速 在構造方法中加入
setLayerType(LAYER_TYPE_SOFTWARE,null);
mPath.addRect(-50, -50, 50, 50, Path.Direction.CW);
mPathMeasure.setPath(mPath,false); //計算的path
mPathMeasure.getSegment(0,150,mDstPath,true); //截取并添加到mDstPath,是添加,不是其他
canvas.drawPath(mPath, mPaint); //繪制原來的path
canvas.translate(200,0); //畫布移動
mPaint.setColor(Color.RED);
canvas.drawPath(mDstPath, mPaint); //繪制添加后的mDstPath
boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
startD path開始截取的點,截取的起始點,是以左上角的點開始的
stopD 截取停止的點
dst 截取后添加到的path
startWithMoveTo 是否保存原狀,true保存原樣,false則會連接初始點和終點,和原來的不一定相同形狀
以上代碼的效果: 截圖的方向,與原來的path的生成方向有關

2.4 動態畫圓的例子
代碼:
public class PathView extends View {
Path mPath, mDstPath;
Paint mPaint;
PathMeasure mPathMeasure;
float mCurAnimValue;
public PathView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setLayerType(LAYER_TYPE_SOFTWARE, null);
mPath = new Path();
mDstPath = new Path();
mPaint = new Paint();
mPathMeasure = new PathMeasure();
mPaint.setColor(Color.BLUE); //畫筆顏色
mPaint.setStrokeWidth(5); //畫筆粗細
mPaint.setStyle(Paint.Style.STROKE); //畫筆風格
mPath.addCircle(100, 100, 50, Path.Direction.CW); //一個完整的圓
mPathMeasure.setPath(mPath, true); //要計算的path
ValueAnimator animator = ValueAnimator.ofFloat(0, 1); //進度 0~1
animator.setRepeatCount(ValueAnimator.INFINITE); //無限回圈
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurAnimValue = https://www.cnblogs.com/lanjiabin/p/(Float) animation.getAnimatedValue(); //得到當前的進度
invalidate();//重繪,重新執行onDraw()方法
}
});
animator.setDuration(5000);
animator.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(100, 100); //畫布移動
float stop=mPathMeasure.getLength()*mCurAnimValue; //一個進度確定一個截取點
mDstPath.reset();
mPathMeasure.getSegment(0,stop,mDstPath,true); //一點點添加
canvas.drawPath(mDstPath,mPaint); //每次有進度更新,就繪制一小段截取
}
}
效果:

2.5 getPosTan()
先看看函式的定義:
boolean getPosTan(float distance, float pos[], float tan[])
float distance 距離path的其實長度
float pos[] 該點的坐標值,x和y pos[0]=x,pos[1]=y
float tan[] 該點的正切值,x和y pos[0]=x,pos[1]=y tan<a=y/x
2.6 箭頭畫圓的例子
代碼:
public class PathView extends View {
Path mPath, mDstPath;
Paint mPaint;
PathMeasure mPathMeasure;
float mCurAnimValue;
Bitmap mArrowBmp;
float[] mPos;
float[] mTan;
int mCenterX,mCenterY;
float mRadius;
public PathView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setLayerType(LAYER_TYPE_SOFTWARE, null);
mPath = new Path();
mDstPath = new Path();
mPaint = new Paint();
mPathMeasure = new PathMeasure();
mPos=new float[2];
mTan=new float[2];
//加載箭頭圖片
mArrowBmp= BitmapFactory.decodeResource(getResources(), R.drawable.arrow);
mPaint.setColor(Color.BLUE); //畫筆顏色
mPaint.setStrokeWidth(5); //畫筆粗細
mPaint.setStyle(Paint.Style.STROKE); //畫筆風格
mPath.addCircle(540, 972, 486, Path.Direction.CW); //一個完整的圓
mPathMeasure.setPath(mPath, true); //要計算的path
ValueAnimator animator = ValueAnimator.ofFloat(0, 1); //進度 0~1
animator.setRepeatCount(ValueAnimator.INFINITE); //無限回圈
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurAnimValue = https://www.cnblogs.com/lanjiabin/p/(Float) animation.getAnimatedValue(); //得到當前的進度
invalidate();//重繪,重新執行onDraw()方法
}
});
animator.setDuration(5000);
animator.start();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
/*
* 得到h,w的最小的那個值;
* >> 1 移位 跟 /2 相同;
* 乘以0.9f,表示占布局的90%
* */
mRadius = (Math.min(h, w) >> 1) * 0.9f;
// 中心坐標
mCenterX = w / 2;
mCenterY = h / 2;
Log.v("showLog",mCenterX+" "+mCenterY+" "+mRadius);
postInvalidate();
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float stop=mPathMeasure.getLength()*mCurAnimValue; //一個進度確定一個截取點
mDstPath.reset();
mPathMeasure.getSegment(0,stop,mDstPath,true); //一點點添加
canvas.drawPath(mDstPath,mPaint); //每次有進度更新,就繪制一小段截取
mPathMeasure.getPosTan(stop,mPos,mTan); //獲得每點的正切值和坐標
/**
* Math.atan2(mTan[1],mTan[0])獲得tan的弧度值
* *180.0/Math.PI將轉化為角度值
* */
float degrees=(float)(Math.atan2(mTan[1],mTan[0])*180.0/Math.PI);
Matrix matrix=new Matrix();
/**
* 將圖片圍繞中心點旋轉指定角度
* postRotate(float degrees, float px, float py)
* degrees是角度 (px,py)是圖片中心點
* */
matrix.postRotate(degrees,mArrowBmp.getWidth()/2,mArrowBmp.getHeight()/2);
/**
* 將圖片從默認的(0,0)點移動到路徑的最前端
* */
matrix.postTranslate(mPos[0]-mArrowBmp.getWidth()/2,mPos[1]-mArrowBmp.getHeight()/2);
//繪制圖片
canvas.drawBitmap(mArrowBmp,matrix,mPaint);
}
}
效果:

2.7 getMatrix()
引數型別:
boolean getMatrix(float distance, Matrix matrix, int flags)
使用方法:
//計算方位角
Matrix matrix = new Matrix();
//獲取位置資訊
mPathMeasure.getMatrix(stop,matrix,PathMeasure.POSITION_MATRIX_FLAG);
//獲取切邊資訊
mPathMeasure.getMatrix(stop,matrix,PathMeasure.TANGENT_MATRIX_FLAG);
2.8 支付成功例子
public class TickView extends View {
Path mPath, mDstPath;
Paint mPaint;
PathMeasure mPathMeasure;
float mCurAnimValue;
int mCenterX, mCenterY;
float mRadius;
public TickView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setLayerType(LAYER_TYPE_SOFTWARE, null);
mPath = new Path();
mDstPath = new Path();
mPaint = new Paint();
mPathMeasure = new PathMeasure();
mPaint.setColor(Color.BLUE); //畫筆顏色
mPaint.setStrokeWidth(5); //畫筆粗細
mPaint.setStyle(Paint.Style.STROKE); //畫筆風格
mCenterX = 540;
mCenterY = 972;
mRadius = 486 / 2;
/**
* 圓
* */
mPath.addCircle(mCenterX, mCenterY, mRadius, Path.Direction.CW);
/**
* 對勾
* */
mPath.moveTo(mCenterX - mRadius / 2, mCenterY);
mPath.lineTo(mCenterX, mCenterY + mRadius / 2);
mPath.lineTo(mCenterX + mRadius / 2, mCenterY - mRadius / 3);
mPathMeasure.setPath(mPath, false); //要計算的path
ValueAnimator animator = ValueAnimator.ofFloat(0, 2); //進度 0~1 是圓,1~2是對勾
animator.setRepeatCount(ValueAnimator.RESTART);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurAnimValue = https://www.cnblogs.com/lanjiabin/p/(Float) animation.getAnimatedValue(); //得到當前的進度
invalidate();//重繪,重新執行onDraw()方法
}
});
animator.setDuration(5000);
animator.start();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
/*
* 得到h,w的最小的那個值;
* >> 1 移位 跟 /2 相同;
* 乘以0.9f,表示占布局的90%
* */
mRadius = (Math.min(h, w) >> 1) * 0.9f;
// 中心坐標
mCenterX = w / 2;
mCenterY = h / 2;
Log.v("showLog", mCenterX + " " + mCenterY + " " + mRadius);
postInvalidate();
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mCurAnimValue < 1) {
float stop = mPathMeasure.getLength() * mCurAnimValue;
mPathMeasure.getSegment(0, stop, mDstPath, true);
} else if (mCurAnimValue == 1) {
mPathMeasure.getSegment(0, mPathMeasure.getLength(), mDstPath, true);
mPathMeasure.nextContour();
} else {
float stop = mPathMeasure.getLength() * (mCurAnimValue - 1);
mPathMeasure.getSegment(0, stop, mDstPath, true);
}
canvas.drawPath(mDstPath, mPaint);
}
}
效果:

編程中我們會遇到多少挫折?表放棄,沙漠盡頭必是綠洲,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/5580.html
標籤:Android
上一篇:CtsSecurityTestCases#ListeningPortsTest定位tcp埠與pid
下一篇:iOS核心影片高級技巧 - 7
