Android自定义控件之滑动开关

自定义开关控件

Android自定义控件一般有三种方式

1、继承Android固有的控件,在Android原生控件的基础上,进行添加功能和逻辑。

2、继承ViewGroup,这类自定义控件是可以往自己的布局里面添加其他的子控件的。

3、继承View,这类自定义控件没有跟原生的控件有太多的相似的地方,也不需要在自己的肚子里添加其他的子控件。

    ToggleView自定义开关控件表征上没有跟Android原生的控件有什么相似的地方,而且在滑动的效果上也没有沿袭Android原生的地方,所以我们的自定义ToggleView选择继承View

同样的自定义控件需要复写三个构造方法

//在布局中使用该控件的时候,而且有额外的style属性的时候调用该构造方法,
public ToggleView(Context context, AttributeSet attrs, int defStyle);
//在布局中使用该控件的时候调用该构造方法
public ToggleView(Context context, AttributeSet attrs)
//在Java代码中直接new该控件的时候,调用该构造方法
public ToggleView(Context context)

因为是自定义的控件,所以属性还是自己定义的比较好用一些。我们这里定义三个属性

1、背景图片

2、滑块的图片

3、布局中默认的开关的状态

所以就需要用到了自定义属性

在values目录下,新建xml文件,attrs.xml

在里面定义自己的属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
     <declare-styleable name="toggle">

            <attr name="switchBackground" format="reference" />
            <attr name="slidingBackground" format="reference" />
            <attr name="toggleState" format="boolean" />
      </declare-styleable>
</resources>
<declare-styleable name属性>是可以在R文件中找到该属性名称的
<attr>标签中,一个标签写一个属性 name属性表示属性名称,format表示属性类型

这里定义了三个属性名和属性类型。

属性和自定义控件的三个构造方法已经完成,就我们就可以在布局文件中添加自定义的控件了

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

    <com.hss.toggle.ToggleView
        android:id="@+id/toggleView"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_centerInParent="true"
        hss:switchBackground="@drawable/switch_background"
        hss:slidingBackground="@drawable/slide_button_background"
        hss:toggleState="true"
        >
        </com.hss.toggle.ToggleView>

</RelativeLayout>

注意:在我自定义控件com.hss.toggle.ToggleView中,部分属性是以android开头的,部分属性是以hss(我自己定义的命名空间)开头的,这是为什么呢?

注意看本片代码第二行,

xmlns:hss="http://schemas.android.com/apk/res/com.hss.toggle"

我在这里写着样一行代码,就说明把values/attrs.xml中的每个条目都导入进来了,就可以直接使用我在attrs.xml里面的属性了

可以直接使用自定义的属性之后,问题应该聚焦到怎么在Java代码中获取到我自定义的属性的值呢?

根据命名空间和自定义属性的name值获取,看代码:

String namespace = "http://schemas.android.com/apk/res/com.hss.toggle";
        int toggle_switchbackground = attrs.getAttributeResourceValue(namespace, "switchBackground", -1);
        int toggle_slidingbackground = attrs.getAttributeResourceValue(namespace, "slidingBackground", -1);
        toggle_state  = attrs.getAttributeBooleanValue(namespace, "toggleState", false);

看到没?该方法用到了attr参数,所以获取自定义属性值的操作应该在两个参数的那里面执行。

整体的自定义控件的类见代码:

package com.hss.toggle;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
 * 自定义开关控件
 * @author hss
 */
public class ToggleView extends View {
    private static final String TAG = "ToogleView";
    private Bitmap sliding_background;
    private Bitmap switch_background;
    private boolean isSliding = false;
    private boolean toggle_state = false;
    private int downX;
    private mToggleStateChangeListener;

