android自定义开关控件-SlideSwitch的实例

iphone上有开关控件,很漂亮,其实android4.0以后也有switch控件,但是只能用在4.0以后的系统中,这就失去了其使用价值,而且我觉得它的界面也不是很好看。最近看到了百度魔拍上面的一个控件,觉得很漂亮啊,然后反编译了下,尽管没有混淆过,但是还是不好读,然后就按照自己的想法写了个,功能和百度魔拍类似。

下面是百度魔拍的效果和SlideSwitch的效果

一、原理

继承自view类,override其onDraw函数,把两个背景图(一个灰的一个红的)和一个开关图(圆开关)通过canvas画出来;同时override其onTouchEvent函数,实现滑动效果;最后开启一个线程做动画,实现缓慢滑动的效果。

二、代码

SlideSwitch.java

package com.example.hellojni; 

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams; 

/**
 * SlideSwitch 仿iphone滑动开关组件,仿百度魔图滑动开关组件
 * 组件分为三种状态:打开、关闭、正在滑动<br/>
 * 使用方法:
 * <pre>SlideSwitch slideSwitch = new SlideSwitch(this);
 *slideSwitch.setOnSwitchChangedListener(onSwitchChangedListener);
 *linearLayout.addView(slideSwitch);
</pre>
注:也可以加载在xml里面使用
 * @author scott
 *
 */
public class SlideSwitch extends View
{
  public static final String TAG = "SlideSwitch";
  public static final int SWITCH_OFF = 0;//关闭状态
  public static final int SWITCH_ON = 1;//打开状态
  public static final int SWITCH_SCROLING = 2;//滚动状态 

  //用于显示的文本
  private String mOnText = "打开";
  private String mOffText = "关闭"; 

  private int mSwitchStatus = SWITCH_OFF; 

  private boolean mHasScrolled = false;//表示是否发生过滚动 

  private int mSrcX = 0, mDstX = 0; 

  private int mBmpWidth = 0;
  private int mBmpHeight = 0;
  private int mThumbWidth = 0; 

  private   Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 

  private OnSwitchChangedListener mOnSwitchChangedListener = null; 

  //开关状态图
  Bitmap mSwitch_off, mSwitch_on, mSwitch_thumb; 

  public SlideSwitch(Context context)
  {
    this(context, null);
  } 

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

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

  //初始化三幅图片
  private void init()
  {
    Resources res = getResources();
    mSwitch_off = BitmapFactory.decodeResource(res, R.drawable.bg_switch_off);
    mSwitch_on = BitmapFactory.decodeResource(res, R.drawable.bg_switch_on);
    mSwitch_thumb = BitmapFactory.decodeResource(res, R.drawable.switch_thumb);
    mBmpWidth = mSwitch_on.getWidth();
    mBmpHeight = mSwitch_on.getHeight();
    mThumbWidth = mSwitch_thumb.getWidth();
  } 

  @Override
  public void setLayoutParams(LayoutParams params)
  {
    params.width = mBmpWidth;
    params.height = mBmpHeight;
    super.setLayoutParams(params);
  } 

  /**
   * 为开关控件设置状态改变监听函数
   * @param onSwitchChangedListener 参见 {@link OnSwitchChangedListener}
   */
  public void setOnSwitchChangedListener(OnSwitchChangedListener onSwitchChangedListener)
  {
    mOnSwitchChangedListener = onSwitchChangedListener;
  } 

  /**
   * 设置开关上面的文本
   * @param onText 控件打开时要显示的文本
   * @param offText 控件关闭时要显示的文本
   */
  public void setText(final String onText, final String offText)
  {
    mOnText = onText;
    mOffText =offText;
    invalidate();
  } 

  /**
   * 设置开关的状态
   * @param on 是否打开开关 打开为true 关闭为false
   */
  public void setStatus(boolean on)
  {
    mSwitchStatus = ( on ? SWITCH_ON : SWITCH_OFF);
  } 

