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

Android自定义view仿IOS开关效果

程序员文章站 2023-11-25 16:48:46
本文主要讲解如何在 android 下实现高仿 ios 的开关按钮,并非是在 android 自带的 togglebutton 上修改,而是使用 api 提供的 ondra...

本文主要讲解如何在 android 下实现高仿 ios 的开关按钮,并非是在 android 自带的 togglebutton 上修改,而是使用 api 提供的 ondraw、onmeasure、canvas 方法,纯手工绘制。基本原理就是在 canvas 上叠着放两张图片,上面的图片根据手指触摸情况,不断移动,实现开关效果。

废话不说,上效果图,看看怎么样

Android自定义view仿IOS开关效果

样式如下: 

 Android自定义view仿IOS开关效果

网上也有实现这种效果的,但是大都滑动没中间消失的动画,或者是很复杂,今天用简单的绘图方式实现,重点就在ondraw里绘图。

功能点:

  • 不滑出边界,超过一半自动切换(边界判断)
  • 可滑动,也可点击(事件共存)
  • 提供状态改变监听(设置回调)
  • 通过属性设置初始状态、背景图片、滑动按钮(自定义属性)

自定义view的概述

android 在绘制 view 时,其实就像蒙上眼睛在画板上画画,它并不知道应该把 view 画多大,画哪儿,怎么画。所以我们必须实现 view 的三个重要方法,以告诉它这些信息。即:onmeasure(画多大),onlayout(画哪儿),ondraw(怎么画)。

view的生命周期

Android自定义view仿IOS开关效果

在动手写之前,必须先了解以下几个概念:

1.view 的默认不支持 wrap_content,必须重写 onmeasure 方法,通过 setmeasureddimension() 设置尺寸
2.基本的事件分发机制:onclicklistener 一定是在 ontouchevent 之后执行

自定义view的流程

Android自定义view仿IOS开关效果

开始动手

1.导入开关的样式文件

<resources>

 <!-- base application theme. -->
 <style name="apptheme" parent="theme.appcompat.light.darkactionbar">
 <!-- customize your theme here. -->
 <item name="colorprimary">@color/colorprimary</item>
 <item name="colorprimarydark">@color/colorprimarydark</item>
 <item name="coloraccent">@color/coloraccent</item>
 </style>
 <!--高仿ios7开关 - 样式-->
 <declare-styleable name="switchbutton">
 <attr name="buttoncolor" format="color" />
 </declare-styleable>

</resources>

2.开始自定义view,重点在ondraw()

/**
 * author:and
 * time:2018/3/20.
 * email:2911743255@qq.com
 * description:
 * detail:仿ios开关
 */
public class switchbutton extends view {
 //画笔
 private final paint mpaint = new paint();
 private static final double mbtnheight = 0.55;
 private static final int offset = 3;
 private int mheight;
 private float manimate = 0l;
 //此处命名不规范,目的和android自带的switch有相同的用法
 private boolean checked = false;
 private float mscale;
 private int mselectcolor;
 private oncheckedchangelistener moncheckedchangelistener;

 public switchbutton(context context) {
 this(context, null);
 }

 public switchbutton(context context, attributeset attrs) {
 super(context, attrs);
 typedarray typedarray = context.obtainstyledattributes(attrs, r.styleable.switchbutton);
 mselectcolor = typedarray.getcolor(r.styleable.switchbutton_buttoncolor, color.parsecolor("#2eaa57"));
 typedarray.recycle();
 }

