文档章节

自定义开关

哦_呢称
 哦_呢称
发布于 2014/05/03 12:07
字数 1318
阅读 456
收藏 4

#程序员薪资揭榜#你做程序员几年了?月薪多少?发量还在么?>>>

参考文档:

    http://blog.csdn.net/singwhatiwanna/article/details/9254309

    Android开发文档API的View

    本文中自定义开关MySwitch是我在阅读了上述文档,根据自己的理解写的,所引用的图片资源来自http://home.ustc.edu.cn/~voa/res/HelloJni.apk

    代码已在Android 2.2的模拟器和Android 4.2.1的手机上测试,均可运行。

    手机测试效果如下:

    a 默认效果

    

    b 向右滑,滑过中间位置动效果

    

    c 松开手指,自动滑到右部效果

    

    d 向左滑,滑过中间位置动效果

    

    松开手指,又回到a所示状态。

    MySwitch代码:

package cn.mswitch;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
/**
 * 自定义开关按钮类,打开状态值为true,关闭为false
 * @author liuwh
 * @since  2014-05-2
 */
public class MySwitch extends View {
    private static final String TAG = "MySwitch";
    
    /**
     * 关闭状态
     */
    private static final int STATE_OFF = 0;
    /**
     * 打开状态
     */
    private static final int STATE_ON = 1;
    /**
     * 正在滑动状态
     */
    private static final int STATE_SCROLL = 2;
    
    private static Bitmap bitmapOFF;//关闭状态背景图片
    private static Bitmap bitmapON;//打开状态背景图片
    private static Bitmap bitmapThumb;//圆点图片(为正方形)
    private static int bgWidth;//背景宽度
    private static int bgHeigth;//背景高度
    private static int thumbWidth;//圆点宽度
    
    /**
     * MySwitch状态标志,值为true或false,默认为false,即关闭
     */
    private boolean mState = false;
    
    /**
     * 滑动事件标志,true为滑动事件,false则为点击事件
     */
    private boolean mHasScroll = false;
    
    /**
     * MySwitch当前状态标志,值为0,1,2,
     * 0对应状态STATE_OFF
     * 1对应状态STATE_ON
     * 2对应状态STATE_SCROLL
     */
    private int currState = 0;
    
    public MySwitch(Context context) {
        super(context);
        //DisplayMetrics dm = new DisplayMetrics();
        //float density = context.getResources().getDisplayMetrics().density;
        //Log.i(TAG, "density= " + density);//屏幕密度
    }
    
    public MySwitch(Context context, AttributeSet ats) {
        super(context, ats);
        init();
    }
    
    public MySwitch(Context context, AttributeSet ats, int dfStyle) {
        super(context, ats, dfStyle);
        init();
    }
    
    /**
     * 初始化图片对象,宽高数据和当前状态
     */
    private void init() {
        Resources res = getResources();
        bitmapThumb = BitmapFactory.decodeResource(res, R.drawable.switch_thumb);
        bitmapON = BitmapFactory.decodeResource(res, R.drawable.bg_switch_on);
        bitmapOFF = BitmapFactory.decodeResource(res, R.drawable.bg_switch_off);
        bgWidth = bitmapON.getWidth();
        bgHeigth = bitmapON.getHeight();
        thumbWidth = bitmapThumb.getWidth();
        currState = mState ? STATE_ON : STATE_OFF;
        Log.i(TAG, "bw: " + bgWidth + ", bh: " + bgHeigth + ", tw: " + thumbWidth);
    }
    
    @Override
    public void setLayoutParams(LayoutParams params) {
        //设置MySwitch的View区域宽高
        params.width = bgWidth;
        params.height = bgHeigth;
        super.setLayoutParams(params);
    }
    
    /**
     * 获取MySwitch对象的状态
     * @return  boolean值
     */
    public boolean getMState(){
        return this.mState;
    }
    
    /**
     * 设置MySwitch对象的状态
     * @param isOn boolean值
     */
    public void setMState(boolean mState){
        if(this.mState != mState){
            //更改当前状态currState的值
            currState = mState ? STATE_ON : STATE_OFF;
            //通知重新画图
            MySwitch.this.postInvalidate();
        }
        this.mState = mState;
    }
    
    //状态改变监听接口对象
    private OnMySwicthStateChangedListener msl;
    /**
     * 设置状态改变监听
     * @param mslOnMySwicthStateChangedListener对象
     */
    public void setOnMySwicthStateChangedListener(OnMySwicthStateChangedListener msl){
        this.msl = msl;
    }
    
