欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android實(shí)現(xiàn)炫酷的CheckBox效果

 更新時(shí)間:2016年10月11日 08:41:56   投稿:daisy  
大家是不是對(duì)系統(tǒng)自帶的CheckBox產(chǎn)生乏味感了呢?今天這篇文章給大家?guī)淼氖且豢钊碌腃heckBox,下面來一起看看下面的CheckBox吧!有需要的朋友們可以參考借鑒。

首先貼出實(shí)現(xiàn)的效果圖:

gif的效果可能有點(diǎn)過快,在真機(jī)上運(yùn)行的效果會(huì)更好一些。我們主要的思路就是利用屬性動(dòng)畫來動(dòng)態(tài)地畫出選中狀態(tài)以及對(duì)勾的繪制過程??吹缴厦娴男Ч麍D,相信大家都迫不及待地要躍躍欲試了,那就讓我們開始吧。

自定義View的第一步:自定義屬性。

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="SmoothCheckBox">
 <!-- 動(dòng)畫持續(xù)時(shí)間 -->
 <attr name="duration" format="integer"></attr>
 <!-- 邊框?qū)挾?-->
 <attr name="strikeWidth" format="dimension|reference"></attr>
 <!-- 邊框顏色 -->
 <attr name="borderColor" format="color|reference"></attr>
 <!-- 選中狀態(tài)的顏色 -->
 <attr name="trimColor" format="color|reference"></attr>
 <!-- 對(duì)勾顏色 -->
 <attr name="tickColor" format="color|reference"></attr>
 <!-- 對(duì)勾寬度 -->
 <attr name="tickWidth" format="dimension|reference"></attr>
 </declare-styleable>
</resources>

我們把CheckBox取名為SmoothCheckBox,定義了幾個(gè)等等要用到的屬性。這一步很簡(jiǎn)單,相信大家都熟練了。

接下來看一看onMeasure(int widthMeasureSpec, int heightMeasureSpec):

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
 if (widthMode == MeasureSpec.EXACTLY) {
  mWidth = widthSize;
 } else {
  mWidth = 40;
 }

 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
 if (heightMode == MeasureSpec.EXACTLY) {
  mHeight = heightSize;
 } else {
  mHeight = 40;
 }
 setMeasuredDimension(mWidth, mHeight);
 int size = Math.min(mWidth, mHeight);
 center = size / 2;
 mRadius = (int) ((size - mStrokeWidth) / 2 / 1.2f);
 startPoint.set(center * 14 / 30, center * 28 / 30);
 breakPoint.set(center * 26 / 30, center * 40 / 30);
 endPoint.set(center * 44 / 30, center * 20 / 30);

 downLength = (float) Math.sqrt(Math.pow(startPoint.x - breakPoint.x, 2f) + Math.pow(startPoint.y - breakPoint.y, 2f));
 upLength = (float) Math.sqrt(Math.pow(endPoint.x - breakPoint.x, 2f) + Math.pow(endPoint.y - breakPoint.y, 2f));
 totalLength = downLength + upLength;
}

一開始是測(cè)量了SmoothCheckBox的寬、高度,默認(rèn)的寬高度隨便定義了一個(gè),當(dāng)然你們可以自己去修改和完善它。然后就是設(shè)置半徑之類的,最后的startPoint、breakPoint、endPoint分別對(duì)應(yīng)著選中時(shí)對(duì)勾的三個(gè)點(diǎn)(至于為何是這幾個(gè)數(shù)字,那完全是經(jīng)驗(yàn)值);downLength就是startPoint和breakPoint的距離,而相對(duì)應(yīng)的upLength就是breakPoint和endPoint的距離。即以下圖示:

在看onDraw(Canvas canvas)之前我們先來看兩組動(dòng)畫,分別是選中狀態(tài)時(shí)的動(dòng)畫以及未選中狀態(tài)的動(dòng)畫:

// 由未選中到選中的動(dòng)畫
private void checkedAnimation() {
 animatedValue = 0f;
 tickValue = 0f;
 // 選中時(shí)底色的動(dòng)畫
 mValueAnimator = ValueAnimator.ofFloat(0f, 1.2f, 1f).setDuration(2 * duration / 5);
 mValueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
 // 對(duì)勾的動(dòng)畫
 mTickValueAnimator = ValueAnimator.ofFloat(0f, 1f).setDuration(3 * duration / 5);
 mTickValueAnimator.setInterpolator(new LinearInterpolator());
 mTickValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator valueAnimator) {
 // 得到動(dòng)畫執(zhí)行進(jìn)度
   tickValue = (float) valueAnimator.getAnimatedValue();
   postInvalidate();
  }
 });
 mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator valueAnimator) {
 // 得到動(dòng)畫執(zhí)行進(jìn)度
   animatedValue = (float) valueAnimator.getAnimatedValue();
   postInvalidate();
  }
 });
 mValueAnimator.addListener(new AnimatorListenerAdapter() {
  @Override
  public void onAnimationEnd(Animator animation) {
 //當(dāng)?shù)咨膭?dòng)畫完成后再開始對(duì)勾的動(dòng)畫
   mTickValueAnimator.start();
   Log.i(TAG," mTickValueAnimator.start();");
  }
 });
 mValueAnimator.start();
}

