陷入自定義view的坑越陷越深,勵志要把自定義view擼給遍,今天給大家介紹一下自定義view————開關button,這個在很多app中都能用的,廢話不多說,先來給動圖。
分析一下上面的效果:四個狀態:關閉狀態、關閉->開啟過程、開啟狀態、開啟->關閉過程,結合ValueAnimator動效實現這個過程
關閉狀態
繪製一個圓角矩形(drawRoundRect()方法),再畫一個圓(drawCircle()),預設關閉就畫好了
RectF rectF = new RectF(padding, padding, mwidth - padding, mheight - padding);
//先畫背景
canvas.drawRoundRect(rectF, backgroundRadius, backgroundRadius, closePaint);
//再畫圓
canvas.drawCircle(closecenterX, closecenterY, circleRadius, circlePaint);複製程式碼
開啟狀態
繪製了關閉狀態,再繪製開啟狀態,,最後繪製關閉->開啟狀態,開啟->關閉狀態,其實這裡和關閉狀態類似的,只是畫圓的x、y的位置不一樣而已
RectF rectF = new RectF(padding, padding, mwidth - padding, mheight - padding);
//先畫背景
canvas.drawRoundRect(rectF, backgroundRadius, backgroundRadius, closePaint);
RectF rectFAnimation = new RectF(padding, padding, o2cright, o2cbottom);
//先畫背景
canvas.drawRoundRect(rectFAnimation, backgroundRadius, backgroundRadius, openPaint);
//再畫圓
canvas.drawCircle(o2ccenterX, o2ccenterY, circleRadius, circlePaint);複製程式碼
關閉->開啟狀態
ValueAnimator動效中的addUpdateListener()的回撥通過valueAnimator.getAnimatedValue();來重新繪製過程,計算圓在狀態變化時的x,y及其圓角矩形的right,bottom值來動態改變然後重新繪製。
記錄動態的變化值
close2OpengvalueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mclose2OpenAnimatorValue = (float) valueAnimator.getAnimatedValue();
//圓在狀態變化時的x,y
c2ocenterX = closecenterX + closeOpenX * mclose2OpenAnimatorValue;
c2ocenterY = backgroundRadius + padding;
//背景的右下標=關閉狀態的位置(2*backgroundRadius+padding)+差值*變化
c2oright = 2 * backgroundRadius + padding + closeOpenRight * mclose2OpenAnimatorValue;
//背景的底下標=關閉狀態的位置(2*backgroundRadius+padding)+差值*變化
c2obottom = 2 * backgroundRadius + padding + closeOpenBottom * mclose2OpenAnimatorValue;
invalidate();
}
});
close2OpengvalueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
setOpen();
invalidate();
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});複製程式碼
根據變化值重新繪製
RectF rectF = new RectF(padding, padding, mwidth - padding, mheight - padding);
//先畫背景
canvas.drawRoundRect(rectF, backgroundRadius, backgroundRadius, closePaint);
RectF rectFAnimation = new RectF(padding, padding, c2oright, c2obottom);
//先畫背景
canvas.drawRoundRect(rectFAnimation, backgroundRadius, backgroundRadius, openPaint);
//再畫圓
canvas.drawCircle(c2ocenterX, c2ocenterY, circleRadius, circlePaint);複製程式碼
開啟->關閉狀態
這個動畫狀態和上面的動畫狀態同理
記錄變化值
openg2ClosevalueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mopeng2CloseAnimatorValue = (float) valueAnimator.getAnimatedValue();
//圓在狀態變化時的x,y
o2ccenterX = closecenterX+closeOpenX * mopeng2CloseAnimatorValue;
o2ccenterY = backgroundRadius + padding;
//背景的右下標=關閉狀態的位置(2*backgroundRadius+padding)+差值*變化
o2cright = 2 * backgroundRadius + padding + closeOpenRight * mopeng2CloseAnimatorValue;
//背景的底下標=關閉狀態的位置(2*backgroundRadius+padding)+差值*變化
o2cbottom = 2 * backgroundRadius + padding + closeOpenBottom * mopeng2CloseAnimatorValue;
invalidate();
}
});
openg2ClosevalueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
setClose();
invalidate();
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});複製程式碼
根據變化值重新繪製
RectF rectF = new RectF(padding, padding, mwidth - padding, mheight - padding);
//先畫背景
canvas.drawRoundRect(rectF, backgroundRadius, backgroundRadius, closePaint);
RectF rectFAnimation = new RectF(padding, padding, o2cright, o2cbottom);
//先畫背景
canvas.drawRoundRect(rectFAnimation, backgroundRadius, backgroundRadius, openPaint);
//再畫圓
canvas.drawCircle(o2ccenterX, o2ccenterY, circleRadius, circlePaint);複製程式碼
上面的四種狀態介紹完畢了,大致功能就實現了,接下來介紹一些屬性的設定、四種狀態設定的方法、點選事件等
四種狀態的設定
/***
* 關閉
*/
private void setClose() {
mcurrentState = State.CLOSE;
invalidate();
}
/***
* 開啟
*/
private void setOpen() {
mcurrentState = State.OPEN;
invalidate();
}
/***
* 關閉到開啟
*/
private void setClose2Open() {
mcurrentState = State.CLOSE2OPEN;
openg2ClosevalueAnimator.removeAllUpdateListeners();
addCloseToOpenAnimator();
close2OpengvalueAnimator.start();
}
/***
* 開啟到關閉
*/
private void setOpen2Close() {
mcurrentState = State.OPEN2CLOSE;
close2OpengvalueAnimator.removeAllUpdateListeners();
addOpenToCloseAnimator();
openg2ClosevalueAnimator.start();
}複製程式碼
點選事件執行開啟關閉
/***
* 控制元件點選執行該方法 改變狀態
*/
public void onclick(){
if(mcurrentState==State.CLOSE){
setClose2Open();
}else if(mcurrentState==State.OPEN){
setOpen2Close();
}
}複製程式碼
屬性的設定
xml設定
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SwitchButton">
<attr name="backgroudColor" format="color"/>
<attr name="closeColor" format="color"/>
<attr name="openColor" format="color"/>
<attr name="circleColor" format="color"/>
<attr name="animatorTime" format="integer"/>
<attr name="padding" format="integer"/>
</declare-styleable>
</resources>複製程式碼
private void initTypeArray(Context context, AttributeSet attrs) {
TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.SwitchButton);
backgroudColor=typedArray.getColor(R.styleable.SwitchButton_backgroudColor,Color.WHITE);
closeColor=typedArray.getColor(R.styleable.SwitchButton_closeColor,Color.GRAY);
openColor=typedArray.getColor(R.styleable.SwitchButton_openColor,Color.GREEN);
circleColor=typedArray.getColor(R.styleable.SwitchButton_circleColor,Color.WHITE);
padding=typedArray.getInteger(R.styleable.SwitchButton_padding,1);
animatorTime=typedArray.getInteger(R.styleable.SwitchButton_animatorTime,500);
typedArray.recycle();
}複製程式碼
xml中設定
<com.switchbutton.SwitchButton
android:id="@+id/btn1"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginTop="20dp"
app:closeColor="@color/colorAccent"
app:openColor="@android:color/holo_green_light"
app:padding="6"
app:animatorTime="1000"
/>複製程式碼
程式碼設定
/**
* 設定背景顏色
* @param backgroudColor
*/
public void setBackgroudColor(int backgroudColor) {
this.backgroudColor = backgroudColor;
}
/**
* 關閉狀態顏色
* @param closeColor
*/
public void setCloseColor(int closeColor) {
this.closeColor = closeColor;
closePaint.setColor(closeColor);
}
/**
* 開啟狀態顏色
* @param openColor
*/
public void setOpenColor(int openColor) {
this.openColor = openColor;
openPaint.setColor(openColor);
}
/**
* 圓的顏色
* @param circleColor
*/
public void setCircleColor(int circleColor) {
this.circleColor = circleColor;
circlePaint.setColor(circleColor);
}
/**
* 間距
* @param padding
*/
public void setPadding(int padding) {
this.padding = padding;
}
/**
* 動畫時間
* @param animatorTime
*/
public void setAnimatorTime(int animatorTime) {
this.animatorTime = animatorTime;
}複製程式碼
考慮實踐應用中,需要獲取相關狀態,上傳至伺服器/根據狀態設定相關設定等
/**
* 獲取當前的狀態 用於使用時的需求
* @return
*/
public State getMcurrentState() {
return mcurrentState;
}複製程式碼
其他方法也可以實現,根據自己的需求去做相應的更改