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

首頁 > 學院 > 開發設計 > 正文

貝塞爾曲線實踐-動畫框架

2019-11-09 16:38:22
字體:
來源:轉載
供稿:網友

前言

動畫有多么重要,相信大家都清楚。它可以讓一個枯燥乏味的靜態界面變成一個充滿動力的動畫世界,提高用戶體驗。反正現在都是用戶體驗至上。android也是前端。苦逼的大前端。想想之前剛畢業的時候搞javaWeb,那個時候感覺前端好low?!,F在不這么認為了。

廢話不多少。直接上效果圖

主要就是中間那部分的動畫效果。

理解Android中動畫實現的本質

在理解Android中動畫實現的本質之前,首先要理解動畫實現的原理,估計這個大家都清楚。

如果要在Android中實現動畫展示,那么就必須要有一個“動畫驅動”每隔1/24秒去調用View的draw()方法,同時改變每一幀中View需要變化的元素,讓這個View不斷的繪制,這樣一來,所有變化就是組合成一個流暢的動畫。

上面就是“Android中動畫實現的本質”,其關鍵就是要有一個“動畫驅動”。回想下我們平時最常用的動畫類Animation或者Animator,其實它們內部實現也是一個“動畫驅動”,驅動View不斷繪制。所以,我們完全可以不用Animation或者Animator去做動畫,只要有一個“驅動”即可,例如Scroller是個不錯的選擇,甚至我們可以寫一個我們自己實現的“動畫驅動”。

常用的“動畫驅動”

View 本身

view本身的onDraw()馬上會觸發下一次繪制。

class MyView extends View { public void onDraw(Canvas canvas) { super.onDraw(canvas); invalidate(); }}

View動畫,屬性動畫(Animation/Animator)

上面的部分就是使用屬性動畫.

Scroller

這個在剛開始的時候滑動主要就靠這個類。郭神的。醫生的,還有愛哥的這些書中都有講到。博客也有很多。它需要結合View的computeScroll()方法實現。

自己實現一個簡易的“動畫驅動”

既然有些需求用原有的方法難以實現或者實現起來不太合適,這個時候我們就需要自己動手了。因此,我也寫了一個簡易的“動畫驅動”

自定義動畫驅動

其實就是自己把 path 的那些 moveTo ,lineTo 這些方法封裝了下。勿噴。

PathPoint類

