【Android】自定义控件实现可滑动的开关(switch)

~转载请注明来源:http://blog.csdn.net/u013015161/article/details/46704745

介绍

昨天晚上写了一个Android的滑动开关, 即SlideSwitch。效果如下:

实现

实现的思路其实很简单,监听控件上的touch事件,并不断刷新,让滑块在手指的位置上绘出,达到滑块跟着手指滑动的显示效果。

先看一下代码:

SlideSwitch.java

package com.example.slideswitchexample;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class SlideSwitch extends View{

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //抗锯齿

    boolean isOn = false;
    float curX = 0;
    float centerY; //y固定
    float viewWidth;
    float radius;
    float lineStart; //直线段开始的位置(横坐标,即
    float lineEnd; //直线段结束的位置(纵坐标
    float lineWidth;
    final int SCALE = 4; // 控件长度为滑动的圆的半径的倍数
    OnStateChangedListener onStateChangedListener;

    public SlideSwitch(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public SlideSwitch(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SlideSwitch(Context context) {
        super(context);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        curX = event.getX();
        if(event.getAction() == MotionEvent.ACTION_UP)
        {
            if(curX > viewWidth / 2)
            {
                curX = lineEnd;
                if(false == isOn)
                {
                    //只有状态发生改变才调用回调函数, 下同
                    onStateChangedListener.onStateChanged(true);
                    isOn = true;
                }
            }
            else
            {
                curX = lineStart;
                if(true == isOn)
                {
                    onStateChangedListener.onStateChanged(false);
                    isOn = false;
                }
            }
        }
        /*通过刷新调用onDraw*/
        this.postInvalidate();
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        /*保持宽是高的SCALE / 2倍, 即圆的直径*/
        this.setMeasuredDimension(this.getMeasuredWidth(), this.getMeasuredWidth() * 2 / SCALE);
        viewWidth = this.getMeasuredWidth();
        radius = viewWidth / SCALE;
        lineWidth = radius * 2f; //直线宽度等于滑块直径
        curX = radius;
        centerY = this.getMeasuredWidth() / SCALE; //centerY为高度的一半
        lineStart = radius;
        lineEnd = (SCALE - 1) * radius;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);

        /*限制滑动范围*/
        curX = curX > lineEnd?lineEnd:curX;
        curX = curX < lineStart?lineStart:curX;

        /*划线*/
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(lineWidth);
        /*左边部分的线,蓝色*/
        mPaint.setColor(Color.BLUE);
        canvas.drawLine(lineStart, centerY, curX, centerY, mPaint);
        /*右边部分的线,灰色*/
        mPaint.setColor(Color.GRAY);
        canvas.drawLine(curX, centerY, lineEnd, centerY, mPaint);

        /*画圆*/
        /*画最左和最右的圆,直径为直线段宽度, 即在直线段两边分别再加上一个半圆*/
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.GRAY);
        canvas.drawCircle(lineEnd, centerY, lineWidth / 2, mPaint);
        mPaint.setColor(Color.BLUE);
        canvas.drawCircle(lineStart, centerY, lineWidth / 2, mPaint);
        /*圆形滑块*/
        mPaint.setColor(Color.LTGRAY);
        canvas.drawCircle(curX, centerY, radius , mPaint);

    }
    /*设置开关状态改变监听器*/
    public void setOnStateChangedListener(OnStateChangedListener o)
    {
        this.onStateChangedListener = o;
    }

    /*内部接口,开关状态改变监听器*/
    public interface OnStateChangedListener
    {
        public void onStateChanged(boolean state);
    }

}

注释应该很详细了。主要有以下几点。

1、重写了onMeasure方法,使控件高度依赖于控件的宽度。这样不论在布局文件中如何设置,总能保证控件的宽高比

2、控制好滑块的活动范围

3、定义内部接口OnStateChangedListener,并在自定义控件里定义了其对象以及从外部赋值的方法setOnStateChangedListener,以便对开关状态更改事件进行监听并调用回调

使用及Demo

在布局文件中添加该控件即可使用。Demo效果为动图展示效果(demo里颜色为绿色,动图为蓝色是因为绿色会导致截取gif时出问题,临时更改的)。

Demo中布局文件如下:

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.example.slideswitchexample.SlideSwitch
        android:id="@+id/slide_switch"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>

</RelativeLayout>

Demo中Activity代码如下:

MainActivity.java

package com.example.slideswitchexample;

import com.example.slideswitchexample.SlideSwitch.OnStateChangedListener;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends Activity {

    SlideSwitch sSwitch;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sSwitch = (SlideSwitch) this.findViewById(R.id.slide_switch);
        sSwitch.setOnStateChangedListener(new OnStateChangedListener(){

            @Override
            public void onStateChanged(boolean state) {
                // TODO Auto-generated method stub
                if(true == state)
                {
                    Toast.makeText(MainActivity.this, "开关已打开", 1000).show();
                }
                else
                {
                    Toast.makeText(MainActivity.this, "开关已关闭", 1000).show();
                }
            }

        });
    }

}