    // 构造方法,在xml文件布局的时候,指定了style的时候调用
    public ToggleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    // 构造方法,在xml文件中布局的时候,没有指定style的时候调用
    public ToggleView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        //在Java代码中 获取到xml中自定义属性对应的值
        String namespace = "http://schemas.android.com/apk/res/com.hss.toggle";
        int toggle_switchbackground = attrs.getAttributeResourceValue(namespace, "switchBackground", -1);
        int toggle_slidingbackground = attrs.getAttributeResourceValue(namespace, "slidingBackground", -1);
        toggle_state  = attrs.getAttributeBooleanValue(namespace, "toggleState", false);
        Log.i(TAG,""+toggle_slidingbackground+"     "+toggle_switchbackground);
        // 设置自定义开关的图片
        setToggleSwitchBackground(toggle_switchbackground);
        setToggleSlidingBackground(toggle_slidingbackground);
        setToggleState(toggle_state);
    }

    // 构造方法 在代码中new的时候调用
    public ToggleView(Context context) {
        this(context, null);

    }

    /**
     * 给滑动的控件设置背景图片
     *
     * @param toggle_slidingbackground 图片ID
     */
    private void setToggleSlidingBackground(int toggle_slidingbackground) {
        sliding_background = BitmapFactory.decodeResource(getResources(),toggle_slidingbackground);
    }

    /**
     * 给背景的控件,设置背景图片
     *
     * @param toggle_switchbackground 图片ID
     */
    private void setToggleSwitchBackground(int toggle_switchbackground) {
        switch_background = BitmapFactory.decodeResource(getResources(),toggle_switchbackground);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //测量控件的大小,设置控件的大小为背景图片的大小
        setMeasuredDimension(switch_background.getWidth(),switch_background.getHeight());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //开始画自定义控件,使用canvas对象先把背景图片画上来
        canvas.drawBitmap(switch_background, 0, 0, null);
        if (isSliding) {
        //如果是滑动状态
            //控件距离左边的相对距离为:(控件每时每刻的距离自己左上方的焦点的x轴距离)-(控件本身一半的x轴宽度)
            int left = downX - sliding_background.getWidth() / 2;
            //控件最大的滑动距离(距离左边最大的距离)就是:(背景图片的宽度)-(滑块图片的宽度)
            int rightAlign = switch_background.getWidth()- sliding_background.getWidth();
            //如果距离左边的距离小于0,,就不让他继续往左边动了
            if (left < 0) {
                left = 0;
            } else if (left > rightAlign) {
                //如果距离左边的距离》应该距离左边的最大距离,也不让他往右边移动了
                left = rightAlign;
            }
            //控制好属性之后就可以时时刻刻的跟着画了
            canvas.drawBitmap(sliding_background, left, 0, null);
        } else {
            //如果不滑动,则根据控件的属性中开关的状态,来画滑块的位置
            if (toggle_state) {
                //如果开关状态为真,滑块移动到最右边
                int left = switch_background.getWidth() - sliding_background.getWidth();
                canvas.drawBitmap(sliding_background, left, 0, null);
            } else {
                //如果开关状态为假,滑块移动到最左边
                canvas.drawBitmap(sliding_background, 0, 0, null);
            }
        }
        super.onDraw(canvas);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //重写触摸事件
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            //开始点击的时候,是否滑动置为真,获取到当前手指的距离
            isSliding = true;
            downX = (int) event.getX();
            break;
        case MotionEvent.ACTION_MOVE:
            downX = (int) event.getX();
            break;
        case MotionEvent.ACTION_UP:
            //当点击结束的时候将是否滑动记为假,获取到移动的x轴的坐标
            downX = (int) event.getX();
            isSliding = false;
            //获取到背景图片中间的那个值
            int center = switch_background.getWidth() / 2;

            boolean state = downX > center;
            //如果先后的状态不相同,则将新的状态赋给成员变量,然后调用监听的方法
            if (toggle_state != state) {
                toggle_state = state;
                if (null != mToggleStateChangeListener) {
                    mToggleStateChangeListener
                            .onToggleState(toggle_state);
                }
            }
            break;
        }
        //调用一次onDraw()方法
        invalidate();
        return true;
    }
    //给自定义开关控件设置监听的方法
    public void setOnToggleStateLinstener(OnToggleStateChangeListener listen){
        mToggleStateChangeListener = listen;

    }
    public void setToggleState(boolean b) {
        toggle_state = b;
    }
    //监听回调接口,方法由实现接口的类实现
    public interface OnToggleStateChangeListener {

        public void onToggleState(boolean state);
    }
}

到此,我们的自定义控件部分的逻辑就写完了,,借下来再MainActivity中调用一下

package com.hss.toggle;

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

import com.hss.toggle.ToggleView.OnToggleStateChangeListener;
import com.hss.toggle.utils.ToastUtil;

public class MainActivity extends Activity{

    private ToggleView toggleView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        toggleView = (ToggleView) findViewById(R.id.toggleView);
        toggleView.setOnToggleStateLinstener(new OnToggleStateChangeListener() {

            @Override
            public void onToggleState(boolean state) {
                showToast(state);
            }
        });
    }

    //这里调用到的自己封装的一个快速弹Toast的工具类
    private void showToast(boolean state) {
        ToastUtil.makeSuddenlyToast(getApplicationContext(), state?"开":"关");
    }
}

ToastUtil类如下:

package com.hss.toggle.utils;

import android.content.Context;
import android.widget.Toast;

/**
 * @title Toast工具类
 * @author hss
 */
public class ToastUtil {
    private static Toast toast;

    /**
     * 弹出短时间Toast
     * @param context 上下文对象
     * @param text 要弹出的文字
     */
    public static void makeShortToast(Context context,String text){
        toast = Toast.makeText(context, text, Toast.LENGTH_SHORT);
        toast.show();
    }

    /**
     * 弹出长时间的Toast
     * @param context 上下文对象
     * @param text 要弹出的文字
     */
    public static void makeLongToast(Context context,String text){
        toast = Toast.makeText(context, text, Toast.LENGTH_LONG);
        toast.show();
    }
    /**
     * 单例Toast
     * @param context 上下文对象
     * @param text 要弹出的文字
     */
    public static void makeSuddenlyToast(Context context,String text){
        if(toast==null){
            toast = Toast.makeText(context, text, Toast.LENGTH_SHORT);
        }
        toast.setText(text);
        toast.show();
    }
}