/** * des:具體的路徑集合 * author: marc * date: 2017/2/8 11:31 * email:aliali_ha@yeah.net */public class PathPoint { //移動指令 public static final int MOVE = 0; //直線運動 public static final int LINE = 1; //貝塞爾曲線 public static final int CURVE = 2; //當前指令 int mOperation; float mX; float mY; float mControl0X, mControl1X;//2個拐點 float mControl0Y, mControl1Y; PRivate PathPoint(int operation, float x, float y) { mOperation = operation; mX = x; mY = y; } private PathPoint(float c0x, float c0y, float c1x, float c1y, float x, float y) { mOperation = CURVE; //終點 mX = x; mY = y; mControl0X = c0x; mControl0Y = c0y; mControl1X = c1x; mControl1Y = c1y; } /** * 移動 * * @param x * @param y * @return */ public static PathPoint moveTo(float x, float y) { return new PathPoint(MOVE, x, y); } /** * 直線 * * @param x * @param y * @return */ public static PathPoint lineTo(float x, float y) { return new PathPoint(LINE, x, y); } /** * 貝塞爾曲線 * * @return */ public static PathPoint curveTo(float c0x, float c0y, float c1x, float c1y, float x, float y) { return new PathPoint(c0x, c0y, c1x, c1y, x, y); }}

AnimatorPath類

/** * des:動畫路徑 * author: marc * date: 2017/2/8 11:26 * email:aliali_ha@yeah.net */public class AnimatorPath { //存儲路徑集合 ArrayList<PathPoint> mPoints = new ArrayList<>(); /** * 移動到哪個位置 * * @param x * @param y */ public void moveTo(float x, float y) { mPoints.add(PathPoint.moveTo(x, y)); } //直線 public void lineTo(float x, float y) { mPoints.add(PathPoint.lineTo(x, y)); } //貝塞爾曲線 public void curveTo(float c0x, float c0y, float c1x, float c1y, float x, float y) { mPoints.add(PathPoint.curveTo(c0x, c0y, c1x, c1y, x, y)); } public Collection<PathPoint> getPoints() { return mPoints; } public void clear() { if (mPoints != null && mPoints.size() > 0) { mPoints.clear(); } }}

PathEvaluator類 主要實現了move line 貝塞爾曲線這些方法

/** * des:自定義估值器 * author: marc * date: 2017/2/8 13:06 * email:aliali_ha@yeah.net */public class PathEvaluator implements TypeEvaluator<PathPoint> { /** * @param t 動畫執行的百分比 ,其實就是時間 * @param startValue * @param endValue * @return */ @Override public PathPoint evaluate(float t, PathPoint startValue, PathPoint endValue) { //進行估值 float x, y; //判斷進行哪種運動 if (endValue.mOperation == PathPoint.CURVE) { //貝塞爾曲線方式 float oneMinusT = 1 - t; //x的實時坐標 x = oneMinusT * oneMinusT * oneMinusT * startValue.mX + 3 * oneMinusT * oneMinusT * t * endValue.mControl0X + 3 * oneMinusT * t * t * endValue.mControl1X + t * t * t * endValue.mX; //y的實時坐標 y = oneMinusT * oneMinusT * oneMinusT * startValue.mY + 3 * oneMinusT * oneMinusT * t * endValue.mControl0Y + 3 * oneMinusT * t * t * endValue.mControl1Y + t * t * t * endValue.mY; } else if (endValue.mOperation == PathPoint.LINE) { //直線運動方式 //當前坐標點(x,y) = 起始點 +t*起始點和終點的距離 x = startValue.mX + t * (endValue.mX - startValue.mX); y = startValue.mY + t * (endValue.mY - startValue.mY); } else { //moveto方式 x = endValue.mX; y = endValue.mY; } //不斷的把控制點 move到 x,y的位置 return PathPoint.moveTo(x, y); }}

Activity類

/** * des: * author: marc * date: 2017/2/8 09:53 * email:aliali_ha@yeah.net */public class BezierActivity extends AppCompatActivity { private static final long ANIMATION_DUARTION = 400;//運動時間 private static final float MINIMUN_X_DISTANCE = 200;//x軸運動距離 private static final float SCALE_FACTOR_EXPAND = 13;//擴大倍數 private static final float SCALE_FACTOR_ORI = 1;//最初的倍數 ImageButton mFab; FrameLayout mFabContainer;//幀布局 LinearLayout mControlsContainer; private int mFabSize;//ImageButton的大小 private boolean mRevealFlag; private boolean mResetFlag; private float startX; private float startY; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_bezier); mFabSize = getResources().getDimensionPixelSize(R.dimen.fab_size); bindViews(); } private void bindViews() { mFab = (ImageButton) findViewById(R.id.fab); mFab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onFabPressed(v); } }); mFabContainer = (FrameLayout) findViewById(R.id.fab_container); mControlsContainer = (LinearLayout) findViewById(R.id.media_controls_container); } public void onFabPressed(View view) { //還沒運動前的X坐標 startX = mFab.getX(); startY = mFab.getY(); //開啟動畫 使用屬性動畫 屬性動畫控制對象身上的任何屬性值 (必須有set方法) AnimatorPath mPath = new AnimatorPath(); mPath.moveTo(0, 0); //貝塞爾曲線 mPath.curveTo(-200, 200, -400, 100, -600, 0); //相對于原來移動的點 //填this是控制當前對象(當前是BezierActivity)的PathPoint p 這個屬性 。 mPath.getPoints()是相當于從某個值到某個值 ObjectAnimator animator = ObjectAnimator.ofObject(this, "fabLocation", new PathEvaluator(), mPath.getPoints().toArray()); animator.setDuration(ANIMATION_DUARTION); //設置加速 animator.start(); //水波紋效果 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //X軸運動距離超過200 開始水波紋擴散 if (Math.abs(startX - mFab.getX()) > MINIMUN_X_DISTANCE) { if (!mRevealFlag) { //高版本 api 運動 //已經設置成透明的了。設置回來 mFab.setImageDrawable(new BitmapDrawable()); mFabContainer.setY(mFabContainer.getY() + mFabSize / 2); mFab.animate() .scaleX(SCALE_FACTOR_EXPAND) .scaleY(SCALE_FACTOR_EXPAND) .setListener(endListener) .setDuration(ANIMATION_DUARTION) .start(); mRevealFlag = true; mResetFlag = false; } } } }); } //反射 不用直接設置屬性 PathPoint fabLocation; public void setFabLocation(PathPoint fabLocation) { //達到不斷的控制view進行移動 mFab.setTranslationX(fabLocation.mX); mFab.setTranslationY(fabLocation.mY); } //開始動畫。效果跟點擊開始按鈕一樣 public void startAnimator(View view) { onFabPressed(view); } /** * fab做正向移動時的listerner */ private AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { super.onAnimationCancel(animation); } @Override public void onAnimationEnd(Animator animation) { mFab.setVisibility(View.INVISIBLE); mFabContainer.setBackgroundColor(getResources().getColor(R.color.brand_accent)); for (int i = 0; i < mFabContainer.getChildCount(); i++) { View v = mControlsContainer.getChildAt(i); ViewPropertyAnimator animator = v.animate().scaleX(1).scaleY(1).setDuration(ANIMATION_DUARTION); //依次顯示 animator.setStartDelay(i * 50).start(); } } }; /** * 重置動畫 1.水波紋 * 2. framelayout布局上移 * 3. fab做位移 * * @param view */ public void reset(View view) { //1.先隱藏 for (int i = 0; i < mFabContainer.getChildCount() + 1; i++) { View v = mControlsContainer.getChildAt(i); ViewPropertyAnimator animator = v.animate().scaleX(0).scaleY(0).setDuration(ANIMATION_DUARTION); //依次顯示 animator.setStartDelay(i * 50); //最后一個動畫的時候監聽動畫結束 ,開始顯示fab。然后進行縮放 if (i == mControlsContainer.getChildCount() - 1) { animator.setListener(reverseListener); } animator.start(); } AnimatorPath mPath1 = new AnimatorPath(); mPath1.moveTo(-600, 0); mPath1.lineTo(0, 0); //相對于原來移動的點 //填this是控制當前對象(當前是BezierActivity)的PathPoint p 這個屬性 。 mPath.getPoints()是相當于從某個值到某個值 ObjectAnimator fabLocation = ObjectAnimator.ofObject(this, "fabLocation", new PathEvaluator(), mPath1.getPoints().toArray()); fabLocation.setDuration(ANIMATION_DUARTION); //設置加速 fabLocation.start(); } /** * 2.結束的時候fab顯示。然后進行縮放 */ private AnimatorListenerAdapter reverseListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { //結束的時候背景顯示成透明 mFabContainer.setBackgroundColor(getResources().getColor(android.R.color.transparent)); //ImageButton設置成顯示狀態 mFab.setVisibility(View.VISIBLE); //2. ImageButton開始縮放 從13-1的縮放 PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 13, 1); PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 13, 1); ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mFab, scaleX, scaleY); objectAnimator.setDuration(ANIMATION_DUARTION); objectAnimator.addListener(listenerAdapter); objectAnimator.start(); } }; private AnimatorListenerAdapter listenerAdapter = new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { //ImageButton縮放動畫結束的時候 if (!mResetFlag) { mFabContainer.setY(mFabContainer.getY() - mFabSize / 2); mFab.setImageBitmap(BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_media_pause)); mResetFlag = true; mRevealFlag = false; } } };}

同時上傳到github

地址: https://github.com/Xiemarc/DesignPatterns


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 陈巴尔虎旗| 聂荣县| 凌源市| 连江县| 黄陵县| 大埔县| 平泉县| 寿阳县| 新营市| 花莲市| 定边县| 凌源市| 师宗县| 色达县| 甘泉县| 英超| 西和县| 湄潭县| 基隆市| 盐津县| 孟津县| 余庆县| 独山县| 敖汉旗| 云梦县| 永济市| 新安县| 繁峙县| 长葛市| 敖汉旗| 都昌县| 格尔木市| 绥中县| 门头沟区| 鹿泉市| 离岛区| 额济纳旗| 武安市| 三台县| 惠安县| 库车县|