  @Override
  public boolean onTouchEvent(MotionEvent event)
  {
    int action = event.getAction();
    Log.d(TAG, "onTouchEvent x=" + event.getX());
    switch (action) {
    case MotionEvent.ACTION_DOWN:
      mSrcX = (int) event.getX();
      break;
    case MotionEvent.ACTION_MOVE:
      mDstX = Math.max( (int) event.getX(), 10);
      mDstX = Math.min( mDstX, 62);
      if(mSrcX == mDstX)
        return true;
      mHasScrolled = true;
      AnimationTransRunnable aTransRunnable = new AnimationTransRunnable(mSrcX, mDstX, 0);
      new Thread(aTransRunnable).start();
      mSrcX = mDstX;
      break;
    case MotionEvent.ACTION_UP:
      if(mHasScrolled == false)//如果没有发生过滑动,就意味着这是一次单击过程
      {
        mSwitchStatus = Math.abs(mSwitchStatus-1);
        int xFrom = 10, xTo = 62;
        if(mSwitchStatus == SWITCH_OFF)
        {
          xFrom = 62;
          xTo = 10;
        }
        AnimationTransRunnable runnable = new AnimationTransRunnable(xFrom, xTo, 1);
        new Thread(runnable).start();
      }
      else
      {
        invalidate();
        mHasScrolled = false;
      }
      //状态改变的时候 回调事件函数
      if(mOnSwitchChangedListener != null)
      {
        mOnSwitchChangedListener.onSwitchChanged(this, mSwitchStatus);
      }
      break; 

    default:
      break;
    }
    return true;
  } 

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh)
  {
    super.onSizeChanged(w, h, oldw, oldh);
  } 

  @Override
  protected void onDraw(Canvas canvas)
  {
    super.onDraw(canvas);
    //绘图的时候 内部用到了一些数值的硬编码,其实不太好,
    //主要是考虑到图片的原因,图片周围有透明边界,所以要有一定的偏移
    //硬编码的数值只要看懂了代码,其实可以理解其含义,可以做相应改进。
    mPaint.setTextSize(14);
    mPaint.setTypeface(Typeface.DEFAULT_BOLD); 

    if(mSwitchStatus == SWITCH_OFF)
    {
      drawBitmap(canvas, null, null, mSwitch_off);
      drawBitmap(canvas, null, null, mSwitch_thumb);
      mPaint.setColor(Color.rgb(105, 105, 105));
      canvas.translate(mSwitch_thumb.getWidth(), 0);
      canvas.drawText(mOffText, 0, 20, mPaint);
    }
    else if(mSwitchStatus == SWITCH_ON)
    {
      drawBitmap(canvas, null, null, mSwitch_on);
      int count = canvas.save();
      canvas.translate(mSwitch_on.getWidth() - mSwitch_thumb.getWidth(), 0);
      drawBitmap(canvas, null, null, mSwitch_thumb);
      mPaint.setColor(Color.WHITE);
      canvas.restoreToCount(count);
      canvas.drawText(mOnText, 17, 20, mPaint);
    }
    else //SWITCH_SCROLING
    {
      mSwitchStatus = mDstX > 35 ? SWITCH_ON : SWITCH_OFF;
      drawBitmap(canvas, new Rect(0, 0, mDstX, mBmpHeight), new Rect(0, 0, (int)mDstX, mBmpHeight), mSwitch_on);
      mPaint.setColor(Color.WHITE);
      canvas.drawText(mOnText, 17, 20, mPaint); 

      int count = canvas.save();
      canvas.translate(mDstX, 0);
      drawBitmap(canvas, new Rect(mDstX, 0, mBmpWidth, mBmpHeight),
             new Rect(0, 0, mBmpWidth - mDstX, mBmpHeight), mSwitch_off);
      canvas.restoreToCount(count); 

      count = canvas.save();
      canvas.clipRect(mDstX, 0, mBmpWidth, mBmpHeight);
      canvas.translate(mThumbWidth, 0);
      mPaint.setColor(Color.rgb(105, 105, 105));
      canvas.drawText(mOffText, 0, 20, mPaint);
      canvas.restoreToCount(count); 

      count = canvas.save();
      canvas.translate(mDstX - mThumbWidth / 2, 0);
      drawBitmap(canvas, null, null, mSwitch_thumb);
      canvas.restoreToCount(count);
    } 

  } 

  public void drawBitmap(Canvas canvas, Rect src, Rect dst, Bitmap bitmap)
  {
    dst = (dst == null ? new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()) : dst);
    Paint paint = new Paint();
    canvas.drawBitmap(bitmap, src, dst, paint);
  } 

  /**
   * AnimationTransRunnable 做滑动动画所使用的线程
   */
  private class AnimationTransRunnable implements Runnable
  {
    private int srcX, dstX;
    private int duration; 

    /**
     * 滑动动画
     * @param srcX 滑动起始点
     * @param dstX 滑动终止点
     * @param duration 是否采用动画,1采用,0不采用
     */
    public AnimationTransRunnable(float srcX, float dstX, final int duration)
    {
      this.srcX = (int)srcX;
      this.dstX = (int)dstX;
      this.duration = duration;
    } 

    @Override
    public void run()
    {
      final int patch = (dstX > srcX ? 5 : -5);
      if(duration == 0)
      {
        SlideSwitch.this.mSwitchStatus = SWITCH_SCROLING;
        SlideSwitch.this.postInvalidate();
      }
      else
      {
        Log.d(TAG, "start Animation: [ " + srcX + " , " + dstX + " ]");
        int x = srcX + patch;
        while (Math.abs(x-dstX) > 5)
        {
          mDstX = x;
          SlideSwitch.this.mSwitchStatus = SWITCH_SCROLING;
          SlideSwitch.this.postInvalidate();
          x += patch;
          try
          {
            Thread.sleep(10);
          }
          catch (InterruptedException e)
          {
            e.printStackTrace();
          }
        }
        mDstX = dstX;
        SlideSwitch.this.mSwitchStatus = mDstX > 35 ? SWITCH_ON : SWITCH_OFF;
        SlideSwitch.this.postInvalidate();
      }
    } 

  } 

  public static interface OnSwitchChangedListener
  {
    /**
     * 状态改变 回调函数
     * @param status SWITCH_ON表示打开 SWITCH_OFF表示关闭
     */
    public abstract void onSwitchChanged(SlideSwitch obj, int status);
  } 

}