    private int startX;
    private int currX = 0;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int pointers = event.getPointerCount();
        //如果不是一个手指,则不做处理
        if(pointers > 1) return true;
        
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN://手指按下
            startX = (int) event.getX();
            //Log.i(TAG, "start: " + startX);
            break;
            
        case MotionEvent.ACTION_MOVE://手指滑动
            //设置滑动标志为true,并将当前状态置为滑动
            mHasScroll = true;
            currState = STATE_SCROLL;
            
            currX = (int)event.getX();
            currX = currX>bgWidth ? bgWidth : currX;
            //通知重画MySwitch的View区域
            this.invalidate();
            //Log.i(TAG, "curr x: " + currX );
            break;
            
        case MotionEvent.ACTION_UP://手指松开
            int endX = (int)event.getX();
            //Log.i(TAG, "endX: " + endX);
            int midX = bgWidth/2;
            //Log.i(TAG, "midX= " + midX);
            
            MyAnimationTrans mat = null;
            if(mHasScroll){//滑动事件
                Log.i(TAG, "scroll event");
                //已滑到0或bgWidth位置,判断是否需要发送状态改变通知,不再重画MySwitch区域图像
                if(endX >= bgWidth || endX <= 0){
                    isChange = startX>midX ? endX<=0 : endX>=bgWidth;
                    mState = isChange? endX>=bgWidth : mState;
                    postStateChanged();
                    mHasScroll = false;
                    return true;
                }
                //图片滑向的终点位置
                int toX = endX < midX ? 0 : bgWidth;
                mat = new MyAnimationTrans(endX, toX);
            }
            else{//点击事件
                Log.i(TAG, "click event");
                if(mState){
                //点击处在关闭区域,滑向0位置
                    if(endX < midX){
                        mat = new MyAnimationTrans(endX, 0);
                    }
                }else{
                //点击处在打开区域,滑向bgWidth位置
                    if(endX > midX){
                        mat = new MyAnimationTrans(endX, bgWidth);
                    }
                }
            }
            //处理状态改变响应事件
            isChange = mState ? endX<midX : endX>midX;
            mState = isChange ? endX > midX : mState;
            postStateChanged();
            
            //将滑动标志更改为false
            mHasScroll = false;
            
            if(mat != null){
                this.invalidate();
                new Thread(mat).start();
            }
            break;
            
