欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义View制作动态炫酷按钮实例解析

程序员文章站 2024-03-31 19:17:04
普通按钮也就那么几种样式,看着都审美疲劳,先放效果图:   你会不会以为这个按钮是集结了很多动画的产物,我告诉你,并没有。所有的实现都是基于自定义view,采...

普通按钮也就那么几种样式,看着都审美疲劳,先放效果图:

Android自定义View制作动态炫酷按钮实例解析 

你会不会以为这个按钮是集结了很多动画的产物,我告诉你,并没有。所有的实现都是基于自定义view,采用最底层的ondraw一点一点的画出来的。没有采用一丁点的动画。虽然演示时间很短,但是要完成这么多变化,还是挺吃力。
首先讲解用法: 

public class mainactivity extends activity {
 @override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 setcontentview(r.layout.activity_main);

 final animationbutton button = (animationbutton) findviewbyid(r.id.button);
// button.settextsizetouch(25); //设置按下时字体的大小,不设置有默认值
// button.setstrokeprogress(10); //设置进度条的厚度,不设置有默认值
// button.setcolorbase(color.green); //设置整体基色,不设置有默认值
// button.setcolorback(color.gray); //设置进度条的背景色,不设置有默认值
// button.setstroke(3); //设置边框的厚度,不设置有默认值
// button.setstroketext(0); //设置文本的厚度,不设置有默认值
// button.settextsize(30); //设置文本的字体大小,不设置有默认值
// button.setround(30); //设置圆角,不设置有默认值
 button.settext("登录"); //设置文本,不设置有默认值
 button.setmode(animationbutton.mode.hand_finish); //设置进度条模式,不设置有默认值mode.auto_finish
 button.setonanimationbuttonclicklistener(new animationbutton.onanimationbuttonclicklistener() {
  @override
  public void onclick() {
  //stopprogress方法 仅仅在button.setmode(animationbutton.mode.hand_finish);之后才有效。
  button.stopprogress();
  }
 });
 }
} 

其实如果只需要最普通的功能,根本什么都不用做。因为几乎所有的参数都已经设置了固定内设置。在上面注释掉的函数用法也是用户唯一能用的几个函数,其他函数虽然标示为public,但是却是因为组件之内的方法传递,而不是给外界用户调用的。因此大家如果想自定义样式,可以调用注释里的方法。  

下面开始源码讲解,首先分解功能,所有的变化可以分为三个状态: 
1、默认状态,也就是最初的状态。主要完成的事情为:接收用户的点击,改变背景的样式从空心变为实心,动态改变文本的大小,然后就是逐渐得缩小成一个圆。 
2、进度条状态。主要完成进度条的递进,演示图上只转了一圈。其实可以通过设置一个参数,转动多圈直到用户手动停止,甚至无限转动 
3、结束状态。主要完成由圆的状态变回圆角矩形的状态,并呈现中间的logo  

既然分割出了状态,那么就采用状态机+代理模式来实现这个功能吧。首先是状态的枚举。 

/**
 * created by ccwxf on 2016/2/29.
 * 用于区别状态,有:默认状态、进度条状态、结束状态
 */
public enum status {
 default,
 progress,
 finish
}

 然后是状态机的接口,也就是所有的状态需要完成的共同的事情:

/**
 * created by ccwxf on 2016/2/29.
 */
public interface buttonstatus {
 /**
 * @return 对应的status值
 */
 status getstatus();

 /**
 * 这个状态的事件处理代理
 * @param mevent
 * @return
 */
 boolean ontouchevent(motionevent mevent);

 /**
 * 这个状态的绘制代理
 * @param mcanvas
 * @param mpaint
 */
 void ondraw(canvas mcanvas, paint mpaint);
}

 然后我们实现按钮的代码,也就是自定义view: 

/**
 * created by ccwxf on 2016/2/29.
 */
public class animationbutton extends view {

 private static int color_base = color.rgb(24, 204, 149);
 private static int color_back = color.rgb(153, 153, 153);
 private static int stroke = 3;
 private static int stroke_text = 0;
 private static int stroke_progress = 10;
 private static int text_size = 30;
 private static int text_size_touch = 25;
 private static int round = 30;
 private static string text = "提交";

 private mode mode = mode.auto_finish;
 private int maxwidth;
 private int maxheight;
 private int colorbase = color_base;
 private int colorback = color_back;
 private int stroke = stroke;
 private int stroketext = stroke_text;
 private int strokeprogress = stroke_progress;
 private int textsize = text_size;
 private int textsizetouch = text_size_touch;
 private int round = round;
 private string text = text;
 //是否停止进度条,由外界设置
 private boolean isprogressstop = false;