// 由選中到未選中的動(dòng)畫
private void uncheckedAnimation() {
 animatedValue = 0f;
 mValueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(2 * duration / 5);
 mValueAnimator.setInterpolator(new AccelerateInterpolator());
 mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator valueAnimator) {
   animatedValue = (float) valueAnimator.getAnimatedValue();
   postInvalidate();
  }
 });
 mValueAnimator.start();
}

這兩組動(dòng)畫在點(diǎn)擊SmoothCheckBox的時(shí)候會(huì)調(diào)用。相似的,都是在動(dòng)畫執(zhí)行中得到動(dòng)畫執(zhí)行的進(jìn)度,再來調(diào)用postInvalidate();讓SmoothCheckBox重繪??赐赀@個(gè)之后就是終極大招onDraw(Canvas canvas)了:

@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 canvas.save();
 drawBorder(canvas);
 drawTrim(canvas);
 if (isChecked) {
  drawTick(canvas);
 }
 canvas.restore();
}

// 畫對(duì)勾
private void drawTick(Canvas canvas) {
 // 得到畫對(duì)勾的進(jìn)度
 float temp = tickValue * totalLength;
 Log.i(TAG, "temp:" + temp + "downlength :" + downLength);
 //判斷是否是剛開始畫對(duì)勾的時(shí)候,即等于startPoint
 if (Float.compare(tickValue, 0f) == 0) {
  Log.i(TAG, "startPoint : " + startPoint.x + ", " + startPoint.y);
  path.reset();
  path.moveTo(startPoint.x, startPoint.y);
 }
 // 如果畫對(duì)勾的進(jìn)度已經(jīng)超過breakPoint的時(shí)候,即(breakPoint,endPoint]
 if (temp > downLength) {
  path.moveTo(startPoint.x, startPoint.y);
  path.lineTo(breakPoint.x, breakPoint.y);
  Log.i(TAG, "endPoint : " + endPoint.x + ", " + endPoint.y);
  path.lineTo((endPoint.x - breakPoint.x) * (temp - downLength) / upLength + breakPoint.x, (endPoint.y - breakPoint.y) * (temp - downLength) / upLength + breakPoint.y);
 } else {
 //畫對(duì)勾的進(jìn)度介于startPoinit和breakPoint之間,即(startPoint,breakPoint]
  Log.i(TAG, "down x : " + (breakPoint.x - startPoint.x) * temp / downLength + ",down y: " + (breakPoint.y - startPoint.y) * temp / downLength);
  path.lineTo((breakPoint.x - startPoint.x) * temp / downLength + startPoint.x, (breakPoint.y - startPoint.y) * temp / downLength + startPoint.y);
 }
 canvas.drawPath(path, tickPaint);
}

// 畫邊框
private void drawBorder(Canvas canvas) {
 float temp;
 // 通過animatedValue讓邊框產(chǎn)生一個(gè)“OverShooting”的動(dòng)畫
 if (animatedValue > 1f) {
  temp = animatedValue * mRadius;
 } else {
  temp = mRadius;
 }
 canvas.drawCircle(center, center, temp, borderPaint);
}

// 畫checkbox內(nèi)部
private void drawTrim(Canvas canvas) {
 canvas.drawCircle(center, center, (mRadius - mStrokeWidth) * animatedValue, trimPaint);
}

onDraw(Canvas canvas)代碼中的邏輯基本都加了注釋,主要就是原理搞懂了就比較簡(jiǎn)單了。在繪制對(duì)勾時(shí)要區(qū)分當(dāng)前處于繪制對(duì)勾的哪種狀態(tài),然后對(duì)應(yīng)做處理畫出線條,剩下的就簡(jiǎn)單了。關(guān)于SmoothCheckBox的講解到這里就差不多了。

下面就貼出SmoothCheckBox的完整代碼:

public class SmoothCheckBox extends View implements View.OnClickListener {

