Android實現(xiàn)史上最簡單自定義開關按鈕的方法
前言
很多時候,我們在很多無論是Android還是IOS的APP中都會遇到這樣的一種效果,有一個按鈕,我們點擊一下,便會滑動一下,一會顯示“開”,一會顯示“關”,這便是開關按鈕了,比如:很多Android手機的設置功能里,就有很多功能是用開關按鈕實現(xiàn)的,那么這些開關按鈕時如何實現(xiàn)的呢?下面,就讓我們一起來實現(xiàn)這個功能吧。
一、原理
我們在界面的某一個區(qū)域里放置一個背景圖A,這個圖片一邊為“開”,一邊為“關”,在這個圖片上放置一個圖片B,圖B大約為圖A的一半,恰好可以覆蓋掉圖A上的“開”或者“關”,當我們手指點擊圖片的時候,圖B在圖A上滑動,相應的覆蓋“開”或者“關”,這樣就實現(xiàn)了開關按鈕的效果。
二、實現(xiàn)
1、自定義View類MyToggle
這個類繼承自View類同時實現(xiàn)了OnTouchListener接口,這個類實現(xiàn)的功能比較多,我們分解來看這個類。
1)屬性字段
這個類中定義了不少的屬性字段,每個屬性字段的具體含義詳見代碼注釋
具體實現(xiàn)代碼如下:
//開關開啟的背景圖片 private Bitmap bkgSwitchOn; //開關關閉的背景圖片 private Bitmap bkgSwitchOff; //開關的滾動圖片 private Bitmap btnSlip; //當前開關是否為開啟狀態(tài) private boolean toggleStateOn; //開關狀態(tài)的監(jiān)聽事件 private OnToggleStateListener toggleStateListener; //記錄開關·當前的狀態(tài) private boolean isToggleStateListenerOn; //手指按下屏幕時的x坐標 private float proX; //手指滑動過程中當前x坐標 private float currentX; //是否處于滑動狀態(tài) private boolean isSlipping; //記錄上一次開關的狀態(tài) private boolean proToggleState \= true; //開關開啟時的矩形 private Rect rect\_on; //開關關閉時的矩形 private Rect rect\_off;
2)覆寫View類的構造方法
我們在構造方法里完成的操作就是調用我們自己創(chuàng)建的init()方法
具體實現(xiàn)代碼如下:
public MyToggle(Context context) {
super(context);
init(context);
}
public MyToggle(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}3)創(chuàng)建init方法
這個方法中實現(xiàn)的操作就是設置觸摸事件。
具體實現(xiàn)代碼如下:
//初始化方法
private void init(Context context) {
setOnTouchListener(this);
}4)手指觸摸事件回調方法onTouch
這個方法是當手指操作手機屏幕時,Android自動回調的方法,我們在這個方法中,監(jiān)聽手指的按下、移動和抬起事件,記錄手指當前的X坐標來判斷圖片B的移動方向,從而實現(xiàn)圖片B在圖片A上的移動來達到按鈕開和關的效果。
具體實現(xiàn)代碼如下:
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION\_DOWN:
//記錄手指按下時的x坐標
proX \= event.getX();
currentX \= proX;
//將滑動標識設置為true
isSlipping \= true;
break;
case MotionEvent.ACTION\_MOVE:
//記錄手指滑動過程中當前x坐標
currentX \= event.getX();
break;
case MotionEvent.ACTION\_UP:
//手指抬起時將是否滑動的標識設置為false
isSlipping \= false;
//處于關閉狀態(tài)
if(currentX < bkgSwitchOn.getWidth() / 2 ){
toggleStateOn \= false;
} else { // 處于開啟狀態(tài)
toggleStateOn \= true;
}
// 如果使用了開關監(jiān)聽器,同時開關的狀態(tài)發(fā)生了改變,這時使用該代碼
if(isToggleStateListenerOn && toggleStateOn != proToggleState){
proToggleState \= toggleStateOn;
toggleStateListener.onToggleState(toggleStateOn);
}
break;
}
invalidate();//重繪
return true;
}5)界面重繪方法onDraw
這個方法主要實現(xiàn)的是界面的重繪操作。
只要的思路是:
畫背景圖A:
當前手指滑動X坐標currentX大于圖A寬度的一般時,按鈕背景為開啟狀態(tài);
當前手指滑動X坐標currentX小于圖A寬度的一般時,按鈕背景為關閉狀態(tài);
記錄滑塊B的X坐標:
B滑動時:
當前手指滑動X坐標currentX大于背景圖A的寬度,則B坐標為圖A寬度減去圖B寬度
當前手指滑動X坐標currentX小于背景圖A的寬度,則B坐標為當前X坐標currentX減去滑塊寬度的一半
B靜止:
當按鈕處于“開”狀態(tài),則B坐標為“開”狀態(tài)的最左邊X坐標
當按鈕處于“關”狀態(tài),則B坐標為“關”狀態(tài)的最左邊X坐標
具體實現(xiàn)代碼如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//用來記錄我們滑動塊的位置
int left\_slip \= 0;
Matrix matrix \= new Matrix();
Paint paint \= new Paint();
if(currentX < bkgSwitchOn.getWidth() / 2){
//在畫布上繪制出開關狀態(tài)為關閉時的 背景圖片
canvas.drawBitmap(bkgSwitchOff, matrix, paint);
}else{
//在畫布上繪制出開關狀態(tài)為開啟時的 背景圖片
canvas.drawBitmap(bkgSwitchOn, matrix, paint);
}
if(isSlipping){//開關是否處于滑動狀態(tài)
// 滑動塊 是否超過了整個滑動按鈕的寬度
if(currentX \> bkgSwitchOn.getWidth()){
//指定滑動塊的位置
left\_slip \= bkgSwitchOn.getWidth() \- btnSlip.getWidth();
} else {
//設置當前滑動塊的位置
left\_slip \= (int) (currentX \- btnSlip.getWidth() /2);
}
} else {//開關是否處于 不滑動狀態(tài)
if(toggleStateOn){
left\_slip \= rect\_on.left;
} else {
left\_slip \= rect\_off.left;
}
}
if(left\_slip < 0){
left\_slip \= 0;
} else if( left\_slip \> bkgSwitchOn.getWidth() \- btnSlip.getWidth()){
left\_slip \= bkgSwitchOn.getWidth() \- btnSlip.getWidth();
}
//繪制圖像
canvas.drawBitmap(btnSlip, left\_slip, 0, paint);
}6)計算開關的寬高
這里我通過覆寫onMeasure來計算開關的寬度和高度
具體實現(xiàn)代碼如下:
//計算開關的寬高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(bkgSwitchOn.getWidth(), bkgSwitchOn.getHeight());
}7)設置圖片資源信息
這個方法主要是供外界調用,向本類提供圖片資源。
具體代碼實現(xiàn)如下:
/\*\*
\* 設置圖片資源信息
\* @param bkgSwitch\_on
\* @param bkgSwitch\_off
\* @param btn\_Slip
\*/
public void setImageRes(int bkgSwitch\_on, int bkgSwitch\_off, int btn\_Slip) {
bkgSwitchOn \= BitmapFactory.decodeResource(getResources(), bkgSwitch\_on);
bkgSwitchOff \= BitmapFactory.decodeResource(getResources(),bkgSwitch\_off);
btnSlip \= BitmapFactory.decodeResource(getResources(), btn\_Slip);
rect\_on \= new Rect(bkgSwitchOn.getWidth() \- btnSlip.getWidth(), 0,bkgSwitchOn.getWidth(), btnSlip.getHeight());
rect\_off \= new Rect(0, 0, btnSlip.getWidth(), btnSlip.getHeight());
}8)設置開關按鈕的狀態(tài)
通過傳遞一個boolean類型的狀態(tài),我們在這個方法中將這個狀態(tài)標識記錄下來。
具體實現(xiàn)代碼如下:
/\*\*
\* 設置開關按鈕的狀態(tài)
\* @param state
\*/
public void setToggleState(boolean state) {
toggleStateOn \= state;
}9)自定義開關狀態(tài)監(jiān)聽器
我在這個類中定義了一個開關狀態(tài)監(jiān)聽器接口OnToggleStateListener,里面有一個onToggleState方法來執(zhí)行按鈕的狀態(tài)變化監(jiān)聽操作。
具體代碼實現(xiàn)如下:
/\*\*
\* 自定義開關狀態(tài)監(jiān)聽器
\* @author liuyazhuang
\*
\*/
interface OnToggleStateListener {
abstract void onToggleState(boolean state);
}10)設置開關監(jiān)聽器
創(chuàng)建setOnToggleStateListener方法,傳遞一個OnToggleStateListener監(jiān)聽器對象,通過外界創(chuàng)建OnToggleStateListener對象,并將OnToggleStateListener對象傳遞進來,我們只需要將外界傳遞過來的OnToggleStateListener對象記錄下來,同時當我們調用OnToggleStateListener接口中的onToggleState方法時,便實現(xiàn)了回調外界OnToggleStateListener實現(xiàn)類中的onToggleState方法。
具體代碼實現(xiàn)如下:
//設置開關監(jiān)聽器并將是否設置了開關監(jiān)聽器設置為true
public void setOnToggleStateListener(OnToggleStateListener listener) {
toggleStateListener \= listener;
isToggleStateListenerOn \= true;
}11)MyToggle完整代碼如下:
package com.lyz.slip.toggle;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
/\*\*
\* 自定義開關類
\* @author liuyazhuang
\*
\*/
public class MyToggle extends View implements OnTouchListener {
//開關開啟的背景圖片
private Bitmap bkgSwitchOn;
//開關關閉的背景圖片
private Bitmap bkgSwitchOff;
//開關的滾動圖片
private Bitmap btnSlip;
//當前開關是否為開啟狀態(tài)
private boolean toggleStateOn;
//開關狀態(tài)的監(jiān)聽事件
private OnToggleStateListener toggleStateListener;
//記錄開關·當前的狀態(tài)
private boolean isToggleStateListenerOn;
//手指按下屏幕時的x坐標
private float proX;
//手指滑動過程中當前x坐標
private float currentX;
//是否處于滑動狀態(tài)
private boolean isSlipping;
//記錄上一次開關的狀態(tài)
private boolean proToggleState \= true;
//開關開啟時的矩形
private Rect rect\_on;
//開關關閉時的矩形
private Rect rect\_off;
public MyToggle(Context context) {
super(context);
init(context);
}
public MyToggle(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
//初始化方法
private void init(Context context) {
setOnTouchListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION\_DOWN:
//記錄手指按下時的x坐標
proX \= event.getX();
currentX \= proX;
//將滑動標識設置為true
isSlipping \= true;
break;
case MotionEvent.ACTION\_MOVE:
//記錄手指滑動過程中當前x坐標
currentX \= event.getX();
break;
case MotionEvent.ACTION\_UP:
//手指抬起時將是否滑動的標識設置為false
isSlipping \= false;
//處于關閉狀態(tài)
if(currentX < bkgSwitchOn.getWidth() / 2 ){
toggleStateOn \= false;
} else { // 處于開啟狀態(tài)
toggleStateOn \= true;
}
// 如果使用了開關監(jiān)聽器,同時開關的狀態(tài)發(fā)生了改變,這時使用該代碼
if(isToggleStateListenerOn && toggleStateOn != proToggleState){
proToggleState \= toggleStateOn;
toggleStateListener.onToggleState(toggleStateOn);
}
break;
}
invalidate();//重繪
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//用來記錄我們滑動塊的位置
int left\_slip \= 0;
Matrix matrix \= new Matrix();
Paint paint \= new Paint();
if(currentX < bkgSwitchOn.getWidth() / 2){
//在畫布上繪制出開關狀態(tài)為關閉時的 背景圖片
canvas.drawBitmap(bkgSwitchOff, matrix, paint);
}else{
//在畫布上繪制出開關狀態(tài)為開啟時的 背景圖片
canvas.drawBitmap(bkgSwitchOn, matrix, paint);
}
if(isSlipping){//開關是否處于滑動狀態(tài)
// 滑動塊 是否超過了整個滑動按鈕的寬度
if(currentX \> bkgSwitchOn.getWidth()){
//指定滑動塊的位置
left\_slip \= bkgSwitchOn.getWidth() \- btnSlip.getWidth();
} else {
//設置當前滑動塊的位置
left\_slip \= (int) (currentX \- btnSlip.getWidth() /2);
}
} else {//開關是否處于 不滑動狀態(tài)
if(toggleStateOn){
left\_slip \= rect\_on.left;
} else {
left\_slip \= rect\_off.left;
}
}
if(left\_slip < 0){
left\_slip \= 0;
} else if( left\_slip \> bkgSwitchOn.getWidth() \- btnSlip.getWidth()){
left\_slip \= bkgSwitchOn.getWidth() \- btnSlip.getWidth();
}
//繪制圖像
canvas.drawBitmap(btnSlip, left\_slip, 0, paint);
}
//計算開關的寬高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(bkgSwitchOn.getWidth(), bkgSwitchOn.getHeight());
}
/\*\*
\* 設置圖片資源信息
\* @param bkgSwitch\_on
\* @param bkgSwitch\_off
\* @param btn\_Slip
\*/
public void setImageRes(int bkgSwitch\_on, int bkgSwitch\_off, int btn\_Slip) {
bkgSwitchOn \= BitmapFactory.decodeResource(getResources(), bkgSwitch\_on);
bkgSwitchOff \= BitmapFactory.decodeResource(getResources(),bkgSwitch\_off);
btnSlip \= BitmapFactory.decodeResource(getResources(), btn\_Slip);
rect\_on \= new Rect(bkgSwitchOn.getWidth() \- btnSlip.getWidth(), 0,bkgSwitchOn.getWidth(), btnSlip.getHeight());
rect\_off \= new Rect(0, 0, btnSlip.getWidth(), btnSlip.getHeight());
}
/\*\*
\* 設置開關按鈕的狀態(tài)
\* @param state
\*/
public void setToggleState(boolean state) {
toggleStateOn \= state;
}
/\*\*
\* 自定義開關狀態(tài)監(jiān)聽器
\* @author liuyazhuang
\*
\*/
interface OnToggleStateListener {
abstract void onToggleState(boolean state);
}
//設置開關監(jiān)聽器并將是否設置了開關監(jiān)聽器設置為true
public void setOnToggleStateListener(OnToggleStateListener listener) {
toggleStateListener \= listener;
isToggleStateListenerOn \= true;
}
}2、MainActivity
這個類實現(xiàn)很簡單,主要的功能就是加載界面布局,初始化界面控件,調用MyToggle類中的方法實現(xiàn)按鈕的開關效果
具體代碼實現(xiàn)如下:
package com.lyz.slip.toggle;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
import com.lyz.slip.toggle.MyToggle.OnToggleStateListener;
/\*\*
\* 程序主入口
\* @author liuyazhuang
\*
\*/
public class MainActivity extends Activity {
//自定義開關對象
private MyToggle toggle;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity\_main);
toggle \= (MyToggle) findViewById(R.id.toggle);
//設置開關顯示所用的圖片
toggle.setImageRes(R.drawable.bkg\_switch, R.drawable.bkg\_switch, R.drawable.btn\_slip);
//設置開關的默認狀態(tài) true開啟狀態(tài)
toggle.setToggleState(true);
//設置開關的監(jiān)聽
toggle.setOnToggleStateListener(new OnToggleStateListener() {
@Override
public void onToggleState(boolean state) {
// TODO Auto-generated method stub
if(state){
Toast.makeText(getApplicationContext(), "開關開啟", 0).show();
} else {
Toast.makeText(getApplicationContext(), "開關關閉", 0).show();
}
}
});
}
}3、布局文件activity_main.xml
這里我引用了自己定義的View類MyToggle。
具體代碼實現(xiàn)如下:
<RelativeLayout xmlns:android\="http://schemas.android.com/apk/res/android"
xmlns:tools\="http://schemas.android.com/tools"
android:layout\_width\="match\_parent"
android:layout\_height\="match\_parent" \>
<com.lyz.slip.toggle.MyToggle
android:id\="@+id/toggle"
android:layout\_width\="wrap\_content"
android:layout\_height\="wrap\_content"
android:layout\_centerInParent\="true"/>
</RelativeLayout\>4、AndroidManifest.xml
具體代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android\="http://schemas.android.com/apk/res/android"
package\="com.lyz.slip.toggle"
android:versionCode\="1"
android:versionName\="1.0" \>
<uses-sdk
android:minSdkVersion\="10"
android:targetSdkVersion\="18" />
<application
android:allowBackup\="true"
android:icon\="@drawable/ic\_launcher"
android:label\="@string/app\_name"
android:theme\="@style/AppTheme" \>
<activity
android:name\="com.lyz.slip.toggle.MainActivity"
android:label\="@string/app\_name" \>
<intent-filter\>
<action android:name\="android.intent.action.MAIN" />
<category android:name\="android.intent.category.LAUNCHER" />
</intent-filter\>
</activity\>
</application\>
</manifest\>三、運行效果


四、溫馨提示
大家可以到鏈接下載Android自定義開關按鈕實現(xiàn)示例完整源代碼
本實例中,為了方面,我把一些文字直接寫在了布局文件中和相關的類中,大家在真實的項目中要把這些文字寫在string.xml文件中,在外部引用這些資源,切記,這是作為一個Android程序員最基本的開發(fā)常識和規(guī)范,我在這里只是為了方便直接寫在了類和布局文件中。
到此這篇關于Android實現(xiàn)史上最簡單自定義開關按鈕的文章就介紹到這了,更多相關Android自定義開關按鈕內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android studio實現(xiàn)PopupWindow彈出框效果
這篇文章主要為大家詳細介紹了Android studio實現(xiàn)PopupWindow彈出框效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-10-10
Android自定義控件ImageView實現(xiàn)點擊之后出現(xiàn)陰影效果
這篇文章主要為大家詳細介紹了Android自定義控件ImageView實現(xiàn)點擊之后有陰影效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12
詳解Android studio 動態(tài)fragment的用法
這篇文章主要介紹了Android studio 動態(tài)fragment的用法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10
Android中修改TabLayout底部導航條Indicator長短的方法
Tablayout在我們日常開發(fā)中經(jīng)常會遇到,下面這篇文章主要給大家介紹了在Android中修改TabLayout底部導航條Indicator長短的方法,文中給出了詳細的示例代碼供大家參考學習,需要的朋友們下面來一起看看吧。2017-06-06

