Android自定義控件實現(xiàn)滑動開關(guān)效果
本文實例為大家分享了Android實現(xiàn)滑動開關(guān)效果的具體代碼,供大家參考,具體內(nèi)容如下
自定義開關(guān)控件

Android自定義控件一般有三種方式
1、繼承Android固有的控件,在Android原生控件的基礎(chǔ)上,進行添加功能和邏輯。
2、繼承ViewGroup,這類自定義控件是可以往自己的布局里面添加其他的子控件的。
3、繼承View,這類自定義控件沒有跟原生的控件有太多的相似的地方,也不需要在自己的肚子里添加其他的子控件。
ToggleView自定義開關(guān)控件表征上沒有跟Android原生的控件有什么相似的地方,而且在滑動的效果上也沒有沿襲Android原生的地方,所以我們的自定義ToggleView選擇繼承View
同樣的自定義控件需要復(fù)寫三個構(gòu)造方法
//在布局中使用該控件的時候,而且有額外的style屬性的時候調(diào)用該構(gòu)造方法, public ToggleView(Context context, AttributeSet attrs, int defStyle); //在布局中使用該控件的時候調(diào)用該構(gòu)造方法 public ToggleView(Context context, AttributeSet attrs) //在Java代碼中直接new該控件的時候,調(diào)用該構(gòu)造方法 public ToggleView(Context context)
因為是自定義的控件,所以屬性還是自己定義的比較好用一些。我們這里定義三個屬性
1、背景圖片
2、滑塊的圖片
3、布局中默認的開關(guān)的狀態(tài)
所以就需要用到了自定義屬性
在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表示屬性類型
這里定義了三個屬性名和屬性類型。
屬性和自定義控件的三個構(gòu)造方法已經(jīng)完成,就我們就可以在布局文件中添加自定義的控件了
<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中的每個條目都導(dǎo)入進來了,就可以直接使用我在attrs.xml里面的屬性了
可以直接使用自定義的屬性之后,問題應(yīng)該聚焦到怎么在Java代碼中獲取到我自定義的屬性的值呢?
根據(jù)命名空間和自定義屬性的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);
看到?jīng)]?該方法用到了attr參數(shù),所以獲取自定義屬性值的操作應(yīng)該在兩個參數(shù)的那里面執(zhí)行。
整體的自定義控件的類見代碼:
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;
/**
* 自定義開關(guān)控件
* @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;
// 構(gòu)造方法,在xml文件布局的時候,指定了style的時候調(diào)用
public ToggleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// 構(gòu)造方法,在xml文件中布局的時候,沒有指定style的時候調(diào)用
public ToggleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
//在Java代碼中 獲取到xml中自定義屬性對應(yīng)的值
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);
// 設(shè)置自定義開關(guān)的圖片
setToggleSwitchBackground(toggle_switchbackground);
setToggleSlidingBackground(toggle_slidingbackground);
setToggleState(toggle_state);
}
// 構(gòu)造方法 在代碼中new的時候調(diào)用
public ToggleView(Context context) {
this(context, null);
}
/**
* 給滑動的控件設(shè)置背景圖片
*
* @param toggle_slidingbackground 圖片ID
*/
private void setToggleSlidingBackground(int toggle_slidingbackground) {
sliding_background = BitmapFactory.decodeResource(getResources(),toggle_slidingbackground);
}
/**
* 給背景的控件,設(shè)置背景圖片
*
* @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) {
//測量控件的大小,設(shè)置控件的大小為背景圖片的大小
setMeasuredDimension(switch_background.getWidth(),switch_background.getHeight());
}
@Override
protected void onDraw(Canvas canvas) {
//開始畫自定義控件,使用canvas對象先把背景圖片畫上來
canvas.drawBitmap(switch_background, 0, 0, null);
if (isSliding) {
//如果是滑動狀態(tài)
//控件距離左邊的相對距離為:(控件每時每刻的距離自己左上方的焦點的x軸距離)-(控件本身一半的x軸寬度)
int left = downX - sliding_background.getWidth() / 2;
//控件最大的滑動距離(距離左邊最大的距離)就是:(背景圖片的寬度)-(滑塊圖片的寬度)
int rightAlign = switch_background.getWidth()- sliding_background.getWidth();
//如果距離左邊的距離小于0,,就不讓他繼續(xù)往左邊動了
if (left < 0) {
left = 0;
} else if (left > rightAlign) {
//如果距離左邊的距離》應(yīng)該距離左邊的最大距離,也不讓他往右邊移動了
left = rightAlign;
}
//控制好屬性之后就可以時時刻刻的跟著畫了
canvas.drawBitmap(sliding_background, left, 0, null);
} else {
//如果不滑動,則根據(jù)控件的屬性中開關(guān)的狀態(tài),來畫滑塊的位置
if (toggle_state) {
//如果開關(guān)狀態(tài)為真,滑塊移動到最右邊
int left = switch_background.getWidth() - sliding_background.getWidth();
canvas.drawBitmap(sliding_background, left, 0, null);
} else {
//如果開關(guān)狀態(tài)為假,滑塊移動到最左邊
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:
//當點擊結(jié)束的時候?qū)⑹欠窕瑒佑洖榧?,獲取到移動的x軸的坐標
downX = (int) event.getX();
isSliding = false;
//獲取到背景圖片中間的那個值
int center = switch_background.getWidth() / 2;
boolean state = downX > center;
//如果先后的狀態(tài)不相同,則將新的狀態(tài)賦給成員變量,然后調(diào)用監(jiān)聽的方法
if (toggle_state != state) {
toggle_state = state;
if (null != mToggleStateChangeListener) {
mToggleStateChangeListener
.onToggleState(toggle_state);
}
}
break;
}
//調(diào)用一次onDraw()方法
invalidate();
return true;
}
//給自定義開關(guān)控件設(shè)置監(jiān)聽的方法
public void setOnToggleStateLinstener(OnToggleStateChangeListener listen){
mToggleStateChangeListener = listen;
}
public void setToggleState(boolean b) {
toggle_state = b;
}
//監(jiān)聽回調(diào)接口,方法由實現(xiàn)接口的類實現(xiàn)
public interface OnToggleStateChangeListener {
public void onToggleState(boolean state);
}
}
到此,我們的自定義控件部分的邏輯就寫完了,借下來再MainActivity中調(diào)用一下
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);
}
});
}
//這里調(diào)用到的自己封裝的一個快速彈Toast的工具類
private void showToast(boolean state) {
ToastUtil.makeSuddenlyToast(getApplicationContext(), state?"開":"關(guān)");
}
}
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();
}
}
總結(jié)一下,其實本次自定義控件的步驟如下:
1、在values/attrs.xml自定義屬性和屬性值的數(shù)據(jù)類型
2、在Java代碼中定義自定義控件類,繼承View或者ViewGroup或者Android原生的控件,實現(xiàn)構(gòu)造方法,獲取到自定義屬性的值,并且編寫對應(yīng)的邏輯和點擊事件。
3、在布局文件中使用自定義控件和自定義屬性(注意命名空間)。
4、在MainActivity中調(diào)用
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
在Android系統(tǒng)源碼中預(yù)置APK的方法
今天小編就為大家分享一篇關(guān)于在Android系統(tǒng)源碼中預(yù)置APK的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12
Android EditText限制輸入字符的方法總結(jié)
這篇文章主要介紹了 Android EditText限制輸入字符的方法總結(jié)的相關(guān)資料,這里提供了五種方法來實現(xiàn)并進行比較,需要的朋友可以參考下2017-07-07
Android開發(fā)時盡管已root但是ddms還是沒有data路徑怎么辦
這篇文章主要介紹了Android開發(fā)時盡管已root但是ddms還是沒有data路徑怎么辦的相關(guān)資料,需要的朋友可以參考下2015-12-12
Android 如何實現(xiàn)彈窗順序&優(yōu)先級控制
這篇文章主要介紹了Android 如何實現(xiàn)彈窗順序&優(yōu)先級控制,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03