 private paint mpaint = new paint(paint.anti_alias_flag);
 private buttonstatus status;
 private onanimationbuttonclicklistener listener;

 public mode getmode() {
 return mode;
 }

 public void setmode(mode mode) {
 this.mode = mode;
 }

 public int getmaxwidth() {
 return maxwidth;
 }

 public int getmaxheight() {
 return maxheight;
 }

 public int gettextsizetouch() {
 return textsizetouch;
 }

 public void settextsizetouch(int textsizetouch) {
 this.textsizetouch = textsizetouch;
 }

 public int getstrokeprogress() {
 return strokeprogress;
 }

 public void setstrokeprogress(int strokeprogress) {
 this.strokeprogress = strokeprogress;
 }

 public int getcolorbase() {
 return colorbase;
 }

 public void setcolorbase(int colorbase) {
 this.colorbase = colorbase;
 }

 public int getcolorback() {
 return colorback;
 }

 public void setcolorback(int colorback) {
 this.colorback = colorback;
 }

 public int getstroke() {
 return stroke;
 }

 public void setstroke(int stroke) {
 this.stroke = stroke;
 }

 public int getstroketext() {
 return stroketext;
 }

 public void setstroketext(int stroketext) {
 this.stroketext = stroketext;
 }

 public int gettextsize() {
 return textsize;
 }

 public void settextsize(int textsize) {
 this.textsize = textsize;
 }

 public int getround() {
 return round;
 }

 public void setround(int round) {
 this.round = round;
 }

 public string gettext() {
 return text;
 }

 public void settext(string text) {
 this.text = text;
 }

 public animationbutton(context context) {
 super(context);
 }

 public animationbutton(context context, attributeset attrs) {
 super(context, attrs);
 }

 public animationbutton(context context, attributeset attrs, int defstyleattr) {
 super(context, attrs, defstyleattr);
 }

 @override
 public boolean ontouchevent(motionevent event) {
 if (status != null) {
  return status.ontouchevent(event);
 }
 return super.ontouchevent(event);
 }

 @override
 protected void ondraw(canvas canvas) {
 if (status != null) {
  status.ondraw(canvas, mpaint);
 }
 }

 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
 maxwidth = measurespec.getsize(widthmeasurespec);
 maxheight = measurespec.getsize(heightmeasurespec);
 if (maxwidth != 0 && maxheight != 0) {
  status = new defaultstatus(this, maxwidth, maxheight);
 }
 super.onmeasure(widthmeasurespec, heightmeasurespec);
 }

 /**
 * 改变整体状态
 *
 * @param s 改变的状态
 * @param width 目前的宽度
 * @param height 目前的高度
 */
 public void changestatus(status s, int width, int height, int centerx, int centery) {
 switch (s) {
  case default:
  break;
  case progress:
  //改变状态,进入进度条状态
  status = new progressstatus(this, width, height, centerx, centery);
  invalidate();
  break;
  case finish:
  //进入结束状态
  status = new finishstatus(this, width, height, centerx, centery);
  invalidate();
  break;
 }
 }

 /**
 * 外界设置停止进度条
 */
 public void stopprogress(){
 this.isprogressstop = true;
 }

 /**
 * 检查是否进度条结束
 * @return
 */
 public boolean isprogressstop(){
 return isprogressstop;
 }

 public enum mode{
 auto_finish,
 hand_finish
 }

 public interface onanimationbuttonclicklistener{
 void onclick();
 }

 public void setonanimationbuttonclicklistener(onanimationbuttonclicklistener listener){
 this.listener = listener;
 }

 public onanimationbuttonclicklistener getonanimationbuttonclicklistener(){
 return listener;
 }
}
 

上面实现了一堆的变量参数供用户自定义。然后在ontouchevent和ondraw方法中,将所有操作都代理出去。 
然后我们来实现第一个状态,也就是默认状态: 

/**
 * created by ccwxf on 2016/2/29.
 */
public class defaultstatus implements buttonstatus {
 //分别表示处于默认状态内部的四个子状态
 private static final int status_default = 0;
 private static final int status_touch = 1;
 private static final int status_up = 2;
 private static final int status_next = 3;
 //刷新width时的渐变量以及时间间距
 private static final int delay_next = 500;
 private static final int delay_frush = 10;
 private static final int pixel_frush = 8;
 //按钮对象
 private animationbutton button;
 //按钮对象的长宽与中点坐标(长宽为绘制的长宽,而不是控件的长宽)
 private int width;
 private int height;
 private int centerx;
 private int centery;
 //子状态变量
 private int status = status_default;
 private handler handler = new handler();

