文档章节

自定义开关

哦_呢称
 哦_呢称
发布于 2014/05/03 12:07
字数 1318
阅读 177
收藏 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
私信 提问
Android 自定义Switch开关按钮的样式

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

容华谢后
2017/12/07
0
0
使用功能开关更好地实现持续部署

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

guzhoujiexing
06/26
0
0
RAMPaperSwitch

RAMPaperSwitch是使用swift所写的开关,开关可以控制其所在的父视图的颜色填充。可以结合自定义的动画来使用。

红薯
2015/01/22
383
0
个性化自己系统的ContextLoaderListener实现

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

ZhenbinLi
2016/01/12
353
0
xmake高级特性之自定义选项

xmake还可以支持一些自定义选项开关,使得工程支持可选编译,方便工程的模块化管理。 增加自定义编译开关 我们拿一个实际的例子来说: 我们想在自己的工程中增加一个新开关选项:hello, 如果...

ruki
2015/12/07
25
0

没有更多内容

加载失败,请刷新页面

加载更多

聊聊flink的FsStateBackend

序 本文主要研究一下flink的FsStateBackend StateBackend flink-runtime_2.11-1.7.0-sources.jar!/org/apache/flink/runtime/state/StateBackend.java @PublicEvolvingpublic interface Sta......

go4it
13分钟前
0
0
webpack配置proxyTable时pathRewrite无效的解决方法

webpack配置接口地址代理 在项目开发中,接口联调的时候一般都是同域名下,且不存在跨域的情况下进行接口联调,但是当我们在本地启动服务器后,比如本地开发服务下是 http://localhost:8080 ...

前端小攻略
13分钟前
0
0
安装jenkins

1.下载 wget https://mirrors.tuna.tsinghua.edu.cn/jenkins/war/2.155/jenkins.war 2.后续操作和 dubbo 安装类似: (1)复制一份空白的tomcat,重命名为:jenkins-tomcat (2)war包放入t...

狼王黄师傅
21分钟前
1
0
zookeeper配置与使用

一.登录官网下载 不要带后缀的,那是公侧版本,下稳定版,比如3.4.9 二.安装与使用 解压后bin里是启动程序 配置文件:在conf下 复制zoo_sample.cfg改名为为zoo.cfg,打开zoo修改文件...

小兵胖胖
45分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部