原理分析:
public class DouYinHeaderView extends View {
private Bitmap bitmap;
BitmapShader bitmapShader;
Paint paint;
Matrix matrix;
private float currentScaleRatio = 1f;
private float minScaleRation = 0.9f;
private Paint circlePaint;
private int countDown;
boolean change;
Bitmap[] bitmaps = new Bitmap[2];
private int currentBitmapIndex;
private int padding;
private int expandOutside = (int) (getResources().getDisplayMetrics().density * 12 + 0.5f);
private boolean drawOutsideCirle;
private float outSideRatio;
private int unMoveCirlceStrokeWidth = (int) (getResources().getDisplayMetrics().density * 2 + 0.5f);
private int moveCircleStrokeWidth = (int) (getResources().getDisplayMetrics().density * 0.8f + 0.5f);
private Paint outsideCirclePaint;
public DouYinHeaderView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
outsideCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
outsideCirclePaint.setStyle(Paint.Style.STROKE);
outsideCirclePaint.setStrokeWidth(moveCircleStrokeWidth);
outsideCirclePaint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
padding += unMoveCirlceStrokeWidth * 2;
padding += expandOutside;
circlePaint.setStrokeWidth(unMoveCirlceStrokeWidth);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
matrix = new Matrix();
postDelayed(new Runnable() {
@Override
public void run() {
final ValueAnimator valueAnimator = ValueAnimator.ofFloat((float) Math.PI);
valueAnimator.setDuration(800);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentValue = (Float) animation.getAnimatedValue();
currentScaleRatio = (float) (1f - Math.sin(currentValue) * (countDown == 5 ? 1 : (1f - minScaleRation)));
//sin(Pi/2)时等于1用于检测缩小到最小临界点
if (currentValue >= ((float) Math.PI) * 0.5f) {
drawOutsideCirle = false;
//在第五次缩小到最小时进行头像切换
if (countDown == 5 && !change) {
change = true;
currentBitmapIndex = (++currentBitmapIndex) % 2;
bitmap = bitmaps[currentBitmapIndex];
bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(bitmapShader);
}
} else {
//是缩小动画因此设置画外圆标志位并计算外圆扩散动画因子outSideRatio
drawOutsideCirle = true;
outSideRatio = (float) Math.sin(currentValue);
}
invalidate();
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationRepeat(Animator animation) {
countDown++;
if (countDown == 6) {
countDown = 1;
change = false;
}
super.onAnimationRepeat(animation);
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
countDown++;
}
});
valueAnimator.setRepeatMode(ValueAnimator.RESTART);
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.start();
}
}, 2000);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
decodeBitmap(R.drawable.test1, w - padding, h - padding);
bitmaps[0] = bitmap;
decodeBitmap(R.drawable.test2, w, h);
bitmaps[1] = bitmap;
bitmap = bitmaps[0];
bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(bitmapShader);
}
private void fillMatrix() {
matrix.reset();
float sourceWidth = bitmap.getWidth();
float sourceHeight = bitmap.getHeight();
float targetWidth = (getWidth() - padding) * currentScaleRatio;
float targetHeight = (getHeight() - padding) * currentScaleRatio;
float wRatio = targetWidth / sourceWidth;
float hRatio = targetHeight / sourceHeight;
float scale = Math.max(wRatio, hRatio);
float translationX = 0f;
float translationY = 0f;
if (wRatio > hRatio) {
translationY = (targetHeight - sourceHeight * scale) * 0.5f;
} else if (wRatio < hRatio) {
translationX = (targetWidth - sourceWidth * scale) * 0.5f;
}
matrix.setScale(scale, scale);
matrix.postTranslate(translationX, translationY);
bitmapShader.setLocalMatrix(matrix);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.translate(getWidth() * 0.5f, getHeight() * 0.5f);
canvas.save();
canvas.translate(-(getWidth() - padding) * currentScaleRatio * 0.5f, -(getHeight() - padding) * currentScaleRatio * 0.5f);
fillMatrix();
//画头像
canvas.drawCircle((getWidth() - padding) * currentScaleRatio * 0.5f, (getHeight() - padding) * currentScaleRatio * 0.5f, (getWidth() - padding) * currentScaleRatio * 0.5f, paint);
canvas.restore();
//画静止的圆
canvas.drawCircle(0, 0, getWidth() * 0.5f - padding * 0.5f + unMoveCirlceStrokeWidth * 0.5f, circlePaint);
//当头像缩小时我们根据缩小因子做外圆扩散动画以及透明度变化
if (drawOutsideCirle) {
float totalExpand = padding * 0.5f - unMoveCirlceStrokeWidth * 0.5f - moveCircleStrokeWidth * 0.5f;
/* int alpha = (int) (255 * (1 - outSideRatio));
outsideCirclePaint.setAlpha(alpha);*/
canvas.drawCircle(0, 0, getWidth() * 0.5f - padding * 0.5f + unMoveCirlceStrokeWidth * 0.5f + totalExpand * outSideRatio, outsideCirclePaint);
}
}
private void decodeBitmap(int resourceId, int targetWidth, int targetHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), resourceId, options);
int sourceWidth = options.outWidth;
int sourceHeight = options.outHeight;
int sample = downSample(sourceWidth, sourceHeight, targetWidth, targetHeight);
options.inJustDecodeBounds = false;
options.inSampleSize = sample;
options.inPreferredConfig = Bitmap.Config.RGB_565;
bitmap = BitmapFactory.decodeResource(getResources(), resourceId, options);
}
private int downSample(int sourceWidth, int sourceHeight, int targetWidth, int targetHeight) {
int sample = 0;
while (true) {
if (sourceWidth / Math.pow(2, sample + 1) > targetWidth && sourceHeight / Math.pow(2, sample + 1) > targetHeight) {
sample++;
} else {
return (int) Math.pow(2, sample);
}
}
}
}
private static void startAni(Object object) {
ObjectAnimator objectAnimatorBig = ObjectAnimator.ofFloat((Button)object, "scaleX", 1, 1.5f,1);
ObjectAnimator objectAnimatorSmall = ObjectAnimator.ofFloat((Button)object, "scaleY", 1f, 1.5f, 1f);
objectAnimatorBig.setRepeatCount(-1);
objectAnimatorSmall.setRepeatCount(-1);
AnimatorSet animSet = new AnimatorSet();
animSet.play(objectAnimatorBig).with(objectAnimatorSmall);
animSet.setDuration(2000);
animSet.start();
}
android 抖音头像缩放
https://blog.csdn.net/sange77/article/details/102597074
https://www.jianshu.com/p/daa6e2710e1c
动画效果:先放大,然后回到原来的效果
https://blog.csdn.net/u010632547/article/details/107204254/