 public defaultstatus(animationbutton button, int width, int height) {
 this.button = button;
 this.width = width;
 this.height = height;
 this.centerx = width / 2;
 this.centery = height / 2;
 }

 @override
 public status getstatus() {
 return status.default;
 }

 @override
 public boolean ontouchevent(motionevent mevent) {
 switch (mevent.getaction()) {
  case motionevent.action_down:
  //按下时,切换到按下子状态
  if(status == status_default){
   status = status_touch;
   button.invalidate();
  }
  return true;
  case motionevent.action_up:
  case motionevent.action_cancel:
  //抬起时,或者移除控件时,切换到抬起子状态
  if(status == status_touch){
   status = status_up;
   button.invalidate();
   //过500ms延迟后开始进行伸缩变化
   handler.postdelayed(new runnable() {
   @override
   public void run() {
    //切换到next子状态
    if(status == status_up){
    status = status_next;
    }
    if(status == status_next){
    //若长宽不一致,则继续渐变,否则改变状态
    if (width >= height) {
     width -= pixel_frush;
     button.invalidate();
     handler.postdelayed(this, delay_frush);
    }else{
     button.changestatus(status.progress, width, height, centerx, centery);
    }
    }
   }
   }, delay_next);
   //响应监听器
   animationbutton.onanimationbuttonclicklistener listener = button.getonanimationbuttonclicklistener();
   if(listener != null){
   listener.onclick();
   }
  }
  break;
 }
 return false;
 }

 @override
 public void ondraw(canvas mcanvas, paint mpaint) {
 switch (status) {
  case status_default:
  ondrawdefault(mcanvas, mpaint);
  break;
  case status_touch:
  ondrawtouch(mcanvas, mpaint);
  break;
  case status_up:
  ondrawup(mcanvas, mpaint);
  break;
  case status_next:
  ondrawnext(mcanvas, mpaint);
  break;
 }
 }

 /**
 * 绘制边框,分为空心和实心两种
 *
 * @param mcanvas 画布
 * @param mpaint 画笔
 * @param style 空心或者实心
 * @param padding 边框补白
 */
 private void drawround(canvas mcanvas, paint mpaint, paint.style style, int padding) {
 mpaint.setcolor(button.getcolorbase());
 int stroke = padding;
 if (style == paint.style.stroke) {
  mpaint.setstyle(paint.style.stroke);
  mpaint.setstrokewidth(button.getstroke());
  stroke += button.getstroke() / 2;
 } else {
  mpaint.setstyle(paint.style.fill);
 }
 //绘制边框
 mcanvas.drawroundrect(new rectf(stroke, stroke, width - stroke, height - stroke), button.getround(), button.getround(), mpaint);
 }

 /**
 * 画文字,有字体大小和颜色的区别
 *
 * @param mcanvas 画布
 * @param mpaint 画笔
 * @param textsize 字体大小
 * @param textcolor 字体颜色
 */
 private void drawtext(canvas mcanvas, paint mpaint, int textsize, int textcolor) {
 mpaint.setcolor(textcolor);
 mpaint.setstrokewidth(button.getstroketext());
 mpaint.settextsize(textsize);
 paint.fontmetrics metrics = mpaint.getfontmetrics();
 int textwidth = (int) mpaint.measuretext(button.gettext());
 int baseline = (int) (height / 2 + (metrics.bottom - metrics.top) / 2 - metrics.bottom);
 mcanvas.drawtext(button.gettext(), (width - textwidth) / 2, baseline, mpaint);
 }

 /**
 * 绘制默认状态的按钮
 *
 * @param mcanvas
 * @param mpaint
 */
 private void ondrawdefault(canvas mcanvas, paint mpaint) {
 drawround(mcanvas, mpaint, paint.style.stroke, 0);
 //绘制居中文字
 drawtext(mcanvas, mpaint, button.gettextsize(), button.getcolorbase());
 }

 /**
 * 绘制按下状态的按钮
 *
 * @param mcanvas
 * @param mpaint
 */
 private void ondrawtouch(canvas mcanvas, paint mpaint) {
 drawround(mcanvas, mpaint, paint.style.fill, button.getstroke());
 //绘制文字,字体要变化
 drawtext(mcanvas, mpaint, button.gettextsizetouch(), color.white);
 }

