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. 转 powerdesigner12.5在64位JDK下连接mysql数据库问题

    前因:由于项目在研发的过程中,数据库字段需要不停的增加和修改,导致最初设计的数据库原型无法使用,后来就想到用powerdesinger来反转数据库表结构. 环境:win7 64位系统,本机装有64位j ...

  2. CCNA第三章子网划分,变长子网掩码(VLSM)和TCP/IP排错考试要点学习笔记

    1. 子网划分的好处      缩减网络流量; 优化网络性能; 简化管理; 可以更为灵活地形成大覆盖范围的网络.    2. 如何创建子网的步骤 首先,确认所需要的网络ID数; 其次,确认每个子网中所 ...

  3. Windows下Oracle安装图解----oracle-win-64-11g 详细安装步骤

    一. Oracle 下载 官方下地址 http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.htm ...

  4. IOS开发——使用数据库

    IOS开发——使用FMDB数据库 简介 需求作用: 如果需要保存大量的结构较为复杂的数据的时候,使用数据库,例如交规考试项目 1.数据库的基本介绍 数据库(DB)是一种数据模型组织起来并存放存储管理的 ...

  5. python if

    根据用户从控制如输入数据,使用if语句实现用户登录功能 代码如下: name = "zy"password = "123"_name = input(" ...

  6. C++之const

    C++中const 允许指定一个语义约束,编译器会强制实施这个约束,允许程序员告诉编译器某值是保持不变的.如果在编程中确实有某个值保持不变,就应该明确使用const,这样可以获得编译器的帮助.cons ...

  7. SCRUM报告(1)

    我们组的PM是白杰,团队SCRUM 报告如下: 一.第一次spring会议内容: 1.确定所做项目的方向: 2.将调查问卷的结果进行统计,做了需求分析,大致了解了用户的想法: 3.确定了团队计划bac ...

  8. file /usr/share/mysql/... conflicts with file from package mysql-libs-5.1.73-3.el6_5.x86_ 64 MySQL安装

    在CentOS 6.5安装MySQL 5.6.17,安装到最后一个rpm文件MySQL-server时 安装命令是:rpm -ivh MySQL-server-5.6.17-1.el6.x86_64. ...

  9. [Voice communications] 让音乐响起来

    本系列文章主要是介绍 Web Audio API 的相关知识,由于该技术还处在 web 草案阶段(很多标准被提出来,至于取舍需要等待稳定版文档来确定,草案阶段的文档很多都会被再次编辑甚至重写.全部删除 ...

  10. Linux gzip、gunzip

    200 ? "200px" : this.width)!important;} --> 介绍 gzip是linux自带的压缩文件命令,它的压缩比大概能达到60%-70%,比z ...