点此下载Demo工程

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-09-30 19:37:42

【Android】自定义控件实现可滑动的开关(switch)的相关文章

android 自定义控件之 tab滑动效果

下午没有白费,终于仿照别人的代码做出来了. 大体思路是这样的 下面上代码 http://yunpan.cn/Qa5i6s9ApvrSt 提取码 79be

Android自定义控件——自定义属性

转载请注明出处:http://blog.csdn.net/allen315410/article/details/39343401 我们在自定义android组件的时候,除了用Java构建出组件的样子外,有时候还需要去申明一些"属性"提供给项目使用,那么什么是组件的属性呢? 例如在清单文件中,创建一个TextView的时候,这是需要制定TextView的android:layout_width="wrap_content" android:layout_height

Android自定义控件之滑动开关

自定义开关控件 Android自定义控件一般有三种方式 1.继承Android固有的控件,在Android原生控件的基础上,进行添加功能和逻辑. 2.继承ViewGroup,这类自定义控件是可以往自己的布局里面添加其他的子控件的. 3.继承View,这类自定义控件没有跟原生的控件有太多的相似的地方,也不需要在自己的肚子里添加其他的子控件. ToggleView自定义开关控件表征上没有跟Android原生的控件有什么相似的地方,而且在滑动的效果上也没有沿袭Android原生的地方,所以我们的自定义

Android自定义控件系列 十:利用添加自定义布局来搞定触摸事件的分发,解决组合界面中特定控件响应特定方向的事件

这个例子是比较有用的,基本上可以说,写完这一次,以后很多情况下,直接拿过来addView一下,然后再addInterceptorView一下,就可以轻轻松松的达到组合界面中特定控件来响应特定方向的触摸事件了. 请尊重原创劳动成果,转载请注明出处:http://blog.csdn.net/cyp331203/article/details/45198549,非允许请勿用于商业或盈利用途,违者必究. 在写Android应用的过程之中,经常会遇到这样的情况:界面包含了多个控件,我们希望触摸在界面上的不

Android自定义控件之日历控件

Android自定义控件之日历控件 2015-10-23 Android开发中文站 三月份学习android,至今也有半年有余,中间也做过两个项目,但是依然感觉自己做的应用不是很有新意,比不上应用市场上那些应用如此绚丽.所以自己仍需继续努力.学习至今,仍感觉自定义控件是一块硬骨头,还没修炼到身后的内功,下面就切入正题,以一次项目的需求,来实现一个自定义的日历控件.效果图先来一发. 我们分析下效果图,然后确定我们的需求. (1).绘制星期的自定义View,用于标识日期的礼拜. (2).绘制日期的自

Android自定义控件——开源组件SlidingMenu的项目集成

转载请注明出处:http://blog.csdn.net/allen315410/article/details/39611355  在实际项目开发中,定制一个菜单,能让用户得到更好的用户体验,诚然菜单的样式各种各样,但是有一种菜单--滑动菜单,是被众多应用广泛使用的.关于这种滑动菜单的实现,我在前面的博文中也介绍了如何自定义去实现,请参考Android自定义控件--侧滑菜单,这篇博文描述的是如何从无到有创建一个侧滑菜单的控件,里面的代码不多,但是处理的逻辑和各种效果比较复杂,如果稍有不慎,这种

[转]Android自定义控件系列五:自定义绚丽水波纹效果

出处:http://www.2cto.com/kf/201411/353169.html 今天我们来利用Android自定义控件实现一个比较有趣的效果:滑动水波纹.先来看看最终效果图: 图一 效果还是很炫的:饭要一口口吃,路要一步步走,这里我们将整个过程分成几步来实现 一.实现单击出现水波纹单圈效果: 图二 照例来说,还是一个自定义控件,这里我们直接让这个控件撑满整个屏幕(对自定义控件不熟悉的可以参看我之前的一篇文章:Android自定义控件系列二:自定义开关按钮(一)).观察这个效果,发现应该

【Android自定义控件】圆圈交替,仿progress效果

还是我们自定View的那几个步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 3.重写onMesure (不是必须) 4.重写onDraw 自定义View的属性 <?xml version="1.0" encoding="utf-8"?> <resources> <attr name="firstColor" format="color" /> <att

Android自定义控件系列三:自定义开关按钮(三)--- 自定义属性

尊重原创,转载请注明出处:http://blog.csdn.net/cyp331203/article/details/40855377 接之前的:Android自定义控件系列二:自定义开关按钮(一)和Android自定义控件系列三:自定义开关按钮(二)继续,今天要讲的就是如何在自定义控件中使用自定义属性,实际上这里有两种方法,一种是配合XML属性资源文件的方式,另一种是不需要XML资源文件的方式:下面我们分别来看看: 一.配合XML属性资源文件来使用自定义属性: 那么还是针对我们之前写的自定义