文档章节

自定义开关

哦_呢称
 哦_呢称
发布于 2014/05/03 12:07
字数 1318
阅读 176
收藏 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
个性化自己系统的ContextLoaderListener实现

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

ZhenbinLi
2016/01/12
353
0
RAMPaperSwitch

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

红薯
2015/01/22
383
0
360项目-02

shape资源创建图片 (重点) 状态选择器 (重点) 展示gridview的数据 Gridview的条目样式细节修改 跳转设置中心界面并搭建设置中心界面 样式抽取操作 (重点) 将设置中心条目的布局抽取到自定义组...

早早的太阳
2016/09/28
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Nginx防盗链、访问控制、Nginx解析PHP相关配置、Nginx代理

Nginx防盗链 在配置文件里写入以下内容: 用curl测试 访问控制 Nginx限制某些IP不能访问或者只允许某些IP访问。 配置文件写入如下内容: allow 表示允许访问的IP,deny限制访问的IP。 匹配正...

黄昏残影
19分钟前
0
0
自己动手实现RPC服务调用框架

转载 TCP的RPC 引言 本文利用java自带的socket编程实现了一个简单的rpc调用框架,由两个工程组成分别名为battercake-provider(服务提供者)、battercake-consumer(服务调用者)。 设计思路...

雨中漫步的鱼
21分钟前
0
0
Centos6.x安装之后的9件事

Centos6.x安装之后的9件事 这些不是必须都做的,只不过是我个人的习惯,在此记录一下。 1.修改yum源到国内 CentOS系统更换软件安装源 备份你的原镜像文件,以免出错后可以恢复。 mv /etc/yu...

叶云轩
27分钟前
6
0
springboot2 使用jsp NoHandlerFoundException

开发图片上传功能,为验证测试功能是否正常,使用JSP编写表单提交进行测试 开发完成后,请求API提示如下异常: No mapping found for HTTP request with URI [/WEB-INF/jsp/avatar_upload.j...

showlike
33分钟前
0
0
springboot踩坑记--springboot正常启动但访问404

一 spring boot的启动类不能直接放在main(src.java.main)这个包下面,把它放在有包的里面就可以了。 二 正常启动了,但是我写了一个controller ,用的@RestController 注解去配置的controlle...

onedotdot
34分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部