Android自定义view仿IOS开关效果
本文主要讲解如何在 android 下实现高仿 ios 的开关按钮,并非是在 android 自带的 togglebutton 上修改,而是使用 api 提供的 ondraw、onmeasure、canvas 方法,纯手工绘制。基本原理就是在 canvas 上叠着放两张图片,上面的图片根据手指触摸情况,不断移动,实现开关效果。
废话不说,上效果图,看看怎么样
样式如下:
网上也有实现这种效果的,但是大都滑动没中间消失的动画,或者是很复杂,今天用简单的绘图方式实现,重点就在ondraw里绘图。
功能点:
- 不滑出边界,超过一半自动切换(边界判断)
- 可滑动,也可点击(事件共存)
- 提供状态改变监听(设置回调)
- 通过属性设置初始状态、背景图片、滑动按钮(自定义属性)
自定义view的概述
android 在绘制 view 时,其实就像蒙上眼睛在画板上画画,它并不知道应该把 view 画多大,画哪儿,怎么画。所以我们必须实现 view 的三个重要方法,以告诉它这些信息。即:onmeasure(画多大),onlayout(画哪儿),ondraw(怎么画)。
view的生命周期
在动手写之前,必须先了解以下几个概念:
1.view 的默认不支持 wrap_content,必须重写 onmeasure 方法,通过 setmeasureddimension() 设置尺寸
2.基本的事件分发机制:onclicklistener 一定是在 ontouchevent 之后执行
自定义view的流程
开始动手
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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: Web前端开发入门不得不看