你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> Android自定義控件——仿ios開關按鈕

Android自定義控件——仿ios開關按鈕

編輯:IOS開發綜合

 

大凡在公司做客戶端產品開發的都會發現,android和ios的差異化,ios得益於“老喬”的精心設計,界面用戶體驗做到了極致,而android秉承開源思想,界面用戶體驗百家各有其長,相互不得統一。不說廢話,先上圖,看看ios的“開關按鈕”:

height=320height=320height=320

 

往往在公司,產品設計原型優先參考了ios的設計,這下可苦了android開發者,android開發者被迫要寫和ios幾乎同樣的效果,這種情況應該很常見吧!例如,下面我提到的“開關按鈕”,在ios中是集成好的,拿來就用,但在android裡面確實在android 4.0以後才出現ToggleButton的組件,拋開界面不談(實際不是很美觀),那麼看兼容性吧,ToggleButton是從4.0以後才開始有的東西,那麼2.3的系統我們不能就此忽略了吧,2.3在市場占有上還是有很大比重的。因此,能做到和ios同樣效果的“開關按鈕”,我們只能“苦逼的”用java代碼去繪制出這樣一個組件了。

簡單講講自繪組件的方法,Android下自繪組件大致分為繼承View和繼承ViewGroup兩種。

實現步驟和對應的方法:
測量寬高 --> 排版(布置控件的位置) --> 繪制到屏幕
| | |
onMeasure onLayout onDraw
值得注意的是:自定義組件若是繼承ViewGroup來實現,就必須實現上述3個步驟,尤其是排版,這裡需要制定組件的顯示位置。自定義組件是繼承View來實現的話,排版這步不需要了,只完成測量和繪制即可。

以下我的項目Demo結構圖:

height=300

自定義“開關按鈕”主要實現代碼:

 

package com.example.slidebutton.view;

import com.example.slidebutton.R;
import com.example.slidebutton.view.interf.OnToggleStateChangeListener;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class SlideButton extends View {

	/** 滑動開關的背景 */
	private Bitmap slideButtonBG;
	/** 滑動塊的背景 */
	private Bitmap switchBG;
	/** 設置開關的狀態,打開/關閉。 默認:關閉 */
	private boolean currentState = false;
	/** 當前滑動塊的移動距離 */
	private int currentX;
	/** 記錄當前滑動塊滑動的狀態。默認,false */
	private boolean isSliding = false;
	/** 開關狀態改變監聽 */
	private OnToggleStateChangeListener mListener;

	public SlideButton(Context context, AttributeSet attrs) {
		super(context, attrs);

		initBitmap();
	}

	private void initBitmap() {
		slideButtonBG = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button_background);
		switchBG = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
	}

	/**
	 * 移動效果的處理
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN: // 手指按下
			currentX = (int) event.getX();
			isSliding = true;
			break;
		case MotionEvent.ACTION_MOVE: // 手指移動
			currentX = (int) event.getX();
			break;
		case MotionEvent.ACTION_UP: // 手指抬起
			isSliding = false;
			// 判斷當前滑動塊,偏向於哪一邊,如果滑動塊的中心點<背景的中心點,設置為關閉狀態
			int bgCenter = switchBG.getWidth() / 2;
			boolean state = currentX >= bgCenter; // 改變後的狀態
			// 手指抬起時,回調監聽,返回當前的開關狀態
			if (state != currentState && mListener != null) {
				mListener.onToggleStateChange(state);
			}
			currentState = state;
			break;
		default:
			break;
		}
		invalidate(); // 刷新控件,該方法會調用onDraw(Canvas canvas)方法
		return true; // 自己處理事件,不讓父類負責消耗事件
	}

	/**
	 * 測量當前控件寬高時回調
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		// 設置開關的寬和高
		setMeasuredDimension(switchBG.getWidth(), switchBG.getHeight());
	}

	/**
	 * 繪制控件
	 */
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		// 1,滑動開關背景繪制到控件
		canvas.drawBitmap(switchBG, 0, 0, null);
		// 2,繪制滑動塊顯示的位置,開啟或關閉
		if (isSliding) {
			int left = currentX - slideButtonBG.getWidth() / 2; // 處理手指觸點,將觸點從slidingButton的左邊移動到中間
			if (left < 0) {
				left = 0;
			} else if (left > switchBG.getWidth() - slideButtonBG.getWidth()) {
				left = switchBG.getWidth() - slideButtonBG.getWidth();
			}
			canvas.drawBitmap(slideButtonBG, left, 0, null);
		} else {
			if (currentState) {
				// 繪制打開狀態
				canvas.drawBitmap(slideButtonBG, switchBG.getWidth() - slideButtonBG.getWidth(), 0, null);
			} else {
				// 繪制關閉狀態
				canvas.drawBitmap(slideButtonBG, 0, 0, null);
			}
		}
	}

	public void setToggleState(boolean b) {
		currentState = b;
	}

	/**
	 * 對外設置監聽方法
	 * 
	 * @param listener
	 */
	public void setOnToggleStateChangeListener(OnToggleStateChangeListener listener) {
		this.mListener = listener;
	}

}
“開關按鈕”的是繼承自View來實現的,關於View的構造函數:

 

 

