編輯:關於Android編程
本文實例講述了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中調用
完整實例代碼點擊此處本站下載。
更多關於Android相關內容感興趣的讀者可查看本站專題:《Android控件用法總結》、《Android視圖View技巧總結》、《Android操作SQLite數據庫技巧總結》、《Android操作json格式數據技巧總結》、《Android數據庫操作技巧總結》、《Android文件操作技巧匯總》、《Android編程開發之SD卡操作方法匯總》、《Android開發入門與進階教程》及《Android資源操作技巧匯總》
希望本文所述對大家Android程序設計有所幫助。
1.首先在設備上調整輸入法 Android鍵盤(AOSP),在輸入語言裡勾選要選擇的語言,比如選“英語(美國)”和“西班牙文”兩種: 3.查看Set
上一篇文章中我們講解了android app實現長連接的幾種方式,各自的優缺點以及具體的實現,一般而言使用第三方的推送服務已經可以滿足了基本的業務需求,當然了若是對技術有
在我們開發APP時不僅要在代碼實現上,做到對App的優化,而在我們的界面布局也有許多要優化的地方,如果布局寫的很low的話,系統加載布局的速度會十分的慢,使得用戶的體驗非
一、概述今天給大家帶來SurfaceView的一個實戰案例,話說自定義View也是各種寫,一直沒有寫過SurfaceView,這個玩意是什麼東西?什麼時候用比較好呢?可以