        default:
            break;
        }
        return true;
    }
    
    //状态是否发生改变,true为发生改变,false为无
    private boolean isChange = false;
    /**
     * 响应状态改变监听事件
     */
    private void postStateChanged(){
    //通知状态改变监听接口
        if(isChange && msl != null){
            msl.onMySwicthChanged(this);
            isChange = false;
            Log.i(TAG, "ACTION_UP,发送状态改变事件,将isChange置为false!");
        }
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //关闭状态
        if(currState == STATE_OFF){
            drawBitmap(canvas, null, null, bitmapOFF);
            drawBitmap(canvas, null, null, bitmapThumb);
        }
        //打开状态
        else if(currState == STATE_ON){
            drawBitmap(canvas, null, null, bitmapON);
            int count = canvas.getSaveCount();
            canvas.translate(bgWidth - thumbWidth, 0);
            drawBitmap(canvas, null, null, bitmapThumb);
            canvas.restoreToCount(count);
        }
        //滑动状态
        else{
            int count = canvas.save();
            if(currX > bgWidth/2){
               drawBitmap(canvas, null, null, bitmapON);
               Log.i(TAG, "状态改变为打开,mState=" + mState);
            }else{
                drawBitmap(canvas, null, null, bitmapOFF);
                Log.i(TAG, "状态改变为关闭,mState=" + mState);
            }
            canvas.restoreToCount(count);
            count = canvas.save();
            int toX = currX - thumbWidth/2;
            toX = toX < 0 ? 0 : toX > (bgWidth - thumbWidth) ? (bgWidth - thumbWidth) : toX;
            //Log.i(TAG, "toX = " + toX);
            canvas.translate( toX, 0);
            drawBitmap(canvas, null, null, bitmapThumb);
            canvas.restoreToCount(count);
        }
    }
    /**
     * 将图片画到屏幕中
     * @param canvasCanvas对象
     * @param fromRC初始位置
     * @param destRC终点位置
     * @param bitmap图片对象
     */
    private void drawBitmap(Canvas canvas, Rect fromRC, Rect destRC, Bitmap bitmap){
        //destRC为null,则从(0,0)位置开始画图
        destRC = destRC==null ? new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()) : destRC;
        Paint paint = new Paint();
        canvas.drawBitmap(bitmap, fromRC, destRC, paint);
    }
    /**
     * 自定义动画类
     * @author liuwh
     *
     */
    private class MyAnimationTrans implements Runnable{
        private int fromX;
        private int endX;
        private static final int SPEED = 5;//默认移动速度
        
        public MyAnimationTrans(final int fromX, final int endX){
            this.fromX = fromX;
            this.endX = endX;
        }
        
        @Override
        public void run() {
            int speed = endX > fromX ? SPEED : -SPEED;
            currX = endX;
            //是否结束循环标志,true为结束
            boolean isEnd = false;
            while(true){
                currX += speed;
                if(currX > bgWidth || currX < 0){
                   currX = speed>0 ? bgWidth : 0;
                   isEnd = true;//结束循环
                }
                MySwitch.this.currState = STATE_SCROLL;
                MySwitch.this.postInvalidate();
                if(isEnd){
                   break;
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 状态改变监听
     * @author liuwh
     *
     */
    public interface OnMySwicthStateChangedListener{
        void onMySwicthChanged(MySwitch mySwitch);
    }
}



© 著作权归作者所有

上一篇: 工作日志(二)
下一篇: 工作日志(一)
哦_呢称
粉丝 1
博文 11
码字总数 13032
作品 0
私信 提问
加载中

评论(0)

Android 自定义Switch开关按钮的样式

封面 GitHub传送门 1.写在前面 本文主要讲的是在Android原生Switch控件的基础上进行样式自定义,内容很简单,但是在实现的过程中还是遇到了一些问题,在此记录下来,希望对大家能够有所帮助,...

容华谢后
2017/12/07
0
0
【新特性速递】新增复选框开关样式

FineUIPro/Mvc/Core的下个版本(v6.1.0),我们对复选框进行了增强,通过一个属性启用开关样式。 先来看下示例效果,浅色主题(Pure Black): 深色主题(Dark Hive): 在这个更新中,我们为...

三生石上(FineUI控件)
2019/12/16
0
0
Android系统移植与调试之------->增加一个双击物理按键打开和关闭闪光灯并将闪光灯状态同步到下拉菜单中

最近有一个客户有这样的需求: 1、在【设置】--->【无障碍】中添加一个开关按钮。 如果打开开关的话,双击某个物理按键的时候,打开闪光灯,再双击该物理按键的时候,关闭闪光灯。 如果关闭开...

欧阳鹏
04/01
0
0
使用功能开关更好地实现持续部署

本文转载自InfoQ上文章http://www.infoq.com/cn/articles/function-switch-realize-better-continuous-implementations 摘要 为了快速发布开发完成的功能,现代的互联网企业通常会以比较快的...

guzhoujiexing
2018/06/26
0
0
个性化自己系统的ContextLoaderListener实现

一、问题描述 WEB项目在继承Spring时候,默认的配置的是ContextLoaderListener <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-con......

ZhenbinLi
2016/01/12
692
0

没有更多内容

加载失败,请刷新页面

加载更多

2020中台建设-技术中台的搭建和运营

2020中台建设-技术中台的搭建和运营

Original123
17分钟前
22
0
CTO:不要在 Java 代码中写 set/get 方法了,逮一次罚款

前言 what?你的 Java 代码中还充斥着大量的 set/get 方法? 我们在刚开始学习 Java 语言的时候讲过,面向对象的三大特征就是封装,继承,和多态。在 Java 中,要保证封装性,需要将成员变量...

码农突围
26分钟前
21
0
为什么不推荐去做安全测试工程师?

对,你没看错。我不推荐大家去做安全测试工程师。 为什么不推荐大家去做安全测试? 今天,很多软件并没有经过专门的安全测试便运行在互联网上,它们携带着各类安全漏洞直接暴露在公众面前,其...

爱码小哥
44分钟前
32
0
Go Gin 简明教程

https://geektutu.com/post/quick-go-gin.html

Java搬砖工程师
今天
19
0
如何更改远程Git存储库的URI(URL)? - How to change the URI (URL) for a remote Git repository?

问题: I have a repo (origin) on a USB key that I cloned on my hard drive (local). 我在硬盘驱动器(本地)上克隆的USB密钥上有一个回购(来源)。 I moved "origin" to a NAS and succ......

技术盛宴
今天
26
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部