 /**
 * @param widthmeasurespec
 * @param heightmeasurespec 高度是是宽度的0.55倍
 */
 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
 int width = measurespec.getsize(widthmeasurespec);
 mheight = (int) (mbtnheight * width);
 setmeasureddimension(width, mheight);
 }

 @override
 protected void ondraw(canvas canvas) {
 super.ondraw(canvas);
 mpaint.setstyle(paint.style.fill);
 mpaint.setantialias(true);
 mpaint.setcolor(mselectcolor);
 rect rect = new rect(0, 0, getwidth(), getheight());
 rectf rectf = new rectf(rect);
 //绘制圆角矩形
 canvas.drawroundrect(rectf, mheight / 2, mheight / 2, mpaint);

 //以下save和restore很重要,确保动画在中间一层 ,如果大家不明白,可以去搜下用法

 canvas.save();
 mpaint.setcolor(color.parsecolor("#e6e6e6"));
 manimate = manimate - 0.1f > 0 ? manimate - 0.1f : 0; // 动画标示 ,重绘10次,借鉴被人的动画
 mscale = (!checked ? 1 - manimate : manimate);
 canvas.scale(mscale, mscale, getwidth() - getheight() / 2, rect.centery());
 //绘制缩放的灰色圆角矩形
 canvas.drawroundrect(rectf, mheight / 2, mheight / 2, mpaint);

 mpaint.setcolor(color.white);
 rect rect_inner = new rect(offset, offset, getwidth() - offset, getheight() - offset);
 rectf rect_f_inner = new rectf(rect_inner);
 //绘制缩放的白色圆角矩形,和上边的重叠实现灰色边框效果
 canvas.drawroundrect(rect_f_inner, (mheight - 8) / 2, (mheight - 8) / 2, mpaint);
 canvas.restore();

 //中间圆形平移
 int swidth = getwidth();
 int btranslatex = swidth - getheight();
 final float translate = btranslatex * (!checked ? manimate : 1 - manimate);
 canvas.translate(translate, 0);

 //以下两个圆带灰色边框
 mpaint.setcolor(color.parsecolor("#e6e6e6"));
 canvas.drawcircle(getheight() / 2, getheight() / 2, getheight() / 2 - offset / 2, mpaint);

 mpaint.setcolor(color.white);
 canvas.drawcircle(getheight() / 2, getheight() / 2, getheight() / 2 - offset, mpaint);

 if (mscale > 0) {
  mpaint.reset();
  invalidate();
 }
 }

 /**
 * 事件分发
 *
 * @param event
 * @return
 */
 @override
 public boolean ontouchevent(motionevent event) {
 switch (event.getaction()) {
  case motionevent.action_down:
  return true;
  case motionevent.action_move:
  break;
  case motionevent.action_up:
  manimate = 1;
  checked = !checked;

  if (moncheckedchangelistener != null) {

   moncheckedchangelistener.oncheckedchanged(checked);

  }
  invalidate();
  break;
 }
 return super.ontouchevent(event);
 }

 /**
 * 状态构造函数
 *
 * @return
 */
 public boolean ischecked() {
 return checked;
 }

 public void setchecked(boolean checked) {
 this.checked = checked;
 }

 /**
 * 构造函数
 *
 * @return
 */
 public oncheckedchangelistener getmoncheckedchangelistener() {
 return moncheckedchangelistener;
 }

 /**
 * 调用方法
 *
 * @param moncheckedchangelistener
 */
 public void setmoncheckedchangelistener(oncheckedchangelistener moncheckedchangelistener) {
 this.moncheckedchangelistener = moncheckedchangelistener;
 }

 /**
 * 滑动接口
 */
 public interface oncheckedchangelistener {
 void oncheckedchanged(boolean ischecked);
 }

}

3.activity中使用

@override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 setcontentview(r.layout.activity_main);
 mbtnswitch = (switchbutton) findviewbyid(r.id.switch_btn);
 mbtnswitch.setmoncheckedchangelistener(new switchbutton.oncheckedchangelistener() {
  @override
  public void oncheckedchanged(boolean ischecked) {
  toast.maketext(mainactivity.this, "" + ischecked, toast.length_short).show();
  }
 });
 }

当然,也可以上来就给开关定义状态值

mbtnswitch.setchecked(boolean);

好了,自定义工作全部完成!!

那么300行左右的代码 完成了我们的仿ios switchbutton 的控件 switchview

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