自定义控件之-----ToggleButton

先来张效果图

banner.gif

步骤思路

  • 继承View
    1、测量自己的宽高
    2、绘制自己
    3、处理触摸事件

1、继承View



        public class MyToggleButton extends View {
            public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {
                super(context, attrs, defStyle);
            }
        
            // 在布局文件中使用控件时 调用
            public MyToggleButton(Context context, AttributeSet attrs) {
                super(context, attrs);
            }
        
            // 在代码中new对象时 调用
            public MyToggleButton(Context context) {
                super(context);
            }
        }

2、在布局中使用MyToggleButton


        <com.itheima.toggle94.MyToggleButton
        android:id="@+id/toggle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />


3、测量宽高



        // 测量自己的宽高,把背景图片的宽高设置为自己的宽高
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            // 把背景图片的宽高设置为自己的宽高
            setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
        }

        // 外界设置开关背景图片资源
        public void setBackgroundRes(int backgroundRes){
            // 把传进来的图片资源 转换成Bitmap对象
            backgroundBitmap = BitmapFactory.decodeResource(getResources(), backgroundRes);
        }


4、绘制




        // 外界设置滑动块图片资源
        public void setSlideButtonRes(int slideButtonRes){
            // 把传进来的图片资源 转换成Bitmap对象
            slideButtonBitmap = BitmapFactory.decodeResource(getResources(), slideButtonRes);
        }

        // 绘制自己
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            // 绘制背景
            canvas.drawBitmap(backgroundBitmap, 0, 0, null);
            // 绘制滑动块
            canvas.drawBitmap(slideButtonBitmap, 0, 0, null);
        }


5、根据状态绘制滑动块



        // 外界设置开关状态
        public void setState(boolean state){
            this.currentState = state;
        }

        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            // 绘制背景
            canvas.drawBitmap(backgroundBitmap, 0, 0, null);
            // 绘制滑动块 根据外界的设置,展示开或关
            if(!currentState){// 关状态
                canvas.drawBitmap(slideButtonBitmap, 0, 0, null);
            }else{// 开状态
                canvas.drawBitmap(slideButtonBitmap, backgroundBitmap.getWidth()-slideButtonBitmap.getWidth(), 0, null);
            }
        }

6、处理事件,让滑动块随手指移动



        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                isTouching = true;
                // 获取手指触摸时相对于自己的x位置
                currentX = (int) event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                currentX = (int) event.getX();
                break;
            case MotionEvent.ACTION_UP:
                isTouching = false;
                currentX = (int) event.getX();
                break;
    
            default:
                break;
            }
            // 重新绘制控件,重新调用onDraw
            invalidate();
            return true;// 消费掉事件
        }

        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            // 绘制背景
            canvas.drawBitmap(backgroundBitmap, 0, 0, null);
            
            // 根据手指触摸的位置绘制滑动块图片
            if(isTouching){
                canvas.drawBitmap(slideButtonBitmap, currentX, 0, null);
            }else{
                // 绘制滑动块 根据外界的设置,展示开或关
                if(!currentState){// 关状态
                    canvas.drawBitmap(slideButtonBitmap, 0, 0, null);
                }else{// 开状态
                    canvas.drawBitmap(slideButtonBitmap, backgroundBitmap.getWidth()-slideButtonBitmap.getWidth(), 0, null);
                }
            }
        }

7、让用户感觉手指触摸到了滑动块中间



        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            // 绘制背景
            canvas.drawBitmap(backgroundBitmap, 0, 0, null);
            
            // 根据手指触摸的位置绘制滑动块图片
            if(isTouching){
                int left = currentX-slideButtonBitmap.getWidth()/2;// 让滑动块画的中心画到手指触摸的位置
                // 限制滑动块的滑动范围
                if(left<0){// 如果超出左边,直接绘制到0位置
                    left = 0;
                }else if(left>backgroundBitmap.getWidth()-slideButtonBitmap.getWidth()){
                    left = backgroundBitmap.getWidth()-slideButtonBitmap.getWidth();
                }
                canvas.drawBitmap(slideButtonBitmap, left, 0, null);
            }else{
                ......
            }
        }

