Android 自定义悬浮小球

第一次自己自定义控件,看了别人的控件,自己模仿出来一个新的,这是精简版,拓展性很差,文章最后将完全版代码附上

效果和支付宝收能量,网易星球首页钻石一样

先上效果图

1.继承 Relativelayout

public MyView(Context context)
public MyView(Context context, AttributeSet attrs)

2.在attrs中,定义属性

<declare-styleable name="MyView">
    <attr name="cicleWidth" format="dimension" />
    <attr name="myViewWidth" format="dimension" />
    <attr name="myViewHeight" format="dimension" />
</declare-styleable>

3.在xml中,要加xmlns:app="http://schemas.android.com/apk/res-auto"

<com.luo.hellocustom.MyView
    android:id="@+id/myView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:cicleWidth="35dp"
    />

4.代码中,拿到属性值

TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MyView);
parentWidth = typedArray.getDimension(R.styleable.MyView_myViewWidth, getScrWidth());
parentHeight = typedArray.getDimension(R.styleable.MyView_myViewHeight, getScrHeight());

拿到控件宽度,getScrWidth()是我们自己默认设置的值

5.给控件添加小球

写一个方法setList();在Activity中直接调用,等于初始化数据

//设置数据添加子小球
public void setList(List<? extends Number> list) {
    this.mFloat = list;
    //使用post方法确保在UI加载完的情况下 调用init() 避免获取到的宽高为0
    post(new Runnable() {
        @Override
        public void run() {
            init();
        }
    });
}

使用post是为了可以拿到宽高,否则为0,在post之前就需要

view = LayoutInflater.from(mContext).inflate(R.layout.view_float, this, false);

否则也拿不到值

6.小球的出现使用随机数,生成xy

Random randomX = new Random();
Random randomY = new Random();
float x = randomX.nextFloat() * (parentWidth - view.getWidth()/2);
float y = randomY.nextFloat() * (parentHeight - view.getWidth()/2);
view.setX(x);
view.setY(y);

取值在,父控件的宽度减小球半径

7,动画,小球出现时动画(渐变)

view.setAlpha(0);
view.setScaleX(0);
view.setScaleY(0);
view.animate().alpha(1).scaleX(1).scaleY(1).setDuration(2000).start();

8.小球的点击事件,我们可以加个回调

public static interface onclickItemListen{
    public void onclick(View view,int postion);
}

private onclickItemListen onclickItemListen;

public void setOnclickItemListen(MyView.onclickItemListen onclickItemListen) {
    this.onclickItemListen = onclickItemListen;
}
view.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        removeFloatAnim(v);
        if (onclickItemListen != null) {
            onclickItemListen.onclick(v,i);
        }
    }
});

9.小球出现后浮动动画,动画(上下位移)

Animation anim = new TranslateAnimation(0, 0, -10, 20);
anim.setInterpolator(new AccelerateDecelerateInterpolator());
anim.setDuration(1000);
anim.setRepeatCount(Integer.MAX_VALUE);
anim.setRepeatMode(Animation.REVERSE);//反方向执行
view.startAnimation(anim);

10.小球点击后,移除动画(位移)

private void removeFloatAnim(final View view) {
    ValueAnimator animator = ValueAnimator.ofFloat(parentHeight, 0);
    animator.setDuration(1000);
    animator.setInterpolator(new LinearInterpolator());

    //动画更新的监听
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float Value = (float) animation.getAnimatedValue();
            Log.d(TAG, "onAnimationUpdate: " + view.getTranslationY());
            Log.d(TAG, "onAnimationUpdate: " + view.getY());
            view.setAlpha(Value / parentHeight);
            view.setTranslationY(view.getY() - (parentHeight - Value));
        }
    });
    animator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            removeView(view);
        }
    });
    animator.start();
}

11.为了小球不出现同一位置且重合,需要递归

