先看效果图(实际效果很丝滑!)
GIF 2020-6-10 19-17-21.gif
代码没太整理,将就看哈,可以自行调整修改。
public class OnOffView extends FrameLayout implements View.OnClickListener {
private static String TAG = "OnOffView";
private int onColor = 0;
private int offColor = 0;
private float iconSize = 0;
private float iconPaddingSize = 0;
private int animationTime = 450;
private CardView icon;
private boolean animationRun = false;
private ImageView backView;
private boolean defOff = false;
private boolean defOff2 = false;
private CheckBoxCall checkBoxCall;
public OnOffView(Context context) {
super(context);
}
@RequiresApi(api = Build.VERSION_CODES.P)
public OnOffView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.OnOffView);
iconSize = typedArray.getDimension(R.styleable.OnOffView_iconSize, 0f);
iconPaddingSize = typedArray.getDimension(R.styleable.OnOffView_paddingSize, 5);
defOff2 = defOff = typedArray.getBoolean(R.styleable.OnOffView_defOff, false);
onColor = typedArray.getColor(R.styleable.OnOffView_onColor, 0x4CAF50);
offColor = typedArray.getColor(R.styleable.OnOffView_offColor, 0xFFFFFF);
backView = new ImageView(getContext());
addView(backView);
FrameLayout.LayoutParams layoutParams1 = (LayoutParams) backView.getLayoutParams();
layoutParams1.height = ViewGroup.LayoutParams.MATCH_PARENT;
layoutParams1.width = ViewGroup.LayoutParams.MATCH_PARENT;
icon = new CardView(getContext());
addView(icon);
FrameLayout.LayoutParams layoutParams2 = (LayoutParams) icon.getLayoutParams();
layoutParams2.height = (int)(iconSize - iconPaddingSize * 2);
layoutParams2.width = (int)(iconSize - iconPaddingSize * 2);
icon.setX(iconPaddingSize);
icon.setY(iconPaddingSize);
icon.setRadius(((iconSize - iconPaddingSize))/2);
typedArray.recycle();
setOnClickListener(this);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
initIcon();
}
public OnOffView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public OnOffView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void startAnimation(final boolean isOff){
TranslateAnimation animation;
int w = backView.getMeasuredWidth();
if ( w == 0 || w == -1){
w = backView.getWidth();
}
if (!defOff2) {
if (isOff)
animation = new TranslateAnimation(w - icon.getLayoutParams().width - iconPaddingSize * 2, 0, 0, 0);
else
animation = new TranslateAnimation(0, w - icon.getLayoutParams().width - iconPaddingSize * 2, 0, 0);
}else{
if (isOff)
animation = new TranslateAnimation(0, -(w - icon.getLayoutParams().width - iconPaddingSize * 2), 0, 0);
else
animation = new TranslateAnimation(-(w - icon.getLayoutParams().width - iconPaddingSize * 2), 0, 0, 0);
}
animation.setDuration(animationTime);
animation.setRepeatMode(Animation.REVERSE);
animation.setInterpolator(new AccelerateDecelerateInterpolator());
animation.setFillAfter(true);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
animationRun = true;
//如果想动画结束再执行回调的话, 就把这段话放在 onAnimationEnd 就阔以了
if (checkBoxCall != null){
checkBoxCall.check(isOff);
}
}
@Override
public void onAnimationEnd(Animation animation) {
animationRun = false;
}
@Override
public void onAnimationRepeat(Animation animation) { }
});
icon.startAnimation(animation);
GradientDrawable background = (GradientDrawable) backView.getBackground();
ValueAnimator animator;
if (isOff)
animator = ObjectAnimator.ofInt(background, "color", onColor, offColor);
else
animator = ObjectAnimator.ofInt(background, "color", offColor, onColor);
animator.setDuration(animationTime);
animator.setEvaluator(new ArgbEvaluator());//渐变色平滑
animator.start();
}
private void initIcon(){
if (defOff){
icon.setX(backView.getMeasuredWidth() - icon.getLayoutParams().width - iconPaddingSize);
}else{
icon.setX(iconPaddingSize);
}
GradientDrawable drawable = new GradientDrawable();
//小球圆角角度
drawable.setCornerRadius(Utils.dp2px(50));
//外框颜色
drawable.setStroke(Utils.dp2px(1), Color.parseColor("#EAEAEA"));
if (defOff)
drawable.setColor(onColor);
else
drawable.setColor(offColor);
backView.setBackground(drawable);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onClick(View v) {
animationCheck();
}
//设置初始状态 也可以在xml中设置 app:defOff = false|true
public void setDefOff(boolean off){
defOff2 = defOff = off;
initIcon();
}
//动画切换按钮状态
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void animationCheck(){
if (animationRun)
return;
if (defOff) {
startAnimation(true);
defOff = false;
}else{
startAnimation(false);
defOff = true;
}
}
public void setCheckBoxCall(CheckBoxCall checkBoxCall){
this.checkBoxCall = checkBoxCall;
}
public interface CheckBoxCall{
void check(boolean isOff);
}
}
另外需要在attrs.xml里加上属性 values > attrs.xml 没有就创建
使用
android:id="@+id/onOffView"
android:layout_width="100dp"
android:layout_height="60dp"
android:layout_centerInParent="true"
app:offColor="#FFFFFF"
app:onColor="#4CAF50"
app:paddingSize="5dp"
app:iconSize="60dp"/>
另外上面用到了一个单位转换工具Utils.dp2px我也把代码贴一下吧
public static int dp2px(final float dp) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}