Android 自定义的开关按钮——SwitchButton
本文转自:http://blog.csdn.net/swust_chenpeng/article/details/19967501
我将原文的控件进行了一些修改,去掉了原来控件的外边框,只留下重要的遮罩、背景和滑块。并且可以在布局文件中预览(预览效果不是太好,凑合看看还可以)。自己修改了下监听器,增加了一些方法。总之目前已经和官方的控件差不多了。重要的是可以自定义控件的大小了!
上面粉红色的那个就是我们自定义的控件了,下面的两个是用的官方的控件,自己改样式。基本处于没用的级别。
好了,现在我们开始讲自定义控件添加入代码中。
首先,定义attrs.xml文件
layout_width 控件的宽度,必须为确定数值的单位,不能用match_parent/wrap_content
layout_height 控件的高度,必须为确定数值的单位,不能用match_parent/wrap_content
mask 控件的遮罩,在这个遮罩范围内的图片才予以显示,可以实现圆角
track 控件的轨道背景图
thumb 控件的滑块图片
这里的5个属性必须同时指定,否则会出问题!
mask.png
track.png
thumb.png(这里可以看见这原点做的时候,应该做到和轨道一个长度,并且正好能在轨道的中间位置)
效果要达到这样能完美显示 → ╮(╯▽╰)╭这种事情就交给美工妹子去做吧,程序员就好好地编程吧~
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <declare-styleable name="SwitchButton">
- <attr name="android:layout_width"/>
- <attr name="android:layout_height"/>
- <attr name="mask" format="reference"/>
- <attr name="android:track"/>
- <attr name="android:thumb"/>
- </declare-styleable>
- </resources>
然后,我们写这个控件的类 SwitchButton.java
- package com.kale.switchbutton;
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Matrix;
- import android.graphics.Paint;
- import android.graphics.PorterDuff.Mode;
- import android.graphics.PorterDuffXfermode;
- import android.graphics.Rect;
- import android.graphics.RectF;
- import android.graphics.drawable.BitmapDrawable;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.View.OnClickListener;
- /**
- * @author:swust_chenpeng
- * 本文转自:http://blog.csdn.net/swust_chenpeng/article/details/19967501
- * @tips : 白色圆点:switch_btn_pressed;
- * 左白右红的长条:switch_bottom;
- * 无色长条:switch_frame;
- * 黑色长条:switch_mask.
- * @date :2014-7-20
- */
- public class SwitchButton extends View implements OnClickListener {
- private Bitmap mSwitchTrack, mSwitchThumb, mSwitchMask;
- private float mCurrentX = ;// 当前的x坐标
- private boolean mSwitchOn = true;// 开关默认是开着的
- private int mMoveLength;// 最大移动距离
- private float mLastX = ;// 第一次按下的有效区域
- private Rect mDest = null;// 绘制的目标区域大小
- private Rect mSrc = null;// 截取源图片的大小
- private int mDeltX = ;// 移动的偏移量
- private int width, height;
- private Paint mPaint = null;
- private OnCheckedChangeListener mListener = null;
- private boolean mFlag = false;
- private float scaleWidth,scaleHeight;//图片缩放的比例
- public SwitchButton(Context context) {
- this(context, null);
- }
- public SwitchButton(Context context, AttributeSet attrs) {
- this(context, attrs, );
- }
- public SwitchButton(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(context, attrs);
- }
- /**
- * 初始化相关资源
- */
- private void init(Context context, AttributeSet attrs) {
- TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
- R.styleable.SwitchButton);
- width = (int) mTypedArray.getDimension(
- R.styleable.SwitchButton_android_layout_width, );
- height = (int) mTypedArray.getDimension(
- R.styleable.SwitchButton_android_layout_height,);
- // 遮罩
- mSwitchMask = getSrcBitmap(mTypedArray, R.styleable.SwitchButton_mask);
- // 背景(轨道的背景)
- mSwitchTrack = getSrcBitmap(mTypedArray, R.styleable.SwitchButton_android_track);
- // 滑块
- mSwitchThumb = getSrcBitmap(mTypedArray, R.styleable.SwitchButton_android_thumb);
- // 计算缩放的比例
- scaleWidth = ((float) width / mSwitchMask.getWidth());
- scaleHeight = ((float) height / mSwitchMask.getHeight());
- mSwitchMask = getScaleBitmap(mSwitchMask);
- mSwitchTrack = getScaleBitmap(mSwitchTrack);
- mSwitchThumb = getScaleBitmap(mSwitchThumb);
- setOnClickListener(this);
- setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return false;
- }
- });
- // 可以移动的长度
- mMoveLength = mSwitchTrack.getWidth() - mSwitchMask.getWidth();
- // 创建一个矩形对象(目标区域的大小),通过使用四个整数来初始化矩形左上角的横坐标、纵坐标以及矩形的高度、宽度
- mDest = new Rect(, , mSwitchMask.getWidth(),mSwitchMask.getHeight());
- // 创建一个矩形对象,矩形左上角的横坐标、纵坐标以及矩形的宽度、高度均为零。这是默认的构造函数
- mSrc = new Rect();
- mPaint = new Paint();
- mPaint.setAntiAlias(true);// 消除锯齿
- mPaint.setAlpha();// 设置透明度
- mPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
- }
- // 从资源中获取图片对象
- private Bitmap getSrcBitmap(TypedArray mTypedArray,int index) {
- BitmapDrawable tempBit;
- tempBit = (BitmapDrawable)mTypedArray.getDrawable(index);
- return tempBit.getBitmap();
- }
- // 将原图,按照布局文件中的控件长度来缩放
- private Bitmap getScaleBitmap(Bitmap bitmap) {
- Matrix matrix = new Matrix();
- if (isInEditMode()) {
- return bitmap;
- }
- matrix.postScale(scaleWidth, scaleHeight);
- bitmap = Bitmap.createBitmap(bitmap, , , bitmap.getWidth(),
- bitmap.getHeight(), matrix, true);
- return bitmap;
- }
- // 该方法指定该控件在屏幕上的大小,这里要计算一下控件的实际大小,然后调用setMeasuredDimension来设置
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(width, height);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- if (mDeltX > || mDeltX == && mSwitchOn) {
- if (mSrc != null) {
- mSrc.set(mMoveLength - mDeltX, ,
- mSwitchTrack.getWidth() - mDeltX, mSwitchMask.getHeight());
- }
- }
- else if (mDeltX < || mDeltX == && !mSwitchOn) {
- if (mSrc != null) {
- mSrc.set(-mDeltX, , mSwitchMask.getWidth() - mDeltX,
- mSwitchMask.getHeight());
- }
- }
- // 这儿是离屏缓冲,自己感觉类似双缓冲机制吧
- int count = canvas.saveLayer(new RectF(mDest), null,
- Canvas.MATRIX_SAVE_FLAG
- | Canvas.CLIP_SAVE_FLAG
- | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
- | Canvas.FULL_COLOR_LAYER_SAVE_FLAG
- | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
- canvas.drawBitmap(mSwitchTrack, mSrc, mDest, null);
- canvas.drawBitmap(mSwitchTrack, mSrc, mDest, null);
- canvas.drawBitmap(mSwitchMask, , , mPaint);
- canvas.drawBitmap(mSwitchThumb, mSrc, mDest, null);
- //这个判断,用来让这个控件可以在布局文件中预览
- if (isInEditMode()) {
- return ;
- }
- canvas.restoreToCount(count);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mLastX = event.getX();
- break;
- case MotionEvent.ACTION_MOVE:
- mCurrentX = event.getX();
- mDeltX = (int) (mCurrentX - mLastX);
- // 如果开关开着向左滑动,或者开关关着向右滑动(这时候是不需要处理的)
- if ((mSwitchOn && mDeltX < ) || (!mSwitchOn && mDeltX > )) {
- mFlag = true;
- mDeltX = ;
- }
- if (Math.abs(mDeltX) > mMoveLength) {
- mDeltX = mDeltX > ? mMoveLength : -mMoveLength;
- }
- invalidate();
- return true;
- case MotionEvent.ACTION_UP:
- if (Math.abs(mDeltX) > && Math.abs(mDeltX) < mMoveLength / ) {
- mDeltX = ;
- invalidate();
- return true;
- } else if (Math.abs(mDeltX) > mMoveLength /
- && Math.abs(mDeltX) <= mMoveLength) {
- mDeltX = mDeltX > ? mMoveLength : -mMoveLength;
- mSwitchOn = !mSwitchOn;
- if (mListener != null) {
- mListener.onCheckedChanged(this, mSwitchOn);
- }
- invalidate();
- mDeltX = ;
- return true;
- } else if (mDeltX == && mFlag) {
- // 这时候得到的是不需要进行处理的,因为已经move过了
- mDeltX = ;
- mFlag = false;
- return true;
- }
- return super.onTouchEvent(event);
- default:
- break;
- }
- invalidate();
- return super.onTouchEvent(event);
- }
- public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
- mListener = listener;
- }
- public interface OnCheckedChangeListener {
- public void onCheckedChanged(SwitchButton button, boolean isChecked);
- }
- @Override
- public void onClick(View v) {
- mDeltX = mSwitchOn ? mMoveLength : -mMoveLength;
- mSwitchOn = !mSwitchOn;
- if (mListener != null) {
- mListener.onCheckedChanged(this, mSwitchOn);
- }
- invalidate();// 重绘
- mDeltX = ;
- }
- public Boolean isChecked() {
- return mSwitchOn;
- }
- public void setChecked(Boolean checked) {
- mSwitchOn = checked;
- invalidate();
- }
- }
最后,在布局文件中使用它。(记得写上命名空间)
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- xmlns:pv="http://schemas.android.com/apk/res/com.kale.switchbutton"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
- <com.kale.switchbutton.SwitchButton
- android:id="@+id/switch_id"
- android:layout_width="80dp"
- android:layout_height="34dp"
- android:layout_marginTop="20dp"
- android:layout_gravity="center_horizontal"
- android:thumb="@drawable/switch_thumb"
- android:track="@drawable/switch_bottom"
- pv:mask="@drawable/switch_mask" />
- <Switch
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:layout_gravity="center_horizontal"
- android:switchMinWidth="40dp"
- android:switchPadding="30dp"
- android:padding="5dp"
- android:text="自定义风格"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textOff=" "
- android:textOn=" "
- android:thumb="@drawable/tt_button"
- android:track="@drawable/tt_path" />
- <Switch
- android:id="@+id/switch1"
- android:layout_marginTop="20dp"
- android:layout_gravity="center_horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
源码下载:http://download.csdn.net/detail/shark0017/7659623
Android 自定义的开关按钮——SwitchButton的更多相关文章
- 自定义的开关按钮——SwitchButton
本文转自:http://blog.csdn.net/swust_chenpeng/article/details/19967501 我将原文的控件进行了一些修改,去掉了原来控件的外边框,只留下重要的遮 ...
- Android 自定义View合集
自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...
- android 自定义动画
android自定义动画注意是继承Animation,重写里面的initialize和applyTransformation,在initialize方法做一些初始化的工作,在applyTransfor ...
- Android自定义View 画弧形,文字,并增加动画效果
一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类 B ...
- Android自定义View4——统计图View
1.介绍 周末在逛慕课网的时候,看到了一张学习计划报告图,详细记录了自己一周的学习情况,天天都是0节课啊!正好在学习Android自定义View,于是就想着自己去写了一个,这里先给出一张慕课网的图,和 ...
- (转)[原] Android 自定义View 密码框 例子
遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...
- Android 自定义View (五)——实践
前言: 前面已经介绍了<Android 自定义 view(四)-- onMeasure 方法理解>,那么这次我们就来小实践下吧 任务: 公司现有两个任务需要我完成 (1)监测液化天然气液压 ...
- Android 自定义 view(四)—— onMeasure 方法理解
前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...
- Android 自定义 view(三)—— onDraw 方法理解
前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...
随机推荐
- POJ 3664 Election Time 题解
这道题网上非常多人都会说easy,水题之类的话,只是我看了下说这种话的人的程序,能够说他们的程序都不及格! 为什么呢?由于他们的程序都是使用简单的二次排序水过(大概你能搜索到的多是这种程序).那样自然 ...
- iOS开发UI调试神器----Reveal
做iOS的开发,UI是非常非常重要的一环.调试时我们一般用模拟器,提交前用真机做測试.用模拟器来调试UI效果尽管快捷方便,但有时仍然希望有更强大的工具来帮助分析UI,尤其是专注在UI的效果调试时.近期 ...
- NOIP2017提高组模拟赛5 (总结)
NOIP2017提高组模拟赛5 (总结) 第一题 最远 奶牛们想建立一个新的城市.它们想建立一条长度为N (1 <= N <= 1,000,000)的 主线大街,然后建立K条 (2 < ...
- bzoj5204: [CodePlus 2018 3 月赛]投票统计(离散化+暴力)
5204: [CodePlus 2018 3 月赛]投票统计 题目:传送门 题解: 谢谢niang老师的一道sui题 离散化之后直接搞啊(打完之后还错了...) 代码: #include<cst ...
- .net core @Html 自定义属性中包含特殊符号解决
最近自己在练手项目用到了VUE 绑定属性的时候发现 有: -符号 这样显然是不支持的.之前发现 v-on 这种-符号也是不支持的 但是可用 @v_on 替代.可是找遍了所有资料也没找到:转义符 当时 ...
- ServiceStack.Redis之IRedisClient<第三篇>【转】
事实上,IRedisClient里面的很多方法,其实就是Redis的命令名.只要对Redis的命令熟悉一点就能够非常快速地理解和掌握这些方法,趁着现在对Redis不是特别了解,我也对着命令来了解一下这 ...
- BigDecimal相除异常
使用两个BigDecimal类型的数字做除法运算时,出现了一个如下的异常信息: 1 java.lang.ArithmeticException: Non-terminating decimal exp ...
- C++之指针与引用,函数和数组
]={,,}; //ptr是指针,该指针类型是int[3] ]=&arr; cout << **ptr << endl;//第一次解指针时得到数组地址,第二次解指针取数 ...
- perl脚本去除文件中重复数据
今天第一天写博客,写的不好请大家多多指教,废话不多说了,干货送上: ############################################################# #!/u ...
- Python学习笔记(二):字符串类型
在上一篇随笔(https://www.cnblogs.com/g-qiang/p/10448813.html)中,说到 Python 有六种标准数据类型,而数字类型和字符串类型又是其中基本的数据类型. ...