layout xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="#fdfdfd"
  android:orientation="vertical"
  android:paddingLeft="10dip"
  android:paddingRight="10dip" > 

  <ImageView
    android:id="@+id/imageView1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:src="@drawable/top" /> 

  <RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" > 

    <TextView
      android:id="@+id/textView1"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentLeft="true"
      android:layout_centerVertical="true"
      android:text="网络构图"
      android:textSize="15sp" /> 

    <com.example.hellojni.SlideSwitch
      android:id="@+id/slideSwitch1"
      android:layout_width="116dip"
      android:layout_height="46dip"
      android:layout_alignParentRight="true"
      android:layout_centerVertical="true" />
  </RelativeLayout> 

  <RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" > 

    <TextView
      android:id="@+id/textView2"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentLeft="true"
      android:layout_centerVertical="true"
      android:text="保留原图"
      android:textSize="15sp" /> 

    <com.example.hellojni.SlideSwitch
      android:id="@+id/slideSwitch2"
      android:layout_width="116dip"
      android:layout_height="46dip"
      android:layout_alignParentRight="true"
      android:layout_centerVertical="true" />
  </RelativeLayout> 

  <RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" > 

    <TextView
      android:id="@+id/textView3"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentLeft="true"
      android:layout_centerVertical="true"
      android:text="拍照声音"
      android:textSize="15sp" /> 

    <com.example.hellojni.SlideSwitch
      android:id="@+id/slideSwitch3"
      android:layout_width="116px"
      android:layout_height="46px"
      android:layout_alignParentRight="true"
      android:layout_centerVertical="true" />
  </RelativeLayout> 

  <TextView
    android:id="@+id/textViewTip"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:text="TextView" /> 

</LinearLayout>

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

(0)

相关推荐

  • Android自定义控件之开关按钮学习笔记分享

    今天来讲讲自定义单个控件,就拿开关按钮来讲讲,相信大家见了非常多这样的了,先看看效果: 我们可以看到一个很常见的开关按钮,那就来分析分析. 首先: 这是由两张图片构成: ①一张为有开和关的背景图片 ②一张为控制开和关的滑动按钮 第一步: 写个类继承View,并重写几个方法: 第一个为构造函数,重写一个参数的函数和两个参数的函数就够了,因为两个参数的函数能够使用自定义属性 第二个为控制控件的大小–>protected void onMeasure(int widthMeasureSpec, int

  • Android自定义实现开关按钮代码

    我们在应用中经常看到一些选择开关状态的配置文件,做项目的时候用的是android的Switch控件,但是感觉好丑的样子子 个人认为还是自定义的比较好,先上个效果图: 实现过程: 1.准备开关不同状态的两张图片放入drawable中. 2.xml文件中添加代码: <ToggleButton android:id="@+id/switch1" android:layout_width="wrap_content" android:layout_height=&qu

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

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

  • Android 仿苹果IOS6开关按钮

    先给大家展示下效果图: 不知道大家对效果图感觉怎么样,个人觉还不错,感兴趣的朋友可以参考下实现代码哦. public class ToggleButton extends View { private SpringSystem springSystem; private Spring spring ; /** */ private float radius; /** 开启颜色*/ private int onColor = Color.parseColor("#4ebb7f"); /*

  • Android模拟开关按钮点击打开动画(属性动画之平移动画)

    在Android里面,一些炫酷的动画确实是很吸引人的地方,让然看了就赏心悦目,一个好看的动画可能会提高用户对软件的使用率.另外说到动画,在Android里面支持两种动画:补间动画和属性动画,至于这两种动画的区别这里不再介绍,希望开发者都能在使用的过程中体会两者的不同. 本文使用属性动画完成,说到属性动画,肯定要提到 JakeWharton大神写的NineOldAndroids动画库,如果你的app需要在android3.0以下使用属性动画,那么这个库就很有作用了,如果只需要在高版本使用,那么直接

  • android自定义开关控件-SlideSwitch的实例

    iphone上有开关控件,很漂亮,其实android4.0以后也有switch控件,但是只能用在4.0以后的系统中,这就失去了其使用价值,而且我觉得它的界面也不是很好看.最近看到了百度魔拍上面的一个控件,觉得很漂亮啊,然后反编译了下,尽管没有混淆过,但是还是不好读,然后就按照自己的想法写了个,功能和百度魔拍类似. 下面是百度魔拍的效果和SlideSwitch的效果 一.原理 继承自view类,override其onDraw函数,把两个背景图(一个灰的一个红的)和一个开关图(圆开关)通过canva

  • Android自定义评分控件的完整实例

    目录 前言 自定义参数 解析参数 绘制 事件处理 评分监听 外部使用 总结 前言 无意中翻到几年前写过的一个RatingBar,可以拖拽,支持自定义星星图片,间距大小等参数. 自定义参数 为了方便扩展,支持更多的样式,这里将大部分参数设置成支持外部可配置的形式. <declare-styleable name="RatingBarPlus"> <attr name="hideImageResource" format="reference

  • Android自定义组合控件之自定义下拉刷新和左滑删除实例代码

    绪论 最近项目里面用到了下拉刷新和左滑删除,网上找了找并没有可以用的,有比较好的左滑删除,但是并没有和下拉刷新上拉加载结合到一起,要不就是一些比较水的结合,并不能在项目里面使用,小编一着急自己组合了一个,做完了和QQ的对比了一下,并没有太大区别,今天分享给大家,其实并不难,但是不知道为什么网上没有比较好的Demo,当你的项目真的很急的时候,又没有比较好的Demo,那么"那条友谊的小船儿真是说翻就翻啊",好了,下面先来具体看一下实现后的效果吧: 代码已经上传到Github上了,小伙伴们记

  • Android自定义DigitalClock控件实现商品倒计时

    本文实例为大家分享了DigitalClock实现商品倒计时的具体代码,供大家参考,具体内容如下 自定义DigitalClock控件: package com.veally.timesale; import java.util.Calendar; import android.content.Context; import android.database.ContentObserver; import android.os.Handler; import android.os.SystemClo

  • Android自定义View控件实现刷新效果

    三种得到LinearInflater的方法 a. LayoutInflater inflater = getLayoutInflater(); b. LayoutInflater localinflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); c. LayoutInflater inflater = LayoutInflater.from(context); onDraw 方法

  • Android 自定义Button控件实现按钮点击变色

    效果图如下所示: 一.shape 样式:(在drawable新建-->new-->Drawable resource file 在父级标签selector添加Item ) <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item and

  • Android自定义日历控件实例详解

    为什么要自定义控件 有时,原生控件不能满足我们对于外观和功能的需求,这时候可以自定义控件来定制外观或功能:有时,原生控件可以通过复杂的编码实现想要的功能,这时候可以自定义控件来提高代码的可复用性. 如何自定义控件 下面我通过我在github上开源的Android-CalendarView项目为例,来介绍一下自定义控件的方法.该项目中自定义的控件类名是CalendarView.这个自定义控件覆盖了一些自定义控件时常需要重写的一些方法. 构造函数 为了支持本控件既能使用xml布局文件声明,也可在ja

  • android自定义倒计时控件示例

    自定义TextView控件TimeTextView代码: 复制代码 代码如下: import android.content.Context;import android.content.res.TypedArray;import android.graphics.Paint;import android.text.Html;import android.util.AttributeSet;import android.widget.TextView; import com.new0315.R;

  • Android自定义表格控件满足人们对视觉的需求

    Android平台已经给我们提供了很多标准的组件,如:TextView.EditView.Button.ImageView.Menu等,还有许多布局控件,常见的有:AbsoluteLayout.LinerLayout.RelativeLayout.TableLayout等.但随着人们对视觉的需求,基本组件已无法满足人们求新求异的要求,于是我们常常会自定义组件,用来实现更美观的UI界面. 实现自定义控件通常有两种途径,一种是继承View类,重写其中的重要方法,另一种是继承ViewGroup类,通过

  • Android自定义Gallery控件实现3D图片浏览器

    本篇文章主要介绍如何使用自定义的Gallery控件,实现3D效果的图片浏览器的效果. 话不多说,先看效果. 上面是一个自定义的Gallery控件,实现倒影和仿3D的效果,下面是一个图片查看器,点击上面的小图片,可以在下面查看大图片. 下面重点说一下,实现图片查看器的思路. 1.手机中图片路径的获取 首先,先不管图片如何展示,如果我们想实现图片查看器的功能,我们首先需要做的是获取到所有的图片的路径信息,只有这样,我们才能实现对图片的查看. 我们可以使用下面的代码实现 private List<St

随机推荐