8、手指抬起时,通过改变当前状态,让滑动块根据状态绘制



        case MotionEvent.ACTION_UP:
            isTouching = false;
            currentX = (int) event.getX();
            // 手指抬起时,根据手指触摸的位置与背景图片一半比较,离哪边近,滑动块就滑动到哪
            currentState = currentX>backgroundBitmap.getWidth()/2;
            
            break;

        default:
            break;
        }

9、对外暴露监听,让外界监听到开关的状态变化



        * 1、暴露接口

            // 对外暴露开关状态发生变化的接口
            public interface OnToggleButtonStateChangeListener{
                // 当开关状态发生变化时 调用
                void onStateChanged(boolean state);
            }

        * 2、对外提供监听器

            // 让外界设置状态变化的监听器
            public void setOnToggleButtonStateChangeListener(OnToggleButtonStateChangeListener listener){
                this.mListener = listener;
            }

        * 3、当状态发生变化时调用监听器的回调方法

            case MotionEvent.ACTION_UP:
            isTouching = false;
            currentX = (int) event.getX();
            // 手指抬起时,根据手指触摸的位置与背景图片一半比较,离哪边近,滑动块就滑动到哪
            // 当状态发生变化时,调用外界传递进来的监听器的onStateChanged
            boolean tempState = currentX>backgroundBitmap.getWidth()/2;
            if(tempState!=currentState){// 状态发生变化
                currentState = tempState;
                if(mListener!=null){
                    mListener.onStateChanged(currentState);
                }
            }
            break;

        * 4、外界监听开关状态

            OnToggleButtonStateChangeListener listener = new OnToggleButtonStateChangeListener() {
            
                @Override
                public void onStateChanged(boolean state) {
                    // 处理业务
                    Toast.makeText(getApplicationContext(), ""+state, 0).show();
                }
            };
            toggle.setOnToggleButtonStateChangeListener(listener);

10、自定义属性



        * 1、定义属性

            /toggle94/res/values/attrs.xml

            <declare-styleable name="MytoggleButton">
                <!-- 背景图片属性 -->
                <attr name="backgroundRes" format="reference" />
                <!-- 滑动块图片属性 -->
                <attr name="slideButtonRes" format="reference" />
                <!-- 开关状态属性 -->
                <attr name="state" format="boolean" />
            </declare-styleable>

        * 2、在布局中使用属性,要先声明命名空间

            <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:itheima="http://schemas.android.com/apk/res/com.itheima.toggle94"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:paddingBottom="@dimen/activity_vertical_margin"
                android:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                tools:context=".MainActivity" >
            
                <com.itheima.toggle94.MyToggleButton
                    android:id="@+id/toggle"
                    itheima:backgroundRes="@drawable/switch_background"
                    itheima:slideButtonRes="@drawable/slide_button_background"
                    itheima:state="false"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
            
            </RelativeLayout>

        * 3、代码中获取自定义属性值,设置给控件

            // 在布局文件中使用控件时 调用
            public MyToggleButton(Context context, AttributeSet attrs) {
                super(context, attrs);
                // 从布局文件中获取控件的自定义属性值
                
                String namespace = "http://schemas.android.com/apk/res/com.itheima.toggle94";
                int backgroundResValue = attrs.getAttributeResourceValue(namespace, "backgroundRes", 0);
                setBackgroundRes(backgroundResValue);
                
                int slideButtonResValue = attrs.getAttributeResourceValue(namespace, "slideButtonRes", 0);
                setSlideButtonRes(slideButtonResValue);
                
                currentState = attrs.getAttributeBooleanValue(namespace, "state", false);
            }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,263评论 25 707
  • 一、Android开发初体验 二、Android与MVC设计模式模型对象存储着应用的数据和业务逻辑。模型类通常用来...
    为梦想战斗阅读 873评论 0 3
  • 写下这句话的时候,窗外正下着雨,家里吵吵闹闹,每个人好像都被雨天感染了情绪,心里好像有些什么要破壳而出,这样不行的...
    不渝子阅读 808评论 1 1
  • 可能是某年,某天黄昏一次没有抬头得擦肩。 可能是某年,某条街道我们不经意的一眼。 可能是某年,某月一瞬间莫名的思念...
    时光以北暮城南阅读 270评论 0 0
  • 工作永远是做不完的,完成一波,另一波紧跟在那后面。画面和植物大战僵尸是一样的,消灭一群,后面来更凶狠的一群,因此,...
    芥空阅读 277评论 0 0