package com.example.compoundbuttonview.view;

import com.example.compoundbuttonview.R;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View; public class SlideSwitchView extends View{
/** Switch底部样式图片 */
private Bitmap mSwitchBottom;
/** Switch 当前样式 */
private Bitmap mSwitchThumb;
/** Switch无操作情况下的样式 */
private Bitmap mSwitchThumbNormal;
/** Switch当前手指触摸式的样式 */
private Bitmap mSwitchThumbPressed;
/** Switch 框架 */
private Bitmap mSwitchFrame;
private Bitmap mSwitchMask;
private float mCurrentX = 0;
/** Switch 开关状态,默认是 开:true */
private boolean mSwitchOn = true;
/** Switch 最大移动距离 */
private int mMoveLength;
/** 第一次按下的有效区域 */
private float mLastX = 0;
/** 绘制的目标区域大小 */
private Rect mDest = null;
/** 截取源图片的大小 */
private Rect mSrc = null;
/** Switch 移动的偏移量 */
private int mMoveDeltX = 0;
/** 画笔工具 */
private Paint mPaint = null;
/** Switch 状态监听接口 */
private OnSwitchChangedListener switchListener = null;
private boolean mFlag = false;
/** enabled 属性 为 true */
private boolean mEnabled = true;
/** 最大透明度,就是不透明 */
private final int MAX_ALPHA = 255;
/** 当前透明度,这里主要用于如果控件的enable属性为false时候设置半透明 ,即不可以点击 */
private int mAlpha = MAX_ALPHA;
/** Switch 判断是否在拖动 */
private boolean mIsScrolled =false; public SlideSwitchView(Context context) {
this(context, null);
} public SlideSwitchView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public SlideSwitchView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
} /**
* 初始化相关资源
*/
public void init() {
mSwitchThumbPressed = BitmapFactory.decodeResource(getResources(),R.drawable.checkswitch_btn_pressed);
mSwitchThumbNormal = BitmapFactory.decodeResource(getResources(),R.drawable.checkswitch_btn_unpressed);
mSwitchBottom = BitmapFactory.decodeResource(getResources(),R.drawable.checkswitch_bottom);
mSwitchFrame = BitmapFactory.decodeResource(getResources(),R.drawable.checkswitch_frame);
mSwitchMask = BitmapFactory.decodeResource(getResources(),R.drawable.checkswitch_mask);
mSwitchThumb = mSwitchThumbNormal;
mMoveLength = mSwitchBottom.getWidth() - mSwitchFrame.getWidth();
//绘制区域大小
mDest = new Rect(0, 0, mSwitchFrame.getWidth(),mSwitchFrame.getHeight());
mSrc = new Rect();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setAlpha(255);
//mPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
setMeasuredDimension(mSwitchFrame.getWidth(), mSwitchFrame.getHeight());
} @Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (mMoveDeltX > 0 || mMoveDeltX == 0 && mSwitchOn) {
if (mSrc != null) {
mSrc.set(mMoveLength - mMoveDeltX, 0, mSwitchBottom.getWidth()
- mMoveDeltX, mSwitchFrame.getHeight());
}
} else if (mMoveDeltX < 0 || mMoveDeltX == 0 && !mSwitchOn) {
if (mSrc != null) {
mSrc.set(-mMoveDeltX, 0, mSwitchFrame.getWidth() - mMoveDeltX,
mSwitchFrame.getHeight());
}
}
Log.d("mAlpha", "mAlpha:" + mAlpha);
//绘制一个不透明矩形 为白色 和黑色底衬 成灰色
canvas.saveLayerAlpha(new RectF(mDest), mAlpha, 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(mSwitchBottom, mSrc, mDest, null);
canvas.drawBitmap(mSwitchThumb, mSrc, mDest, null);
canvas.drawBitmap(mSwitchFrame, 0, 0, null);
canvas.drawBitmap(mSwitchMask, 0, 0, mPaint);
canvas.restore();
} @Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
//如果Enabled属性设定为true,触摸效果才有效
if(!mEnabled){
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mSwitchThumb = mSwitchThumbPressed;
mLastX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
mCurrentX = event.getX();
mMoveDeltX = (int) (mCurrentX - mLastX);
if(mMoveDeltX > 10){
//设置了10这个误差距离,可以更好的实现点击效果
mIsScrolled = true;
}
// 如果开关开着向左滑动,或者开关关着向右滑动(这时候是不需要处理的)
if ((mSwitchOn && mMoveDeltX < 0) || (!mSwitchOn && mMoveDeltX > 0)) {
mFlag = true;
mMoveDeltX = 0;
} if (Math.abs(mMoveDeltX) > mMoveLength) {
mMoveDeltX = mMoveDeltX > 0 ? mMoveLength : -mMoveLength;
}
invalidate();
break;
case MotionEvent.ACTION_UP:
mSwitchThumb = mSwitchThumbNormal;
//如果没有滑动过,就看作一次点击事件
if(!mIsScrolled){
mMoveDeltX = mSwitchOn ? mMoveLength : -mMoveLength;
mSwitchOn = !mSwitchOn;
if (switchListener != null) {
switchListener.onSwitchChange(this, mSwitchOn);
}
invalidate();
mMoveDeltX = 0;
break;
}
mIsScrolled = false;
if (Math.abs(mMoveDeltX) > 0 && Math.abs(mMoveDeltX) < mMoveLength / 2) {
mMoveDeltX = 0;
invalidate();
} else if (Math.abs(mMoveDeltX) > mMoveLength / 2
&& Math.abs(mMoveDeltX) <= mMoveLength) {
mMoveDeltX = mMoveDeltX > 0 ? mMoveLength : -mMoveLength;
mSwitchOn = !mSwitchOn;
if (switchListener != null) {
switchListener.onSwitchChange(this, mSwitchOn);
}
invalidate();
mMoveDeltX = 0;
} else if (mMoveDeltX == 0 && mFlag) {
// 这时候得到的是不需要进行处理的,因为已经move过了
mMoveDeltX = 0;
mFlag = false;
}
default:
break;
}
invalidate();
return true;
}
/**
* 设置 switch 状态监听
* */
public void setOnChangeListener(OnSwitchChangedListener listener) {
switchListener = listener;
}
/**
* switch 开关监听接口
* */
public interface OnSwitchChangedListener{
public void onSwitchChange(SlideSwitchView switchView, boolean isChecked);
} @Override
public void setEnabled(boolean enabled) {
// TODO Auto-generated method stub
mEnabled = enabled;
mAlpha = enabled ? MAX_ALPHA : MAX_ALPHA/2;
Log.d("enabled",enabled ? "true": "false");
super.setEnabled(enabled);
invalidate();
} /** 自动判断切换至相反的属性 : true -->false ;false -->true */
public void toggle() {
setChecked(!mSwitchOn);
} /** 设置选中的状态(选中:true 非选中: false) */
public void setChecked(boolean checked) {
mSwitchOn = checked;
invalidate();
}
}