 /**
 * 绘制抬起状态的按钮
 *
 * @param mcanvas
 * @param mpaint
 */
 private void ondrawup(canvas mcanvas, paint mpaint) {
 drawround(mcanvas, mpaint, paint.style.fill, 0);
 drawtext(mcanvas, mpaint, button.gettextsize(), color.white);
 }

 /**
 * 绘制进入下一状态的按钮
 *
 * @param mcanvas
 * @param mpaint
 */
 private void ondrawnext(canvas mcanvas, paint mpaint) {
 mpaint.setcolor(button.getcolorbase());
 mpaint.setstyle(paint.style.fill);
 //绘制边框
 if (width >= height) {
  mcanvas.drawroundrect(new rectf(centerx - width / 2, centery - height / 2, centerx + width / 2, centery + height / 2),
   button.getround(), button.getround(), mpaint);
  //绘制文字
  mpaint.setcolor(color.white);
  mpaint.setstrokewidth(button.getstroketext());
  mpaint.settextsize(button.gettextsize());
  paint.fontmetrics metrics = mpaint.getfontmetrics();
  int textwidth = (int) mpaint.measuretext(button.gettext());
  int baseline = (int) (centery + (metrics.bottom - metrics.top) / 2 - metrics.bottom);
  mcanvas.drawtext(button.gettext(), centerx - textwidth / 2, baseline, mpaint);
 } else {
  mcanvas.drawoval(new rectf(centerx - width / 2, centery - height / 2, centerx + width / 2, centery + height / 2), mpaint);
 }
 }
}

然后是第二个状态,进度条状态: 

/**
 * created by ccwxf on 2016/2/29.
 */
public class progressstatus implements buttonstatus {
 //转圈的子状态
 private static final int status_once = 0;
 private static final int status_twice = 1;
 //转圈的渐变量
 private static final int delay_progress = 10;
 private static final int angle_progress = 5;
 private static final int angle_default = -90;
 private static final int andle_full = 270;

 private animationbutton button;
 private int width;
 private int height;
 private int centerx;
 private int centery;
 private int radius;
 private int status = status_once;
 //当前的进度
 private float progress = angle_default;
 private handler handler = new handler();

 public progressstatus(animationbutton button, int width, int height, int centerx, int centery) {
 this.button = button;
 this.width = width;
 this.height = height;
 this.centerx = centerx;
 this.centery = centery;
 //绘制起点是stroke的中点,若不减去这个值,则ondraw时,会不完整。
 this.radius = (width - button.getstrokeprogress()) / 2;

 startprogress();
 }

 /**
 * 开始递归转动进度条
 */
 private void startprogress() {
 handler.postdelayed(new runnable() {
  @override
  public void run() {
  if(progress >= andle_full){
   //如果是手动结束模式
   if(button.getmode() == animationbutton.mode.hand_finish && button.isprogressstop()){
   //改变状态
   button.changestatus(status.finish, width, height, centerx, centery);
   return;
   }else{
   if(status == status_once){
    status = status_twice;
   }else if(status == status_twice){
    //如果是自动结束模式,则在第二次进度结束时改变状态
    if(button.getmode() == animationbutton.mode.auto_finish){
    //改变状态
    button.changestatus(status.finish, width, height, centerx, centery);
    return;
    }else{
    status = status_once;
    }
   }
   //重置进度
   progress = angle_default;
   }
  }
  progress += angle_progress;
  button.invalidate();
  handler.postdelayed(this, delay_progress);
  }
 }, delay_progress);
 }

 @override
 public status getstatus() {
 return status.progress;
 }

 @override
 public boolean ontouchevent(motionevent mevent) {
 return false;
 }

 @override
 public void ondraw(canvas mcanvas, paint mpaint) {
 if(status == status_once){
  //绘制灰色背景
  ondrawcircle(mcanvas, mpaint, button.getcolorback());
  //绘制绿色进度
  ondrawarc(mcanvas, mpaint, button.getcolorbase(), angle_default, progress);
 }else if(status == status_twice){
  //绘制绿色背景
  ondrawcircle(mcanvas, mpaint, button.getcolorbase());
  //绘制灰色进度
  ondrawarc(mcanvas, mpaint, button.getcolorback(), angle_default, progress);
 }
 }

 /**
 * 画一整个圆作为背景
 * @param mcanvas 画布
 * @param mpaint 画笔
 * @param color 颜色
 */
 private void ondrawcircle(canvas mcanvas, paint mpaint, int color){
 mpaint.setcolor(color);
 mpaint.setstrokewidth(button.getstrokeprogress());
 mpaint.setstyle(paint.style.stroke);
 mcanvas.drawcircle(centerx, centery, radius, mpaint);
 }

