第一次自己自定义控件,看了别人的控件,自己模仿出来一个新的,这是精简版,拓展性很差,文章最后将完全版代码附上
效果和支付宝收能量,网易星球首页钻石一样
先上效果图
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;
}
}