欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义控件实现滑动开关效果

程序员文章站 2024-03-06 10:11:19
自定义开关控件   android自定义控件一般有三种方式 1、继承android固有的控件,在android原生控件的基础上,进行添加功能和逻辑。...

自定义开关控件

 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=""

我在这里写着样一行代码,就说明把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中调用

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。