 /**
 * 画一端圆弧
 * @param mcanvas 画布
 * @param mpaint 画笔
 * @param color 颜色
 * @param start 开始角度
 * @param stop 结束角度
 */
 private void ondrawarc(canvas mcanvas, paint mpaint, int color, float start, float stop){
 mpaint.setcolor(color);
 mpaint.setstrokewidth(button.getstrokeprogress());
 mpaint.setstyle(paint.style.stroke);
 //第三个参数是扫过的角度,起点0默认为右边
 mcanvas.drawarc(new rectf(centerx - radius, centery - radius, centerx + radius, centery + radius),
  start, stop - start, false, mpaint);
 }
}
 

最后一个状态:

/**
 * created by ccwxf on 2016/2/29.
 */
public class finishstatus implements buttonstatus {

 private static final int status_stretch = 0;
 private static final int status_finish = 1;
 private static final int stroke_over = 10;
 private static final string text_over = "√";
 private static final int text_over_size = 40;
 private static final int delay_stretch = 10;
 private static final int pixel_stretch = 8;

 private animationbutton button;
 private int width;
 private int height;
 private int centerx;
 private int centery;
 private int status = status_stretch;
 private handler handler = new handler();

 public finishstatus(animationbutton button, int width, int height, int centerx, int centery) {
 this.button = button;
 this.width = width;
 this.height = height;
 this.centerx = centerx;
 this.centery = centery;

 startstretch();
 }

 /**
 * 开始伸展背景
 */
 private void startstretch() {
 handler.postdelayed(new runnable() {
  @override
  public void run() {
  if(width < button.getmaxwidth()){
   width += pixel_stretch;
   button.invalidate();
   handler.postdelayed(this, delay_stretch);
  }else{
   width = button.getmaxwidth();
   if(status == status_stretch){
   status = status_finish;
   }
   button.invalidate();
  }
  }
 }, delay_stretch);
 }

 @override
 public status getstatus() {
 return status.finish;
 }

 @override
 public boolean ontouchevent(motionevent mevent) {
 return false;
 }

 @override
 public void ondraw(canvas mcanvas, paint mpaint) {
 //绘制背景
 mpaint.setcolor(button.getcolorbase());
 mpaint.setstyle(paint.style.fill);
 mcanvas.drawroundrect(new rectf(centerx - width / 2, centery - height / 2, centerx + width / 2, centery + height / 2 ),
  button.getround(), button.getround(), mpaint);
 //绘制图片
 if(status == status_finish){
  mpaint.setcolor(color.white);
  mpaint.setstrokewidth(stroke_over);
  mpaint.settextsize(text_over_size);
  paint.fontmetrics metrics = mpaint.getfontmetrics();
  int textwidth = (int) mpaint.measuretext(text_over);
  int baseline = (int) (height / 2 + (metrics.bottom - metrics.top) / 2 - metrics.bottom);
  mcanvas.drawtext(text_over, (width - textwidth) / 2, baseline, mpaint);
 }
 }
}

好了上面就是所有的源码了。虽然被我概括成三个大状态,但是如果分细一点的话,大概需要9个状态。在各个大状态代码里面的子状态就是这个了。
 怎么使用呢,也非常简单,因为大部分的参数都有内设值了。 

/**
 * created by ccwxf on 2016/2/29.
 */
public class mainactivity extends activity {
 @override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 setcontentview(r.layout.activity_main);

 final animationbutton button = (animationbutton) findviewbyid(r.id.button);
// button.settextsizetouch(25); //设置按下时字体的大小,不设置有默认值
// button.setstrokeprogress(10); //设置进度条的厚度,不设置有默认值
// button.setcolorbase(color.green); //设置整体基色,不设置有默认值
// button.setcolorback(color.gray); //设置进度条的背景色,不设置有默认值
// button.setstroke(3); //设置边框的厚度,不设置有默认值
// button.setstroketext(0); //设置文本的厚度,不设置有默认值
// button.settextsize(30); //设置文本的字体大小,不设置有默认值
// button.setround(30); //设置圆角,不设置有默认值
 button.settext("登录"); //设置文本,不设置有默认值
 button.setmode(animationbutton.mode.hand_finish); //设置进度条模式,不设置有默认值mode.auto_finish
 button.setonanimationbuttonclicklistener(new animationbutton.onanimationbuttonclicklistener() {
  @override
  public void onclick() {
  //stopprogress方法 仅仅在button.setmode(animationbutton.mode.hand_finish);之后才有效。
  button.stopprogress();
  }
 });
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。