編輯:關於Android編程
前段時間在寫直播的時候,需要觀眾在看直播的時候點贊的效果,在此參照了騰訊大神寫的點贊(飄心動畫效果)。下面是效果圖:
在attrs.xml 中增加自定義的屬性
2.1 dimens.xml
50.0dp 50.0dp 25.0dp 400.0dp 350.0dp 30.0dp 27.3dp 32.5dp
2.2 integers.xml
6 3000
3.1 AbstractPathAnimator.java
public abstract class AbstractPathAnimator { private final Random mRandom; protected final Config mConfig; public AbstractPathAnimator(Config config) { mConfig = config; mRandom = new Random(); } public float randomRotation() { return mRandom.nextFloat() * 28.6F - 14.3F; } public Path createPath(AtomicInteger counter, View view, int factor) { Random r = mRandom; int x = r.nextInt(mConfig.xRand); int x2 = r.nextInt(mConfig.xRand); int y = view.getHeight() - mConfig.initY; int y2 = counter.intValue() * 15 + mConfig.animLength * factor + r.nextInt(mConfig.animLengthRand); factor = y2 / mConfig.bezierFactor; x = mConfig.xPointFactor + x; x2 = mConfig.xPointFactor + x2; int y3 = y - y2; y2 = y - y2 / 2; Path p = new Path(); p.moveTo(mConfig.initX, y); p.cubicTo(mConfig.initX, y - factor, x, y2 + factor, x, y2); p.moveTo(x, y2); p.cubicTo(x, y2 - factor, x2, y3 + factor, x2, y3); return p; } public abstract void start(View child, ViewGroup parent); public static class Config { public int initX; public int initY; public int xRand; public int animLengthRand; public int bezierFactor; public int xPointFactor; public int animLength; public int heartWidth; public int heartHeight; public int animDuration; static public Config fromTypeArray(TypedArray typedArray, float x, float y, int pointx, int heartWidth, int heartHeight) { Config config = new Config(); Resources res = typedArray.getResources(); config.initX = (int) typedArray.getDimension(R.styleable.HeartLayout_initX, x); config.initY = (int) typedArray.getDimension(R.styleable.HeartLayout_initY, y); config.xRand = (int) typedArray.getDimension(R.styleable.HeartLayout_xRand, res.getDimensionPixelOffset(R.dimen.heart_anim_bezier_x_rand)); config.animLength = (int) typedArray.getDimension(R.styleable.HeartLayout_animLength, res.getDimensionPixelOffset(R.dimen.heart_anim_length));//動畫長度 config.animLengthRand = (int) typedArray.getDimension(R.styleable.HeartLayout_animLengthRand, res.getDimensionPixelOffset(R.dimen.heart_anim_length_rand)); config.bezierFactor = typedArray.getInteger(R.styleable.HeartLayout_bezierFactor, res.getInteger(R.integer.heart_anim_bezier_factor)); config.xPointFactor = pointx; // config.heartWidth = (int) typedArray.getDimension(R.styleable.HeartLayout_heart_width, // res.getDimensionPixelOffset(R.dimen.heart_size_width));//動畫圖片寬度 // config.heartHeight = (int) typedArray.getDimension(R.styleable.HeartLayout_heart_height, // res.getDimensionPixelOffset(R.dimen.heart_size_height));//動畫圖片高度 config.heartWidth = heartWidth; config.heartHeight = heartHeight; config.animDuration = typedArray.getInteger(R.styleable.HeartLayout_anim_duration, res.getInteger(R.integer.anim_duration));//持續期 return config; } } }
3.2 PathAnimator.java
/** * 飄心路徑動畫器 */ public class PathAnimator extends AbstractPathAnimator { private final AtomicInteger mCounter = new AtomicInteger(0); private Handler mHandler; public PathAnimator(Config config) { super(config); mHandler = new Handler(Looper.getMainLooper()); } @Override public void start(final View child, final ViewGroup parent) { parent.addView(child, new ViewGroup.LayoutParams(mConfig.heartWidth, mConfig.heartHeight)); FloatAnimation anim = new FloatAnimation(createPath(mCounter, parent, 2), randomRotation(), parent, child); anim.setDuration(mConfig.animDuration); anim.setInterpolator(new LinearInterpolator()); anim.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationEnd(Animation animation) { mHandler.post(new Runnable() { @Override public void run() { parent.removeView(child); } }); mCounter.decrementAndGet(); } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationStart(Animation animation) { mCounter.incrementAndGet(); } }); anim.setInterpolator(new LinearInterpolator()); child.startAnimation(anim); } static class FloatAnimation extends Animation { private PathMeasure mPm; private View mView; private float mDistance; private float mRotation; public FloatAnimation(Path path, float rotation, View parent, View child) { mPm = new PathMeasure(path, false); mDistance = mPm.getLength(); mView = child; mRotation = rotation; parent.setLayerType(View.LAYER_TYPE_HARDWARE, null); } @Override protected void applyTransformation(float factor, Transformation transformation) { Matrix matrix = transformation.getMatrix(); mPm.getMatrix(mDistance * factor, matrix, PathMeasure.POSITION_MATRIX_FLAG); mView.setRotation(mRotation * factor); float scale = 1F; if (3000.0F * factor < 200.0F) { scale = scale(factor, 0.0D, 0.06666667014360428D, 0.20000000298023224D, 1.100000023841858D); } else if (3000.0F * factor < 300.0F) { scale = scale(factor, 0.06666667014360428D, 0.10000000149011612D, 1.100000023841858D, 1.0D); } mView.setScaleX(scale); mView.setScaleY(scale); transformation.setAlpha(1.0F - factor); } } private static float scale(double a, double b, double c, double d, double e) { return (float) ((a - b) / (c - b) * (e - d) + d); } }
4.1 HeartView.java
/** * 飄心動畫的界面 */ public class HeartView extends ImageView{ //繪制的時候抗鋸齒 private static final Paint sPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); private static final Canvas sCanvas = new Canvas(); private int mHeartResId = R.drawable.heart0; private int mHeartBorderResId = R.drawable.heart1; private static Bitmap sHeart; private static Bitmap sHeartBorder; public HeartView(Context context) { super(context); } public HeartView(Context context, AttributeSet attrs) { super(context, attrs); } public HeartView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void setDrawable(int resourceId){ Bitmap heart = BitmapFactory.decodeResource(getResources(), resourceId); // Sets a drawable as the content of this ImageView. setImageDrawable(new BitmapDrawable(getResources(),heart)); } public void setColor(int color) { Bitmap heart = createHeart(color); setImageDrawable(new BitmapDrawable(getResources(), heart)); } public void setColorAndDrawables(int color, int heartResId, int heartBorderResId) { if (heartResId != mHeartResId) { sHeart = null; } if (heartBorderResId != mHeartBorderResId) { sHeartBorder = null; } mHeartResId = heartResId; mHeartBorderResId = heartBorderResId; setColor(color); } public Bitmap createHeart(int color) { if (sHeart == null) { sHeart = BitmapFactory.decodeResource(getResources(), mHeartResId); } if (sHeartBorder == null) { sHeartBorder = BitmapFactory.decodeResource(getResources(), mHeartBorderResId); } Bitmap heart = sHeart; Bitmap heartBorder = sHeartBorder; Bitmap bm = createBitmapSafely(heartBorder.getWidth(), heartBorder.getHeight()); if (bm == null) { return null; } Canvas canvas = sCanvas; canvas.setBitmap(bm); Paint p = sPaint; canvas.drawBitmap(heartBorder, 0, 0, p); p.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)); float dx = (heartBorder.getWidth() - heart.getWidth()) / 2f; float dy = (heartBorder.getHeight() - heart.getHeight()) / 2f; canvas.drawBitmap(heart, dx, dy, p); p.setColorFilter(null); canvas.setBitmap(null); return bm; } private static Bitmap createBitmapSafely(int width, int height) { try { return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); } catch (OutOfMemoryError error) { error.printStackTrace(); } return null; } }
4.2 飄心動畫路徑布局
HeartLayout.java
/** * 飄心動畫路徑 */ public class HeartLayout extends RelativeLayout implements View.OnClickListener { private AbstractPathAnimator mAnimator; private AttributeSet attrs = null; private int defStyleAttr = 0; private OnHearLayoutListener onHearLayoutListener; private static HeartHandler heartHandler; private static HeartThread heartThread; public void setOnHearLayoutListener(OnHearLayoutListener onHearLayoutListener) { this.onHearLayoutListener = onHearLayoutListener; } public interface OnHearLayoutListener { boolean onAddFavor(); } public HeartLayout(Context context) { super(context); findViewById(context); } public HeartLayout(Context context, AttributeSet attrs) { super(context, attrs); this.attrs = attrs; findViewById(context); } public HeartLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.attrs = attrs; this.defStyleAttr = defStyleAttr; findViewById(context); } private Bitmap bitmap; private void findViewById(Context context) { LayoutInflater.from(context).inflate(R.layout.ly_periscope, this); bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_like); dHeight = bitmap.getWidth()/2; dWidth = bitmap.getHeight()/2; textHight = sp2px(getContext(), 20) + dHeight / 2; pointx = dWidth;//隨機上浮方向的x坐標 bitmap.recycle(); } private int mHeight; private int mWidth; private int textHight; private int dHeight; private int dWidth; private int initX; private int pointx; public static int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } public class HeartHandler extends Handler { public final static int MSG_SHOW = 1; WeakReferencewf; public HeartHandler(HeartLayout layout) { wf = new WeakReference (layout); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); HeartLayout layout = wf.get(); if (layout == null) return; switch (msg.what) { case MSG_SHOW: addFavor(); break; } } } public class HeartThread implements Runnable { private long time = 0; private int allSize = 0; public void addTask(long time, int size) { this.time = time; allSize += size; } public void clean() { allSize = 0; } @Override public void run() { if (heartHandler == null) return; if (allSize > 0) { heartHandler.sendEmptyMessage(HeartHandler.MSG_SHOW); allSize--; } postDelayed(this, time); } } private void init(AttributeSet attrs, int defStyleAttr) { final TypedArray a = getContext().obtainStyledAttributes( attrs, R.styleable.HeartLayout, defStyleAttr, 0); if (pointx <= initX && pointx >= 0) { pointx -= 10; } else if (pointx >= -initX && pointx <= 0) { pointx += 10; } else pointx = initX; mAnimator = new PathAnimator(AbstractPathAnimator.Config.fromTypeArray(a, initX, textHight, pointx, dWidth, dHeight)); a.recycle(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //獲取本身的寬高 這裡要注意,測量之後才有寬高 mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); initX = mWidth / 2 - dWidth / 2; } public AbstractPathAnimator getAnimator() { return mAnimator; } public void setAnimator(AbstractPathAnimator animator) { clearAnimation(); mAnimator = animator; } public void clearAnimation() { for (int i = 0; i < getChildCount(); i++) { getChildAt(i).clearAnimation(); } removeAllViews(); } private static int[] drawableIds = new int[]{R.drawable.heart0, R.drawable.heart1, R.drawable.heart2, R.drawable.heart3, R.drawable.heart4, R.drawable.heart5, R.drawable.heart6, R.drawable.heart7, R.drawable.heart8,}; private Random random = new Random(); public void addFavor() { HeartView heartView = new HeartView(getContext()); heartView.setDrawable(drawableIds[random.nextInt(8)]); init(attrs, defStyleAttr); mAnimator.start(heartView, this); } private long nowTime, lastTime; final static int[] sizeTable = {9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE}; public static int sizeOfInt(int x) { for (int i = 0; ; i++) if (x <= sizeTable[i]) return i + 1; } public void addFavor(int size) { switch (sizeOfInt(size)) { case 1: size = size % 10; break; default: size = size % 100; } if (size == 0) return; nowTime = System.currentTimeMillis(); long time = nowTime - lastTime; if (lastTime == 0) time = 2 * 1000;//第一次分為2秒顯示完 time = time / (size + 15); if (heartThread == null) { heartThread = new HeartThread(); } if (heartHandler == null) { heartHandler = new HeartHandler(this); heartHandler.post(heartThread); } heartThread.addTask(time, size); lastTime = nowTime; } public void addHeart(int color) { HeartView heartView = new HeartView(getContext()); heartView.setColor(color); init(attrs, defStyleAttr); mAnimator.start(heartView, this); } public void addHeart(int color, int heartResId, int heartBorderResId) { HeartView heartView = new HeartView(getContext()); heartView.setColorAndDrawables(color, heartResId, heartBorderResId); init(attrs, defStyleAttr); mAnimator.start(heartView, this); } @Override public void onClick(View v) { int i = v.getId(); if (i == R.id.img) { if (onHearLayoutListener != null) { boolean isAdd = onHearLayoutListener.onAddFavor(); if (isAdd) addFavor(); } } } public void clean() { if (heartThread != null) { heartThread.clean(); } } public void release() { if (heartHandler != null) { heartHandler.removeCallbacks(heartThread); heartThread = null; heartHandler = null; } } }
5.1 activity_heart_animal.xml
5.2 activity 中的使用
heartLayout = (HeartLayout)findViewById(R.id.heart_layout); findViewById(R.id.member_send_good).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { heartLayout.addFavor(); } });
heartLayout.addFavor(); 就是觸發飄心動畫效果的關鍵代碼
Android_Suixinbo">https://github.com/zhaoyang21cn/Android_Suixinbo
好,我們還是先復習一下上上節學到的圖:在開始InlineMethod之前,我們再繼續補充一點BasicBlock的知識。BasicBlock中針對MIR的相關操作Appe
最近看了一些android的源碼,發現設計模式無處不在啊!感覺有點亂,於是決定要把設計模式好好梳理一下,於是有了這篇文章。1. Singleton(單例模式)作用:
先上效果圖:本篇文章我們來學習一個開源項目Android-ItemTouchHelper-Demo這個項目使用了RecyclerView的ItemTouchHelper類
小米是目前國內用的較多的手機,米粉們用手機用多了發現小米手機變慢了,怎麼提高小米手機的速度呢!要注意以下幾點。 1:開機時間 需要打開權