private void addNewPosition(List<Bean> allPostion, View childView) {
    Random randomX = new Random();
    Random randomY = new Random();
    float x = randomX.nextFloat() * (parentWidth - 70 - childView.getMeasuredWidth());
    float y = randomY.nextFloat() * (parentHeight - 70 - childView.getMeasuredWidth());
    boolean flag = false;
    if (allPostion.size() == 0) {
        allPostion.add(new Bean(x, y));
    }
    for (Bean bean : allPostion) {
        float x1 = bean.getX();
        float y1 = bean.getY();

        if (x > (x1 + 140) || y > (y1 + 140) || x < (x1 - 140) || y < (y1 - 140)) {
        } else {
            flag = true;
        }
    }
    if (!flag) {
        allPostion.add(new Bean(x, y));
        return;
    } else {
        addNewPosition(allPostion, childView);
    }
}

12.分成两拨显示,第一拨点击完以后,出现第二波

public void notifychangeAll(List<? extends Number> list) {
    allPostion.clear();
    this.mFloat = list;
    //使用post方法确保在UI加载完的情况下 调用init() 避免获取到的宽高为0
    post(new Runnable() {
        @Override
        public void run() {
            centerView.clearAnimation();
            centerView.setVisibility(GONE);
            addChidView();
        }
    });
}

和初始化一样,不过要去掉存放位置的list

ok,最终就完成了,因为时间有限这只是简版,拓展性不是很大

下面我放一个最终版本,不过这里面有个问题,就是获得界面的宽高有问题,可以按我上面说的解决

1.post

2.加个宽高的属性,直接获得

public class FloatView extends RelativeLayout {
    private static final long ANIMATION_TIME = 1000;
    private static final long ANIMATION_DEFAULT_TIME = 2000;
    private static final String TAG = "FloatView";
    private Context mcontext;
    private List<? extends Number> mFloat;
    private List<View> mViews = new ArrayList<>();

    public RelativeLayout parentView;
    private int parentWidth;
    private int parentHeight;
    private TextView defaultView, defaultBg;
    private OnItemClickListener mListener;
    private int textColor;
    private int childId;
    private int parentId;
    private String defaultViewText;
    private float childSize;
    private View centerView;

    //记录小球位置
    private List<Bean> allPostion = new ArrayList<>();

    public FloatView(Context context) {
        this(context, null);
        mcontext = context;
    }

