android自定义开关控件-SlideSwitch的实例
程序员文章站
2024-03-03 18:42:34
iphone上有开关控件,很漂亮,其实android4.0以后也有switch控件,但是只能用在4.0以后的系统中,这就失去了其使用价值,而且我觉得它的界面也不是很好看。最...
iphone上有开关控件,很漂亮,其实android4.0以后也有switch控件,但是只能用在4.0以后的系统中,这就失去了其使用价值,而且我觉得它的界面也不是很好看。最近看到了百度魔拍上面的一个控件,觉得很漂亮啊,然后反编译了下,尽管没有混淆过,但是还是不好读,然后就按照自己的想法写了个,功能和百度魔拍类似。
下面是百度魔拍的效果和slideswitch的效果
一、原理
继承自view类,override其ondraw函数,把两个背景图(一个灰的一个红的)和一个开关图(圆开关)通过canvas画出来;同时override其ontouchevent函数,实现滑动效果;最后开启一个线程做动画,实现缓慢滑动的效果。
二、代码
slideswitch.java
package com.example.hellojni; import android.content.context; import android.content.res.resources; import android.graphics.bitmap; import android.graphics.bitmapfactory; import android.graphics.canvas; import android.graphics.color; import android.graphics.paint; import android.graphics.rect; import android.graphics.typeface; import android.util.attributeset; import android.util.log; import android.view.motionevent; import android.view.view; import android.view.viewgroup.layoutparams; /** * slideswitch 仿iphone滑动开关组件,仿百度魔图滑动开关组件 * 组件分为三种状态:打开、关闭、正在滑动<br/> * 使用方法: * <pre>slideswitch slideswitch = new slideswitch(this); *slideswitch.setonswitchchangedlistener(onswitchchangedlistener); *linearlayout.addview(slideswitch); </pre> 注:也可以加载在xml里面使用 * @author scott * */ public class slideswitch extends view { public static final string tag = "slideswitch"; public static final int switch_off = 0;//关闭状态 public static final int switch_on = 1;//打开状态 public static final int switch_scroling = 2;//滚动状态 //用于显示的文本 private string montext = "打开"; private string mofftext = "关闭"; private int mswitchstatus = switch_off; private boolean mhasscrolled = false;//表示是否发生过滚动 private int msrcx = 0, mdstx = 0; private int mbmpwidth = 0; private int mbmpheight = 0; private int mthumbwidth = 0; private paint mpaint = new paint(paint.anti_alias_flag); private onswitchchangedlistener monswitchchangedlistener = null; //开关状态图 bitmap mswitch_off, mswitch_on, mswitch_thumb; public slideswitch(context context) { this(context, null); } public slideswitch(context context, attributeset attrs) { super(context, attrs); init(); } public slideswitch(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); init(); } //初始化三幅图片 private void init() { resources res = getresources(); mswitch_off = bitmapfactory.decoderesource(res, r.drawable.bg_switch_off); mswitch_on = bitmapfactory.decoderesource(res, r.drawable.bg_switch_on); mswitch_thumb = bitmapfactory.decoderesource(res, r.drawable.switch_thumb); mbmpwidth = mswitch_on.getwidth(); mbmpheight = mswitch_on.getheight(); mthumbwidth = mswitch_thumb.getwidth(); } @override public void setlayoutparams(layoutparams params) { params.width = mbmpwidth; params.height = mbmpheight; super.setlayoutparams(params); } /** * 为开关控件设置状态改变监听函数 * @param onswitchchangedlistener 参见 {@link onswitchchangedlistener} */ public void setonswitchchangedlistener(onswitchchangedlistener onswitchchangedlistener) { monswitchchangedlistener = onswitchchangedlistener; } /** * 设置开关上面的文本 * @param ontext 控件打开时要显示的文本 * @param offtext 控件关闭时要显示的文本 */ public void settext(final string ontext, final string offtext) { montext = ontext; mofftext =offtext; invalidate(); } /** * 设置开关的状态 * @param on 是否打开开关 打开为true 关闭为false */ public void setstatus(boolean on) { mswitchstatus = ( on ? switch_on : switch_off); } @override public boolean ontouchevent(motionevent event) { int action = event.getaction(); log.d(tag, "ontouchevent x=" + event.getx()); switch (action) { case motionevent.action_down: msrcx = (int) event.getx(); break; case motionevent.action_move: mdstx = math.max( (int) event.getx(), 10); mdstx = math.min( mdstx, 62); if(msrcx == mdstx) return true; mhasscrolled = true; animationtransrunnable atransrunnable = new animationtransrunnable(msrcx, mdstx, 0); new thread(atransrunnable).start(); msrcx = mdstx; break; case motionevent.action_up: if(mhasscrolled == false)//如果没有发生过滑动,就意味着这是一次单击过程 { mswitchstatus = math.abs(mswitchstatus-1); int xfrom = 10, xto = 62; if(mswitchstatus == switch_off) { xfrom = 62; xto = 10; } animationtransrunnable runnable = new animationtransrunnable(xfrom, xto, 1); new thread(runnable).start(); } else { invalidate(); mhasscrolled = false; } //状态改变的时候 回调事件函数 if(monswitchchangedlistener != null) { monswitchchangedlistener.onswitchchanged(this, mswitchstatus); } break; default: break; } return true; } @override protected void onsizechanged(int w, int h, int oldw, int oldh) { super.onsizechanged(w, h, oldw, oldh); } @override protected void ondraw(canvas canvas) { super.ondraw(canvas); //绘图的时候 内部用到了一些数值的硬编码,其实不太好, //主要是考虑到图片的原因,图片周围有透明边界,所以要有一定的偏移 //硬编码的数值只要看懂了代码,其实可以理解其含义,可以做相应改进。 mpaint.settextsize(14); mpaint.settypeface(typeface.default_bold); if(mswitchstatus == switch_off) { drawbitmap(canvas, null, null, mswitch_off); drawbitmap(canvas, null, null, mswitch_thumb); mpaint.setcolor(color.rgb(105, 105, 105)); canvas.translate(mswitch_thumb.getwidth(), 0); canvas.drawtext(mofftext, 0, 20, mpaint); } else if(mswitchstatus == switch_on) { drawbitmap(canvas, null, null, mswitch_on); int count = canvas.save(); canvas.translate(mswitch_on.getwidth() - mswitch_thumb.getwidth(), 0); drawbitmap(canvas, null, null, mswitch_thumb); mpaint.setcolor(color.white); canvas.restoretocount(count); canvas.drawtext(montext, 17, 20, mpaint); } else //switch_scroling { mswitchstatus = mdstx > 35 ? switch_on : switch_off; drawbitmap(canvas, new rect(0, 0, mdstx, mbmpheight), new rect(0, 0, (int)mdstx, mbmpheight), mswitch_on); mpaint.setcolor(color.white); canvas.drawtext(montext, 17, 20, mpaint); int count = canvas.save(); canvas.translate(mdstx, 0); drawbitmap(canvas, new rect(mdstx, 0, mbmpwidth, mbmpheight), new rect(0, 0, mbmpwidth - mdstx, mbmpheight), mswitch_off); canvas.restoretocount(count); count = canvas.save(); canvas.cliprect(mdstx, 0, mbmpwidth, mbmpheight); canvas.translate(mthumbwidth, 0); mpaint.setcolor(color.rgb(105, 105, 105)); canvas.drawtext(mofftext, 0, 20, mpaint); canvas.restoretocount(count); count = canvas.save(); canvas.translate(mdstx - mthumbwidth / 2, 0); drawbitmap(canvas, null, null, mswitch_thumb); canvas.restoretocount(count); } } public void drawbitmap(canvas canvas, rect src, rect dst, bitmap bitmap) { dst = (dst == null ? new rect(0, 0, bitmap.getwidth(), bitmap.getheight()) : dst); paint paint = new paint(); canvas.drawbitmap(bitmap, src, dst, paint); } /** * animationtransrunnable 做滑动动画所使用的线程 */ private class animationtransrunnable implements runnable { private int srcx, dstx; private int duration; /** * 滑动动画 * @param srcx 滑动起始点 * @param dstx 滑动终止点 * @param duration 是否采用动画,1采用,0不采用 */ public animationtransrunnable(float srcx, float dstx, final int duration) { this.srcx = (int)srcx; this.dstx = (int)dstx; this.duration = duration; } @override public void run() { final int patch = (dstx > srcx ? 5 : -5); if(duration == 0) { slideswitch.this.mswitchstatus = switch_scroling; slideswitch.this.postinvalidate(); } else { log.d(tag, "start animation: [ " + srcx + " , " + dstx + " ]"); int x = srcx + patch; while (math.abs(x-dstx) > 5) { mdstx = x; slideswitch.this.mswitchstatus = switch_scroling; slideswitch.this.postinvalidate(); x += patch; try { thread.sleep(10); } catch (interruptedexception e) { e.printstacktrace(); } } mdstx = dstx; slideswitch.this.mswitchstatus = mdstx > 35 ? switch_on : switch_off; slideswitch.this.postinvalidate(); } } } public static interface onswitchchangedlistener { /** * 状态改变 回调函数 * @param status switch_on表示打开 switch_off表示关闭 */ public abstract void onswitchchanged(slideswitch obj, int status); } }
layout xml
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#fdfdfd" android:orientation="vertical" android:paddingleft="10dip" android:paddingright="10dip" > <imageview android:id="@+id/imageview1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:src="@drawable/top" /> <relativelayout android:layout_width="fill_parent" android:layout_height="wrap_content" > <textview android:id="@+id/textview1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignparentleft="true" android:layout_centervertical="true" android:text="网络构图" android:textsize="15sp" /> <com.example.hellojni.slideswitch android:id="@+id/slideswitch1" android:layout_width="116dip" android:layout_height="46dip" android:layout_alignparentright="true" android:layout_centervertical="true" /> </relativelayout> <relativelayout android:layout_width="fill_parent" android:layout_height="wrap_content" > <textview android:id="@+id/textview2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignparentleft="true" android:layout_centervertical="true" android:text="保留原图" android:textsize="15sp" /> <com.example.hellojni.slideswitch android:id="@+id/slideswitch2" android:layout_width="116dip" android:layout_height="46dip" android:layout_alignparentright="true" android:layout_centervertical="true" /> </relativelayout> <relativelayout android:layout_width="fill_parent" android:layout_height="wrap_content" > <textview android:id="@+id/textview3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignparentleft="true" android:layout_centervertical="true" android:text="拍照声音" android:textsize="15sp" /> <com.example.hellojni.slideswitch android:id="@+id/slideswitch3" android:layout_width="116px" android:layout_height="46px" android:layout_alignparentright="true" android:layout_centervertical="true" /> </relativelayout> <textview android:id="@+id/textviewtip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:text="textview" /> </linearlayout>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
Android 中TabLayout自定义选择背景滑块的实例代码
-
一款超酷的Android自定义加载控件
-
Android UI设计系列之自定义ViewGroup打造通用的关闭键盘小控件ImeObserverLayout(9)
-
Android自定义控件之开关按钮学习笔记分享
-
Android通过自定义ImageView控件实现图片的缩放和拖动的实现代码
-
【Android自定义控件】一个简单的长按控件,用以解决长按多次触发点击事件 博客分类: 【Android自定义控件】
-
Android自定义日历控件实例详解
-
Android App开发中自定义View和ViewGroup的实例教程
-
Android 自定义输入支付密码的软键盘实例代码
-
Android 自定义控件实现显示文字的功能