马上春节,写个应景的控件
private void init() {
mPath = new Path();
mRandom = new Random();
initPaint();
initMoneyPaint();
mText = moneys[mRandom.nextInt(moneys.length)];
//获取字体的宽高
moneyPaint.getTextBounds(mText, 0, mText.length(), mTextBound);
}
private void initPaint() {
mPaint = new Paint();
mPaint.setColor(Color.parseColor("#c0c0c0"));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
/**
* 设置接合处的形态
*/
mPaint.setStrokeJoin(Paint.Join.ROUND);
/**
* 抗抖动
*/
mPaint.setDither(true);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(PAINT_WIDTH);
}
/**
* money画笔
*/
private void initMoneyPaint() {
moneyPaint = new Paint();
moneyPaint.setColor(Color.RED);
moneyPaint.setAntiAlias(true);
moneyPaint.setTextSize(30);
mTextBound = new Rect();
moneyPaint.getTextBounds(moneys[0], 0, moneys[0].length(), mTextBound);
}
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
Bitmap bitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.red_packet));
mCanvas.drawBitmap(bitmap, null, new RectF(0, 0, width, height), null);
//设置图片的结合方式
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mCanvas.drawPath(mPath, mPaint);
canvas.drawBitmap(mBitmap, 0, 0, null);
public boolean onTouchEvent(MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//路径的初始化位置
mPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
if (movable) {
// 跟手滑效果
setX(x + getLeft() + getTranslationX() - getWidth() / 2);
setY(y + getTop() + getTranslationY() - getHeight() / 2);
} else if (Math.abs(x - mLastX) > DEFAULT_PATH_INSTANCE || Math.abs(y - mLastY) > DEFAULT_PATH_INSTANCE) {
// 记录手指擦除路径
mPath.lineTo(x, y);
invalidate();
}
case MotionEvent.ACTION_UP:
MyAsyncTask task = new MyAsyncTask();
task.execute();
break;
}
//记录上次位置
mLastX = x;
mLastY = y;
return true;
}
public class RedPacketView extends ImageView {
private Paint mPaint, moneyPaint;
private Path mPath;
private Canvas mCanvas;
private Bitmap mBitmap;
private int x, y, mLastX, mLastY;
public boolean movable = true;
public boolean isTouch = false;
private String[] moneys = new String[]{"¥5", "¥10", "¥20", "¥50"};
private Rect mTextBound;
private String mText;
private Random mRandom;
private boolean isComplete = false;
/**
* 笔触的宽度
*/
private static final float PAINT_WIDTH = 20;
/**
* 默认绘制的最小距离
*/
private static final float DEFAULT_PATH_INSTANCE = 5;
public RedPacketView(Context context) {
super(context);
init();
}
public RedPacketView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RedPacketView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPath = new Path();
mRandom = new Random();
initPaint();
initMoneyPaint();
//随机产生一个面值
mText = moneys[mRandom.nextInt(moneys.length)];
//获取字体的宽高
moneyPaint.getTextBounds(mText, 0, mText.length(), mTextBound);
}
private void initPaint() {
mPaint = new Paint();
mPaint.setColor(Color.parseColor("#c0c0c0"));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
/**
* 设置接合处的形态
*/
mPaint.setStrokeJoin(Paint.Join.ROUND);
/**
* 抗抖动
*/
mPaint.setDither(true);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(PAINT_WIDTH);
}
/**
* money画笔
*/
private void initMoneyPaint() {
moneyPaint = new Paint();
moneyPaint.setColor(Color.RED);
moneyPaint.setAntiAlias(true);
moneyPaint.setTextSize(30);
mTextBound = new Rect();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
try {
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
Bitmap bitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.red_packet));
mCanvas.drawBitmap(bitmap, null, new RectF(0, 0, width, height), null);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onDraw(Canvas canvas) {
try {
canvas.drawText(mText, getWidth() / 2 - mTextBound.width() / 2, getHeight() / 2 + mTextBound.height() / 2, moneyPaint);
if (isComplete) return;
//设置图片的结合方式
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mCanvas.drawPath(mPath, mPaint);
canvas.drawBitmap(mBitmap, 0, 0, null);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//路径的初始化位置
mPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
if (movable) {
// 跟手滑效果
setX(x + getLeft() + getTranslationX() - getWidth() / 2);
setY(y + getTop() + getTranslationY() - getHeight() / 2);
} else if (Math.abs(x - mLastX) > DEFAULT_PATH_INSTANCE || Math.abs(y - mLastY) > DEFAULT_PATH_INSTANCE) {
// 记录手指擦除路径
mPath.lineTo(x, y);
invalidate();
}
case MotionEvent.ACTION_UP:
MyAsyncTask task = new MyAsyncTask();
task.execute();
break;
}
//记录上次位置
mLastX = x;
mLastY = y;
return true;
}
/**
* 查看目前的红包的擦除比例,实现完全擦除
*/
class MyAsyncTask extends AsyncTask{
@Override
protected Object doInBackground(Object[] params) {
clearOverPercent();
return null;
}
private void clearOverPercent()
{
int[] mPixels;
int w = getWidth();
int h = getHeight();
float wipeArea = 0;
float totalArea = w * h;
Bitmap bitmap = Bitmap.createBitmap(mBitmap);
mPixels = new int[w * h];
//拿到所有像素信息
bitmap.getPixels(mPixels, 0, w, 0, 0, w, h);
//获取擦除部分的面积
int index = 0;
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
if (mPixels[index] == 0) {
wipeArea++;
}
index++;
}
}
int percent = (int) (wipeArea / totalArea * 100);
if (percent > 70) {
isComplete = true;
postInvalidate();
}
}
};
}
/**
* 估值器
*/
static class BSEEvaluator implements TypeEvaluator {
private PointF pointF1;
private PointF pointF2;
public BSEEvaluator(PointF pointF1, PointF pointF2) {
this.pointF1 = pointF1;
this.pointF2 = pointF2;
}
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
PointF pointF = new PointF();
float lFraction = 1 - fraction;
pointF.x = (float) (startValue.x * Math.pow(lFraction, 3) +
3 * pointF1.x * fraction * Math.pow(lFraction, 2) +
3 * pointF2.x * Math.pow(lFraction, 2) * fraction +
endValue.x * Math.pow(fraction, 3));
pointF.y = (float) (startValue.y * Math.pow(lFraction, 3) +
3 * pointF1.y * fraction * Math.pow(lFraction, 2) +
3 * pointF2.y * Math.pow(fraction, 2) * lFraction +
endValue.y * Math.pow(fraction, 3));
return pointF;
}
}
private ValueAnimator getBSEValueAnimator(View target) {
//贝赛尔估值器
BSEEvaluator evaluator = new BSEEvaluator(getPoint(), getPoint());
ValueAnimator animator = ValueAnimator.ofObject(evaluator, new PointF((mWidth - dWidth) / 2, mHeight - dHeight), new PointF(random.nextInt(mWidth), 0));
animator.addUpdateListener(new BSEListenr(target));
animator.setTarget(target);
animator.setDuration(3000);
return animator;
}
private class BSEListenr implements ValueAnimator.AnimatorUpdateListener {
private View target;
public BSEListenr(View target) {
this.target = target;
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//这里获取到贝塞尔曲线计算出来的的xy值
PointF pointF = (PointF) animation.getAnimatedValue();
target.setX(pointF.x);
target.setY(pointF.y);
}
}
/**
* 发射多个红包
*
* @param numb
*/
public void launch(int numb) throws Exception {
for (int i = 0; i < numb; i++)
launch();
}
/**
* 发射红包
*/
public void launch() throws Exception {
final RedPacketView imageView = new RedPacketView(getContext());
imageView.setImageDrawable(drawable);
//设置位置
LayoutParams layoutParams = new LayoutParams(dWidth, dHeight);
layoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE);
layoutParams.addRule(CENTER_HORIZONTAL, TRUE);
imageView.setLayoutParams(layoutParams);
final Animator set = addAnimatior(imageView);
imageView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
x = (int) imageView.getX();
y = (int) imageView.getY();
if (!imageView.isTouch) {
imageView.isTouch = true;
set.end();
}
if (MotionEvent.ACTION_UP == event.getAction()) {
if (imageView.movable) {
ObjectAnimator.ofFloat(imageView, View.ALPHA, 1f).start();
AnimatorSet setDown = new AnimatorSet();
setDown.playTogether(
ObjectAnimator.ofFloat(imageView, "scaleX", 0.8f, 1.5f),
ObjectAnimator.ofFloat(imageView, "scaleY", 0.8f, 1.5f)
);
setDown.start();
imageView.movable = false;
}
}
return false;
}
});
addView(imageView);
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
// 动画结束移除view
if (imageView.isTouch) {
imageView.setX(x);
imageView.setY(y);
} else {
removeView(imageView);
}
}
});
set.start();
}
public class LaunchRedPacketLayout extends RelativeLayout {
private Drawable drawable;
private int dWidth;
private int dHeight;
private int mWidth;
private int mHeight;
int x, y;
/**
* 插值器组
*/
private Interpolator[] interpolatorsArray;
private Random random;
public LaunchRedPacketLayout(Context context) {
super(context);
init();
}
public LaunchRedPacketLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
drawable = getResources().getDrawable(R.drawable.red_packet);
dWidth = drawable.getIntrinsicWidth();
dHeight = drawable.getIntrinsicHeight();
random = new Random();
interpolatorsArray = new Interpolator[4];
interpolatorsArray[0] = new LinearInterpolator();
interpolatorsArray[1] = new AccelerateInterpolator();
interpolatorsArray[2] = new DecelerateInterpolator();
interpolatorsArray[3] = new AccelerateDecelerateInterpolator();
post(new Runnable() {
@Override
public void run() {
mHeight = getMeasuredHeight();
mWidth = getMeasuredWidth();
int curWidth = dWidth;
dWidth = mWidth / 5;
dHeight = dHeight * dWidth / curWidth;
}
});
}
/**
* 发射多个红包
*
* @param numb
*/
public void launch(int numb) throws Exception {
for (int i = 0; i < numb; i++)
launch();
}
/**
* 发射红包
*/
public void launch() throws Exception {
final RedPacketView imageView = new RedPacketView(getContext());
imageView.setImageDrawable(drawable);
//设置位置
LayoutParams layoutParams = new LayoutParams(dWidth, dHeight);
layoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE);
layoutParams.addRule(CENTER_HORIZONTAL, TRUE);
imageView.setLayoutParams(layoutParams);
final Animator set = addAnimatior(imageView);
imageView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
x = (int) imageView.getX();
y = (int) imageView.getY();
if (!imageView.isTouch) {
imageView.isTouch = true;
set.end();
}
if (MotionEvent.ACTION_UP == event.getAction()) {
if (imageView.movable) {
ObjectAnimator.ofFloat(imageView, View.ALPHA, 1f).start();
AnimatorSet setDown = new AnimatorSet();
setDown.playTogether(
ObjectAnimator.ofFloat(imageView, "scaleX", 0.8f, 1.5f),
ObjectAnimator.ofFloat(imageView, "scaleY", 0.8f, 1.5f)
);
setDown.start();
imageView.movable = false;
}
}
return false;
}
});
addView(imageView);
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
// 动画结束移除view
if (imageView.isTouch) {
imageView.setX(x);
imageView.setY(y);
} else {
removeView(imageView);
}
}
});
set.start();
}
/**
* 设置动画
*
* @param target
*/
private Animator addAnimatior(View target) throws Exception {
AnimatorSet set = new AnimatorSet();
AnimatorSet enterSet = getEnterSet(target);
ValueAnimator bezierValueAnimator = getBSEValueAnimator(target);
set.playSequentially(enterSet, bezierValueAnimator);
set.setInterpolator(interpolatorsArray[random.nextInt(4)]);
set.setTarget(target);
return set;
}
private class BSEListenr implements ValueAnimator.AnimatorUpdateListener {
private View target;
public BSEListenr(View target) {
this.target = target;
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//这里获取到贝塞尔曲线计算出来的的x y值
PointF pointF = (PointF) animation.getAnimatedValue();
target.setX(pointF.x);
target.setY(pointF.y);
}
}
/**
* 设置贝赛尔曲线动画
*
* @param target
* @return
*/
private ValueAnimator getBSEValueAnimator(View target) {
//贝赛尔估值器
BSEEvaluator evaluator = new BSEEvaluator(getPoint(), getPoint());
ValueAnimator animator = ValueAnimator.ofObject(evaluator, new PointF((mWidth - dWidth) / 2, mHeight - dHeight), new PointF(random.nextInt(mWidth), 0));
animator.addUpdateListener(new BSEListenr(target));
animator.setTarget(target);
animator.setDuration(3000);
return animator;
}
private PointF getPoint() {
PointF pointF = new PointF();
pointF.x = random.nextInt(mWidth);
pointF.y = random.nextInt(mHeight - dHeight);
return pointF;
}
/**
* 估值器
*/
static class BSEEvaluator implements TypeEvaluator {
private PointF pointF1;
private PointF pointF2;
public BSEEvaluator(PointF pointF1, PointF pointF2) {
this.pointF1 = pointF1;
this.pointF2 = pointF2;
}
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
PointF pointF = new PointF();
float lFraction = 1 - fraction;
pointF.x = (float) (startValue.x * Math.pow(lFraction, 3) +
3 * pointF1.x * fraction * Math.pow(lFraction, 2) +
3 * pointF2.x * Math.pow(lFraction, 2) * fraction +
endValue.x * Math.pow(fraction, 3));
pointF.y = (float) (startValue.y * Math.pow(lFraction, 3) +
3 * pointF1.y * fraction * Math.pow(lFraction, 2) +
3 * pointF2.y * Math.pow(fraction, 2) * lFraction +
endValue.y * Math.pow(fraction, 3));
return pointF;
}
}
/**
* 入场动画
*
* @param target
* @return
*/
private AnimatorSet getEnterSet(View target) {
try {
AnimatorSet enterSet = new AnimatorSet();
enterSet.playTogether(
ObjectAnimator.ofFloat(target, View.ALPHA, 0, 1f),
ObjectAnimator.ofFloat(target, View.SCALE_X, 0.1f, 0.8f),
ObjectAnimator.ofFloat(target, View.SCALE_Y, 0.1f, 0.8f)
);
enterSet.setDuration(500);
enterSet.setInterpolator(new LinearInterpolator());
enterSet.setTarget(target);
return enterSet;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private LaunchRedPacketLayout launchRedPacketLayout;
private Button launchBtn, reStartBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
launchRedPacketLayout = (LaunchRedPacketLayout) findViewById(R.id.launchRedPacket);
launchBtn = (Button) findViewById(R.id.launchBtn);
reStartBtn = (Button) findViewById(R.id.reStart);
launchBtn.setOnClickListener(this);
reStartBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
try {
switch (v.getId()) {
case R.id.reStart:
startActivity(new Intent(this, MainActivity.class));
finish();
overridePendingTransition(0, 0);
break;
case R.id.launchBtn:
launchRedPacketLayout.launch(3);
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
十分感谢 程序亦非猿,hongyang 大神的博客
源码地址 https://github.com/wolow3/LaunchRedPacket