 // 動(dòng)畫持續(xù)時(shí)間
 private long duration;
 // 邊框?qū)挾?
 private float mStrokeWidth;
 // 對(duì)勾寬度
 private float mTickWidth;
 // 內(nèi)飾畫筆
 private Paint trimPaint;
 // 邊框畫筆
 private Paint borderPaint;
 // 對(duì)勾畫筆
 private Paint tickPaint;
 // 默認(rèn)邊框?qū)挾?
 private float defaultStrikeWidth;
 // 默認(rèn)對(duì)勾寬度
 private float defaultTickWidth;
 // 寬度
 private int mWidth;
 // 高度
 private int mHeight;
 // 邊框顏色
 private int borderColor;
 // 內(nèi)飾顏色
 private int trimColor;
 // 對(duì)勾顏色
 private int tickColor;
 // 半徑
 private int mRadius;
 // 中心點(diǎn)
 private int center;
 // 是否是選中
 private boolean isChecked;
 //對(duì)勾向下的長度
 private float downLength;
 //對(duì)勾向上的長度
 private float upLength;
 // 對(duì)勾的總長度
 private float totalLength;
 // 監(jiān)聽器
 private OnCheckedChangeListener listener;

 private ValueAnimator mValueAnimator;

 private ValueAnimator mTickValueAnimator;

 private float animatedValue;

 private float tickValue;
 // 對(duì)勾開始點(diǎn)
 private Point startPoint = new Point();
 // 對(duì)勾轉(zhuǎn)折點(diǎn)
 private Point breakPoint = new Point();
 // 對(duì)勾結(jié)束點(diǎn)
 private Point endPoint = new Point();

 private static final String TAG = "SmoothCheckBox";

 private static final String KEY_INSTANCE_STATE = "InstanceState";

 private Path path = new Path();

