Android 抖音头像缩放特效 直播间特效 100%高度还原

16 篇文章 0 订阅
8 篇文章 0 订阅

原理分析:

 

 

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/

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 仿抖音APP下拉刷新功能,首先分析这个效果的实现思路,大致如下:   1、上拉时页面有翻页效果,可以用scrollview的pagingEnabled来实现,也就是说列表页不管你用tableview还是collectionview,只要每个cell是全屏的就可以。   2、下拉:当页面不是停留在第一个cell时,下拉就只是scrollView的滚动效果,不会触发刷新,当页面停留在第一个cell,也就是说scrollView.contentOffset.y = 0的时候,手指下拉才会触发刷新效果,并且下拉时scrollView不动,也就是没有scrollview的弹性效果,因此scrollView.bounces = NO。   3、既然下拉时scrollView不动,就不能使用代理来监听scrollView的滑动实现刷新,于是我想到了用touches的系列方法来监控手指下滑位移。   4、动画分解有五步:   (1)下拉时“推荐、附近”的那个导航条和“下拉刷新内容”的视图有渐隐渐显的效果,位置也随着手指下移,可以通过手指下滑位移计算alpha来实现   (2)下拉时,“下拉刷新内容”的视图右边那个有缺口的小圆环会随着手指滑动转圈,下滑时逆时针旋转   (3)下滑一定距离后如果不松手,又继续上滑,会执行前两步的反效果,圆环顺时针旋转,手指停在屏幕上,圆环就停止转动   (4)下滑到某个临界点,导航条和刷新视图都不再移动(此时导航条已经完全透明),所以可以通过计算起始点和当前点移动距离来计算透明度、位移、旋转角度,这些操作都在touchesMoved中实现   (5)到临界点松手后,导航条和刷新视图都回到原始位置,小圆环一直顺时针转圈,直到刷新结束,停止动画,隐藏刷新视图,显示导航条,如果没达到临界点就松手,不会触发刷新。。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值