总结一下,其实本次自定义控件的步骤如下:

1、在values/attrs.xml自定义属性和属性值的数据类型

2、在Java代码中定义自定义控件类,继承View或者ViewGroup或者Android原生的控件,实现构造方法,获取到自定义属性的值,并且编写对应的逻辑和点击事件。

3、在布局文件中使用自定义控件和自定义属性(注意命名空间)。

4、在MainActivity中调用

源码下载地址:http://download.csdn.net/detail/sinat_29174099/9583318

时间: 2024-10-09 14:48:45

Android自定义控件之滑动开关的相关文章

Android SwitchButton(滑动开关)

版本:1.0 日期:2014.5.17 2014.6.1 版权:© 2014 kince 转载注明出处 在介绍SwitchButton之前,先来看一下系统Button是如何实现的.源码如下: @RemoteView public class Button extends TextView { public Button(Context context) { this(context, null); } public Button(Context context, AttributeSet att

Android自定义控件之自定义组合控件(三)

前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发成本,以及维护成本. 使用自定义组合控件的好处? 我们在项目开发中经常会遇见很多相似或者相同的布局,比如APP的标题栏,我们从三种方式实现标题栏来对比自定义组件带来的好处,毕竟好的东西还是以提高开发效率,降低开发成本为导向的. 1.)第一种方式:直接在每个xml布局中写相同的标题栏布局代码 <?xm

android自定义控件实现TextView按下后字体颜色改变

今天跟大家分享一下Android自定义控件入门,先介绍一个简单的效果TextView,按下改变字体颜色,后期慢慢扩展更强大的功能 直接看图片             第一张是按下后截的图,功能很简单,也很容易实现,下面来看一下如何通过重写TextView来实现 一共三个文件  TextViewM.java,MainActivity.java,activity_textview.xml TextViewM.java 1 package landptf.control; 2 3 import and

android 自定义控件---圆形方向盘

在做Android平台开发的时候,经常会遇到安卓原生控件无法满足需求的情况,安卓允许开发者去继承已经存在的控件或者实现你自己的控件. 先来看一下效果图 采用直接集成View类,重写onDrow方法绘制. 下面附上主要代码. 1 新建一个类CircleView 继承自View 1 package com.lennon.view; 2 3 import android.content.Context; 4 import android.graphics.Canvas; 5 import androi

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

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

Android自定义控件,轻松实现360软件详情页

Android自定义控件,轻松实现360软件详情页在海军陆战队服役超过 10 年后,我于去年 7 月份退役了.随后在 8 月份找到了一份赌场的工作做公关,到今年 2 月中旬的时候又被辞退了.到 5 月中旬的时候我在 DE 协会找到了一份临时的"初级用户体验工程师"工作,而到了 8 月底我则成了正式的"用户体验工程师". 当我丢掉赌场的那份工作时,我就在想公关这行可能真的不适合我.我想做一名程序员.于是我开始节衣缩食学习编程.家人对我的情况非常担心.从 2 月份到 5

Android自定义控件_View的绘制流程

每一个View/ViewGroup的显示都会经过三个过程:1.measure过程(测量View显示的大小,位置):2.layout过程(布局view的位置):3.draw过程(上一篇文章说到的通过canvas绘制到界面上显示,形成了各色的View) 下面分析一下各个过程:measure过程: 因为DecorView实际上是派生自FrameLayout的类,也即一个ViewGroup实例,该ViewGroup内部的ContentViews又是一个ViewGroup实例,依次内嵌View或ViewG

一起来学习Android自定义控件1

概述 Android已经为我们提供了大量的View供我们使用,但是可能有时候这些组件不能满足我们的需求,这时候就需要自定义控件了.自定义控件对于初学者总是感觉是一种复杂的技术.因为里面涉及到的知识点会比较多.但是任何复杂的技术后面都是一点点简单知识的积累.通过对自定义控件的学习去可以更深入的掌握android的相关知识点,所以学习android自定义控件是很有必要的.记得以前学习总是想着去先理解很多知识点,然后再来学着自定义控件,但是每次写自定义控件的时候总是不知道从哪里下手啊.后来在学习的过程

Android 自定义控件之第三讲:obtainStyledAttributes 系列函数详解

在项目中开发自定义控件时,或多或少都会用到 obtainStyledAttributes(AttributeSet, int[], int, int) 或者 obtainAttributes(AttributeSet, int[]) 函数,它们的主要作用是:根据传入的参数,返回一个对应的 TypedArray ,如果小伙伴还没有看过 LZ 的第二讲,那么请自行移步 Android 自定义控件之第二讲:TypedArray 详解,好了,就先扯到这里,下面开始今天内容讲解: 获取 TypedArra