    public FloatView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mcontext = context;
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.myFloatView);
        textColor = typedArray.getColor(R.styleable.myFloatView_childTextColor, getResources().getColor(R.color.white));
        childSize = typedArray.getDimension(R.styleable.myFloatView_chidTextSize, 6);
        childId = typedArray.getResourceId(R.styleable.myFloatView_childViewBackground, R.drawable.shape_circle);
        parentId = typedArray.getResourceId(R.styleable.myFloatView_parentViewBackground, R.mipmap.star_bg);
        defaultViewText = typedArray.getString(R.styleable.myFloatView_defaultViewText);
        //一定会要释放资源
        typedArray.recycle();
    }

    public FloatView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs);
        mcontext = context;
    }

    private void init() {
        setDefaultView();
        addChidView();
    }

    //添加小球
    private void addChidView() {
        for (int i = 0; i < mFloat.size(); i++) {
            View view = LayoutInflater.from(mcontext).inflate(R.layout.view_float, this, false);
            TextView floatview = view.findViewById(R.id.float_view);
            TextView floatviewBg = view.findViewById(R.id.float_bg);
            floatview.setTextColor(textColor);
            floatview.setTextSize(childSize);
            floatviewBg.setBackgroundResource(childId);
            floatview.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
            floatview.setText(mFloat.get(i) + "");
            view.setTag(i);
            view.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    childClick(v);
                }
            });
            setChildViewPosition(view, i);
            initAnim(view);
            initFloatAnim(view);
            mViews.add(view);
            addView(view);
        }
    }

    //设置初始化的小球
    private void setDefaultView() {
        parentView = (RelativeLayout) LayoutInflater.from(mcontext).inflate(R.layout.view_item, this, true);
        parentView.setBackgroundResource(parentId);
        int width = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        parentView.measure(width, height);
        parentHeight = parentView.getMeasuredHeight();
        parentWidth = parentView.getMeasuredWidth();


        LayoutParams params = new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        centerView = LayoutInflater.from(mcontext).inflate(R.layout.view_float, this, false);
        defaultView = centerView.findViewById(R.id.float_view);
        defaultBg = centerView.findViewById(R.id.float_bg);
        params.addRule(RelativeLayout.CENTER_IN_PARENT);
        defaultView.setTextColor(textColor);
        defaultView.setTextSize(childSize);
        defaultView.setText(defaultViewText);
        defaultBg.setBackgroundResource(childId);
        if (mFloat.size() != 0) {
            centerView.setVisibility(GONE);
        }
        addView(centerView, params);
        //设置动画
        initAnim(centerView);
        //设置上下抖动的动画
        initFloatAnim(centerView);
    }

    //FloatView上下抖动的动画
    private void initFloatAnim(View view) {
        Animation anim = new TranslateAnimation(0, 0, -10, 20);
        anim.setInterpolator(new AccelerateDecelerateInterpolator());
        anim.setDuration(ANIMATION_TIME);
        anim.setRepeatCount(Integer.MAX_VALUE);
        anim.setRepeatMode(Animation.REVERSE);//反方向执行
        view.startAnimation(anim);
    }

    //FloatView初始化时动画
    private void initAnim(View view) {
        view.setAlpha(0);
        view.setScaleX(0);
        view.setScaleY(0);
        view.animate().alpha(1).scaleX(1).scaleY(1).setDuration(ANIMATION_DEFAULT_TIME).start();
    }

    //设置数据添加子小球
    public void setList(List<? extends Number> list) {
        this.mFloat = list;
        //使用post方法确保在UI加载完的情况下 调用init() 避免获取到的宽高为0
        post(new Runnable() {
            @Override
            public void run() {
                init();
            }
        });
    }

    public void notifychangeAll(List<? extends Number> list) {
        allPostion.clear();
        this.mFloat = list;
        //使用post方法确保在UI加载完的情况下 调用init() 避免获取到的宽高为0
        post(new Runnable() {
            @Override
            public void run() {
                centerView.clearAnimation();
                centerView.setVisibility(GONE);
                addChidView();
            }
        });
    }

    //设置子view的位置
    private void setChildViewPosition(View childView, int postion) {
        addNewPosition(allPostion, childView);
        childView.setX(allPostion.get(postion).getX());
        childView.setY(allPostion.get(postion).getY());

    }


    private void addNewPosition(List<Bean> allPostion, View childView) {
        Random randomX = new Random();
        Random randomY = new Random();
        float x = randomX.nextFloat() * (parentWidth - 70 - childView.getMeasuredWidth());
        float y = randomY.nextFloat() * (parentHeight - 70 - childView.getMeasuredWidth());
        boolean flag = false;
        if (allPostion.size() == 0) {
            allPostion.add(new Bean(x, y));
        }
        for (Bean bean : allPostion) {
            float x1 = bean.getX();
            float y1 = bean.getY();

            if (x > (x1 + 140) || y > (y1 + 140) || x < (x1 - 140) || y < (y1 - 140)) {
            } else {
                flag = true;
            }
        }
        if (!flag) {
            allPostion.add(new Bean(x, y));
            return;
        } else {
            addNewPosition(allPostion, childView);
        }
    }

    private void childClick(View view) {
        //设置接口回调

        mViews.remove(view);
        animRemoveView(view);
        if (mViews.size() == 0) {
            centerView.setVisibility(VISIBLE);
            initAnim(centerView);
            //设置上下抖动的动画
            initFloatAnim(centerView);
        }
        mListener.itemClick((int) view.getTag(), mFloat.get((int) view.getTag()));
    }

    private void animRemoveView(final View view) {
//
        ValueAnimator animator = ValueAnimator.ofFloat(parentHeight, 0);
        animator.setDuration(1000);
        animator.setInterpolator(new LinearInterpolator());

        //动画更新的监听
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float Value = (float) animation.getAnimatedValue();
                Log.d(TAG, "onAnimationUpdate: " + view.getTranslationY());
                Log.d(TAG, "onAnimationUpdate: " + view.getY());
                view.setAlpha(Value / parentHeight);
                view.setTranslationY(view.getY() - (parentHeight - Value));
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                removeView(view);
            }
        });
        animator.start();
    }

    public interface OnItemClickListener {
        void itemClick(int position, Number value);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        mListener = listener;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_30711091/article/details/82865132
今日推荐