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中调用

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

(0)

相关推荐

  • Android仿微信列表滑动删除之可滑动控件(一)

    这次是列表滑动删除的第三波,仿微信的列表滑动删除.先上个效果图: 前面的文章里面说过开源框架SwipeListView的实现原理是每个列表item中包含上下两层view,普通状态下上层的view覆盖着下层的view,当用户滑开上层的view,下层的view就显示出来了.但是仔细观察微信列表的item,很明显并非这个实现方案,微信的item应该一个单层view,只不过这个item超出了所在的ListView的宽度,在用户滑动item的时候,item超出屏幕的view则会显示在屏幕之上,这种滑动实现

  • Android实现IOS相机滑动控件

    IOS相比于Android,动画效果是一方面优势,IOS相机切换时滑动的动画很不错,看着是有一个3D的效果,而且变化感觉很自然.Android也可以通过Graphics下面的Camera可以实现3D效果,开始尝试着用这个做了一下,效果不理想,滑动之后各组文字之间的距离就变了,从立体空间来说这是合逻辑的,但是看着很别捏.IOS相机的滑动效果文字之间的间隔在滑动的时候是不变的. 后面通过调整TextView X方向的scale使文字看着紧凑一点,然后通过计算的距离的方式,在滑动的时候保持各组文字之间

  • Android控件之SlidingDrawer(滑动式抽屉)详解与实例分享

    SlidingDrawer效果想必大家也见到过,它就是1.5模拟器上进入应用程序列表的效果.下面是截图 一.简介  SlidingDrawer隐藏屏外的内容,并允许用户通过handle以显示隐藏内容.它可以垂直或水平滑动,它有俩个View组成,其一 是可以拖动的handle,其二是隐藏内容的View.它里面的控件必须设置布局,在布局文件中必须指定handle和content.例如下面 复制代码 代码如下: <SlidingDrawer android:layout_width="fill_

  • Android自定义View实现随手势滑动控件

    本文控件为大家分享了Android随手势滑动控件的具体代码,供大家参考,具体内容如下 1.新建自定义控件类:MyView public class MyView extends Button{ //记录上次滑动后的坐标值 private int lastX; private int lastY; public MyView(Context context) { super(context); // TODO Auto-generated constructor stub } public MyV

  • Android自定义控件实现可左右滑动的导航条

    先上效果图: 这个控件其实算是比较轻量级的,相信不少小伙伴都能做出来.因为项目中遇到了一些特殊的定制要求,所以就自己写了一个,这里放出来.  首先来分析下这个控件的功能:  •能够响应左右滑动,并且能响应快速滑动 •选择项和未选择项有不同的样式表现,比如前景色,背景色,字体大小变粗之内的 •在切换选项的时候,如果当前选项未完全呈现在界面前,则自动滚动直至当前选项完全暴露显示 前两条还有,简简单单就实现了,主要是第三点,这才是我自定义这个控件的原因!那么如果要实现这个控件,需要用到哪些知识呢? 

  • Android控件SeekBar仿淘宝滑动验证效果

    SeekBar是一个拖动条控件,最简单的案例就是我们的调节音量,还有音频视频的播放,传统的SeekBar样式,如图 传统的实现太简单,不足以让我们到能装逼的地步.本来是打算实现滴滴出行滑动完成订单的效果,可惜找不到效果图,今天也就用淘宝的滑动验证来作为实例 1.1 实现分析 SeekBar:使用progressDrawable属性自定义SeekBar 拖动块:使用thumb属性更改,其实就是一张图片 文字:使用RelativeLayout嵌套在一起 1.2 实现布局 <?xml version=

  • Android自定义滑动接听电话控件组实例

    本文根据组件开发思想,首先介绍android自定义控件,然后将自定义的控件封装为jar包.最为实现滑动接听电话控件组. 一.目录结构 二.运行效果 三.代码实现 首先,自定义一个类IncomingPhone继承RelativeLayout public IncomingPhone(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; TextView textView = new Tex

  • Android实现果冻滑动效果的控件

    前言 在微信是的处理方法是让用户滑动,但最终还是回滚到最初的地方,这样的效果很生动(毕竟成功还是取决于细节).那么在安卓我们要怎么弄呢.下面为大家介绍一下JellyScrollView,是我继承ScrollView的一个有阻尼的效果的果冻滑动控件. 下面话不多说了,先来看看效果图 (在虚拟机或者真机跑起来是很流畅,可能是录制视频做成gif的时候有点卡顿.) 实现原理 其实只需要重写下它的拦截方法的逻辑就好了,ScrollView的拦截方法onInterceptTouchEvent一般情况下都默认

  • Android开源堆叠滑动控件仿探探效果

    堆叠滑动控件,类似于社交软件探探的效果,并增加以下扩展: 支持滑动方向控制 支持消失方向控制 支持嵌入到ViewPager等滑动控件 支持内嵌ListView,RecycleView等滑动控件 效果演示 如何使用 xml引入StackCardsView: <com.beyondsw.lib.widget.StackCardsView android:id="@+id/cards" android:layout_width="match_parent" andr

  • 使用Android自定义控件实现滑动解锁九宫格

    本文概述:  滑动解锁九宫格的分析: 1.需要自定义控件: 2.需要重写事件onTouchEvent(); 3.需要给九个点设置序号和坐标,这里用Map类就行: 4.需要判断是否到滑到过九点之一,并存储滑到过的点的序号,而且需要一个方法可以返回它们,这里用List类就行: 滑动解锁当前还是比较流行的,今天写了个简单的滑动解锁九宫格的例程,分享出来让初学者看看. 我的是这样的: Demo 首先,自定义一个View /** * 九宫格 */ public class NineGridView ext

随机推荐