绘制 ToggleButton --重写view的更多相关文章

  1. Android 自定义View 三板斧之三——重写View来实现全新控件

    通常情况下,Android实现自定义控件无非三种方式. Ⅰ.继承现有控件,对其控件的功能进行拓展. Ⅱ.将现有控件进行组合,实现功能更加强大控件. Ⅲ.重写View实现全新的控件 本文来讨论最难的一种 ...

  2. 128、View 绘制流程 & 自定义View

    记清楚函数调用的顺序才能准确地进行调用. 根据调用链,可将整个绘制过程分为三部分:Measure - Layout - Draw Measure 过程 1. 测量过程由上至下,在measure过程的最 ...

  3. Android重写view时onAttachedToWindow () 和 onDetachedFromWindow ()

    在重写View的时候,会遇到这两个方法 protected void onAttachedToWindow() Description copied from class: View This is ...

  4. android重写view和viewgroup的区别

    重写view: View类一般用于绘图操作,重写它的onDraw方法,但它不可以包含其他组件,没有addView(View view)方法. 重写viewgroup: ViewGroup是一个组件容器 ...

  5. 绘制圆动画--重写view

    /** * @FileName CircleProgressBar.java * @Package com.read.view * @Description TODO * @Author Alpha ...

  6. 重写 View 的 Touch 方法,实现一个酷炫的九宫格图片

    前几天翻看代码库,发现一个之前写过的一个有意思的小玩意,共享给大家

  7. Android应用层View绘制流程与源码分析

    1  背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...

  8. 自定义View_1_关于View,ViewGroup的测量和绘制流程

    自定义View(1) ------ 关于View,ViewGroup的测量和绘制流程 在Android当中,自定义控件属于比较高级的知识体系,今天我们就一起研究研究关于自定义View的那点事,看看它到 ...

  9. Android view的测量及绘制

    讲真,自我感觉,我的水平真的是渣的一匹,好多东西都只停留在知道和会用的阶段,也想去研究原理和底层的实现,可是一看到代码就懵逼了,然后就看不下去了, 说自己不着急都是骗人的,我自己都不信,前两天买了本& ...

随机推荐

  1. php模拟登陆的两种实现方法分析

    php模拟登陆的实现方法分析 本文实例分析了php模拟登陆的实现方法.分享给大家供大家参考.具体分析如下: php模拟登陆的实现方法,这里分别列举两种方法实现模拟登陆人人网.具体实例代码如下: 1)使 ...

  2. 工作当中实际运用(3)——js原生实现鼠标点击弹出div层 在点击隐藏

    function onmou(){ var divs=document.getElementById('kefuDV');//获取到你要操作的div if (divs.style.display==& ...

  3. 高效简易开发基于websocket 的通讯应用

    websocket的主要是为了解决在web上应用长连接进行灵活的通讯应用而产生,但websocket本身只是一个基础协议,对于消息上还不算灵活,毕竟websocket只提供文本和二进制流这种基础数据格 ...

  4. Android前端人员与后台开发的撕逼(一)

    首先表明一下身份,本人是Android前端开发人员,本篇只做合理性探讨,不进行人身攻击: 其次希望各位大神进行点评!点评!点评! 我们讨论一下接口的两种返回方式,直接举例说明一下,假设书籍信息表有30 ...

  5. 打开mysql时,提示 1040,Too many connections

    打开mysql时,提示 1040,Too many connections,这样就无法打开数据库,看不了表里边的内容了. 出现这个问题的原因是,同时对数据库的连接数过大,mysql默认的最大连接数是1 ...

  6. Mac中体验ASP.NET 5 beta2的K gen代码生成

    ASP.NET 5 beta2中增加了一个新特性(详见ASP.NET 5 Beta2 发布),可以通过K命令生成MVC的代码,比如:k gen controller -name HomeControl ...

  7. RCP: MANIFEST.MF, plugin.xml, build.properties三种文件的区别

    在Eclipse插件开发中, MANIFEST.MF, plugin.xml, build.properties是三种最常见的文件,由于它们共享同一个编辑器(Plug-in Manifest Edit ...

  8. Nim编码风格

    介绍 Nim语言不限制开发人员使用哪种具体的编码风格, 但为了社区的发展,在编写一些标准库的时候还是应该遵从统一的编码风格 这篇文章会列出一系列的编码风格准则,供大家参考. 但值得注意的是,有很多例外 ...

  9. 使用ACE_Task管理线程

    为什么要使用ACE_Task来管理线程 从C#转到C++后,感觉到C++比C#最难的地方,就是在系统编程时,C#中有对应的类库,我接触到一个类后,就可以通过这个类,知道很多相关的功能.而在C++中,必 ...

  10. windbg 基础命令实战 - 简单程序破解

    以前玩游戏遇到一些实在过不去的管卡,经常会找一些游戏修改软件来修改游戏,让自己变得无比强大,将boss一路砍瓜切菜过足游戏瘾.其实游戏修改软件的功能大多都比较简单,我们可以通过windbg的一些简单命 ...