 public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
  this.listener = listener;
 }

 public SmoothCheckBox(Context context) {
  this(context, null);
 }

 public SmoothCheckBox(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

 public SmoothCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.SmoothCheckBox);
  duration = a.getInt(R.styleable.SmoothCheckBox_duration, 600);

  defaultStrikeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
  mStrokeWidth = a.getDimension(R.styleable.SmoothCheckBox_strikeWidth, defaultStrikeWidth);
  defaultTickWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics());
  mTickWidth = a.getDimension(R.styleable.SmoothCheckBox_tickWidth, defaultTickWidth);
  borderColor = a.getColor(R.styleable.SmoothCheckBox_borderColor, getResources().getColor(android.R.color.darker_gray));
  trimColor = a.getColor(R.styleable.SmoothCheckBox_trimColor, getResources().getColor(android.R.color.holo_green_light));
  tickColor = a.getColor(R.styleable.SmoothCheckBox_tickColor, getResources().getColor(android.R.color.white));
  a.recycle();

  trimPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  trimPaint.setStyle(Paint.Style.FILL);
  trimPaint.setColor(trimColor);

  borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  borderPaint.setStrokeWidth(mStrokeWidth);
  borderPaint.setColor(borderColor);
  borderPaint.setStyle(Paint.Style.STROKE);

  tickPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  tickPaint.setColor(tickColor);
  tickPaint.setStyle(Paint.Style.STROKE);
  tickPaint.setStrokeCap(Paint.Cap.ROUND);
  tickPaint.setStrokeWidth(mTickWidth);

  setOnClickListener(this);
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  if (widthMode == MeasureSpec.EXACTLY) {
   mWidth = widthSize;
  } else {
   mWidth = 40;
  }

  int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  if (heightMode == MeasureSpec.EXACTLY) {
   mHeight = heightSize;
  } else {
   mHeight = 40;
  }
  setMeasuredDimension(mWidth, mHeight);
  int size = Math.min(mWidth, mHeight);
  center = size / 2;
  mRadius = (int) ((size - mStrokeWidth) / 2 / 1.2f);
  startPoint.set(center * 14 / 30, center * 28 / 30);
  breakPoint.set(center * 26 / 30, center * 40 / 30);
  endPoint.set(center * 44 / 30, center * 20 / 30);

  downLength = (float) Math.sqrt(Math.pow(startPoint.x - breakPoint.x, 2f) + Math.pow(startPoint.y - breakPoint.y, 2f));
  upLength = (float) Math.sqrt(Math.pow(endPoint.x - breakPoint.x, 2f) + Math.pow(endPoint.y - breakPoint.y, 2f));
  totalLength = downLength + upLength;
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  canvas.save();
  drawBorder(canvas);
  drawTrim(canvas);
  if (isChecked) {
   drawTick(canvas);
  }
  canvas.restore();
 }

 @Override
 protected Parcelable onSaveInstanceState() {
  Bundle bundle = new Bundle();
  bundle.putParcelable(KEY_INSTANCE_STATE, super.onSaveInstanceState());
  bundle.putBoolean(KEY_INSTANCE_STATE, isChecked);
  return bundle;
 }

 @Override
 protected void onRestoreInstanceState(Parcelable state) {
  if (state instanceof Bundle) {
   Bundle bundle = (Bundle) state;
   boolean isChecked = bundle.getBoolean(KEY_INSTANCE_STATE);
   setChecked(isChecked);
   super.onRestoreInstanceState(bundle.getParcelable(KEY_INSTANCE_STATE));
   return;
  }
  super.onRestoreInstanceState(state);
 }

 // 切換狀態(tài)
 private void toggle() {
  isChecked = !isChecked;
  if (listener != null) {
   listener.onCheckedChanged(this, isChecked);
  }
  if (isChecked) {
   checkedAnimation();
  } else {
   uncheckedAnimation();
  }
 }

 // 由未選中到選中的動(dòng)畫
 private void checkedAnimation() {
  animatedValue = 0f;
  tickValue = 0f;
  mValueAnimator = ValueAnimator.ofFloat(0f, 1.2f, 1f).setDuration(2 * duration / 5);
  mValueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
  mTickValueAnimator = ValueAnimator.ofFloat(0f, 1f).setDuration(3 * duration / 5);
  mTickValueAnimator.setInterpolator(new LinearInterpolator());
  mTickValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    tickValue = (float) valueAnimator.getAnimatedValue();
    postInvalidate();
   }
  });
  mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    animatedValue = (float) valueAnimator.getAnimatedValue();
    postInvalidate();
   }
  });
  mValueAnimator.addListener(new AnimatorListenerAdapter() {
   @Override
   public void onAnimationEnd(Animator animation) {
    mTickValueAnimator.start();
    Log.i(TAG," mTickValueAnimator.start();");
   }
  });
  mValueAnimator.start();
 }

 // 由選中到未選中的動(dòng)畫
 private void uncheckedAnimation() {
  animatedValue = 0f;
  mValueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(2 * duration / 5);
  mValueAnimator.setInterpolator(new AccelerateInterpolator());
  mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    animatedValue = (float) valueAnimator.getAnimatedValue();
    postInvalidate();
   }
  });
  mValueAnimator.start();
 }

 // 畫對(duì)勾
 private void drawTick(Canvas canvas) {
  float temp = tickValue * totalLength;
  Log.i(TAG, "temp:" + temp + "downlength :" + downLength);
  if (Float.compare(tickValue, 0f) == 0) {
   Log.i(TAG, "startPoint : " + startPoint.x + ", " + startPoint.y);
   path.reset();
   path.moveTo(startPoint.x, startPoint.y);
  }
  if (temp > downLength) {
   path.moveTo(startPoint.x, startPoint.y);
   path.lineTo(breakPoint.x, breakPoint.y);
   Log.i(TAG, "endPoint : " + endPoint.x + ", " + endPoint.y);
   path.lineTo((endPoint.x - breakPoint.x) * (temp - downLength) / upLength + breakPoint.x, (endPoint.y - breakPoint.y) * (temp - downLength) / upLength + breakPoint.y);
  } else {
   Log.i(TAG, "down x : " + (breakPoint.x - startPoint.x) * temp / downLength + ",down y: " + (breakPoint.y - startPoint.y) * temp / downLength);
   path.lineTo((breakPoint.x - startPoint.x) * temp / downLength + startPoint.x, (breakPoint.y - startPoint.y) * temp / downLength + startPoint.y);
  }
  canvas.drawPath(path, tickPaint);
 }

 // 畫邊框
 private void drawBorder(Canvas canvas) {
  float temp;
  if (animatedValue > 1f) {
   temp = animatedValue * mRadius;
  } else {
   temp = mRadius;
  }
  canvas.drawCircle(center, center, temp, borderPaint);
 }

 // 畫checkbox內(nèi)部
 private void drawTrim(Canvas canvas) {
  canvas.drawCircle(center, center, (mRadius - mStrokeWidth) * animatedValue, trimPaint);
 }

 @Override
 public void onClick(View view) {
  toggle();
 }

 /**
  * 判斷checkbox是否選中狀態(tài)
  *
  * @return
  */
 public boolean isChecked() {
  return isChecked;
 }

 /**
  * 設(shè)置checkbox的狀態(tài)
  *
  * @param isChecked 是否選中
  */
 public void setChecked(boolean isChecked) {
  this.setChecked(isChecked, false);
 }

 /**
  * 設(shè)置checkbox的狀態(tài)
  *
  * @param isChecked 是否選中
  * @param isAnimation 切換時(shí)是否有動(dòng)畫
  */
 public void setChecked(boolean isChecked, boolean isAnimation) {
  this.isChecked = isChecked;
  if (isAnimation) {
   if (isChecked) {
    checkedAnimation();
   } else {
    uncheckedAnimation();
   }
  } else {
   animatedValue = isChecked ? 1f : 0f;
   tickValue = 1f;
   invalidate();
  }
  if (listener != null) {
   listener.onCheckedChanged(this, isChecked);
  }
 }

 public interface OnCheckedChangeListener {
  void onCheckedChanged(SmoothCheckBox smoothCheckBox, boolean isChecked);
 }
}

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。

相關(guān)文章

最新評(píng)論