public SlideButton(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}
該構造用於該組件僅僅在代碼中實例化,不能在布局文件中聲明

 

 

public SlideButton(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}
聲明該構造後,可以在布局文件中聲明出這個控件,一般自定義組件都復寫這個構造方法。

 

 

public SlideButton(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
	}
這是View的第三個構造,可以在布局文件中聲明之外,還需要制定組件的顯示樣式,用的較少。

 

復寫父類View的構造器之後,就需要復寫View的另兩個重要的方法了,onMeasure和onDraw。onMeasure是用於測量組件的大小寬高的方法,在這裡,只處理“開關按鈕”的背景圖的寬高,使用setMeasuredDimension方法,指定測量後的值。然後在onDraw方法中,在畫布Canvas上畫出這個自定義組件,裡面會包含一系列的邏輯判斷,請大家仔細閱讀源碼,這裡不介紹了。

為了增強“開關按鈕”的用戶體驗,需要判斷按鈕的滑動方向來確定按鈕的狀態,所以需要復寫onTouchEvent方法,在這個方法裡處理手指觸發的各種狀態。值得一提的是,在處理完手指移動的觸發後,接下來就是重繪組件,達到動態的效果,這時必須在每次觸發完成時,調用一下View的invalidate()方法,該方法的作用是:刷新控件,該方法會調用onDraw(Canvas canvas)方法。

最後,“開發按鈕”要與使用該控件的界面或者用戶實現交互或狀態傳遞,這裡需要寫一個Java回調函數,用於在主界面上回調監聽“開關按鈕”的狀態,代碼如下:

 

package com.example.slidebutton.view.interf;

/**
 * 開關狀態改變監聽事件
 * 
 * @author Administrator
 * 
 */
public interface OnToggleStateChangeListener {
	/**
	 * 當開關狀態改變回調此方法
	 * 
	 * @param b
	 *            當前開關的最新狀態
	 */
	void onToggleStateChange(boolean b);
}

關於監聽的內部實現,請參考SlidingButton自定義組件代碼。這裡主要為外部引用該控件的界面提供了一個設置監聽的方法setOnToggleStateChangeListener(OnToggleStateChangeListener listener),在判斷每次手指抬起的時候,說明開關狀態改變的過程結束,需要在那裡為監聽器設置回調參數,即當前的開關的狀態boolean變量。這樣,外部引用該控件的界面就可以拿到這個boolean值,做出相應的處理。那麼,以下就是外部引用該自定義組件的主要代碼:

 

布局文件,activity_main.xml:

 



    

主界面的代碼MainActivity.java:

 

 

package com.example.slidebutton;

import com.example.slidebutton.view.SlideButton;
import com.example.slidebutton.view.interf.OnToggleStateChangeListener;

import android.os.Bundle;
import android.widget.Toast;
import android.app.Activity;

public class MainActivity extends Activity implements OnToggleStateChangeListener {

	public SlideButton slideButton;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		slideButton = (SlideButton) findViewById(R.id.slidebutton);
		slideButton.setToggleState(true);
		slideButton.setOnToggleStateChangeListener(this);
	}

	@Override
	public void onToggleStateChange(boolean b) {
		// TODO Auto-generated method stub
		if (b) {
			Toast.makeText(MainActivity.this, 開關打開, Toast.LENGTH_SHORT).show();
		} else {
			Toast.makeText(MainActivity.this, 開關關閉, Toast.LENGTH_SHORT).show();
		}
	}

}

以下是運行效果圖:

 

height=320

以上內容,有諸多不專業不正確或者疏忽的地方,歡迎大家批評指正,共同學習~

 

 

 

 

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved