Commit d323f1ad by gao.chao

仿IOS 开关控件

parent b8181690
......@@ -4,7 +4,7 @@ android {
compileSdkVersion 28
defaultConfig {
applicationId "com.mayi.demo"
minSdkVersion 20
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
......
......@@ -9,6 +9,19 @@
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="仿IOS 开关控件"
android:textColor="@color/color_666666" />
<com.mayi.fastdevelop.view.IOSSwitchView
android:id="@+id/s"
android:layout_width="@dimen/size_36"
android:layout_height="@dimen/size_18"
android:layout_margin="@dimen/size_15" />
<Button
android:id="@+id/b1"
android:layout_width="wrap_content"
......
......@@ -4,7 +4,7 @@ android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 20
minSdkVersion 21
targetSdkVersion 28
versionCode 3
versionName "1.2"
......
package com.mayi.fastdevelop.view;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.FloatEvaluator;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.mayi.fastdevelop.R;
import com.mayi.fastdevelop.util.DisplayUtil;
public class IOSSwitchView extends View {
private Context mContext;
private int mHeight, mWidth;
private Path mPath;
private Paint mStrokePaint; //边框画笔
private Paint mSolidPaint; //填充色画笔
private Paint mCirclePaint; //小圆球画笔
private float mMarginLeft; //小圆球到左边距离
private boolean mIsCheck; //是否被选中
private int CIRCLEPADDING;
private int mCircleWidth;
private int mDefaultSolidColor, mTargetSolidColor;
private int mDefaultStrokeColor, mTargetStrokeColor;
private int mDefaultCircleColor, mTargetCircleColor;
private int mSolidColor_;
private int mCircleColor_;
private int mStrokeColor_;
private ObjectAnimator mTranslateAnim, mSolidColorAnim, mCircleColorAnim, mStrokeColorAnim;
private int mStrokeWidth;
private int mRealWidth;
private int mRealHeight;
public interface OnCheckedChange {
void onCheckChange(boolean isChecked);
}
public IOSSwitchView(Context context) {
this(context, null);
}
public IOSSwitchView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public IOSSwitchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
CIRCLEPADDING = DisplayUtil.dip2px(mContext, 1);
TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.IOSSwitchView);
mDefaultStrokeColor = a.getColor(R.styleable.IOSSwitchView_default_stroke_color, Color.parseColor("#707070"));
mTargetStrokeColor = a.getColor(R.styleable.IOSSwitchView_target_stroke_color, Color.parseColor("#E83C3C"));
mTargetCircleColor = a.getColor(R.styleable.IOSSwitchView_target_circle_color, Color.parseColor("#E83C3C"));
mDefaultCircleColor = a.getColor(R.styleable.IOSSwitchView_default_circle_color, Color.parseColor("#707070"));
mDefaultSolidColor = a.getColor(R.styleable.IOSSwitchView_default_solid_color, Color.WHITE);
mTargetSolidColor = a.getColor(R.styleable.IOSSwitchView_target_solid_color, Color.WHITE);
mIsCheck = a.getBoolean(R.styleable.IOSSwitchView_checked, false);
mSolidColor_ = mIsCheck ? mTargetSolidColor : mDefaultSolidColor;
mCircleColor_ = mIsCheck ? mTargetCircleColor : mDefaultCircleColor;
mStrokeColor_ = mIsCheck ? mTargetStrokeColor : mDefaultStrokeColor;
mTranslateAnim = initAnim("mMarginLeft", new FloatEvaluator());
mSolidColorAnim = initAnim("mSolidColor_", new ArgbEvaluator());
mCircleColorAnim = initAnim("mCircleColor_", new ArgbEvaluator());
mStrokeColorAnim = initAnim("mStrokeColor_", new ArgbEvaluator());
mStrokeWidth = DisplayUtil.dip2px(mContext, 2f);
mMarginLeft = 0;
mPath = new Path();
mCirclePaint = new Paint();
mStrokePaint = new Paint();
mSolidPaint = new Paint();
initPaint(mCirclePaint, mSolidPaint, mStrokePaint);
mStrokePaint.setStyle(Paint.Style.STROKE);
mTranslateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mMarginLeft = (float) (animation.getAnimatedValue());
postInvalidate();
}
});
mCircleColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCircleColor_ = (int) animation.getAnimatedValue();
postInvalidate();
}
});
mSolidColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mSolidColor_ = (int) animation.getAnimatedValue();
postInvalidate();
}
});
mStrokeColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mStrokeColor_ = (int) animation.getAnimatedValue();
postInvalidate();
}
});
}
private ObjectAnimator initAnim(String propertyName, TypeEvaluator evaluator) {
ObjectAnimator anim = new ObjectAnimator();
anim.setTarget(this); //这里千万记住别写错,楼主因为写成了setObjectValue被坑了好几个小时
anim.setPropertyName(propertyName);
anim.setEvaluator(evaluator);
return anim;
}
public void initPaint(Paint... paints) {
for (int i = 0; i < paints.length; i++) {
paints[i].setAntiAlias(true);
paints[i].setStrokeWidth(mStrokeWidth);
}
}
public void startAnim(boolean isCheck) {
if (isCheck) {
mCircleColorAnim.setIntValues(mDefaultCircleColor, mTargetCircleColor);
mTranslateAnim.setFloatValues(0, mWidth - mHeight);
mSolidColorAnim.setIntValues(mDefaultSolidColor, mTargetSolidColor);
mStrokeColorAnim.setIntValues(mDefaultStrokeColor, mTargetStrokeColor);
} else {
mCircleColorAnim.setIntValues(mTargetCircleColor, mDefaultCircleColor);
mTranslateAnim.setFloatValues(mWidth - mHeight, 0);
mSolidColorAnim.setIntValues(mTargetSolidColor, mDefaultSolidColor);
mStrokeColorAnim.setIntValues(mTargetStrokeColor, mDefaultStrokeColor);
}
AnimatorSet set = new AnimatorSet();
set.setDuration(200);
set.setInterpolator(new AccelerateDecelerateInterpolator());
set.playTogether(mTranslateAnim, mCircleColorAnim, mSolidColorAnim, mStrokeColorAnim);
set.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mWidth == 0) {
mWidth = getWidth();
mHeight = getHeight();
mRealWidth = mWidth - mStrokeWidth * 2;
mRealHeight = mHeight - mStrokeWidth * 2;
mMarginLeft = mIsCheck ? mWidth - mHeight : 0;
mCircleWidth = mHeight - CIRCLEPADDING * 2 - mStrokeWidth * 2;
}
if (mWidth <= 0 || mHeight <= 0) return;
mPath.reset();
mSolidPaint.setColor(mSolidColor_);
mStrokePaint.setColor(mStrokeColor_);
mCirclePaint.setColor(mCircleColor_);
mPath.addRoundRect(mStrokeWidth, mStrokeWidth, mRealWidth + mStrokeWidth, mRealHeight + mStrokeWidth, mRealHeight / 2, mRealHeight / 2, Path.Direction.CW);
canvas.drawPath(mPath, mStrokePaint);
canvas.drawPath(mPath, mSolidPaint);
canvas.drawCircle(mHeight / 2.0f + mMarginLeft, mHeight / 2f, mCircleWidth / 2, mCirclePaint);
}
public void setCheck(boolean checked) {
this.setCheck(checked, true);
}
private void setCheck(boolean checked, boolean fromOut) {
boolean flag = mIsCheck == checked;
mIsCheck = checked;
if (mListener != null && !flag) {
mListener.onCheckChange(mIsCheck);
}
if (!fromOut) {
startAnim(mIsCheck);
} else {
mSolidColor_ = mIsCheck ? mTargetSolidColor : mDefaultSolidColor;
mCircleColor_ = mIsCheck ? mTargetCircleColor : mDefaultCircleColor;
mStrokeColor_ = mIsCheck ? mTargetStrokeColor : mDefaultStrokeColor;
mMarginLeft = mIsCheck ? mWidth - mHeight : 0;
postInvalidate();
}
}
public float getMarginLeft() {
return mMarginLeft;
}
private OnCheckedChange mListener;
public void setOnCheckedChangeListener(OnCheckedChange listener) {
mListener = listener;
}
public void setMarginLeft(float marginLeft) {
mMarginLeft = marginLeft;
}
public int getSolidColor_() {
return mSolidColor_;
}
public void setSolidColor_(int solidColor_) {
mSolidColor_ = solidColor_;
}
public int getCircleColor_() {
return mCircleColor_;
}
public void setCircleColor_(int circleColor_) {
mCircleColor_ = circleColor_;
}
public int getStrokeColor_() {
return mStrokeColor_;
}
public void setStrokeColor_(int strokeColor_) {
mStrokeColor_ = strokeColor_;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mTranslateAnim.isRunning()) {
return false;
}
return true;
case MotionEvent.ACTION_CANCEL:
return false;
case MotionEvent.ACTION_UP:
setCheck(!mIsCheck, false);
return true;
}
return true;
}
public boolean isCheck() {
return mIsCheck;
}
}
\ No newline at end of file
......@@ -17,4 +17,16 @@
<attr name="label_text" format="string"/>
<attr name="label_text_size" format="float"/>
</declare-styleable>
<declare-styleable name="IOSSwitchView">
<attr name="default_stroke_color" format="color"/>
<attr name="target_stroke_color" format="color"/>
<attr name="default_circle_color" format="color"/>
<attr name="default_solid_color" format="color"/>
<attr name="target_circle_color" format="color"/>
<attr name="target_solid_color" format="color"/>
<attr name="checked" format="boolean"/>
</declare-styleable>
</resources>
\ No newline at end of file
......@@ -3,7 +3,7 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 20
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
......
......@@ -3,7 +3,7 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 20
minSdkVersion 21
targetSdkVersion 28
versionCode 2
versionName "1.1"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment