android自定義開關(guān)控件-SlideSwitch的實例
iphone上有開關(guān)控件,很漂亮,其實android4.0以后也有switch控件,但是只能用在4.0以后的系統(tǒng)中,這就失去了其使用價值,而且我覺得它的界面也不是很好看。最近看到了百度魔拍上面的一個控件,覺得很漂亮啊,然后反編譯了下,盡管沒有混淆過,但是還是不好讀,然后就按照自己的想法寫了個,功能和百度魔拍類似。
下面是百度魔拍的效果和SlideSwitch的效果

一、原理
繼承自view類,override其onDraw函數(shù),把兩個背景圖(一個灰的一個紅的)和一個開關(guān)圖(圓開關(guān))通過canvas畫出來;同時override其onTouchEvent函數(shù),實現(xiàn)滑動效果;最后開啟一個線程做動畫,實現(xiàn)緩慢滑動的效果。
二、代碼
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滑動開關(guān)組件,仿百度魔圖滑動開關(guān)組件
* 組件分為三種狀態(tài):打開、關(guān)閉、正在滑動<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;//關(guān)閉狀態(tài)
public static final int SWITCH_ON = 1;//打開狀態(tài)
public static final int SWITCH_SCROLING = 2;//滾動狀態(tài)
//用于顯示的文本
private String mOnText = "打開";
private String mOffText = "關(guān)閉";
private int mSwitchStatus = SWITCH_OFF;
private boolean mHasScrolled = false;//表示是否發(fā)生過滾動
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;
//開關(guān)狀態(tài)圖
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);
}
/**
* 為開關(guān)控件設(shè)置狀態(tài)改變監(jiān)聽函數(shù)
* @param onSwitchChangedListener 參見 {@link OnSwitchChangedListener}
*/
public void setOnSwitchChangedListener(OnSwitchChangedListener onSwitchChangedListener)
{
mOnSwitchChangedListener = onSwitchChangedListener;
}
/**
* 設(shè)置開關(guān)上面的文本
* @param onText 控件打開時要顯示的文本
* @param offText 控件關(guān)閉時要顯示的文本
*/
public void setText(final String onText, final String offText)
{
mOnText = onText;
mOffText =offText;
invalidate();
}
/**
* 設(shè)置開關(guān)的狀態(tài)
* @param on 是否打開開關(guān) 打開為true 關(guān)閉為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)//如果沒有發(fā)生過滑動,就意味著這是一次單擊過程
{
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;
}
//狀態(tài)改變的時候 回調(diào)事件函數(shù)
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);
//繪圖的時候 內(nèi)部用到了一些數(shù)值的硬編碼,其實不太好,
//主要是考慮到圖片的原因,圖片周圍有透明邊界,所以要有一定的偏移
//硬編碼的數(shù)值只要看懂了代碼,其實可以理解其含義,可以做相應改進。
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
{
/**
* 狀態(tài)改變 回調(diào)函數(shù)
* @param status SWITCH_ON表示打開 SWITCH_OFF表示關(guān)閉
*/
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="網(wǎng)絡構(gòu)圖"
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>
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- android開發(fā)教程之switch控件使用示例
- Android UI控件Switch的使用方法
- Android開關(guān)控件Switch的使用案例
- Android 自定義Switch開關(guān)按鈕的樣式實例詳解
- Android UI設(shè)計系列之自定義SwitchButton開關(guān)實現(xiàn)類似IOS中UISwitch的動畫效果(2)
- Android自定義控件實現(xiàn)滑動開關(guān)效果
- Android自定義控件之開關(guān)按鈕學習筆記分享
- Android開發(fā)進階自定義控件之滑動開關(guān)實現(xiàn)方法【附demo源碼下載】
- Android開發(fā)實現(xiàn)Switch控件修改樣式功能示例【附源碼下載】
相關(guān)文章
Android布局控件View?ViewRootImpl?WindowManagerService關(guān)系
這篇文章主要為大家介紹了Android布局控件View?ViewRootImpl?WindowManagerService關(guān)系示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02
Android開發(fā)之MediaPlayer多媒體(音頻,視頻)播放工具類
這篇文章主要介紹了Android開發(fā)之MediaPlayer多媒體(音頻,視頻)播放工具類,涉及Android針對音頻文件的讀取、播放、暫停、繼續(xù)等操作實現(xiàn)技巧,需要的朋友可以參考下2017-12-12
android使用SkinManager實現(xiàn)換膚功能的示例
本篇文章主要介紹了android使用SkinManager實現(xiàn)換膚功能的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02

