仿直播送礼物控件

仿直播送礼物控件

在这里插入图片描述

源码

点击添加子视图,并播放动画集合。其中包含子视图的贝塞尔曲线运动动画、放大动画和渐变动画。贝塞尔曲线运动动画通过值动画ValueAnimator贝塞尔曲线工具类取点实现。

public class PraiseView extends FrameLayout implements View.OnClickListener, LifecycleObserver {

    private static PointF start;
    private float v;
    private int measuredWidth;
    private ArrayList<ImageView> imageViews;
    private ImageView view;
    private int count = 0;

    private ArrayList<Integer> resIds;
    private int minMultiplicator = 0;
    private int maxMultiplicator = 5;
    private int maxNumExpression = 5;
    private int initExpressionSize = dp2px(10);
    private int duration = 1500;
    private LayoutParams layoutParams;

    private AnimatorSetConfig config;

    public PraiseView(Context context) {
        this(context, null);
    }

    public PraiseView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PraiseView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        if (context instanceof LifecycleOwner) {
            ((LifecycleOwner) context).getLifecycle().addObserver(this);
        }
        Drawable drawable = null;
        int buttonW = dp2px(20);
        int buttonH = dp2px(20);
        boolean isShowButton = true;
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PraiseView);
        if (typedArray != null) {
            isShowButton = typedArray.getBoolean(R.styleable.PraiseView_isShowButton, true);
            buttonW = typedArray.getDimensionPixelSize(R.styleable.PraiseView_button_width, buttonW);
            buttonH = typedArray.getDimensionPixelSize(R.styleable.PraiseView_button_height, buttonH);
            initExpressionSize = typedArray.getDimensionPixelSize(R.styleable.PraiseView_expression_size, initExpressionSize);
            drawable = typedArray.getDrawable(R.styleable.PraiseView_button_icon_res);
            duration = typedArray.getInteger(R.styleable.PraiseView_anim_duration, duration);
            maxMultiplicator = typedArray.getInteger(R.styleable.PraiseView_multiplying_power, maxMultiplicator);//倍率(放大倍率)
            maxNumExpression = typedArray.getInteger(R.styleable.PraiseView_expression_max_num, maxNumExpression);//倍率(同一张资源图同时出现的最大数)
            //获取arrays.xml文件中图片资源集合
            int resourceId = typedArray.getResourceId(R.styleable.PraiseView_expression_list, -1);
            if (resourceId != -1) {
                resIds = new ArrayList<>();
                TypedArray array = getResources().obtainTypedArray(resourceId);
                for (int i = 0; i < array.length(); i++) {
                    int resourceId1 = array.getResourceId(i, -1);
                    if (resourceId1 != -1) {
                        resIds.add(resourceId1);
                    }
                    array.recycle();
                }
            }
            typedArray.recycle();
        }
        view = new ImageView(context);
        layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
        setButtonSize(buttonW, buttonH);
        view.setOnClickListener(this);
        if (drawable != null) {
            view.setImageDrawable(drawable);
        } else {
            view.setBackgroundColor(Color.RED);
        }
        imageViews = new ArrayList<>();
        addView(view);
        isShowButton(isShowButton);
    }

    /**
     * 设置按钮图片资源
     */
    public void setButtonBgRes(@DrawableRes int res) {
        if (view != null) {
            view.setImageResource(res);
        }
    }

    /**
     * 是否显示按钮
     */
    public void isShowButton(boolean isShow) {
        if (view != null) {
            view.setEnabled(isShow);
            view.setVisibility(isShow ? VISIBLE : GONE);
        }
    }

    /**
     * 设置按钮尺寸
     */
    public void setButtonSize(int wdp, int hdp) {
        if (layoutParams != null && view != null) {
            layoutParams.height = dp2px(wdp);
            layoutParams.width = dp2px(hdp);
            view.setLayoutParams(layoutParams);
        }
    }

    /**
     * 设置动画图片资源
     */
    public void setImageResIds(ArrayList<Integer> resIds) {
        this.resIds = resIds;
    }

    /**
     * 设置动画图片放大倍率
     */
    public void setMaxMultiplicator(@IntRange(from = 1) int maxMultiplicator) {
        this.maxMultiplicator = maxMultiplicator;
    }

    /**
     * 设置动画图片最小显示倍率
     */
    public void setMinMultiplicator(@IntRange(from = 0) int minMultiplicator) {
        this.minMultiplicator = minMultiplicator;
    }

    /**
     * 设置同一时间同一资源图片在页面的最大显示数量
     */
    public void setMaxNumExpression(@IntRange(from = 1) int maxNumExpression) {
        this.maxNumExpression = maxNumExpression;
    }

    /**
     * 设置资源图片尺寸(最大显示尺寸= 资源图片尺寸*(放大倍率+最小显示倍率)
     */
    public void setInitExpressionSize(int dp) {
        initExpressionSize = dp2px(dp);
    }

    /**
     * 自定义动画集合
     */
    public void setAnimators(AnimatorSetConfig config) {
        this.config = config;
    }

    public void setDuration(int duration) {
        this.duration = duration;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        measuredWidth = getMeasuredWidth();
        int measuredHeight = getMeasuredHeight();
        v = measuredHeight / 5f;
        start = new PointF(measuredWidth / 2f, measuredHeight - (view.getMeasuredHeight() / 2f));
    }

    /**
     * 播放动画
     */
    public void playAnim() {
        if (resIds == null || resIds.size() <= 0) {
            return;
        }
        AnimatorSet animatorSet = null;
        if (imageViews.size() > count && imageViews.get(count) != null) {
            animatorSet = (AnimatorSet) imageViews.get(count).getTag(R.id.image_anim);
        } else {
            ImageView imageView = new ImageView(getContext());
            LayoutParams layoutParams = new LayoutParams(initExpressionSize, initExpressionSize);
            layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
            imageView.setLayoutParams(layoutParams);
            imageView.setImageResource(resIds.get(count % resIds.size()));
            imageViews.add(imageView);
            addView(imageView);

            animatorSet = new AnimatorSet();
            BaseBezierAnimUpdateListener myUpdateListener = null;
            if (config == null) {
                //贝塞尔曲线运动动画(含放大动画)
                ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
                myUpdateListener = new BezierAnimUpdateListener(imageView);
                animator.addUpdateListener(myUpdateListener);
                animator.setInterpolator(new LinearInterpolator());
                //渐变动画
                ValueAnimator alpha = ValueAnimator.ofFloat(1, 0);
                alpha.addUpdateListener(new AlphaAnimUpdateListener(imageView));
                alpha.setInterpolator(new AccelerateInterpolator());
                animatorSet.playTogether(animator, alpha);
            } else {
                myUpdateListener = config.config(animatorSet, imageView);
            }
            animatorSet.setDuration(duration);
            imageView.setTag(R.id.image_anim, animatorSet);
            imageView.setTag(R.id.image_anim_update, myUpdateListener);
        }

        if (animatorSet != null && !animatorSet.isRunning()) {
            if (imageViews.get(count).getTag(R.id.image_anim_update) != null) {
                ((BaseBezierAnimUpdateListener) imageViews.get(count).getTag(R.id.image_anim_update)).resettingPoint();
            }
            animatorSet.start();
        }
        count++;
        if (count >= resIds.size() * maxNumExpression) {
            count = 0;
        }
    }

    /**
     * 获取X轴随机点
     *
     * @param y 轴位置
     */
    public PointF getRandomP(float y) {
        return new PointF((float) Math.random() * measuredWidth, y);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onStart() {
        isPauseAnim(false);
    }


    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onStop() {
        isPauseAnim(true);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
        for (ImageView image : imageViews) {
            if (image != null) {
                if (image.getTag(R.id.image_anim) != null) {
                    ((AnimatorSet) image.getTag(R.id.image_anim)).cancel();
                }
            }
            image = null;
        }
        imageViews.clear();
        imageViews = null;
        if (resIds != null) {
            resIds.clear();
        }
        if (getContext() instanceof LifecycleOwner) {
            ((LifecycleOwner) getContext()).getLifecycle().removeObserver(this);
        }
        removeAllViews();
    }

    public int dp2px(float dpValue) {
        final float scale = getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    private void isPauseAnim(boolean isPause) {
        for (ImageView image : imageViews) {
            if (image != null && image.getTag(R.id.image_anim) != null) {
                AnimatorSet animator = (AnimatorSet) image.getTag(R.id.image_anim);
                if (isPause) {
                    if (animator.isRunning()) {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                            animator.pause();
                        } else {
                            animator.end();
                        }
                    }
                } else {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && animator.isPaused()) {
                        animator.resume();
                    }
                }
            }
        }
    }

    @Override
    public void onClick(View v) {
        playAnim();
    }

    public interface AnimatorSetConfig {
        BaseBezierAnimUpdateListener config(AnimatorSet set, ImageView imageView);
    }

    private class AlphaAnimUpdateListener implements ValueAnimator.AnimatorUpdateListener {
        private ImageView imageView;

        public AlphaAnimUpdateListener(ImageView imageView) {
            this.imageView = imageView;
        }

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            imageView.setAlpha((Float) animation.getAnimatedValue());
        }
    }

    public abstract static class BaseBezierAnimUpdateListener implements ValueAnimator.AnimatorUpdateListener {
        private ImageView imageView;

        public BaseBezierAnimUpdateListener(ImageView imageView) {
            this.imageView = imageView;
        }

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            onAnimationUpdate(animation, start, imageView);
        }

        public abstract void onAnimationUpdate(ValueAnimator animation, PointF startP, ImageView imageView);

        public abstract void resettingPoint();
    }

    private class BezierAnimUpdateListener extends BaseBezierAnimUpdateListener {
        private PointF p1;
        private PointF p2;
        private PointF end;

        public BezierAnimUpdateListener(ImageView imageView) {
            super(imageView);
            resettingPoint();
        }

        @Override
        public void resettingPoint() {
            p1 = getRandomP((float) Math.random() * v + (3 * v));
            p2 = getRandomP((float) Math.round(2) * v + (2 * v));
            end = getRandomP((float) Math.random() * v);
        }

        @Override
        public void onAnimationUpdate(ValueAnimator animation, PointF startP, ImageView imageView) {
            float t = (float) animation.getAnimatedValue();
            PointF bezierPoint = BezierUtils.getBezierPoint(t, startP, p1, p2, end);
            imageView.setY(bezierPoint.y);
            imageView.setX(bezierPoint.x);
            imageView.setScaleY(t * maxMultiplicator + minMultiplicator);
            imageView.setScaleX(t * maxMultiplicator + minMultiplicator);
        }
    }
}

attr.xml

<declare-styleable name="PraiseView">
        <attr name="button_width" format="dimension" />
        <attr name="button_height" format="dimension" />
        <attr name="button_icon_res" format="reference" />
        <attr name="isShowButton" format="boolean" />
        <attr name="expression_list" format="reference" />
        <attr name="expression_max_num" format="integer" />
        <attr name="expression_size" format="dimension" />
        <attr name="anim_duration" format="integer" />
        <attr name="multiplying_power" format="integer" />
    </declare-styleable>

ids.xml

<resources>
    <item name="image_anim" type="id"/>
    <item name="image_anim_update" type="id"/>
</resources>

使用示例

<com.***.widget.PraiseView
        android:id="@+id/praise_view"
        android:layout_width="100dp"
        android:layout_height="500dp"
        app:button_icon_res="@drawable/image_1" />

贝塞尔曲线

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值