1. 程式人生 > >android 自定義開關(SwitchButton)

android 自定義開關(SwitchButton)

最近心血來潮,寫了一個自定義仿iPhone的開關。有需要的同學可以來下載啦。支援點選自動滾動,速率可以自己根據需要修改。觸控滾動,大小自定義,支援修改樣式。就不錄製動畫,就上傳了兩張圖給大家看看。

      

主要程式碼:

package com.example.switchbutton;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

public class SwitchButton extends View {
	
	public static final int DEFAULT_WIDTH = 100;
	public static final int DEFAULT_HEIGTH = 45;
	public static final int PER_POST_TIME = 20;
	public static final int CLICK = 0;
	public static final int LEFT  = 1;
	public static final int RIGHT  = 2;
	
	private int mSelectBg;
	private int mUnSelectBg;
	private int mBorderColor;
	private int mSelectCirlceColor;
	private int mUnSelectCircleColor;
	private boolean isChecked;
	private Paint mPaint;
	private int mWidth;
	private int mHeight;
	private int mClickTimeout;  
	private int mTouchSlop; 
	private float mAnimMove;
	private final float VELOCITY = 350;
	private float firstDownX;
	private float firstDownY;
	private float lastDownX;
	private int mCriclePostion;
	private int mStartCriclePos;
	private int mEndCirclePos;
	private boolean isScroll;
	private int status;
	private Handler mHander;
	private AnimRunnable mAnim;

	private OnCheckChangeListener mOnCheckChangeListener;
	
	
	public interface OnCheckChangeListener {
		
		void OnCheck(SwitchButton switchButton,boolean isChecked);
	}

	public SwitchButton(Context context, AttributeSet attrs) {
		super(context, attrs);
		Resources res = getResources();
		int defaultSelectBg = res.getColor(R.color.default_switch_button_select_bg);
		int defaultUnSelectBg = res.getColor(R.color.default_switch_button_unselect_bg);
		int defaultBorderColor = res.getColor(R.color.default_switch_button_border_color);
		int defaultSelectCircleColor = res.getColor(R.color.default_switch_button_select_color);
		int defaultUnSelectCircleColor = res.getColor(R.color.default_switch_button_unselect_color);
		TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.SwitchButton);
		mSelectBg = a.getColor(R.styleable.SwitchButton_select_bg, defaultSelectBg);
		mUnSelectBg = a.getColor(R.styleable.SwitchButton_unselect_bg, defaultUnSelectBg);
		mBorderColor = a.getColor(R.styleable.SwitchButton_select_border_color, defaultBorderColor);
		mSelectCirlceColor = a.getColor(R.styleable.SwitchButton_select_cricle_color, defaultSelectCircleColor);
		mUnSelectCircleColor = a.getColor(R.styleable.SwitchButton_unselect_cricle_color, defaultUnSelectCircleColor);
		isChecked = a.getBoolean(R.styleable.SwitchButton_isChecked,false);
		a.recycle();
		initView(context);
	}

	private void initView(Context context) {
		
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		final float density = getResources().getDisplayMetrics().density;  
		mAnimMove = (int) (VELOCITY * density + 0.5f) / 150; //自動滾動速度
        mClickTimeout = ViewConfiguration.getPressedStateDuration()  
                + ViewConfiguration.getTapTimeout();  //點選時間
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();//觸控滾動Slop
        mHander = new Handler();
        mAnim = new AnimRunnable();
	}
	
	
	@SuppressLint("DrawAllocation")
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		
		mPaint.setColor(mBorderColor);
		mPaint.setStyle(Paint.Style.STROKE);
		RectF rect = new RectF(0, 0, mWidth, mHeight);
		canvas.drawRoundRect(rect, mHeight/2, mHeight/2, mPaint);
		mPaint.setColor(isChecked ? mSelectBg :mUnSelectBg);
		RectF innerRect = new RectF(1, 1, mWidth -1, mHeight -1);
		canvas.drawRoundRect(innerRect, mHeight/2 -1, mHeight/2 -1, mPaint);
		mPaint.setColor(isChecked ? mSelectCirlceColor : mUnSelectCircleColor);
		mPaint.setStyle(Paint.Style.FILL);
		canvas.drawCircle(mCriclePostion, mHeight/2, mHeight/2 -1, mPaint);
	}
	
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		
		float x = event.getX();
		float y = event.getY();
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if(isScroll){
				return false;
			}
			firstDownX = x;
			firstDownY = y;
			lastDownX = x;
			mCriclePostion = isChecked ? mEndCirclePos : mStartCriclePos;
			break;
		case MotionEvent.ACTION_MOVE:
			float delaX = x - lastDownX;
			setCriclePositon(delaX);
			lastDownX  = x;
			invalidate();
			break;
		case MotionEvent.ACTION_UP:
			float totalX = x - firstDownX;
			float totalY = y - firstDownY;
			float time = event.getEventTime() - event.getDownTime();
			if(totalX < mTouchSlop && totalY <mTouchSlop && time < mClickTimeout){
				status = CLICK;
				startAutoScroll();
			}else{
				delaX = x - lastDownX;
				setCriclePositon(delaX);
				status = mCriclePostion < mWidth / 2 ? LEFT :RIGHT;
				startAutoScroll();
			}
			break;
		}
		return true;
	}
	
	//開始自動滾動
	private void startAutoScroll(){
		
		isScroll = true;
		mHander.postDelayed(mAnim, PER_POST_TIME);
		
	}
	
	
	class AnimRunnable implements Runnable{

		@Override
		public void run() {
			
			mHander.postDelayed(this, PER_POST_TIME);
			moveView();	
		}
		//移動
		private void moveView() {
			
			if((status == CLICK && isChecked) || status == LEFT){
				mCriclePostion -= mAnimMove;
				if(mCriclePostion < mHeight /2){
					mCriclePostion = mHeight/2;
					stopView(false);
				}
			}else if((status == CLICK && !isChecked) || status == RIGHT) {
				
				mCriclePostion += mAnimMove;
				if(mCriclePostion > mWidth -mHeight /2){
					mCriclePostion = mWidth -mHeight /2;
					stopView(true);
				}
			}
			invalidate();
			
		}
		//停止移動View
		private void stopView(boolean endChecked) {
			
			mHander.removeCallbacks(mAnim);
			isScroll = false;
			isChecked = endChecked;
			//回撥監聽事件
			if(mOnCheckChangeListener != null){
				mOnCheckChangeListener.OnCheck(SwitchButton.this, isChecked);
			}
		}
		
	}
	
	//設定圓心的位置
	private synchronized void setCriclePositon(float delaX){
		
		int pos = (int) (mCriclePostion + delaX);
		if(pos < mHeight / 2){
			mCriclePostion =  mHeight / 2;
		}else if(pos > mWidth - mHeight /2){
			mCriclePostion =  mWidth - mHeight /2;
		}else{
			mCriclePostion = pos;
		}
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		
		int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		if(widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST){
			mWidth = DEFAULT_WIDTH;
		}else{
			mWidth = MeasureSpec.getSize(widthMeasureSpec);
		}
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		if(heightMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.AT_MOST){
			mHeight = DEFAULT_HEIGTH;
		}else{
			mHeight = MeasureSpec.getSize(heightMeasureSpec);
		}
		mStartCriclePos = mHeight / 2;
		mEndCirclePos = mWidth - mHeight /2;
		mCriclePostion = isChecked ? mEndCirclePos :mStartCriclePos; 
		setMeasuredDimension(mWidth, mHeight);
	}

	public boolean isChecked() {
		return isChecked;
	}

	public void setChecked(boolean isChecked) {
		
		this.isChecked = isChecked;
		invalidate();
	}



	public void setOnCheckChangeListener(OnCheckChangeListener onCheckChangeListener) {
		
		this.mOnCheckChangeListener = onCheckChangeListener;
	}
	
	
}

attrs:

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <declare-styleable name="SwitchButton"> 
        <attr name="select_bg" format="color"/> 
        <attr name="unselect_bg" format="color"/> 
        <attr name="select_border_color" format="color"/>
         <attr name="select_cricle_color" format="color"/>
          <attr name="unselect_cricle_color" format="color"/>
          <attr name="isChecked" format="boolean"></attr>
    </declare-styleable> 
</resources>

下載地址:http://download.csdn.net/detail/huangyanbin123/8173521

相關推薦

android 定義開關SwitchButton

最近心血來潮,寫了一個自定義仿iPhone的開關。有需要的同學可以來下載啦。支援點選自動滾動,速率可以自己根據需要修改。觸控滾動,大小自定義,支援修改樣式。就不錄製動畫,就上傳了兩張圖給大家看看。        主要程式碼: package com.example.swit

Android定義View-Draw原理篇

Android自定義View通常需要經過measure、layout和draw過程。 如果你沒有了解過measure過程,可以先看看這篇文章。 如果你沒有了解過layout過程,可以先看看這篇文章。 一、draw的作用:繪製View檢視 二、draw過程:類似meas

Android 定義View

前言:可是有時候我們總感覺官方定義的一些基本元件不夠用,自定義元件就不可避免了。那麼如何才能做到像官方提供的那些元件一樣用xml來定義他的屬性呢? 先總結下自定義View的步驟: 1、自定義View的屬性; 2、在View的構造方法中獲得自定義的屬性。 一、在re

Android定義元件

1.陰影 陰影即可以為文字和圖形指定陰影,預設情況下,我們的文字和圖形都是繪製在主層(main layer)上,我們也可以將內容繪製到新的layer上,實際上陰影就是在main layer的下面添加了一個陰影層。 Paint類為此定義了一個名為setShadowLayer

android定義View、正弦波水波紋

文章目錄 1、正弦曲線知識 2、靜態正弦曲線繪製 3、動態正弦曲線繪製 4、完整原始碼 1、正弦曲線知識 對這個初中知識遺忘了的可以先看看正弦曲線百度百科詞條方便加深理解。

Android定義view,打造絢麗的驗證碼

前言:我相信信念的力量,信念可以支撐起一個人,一個名族,一個國家。正如“人沒有夢想和鹹魚有什麼區別”一樣,我有信念,有理想,所以我正在努力向著夢想前進~。 自定義view,如果是我,我首先要看到自定義view的效果圖,然後再想想怎麼實現這種效果或功能,所以先貼

android定義View2:實現百分比適應佈局

android介面適配難是歷史原因,我們只能想辦法解決。github上面已有一些佈局自適應的解決方案,今天我分享的是自定義控制元件:RelativieLayout自適應百分比寬高。直接上菜。 一,實現的效果圖 眼見為實,截圖所示,寬高都是50%,實現了自

android 定義View完美

原文連結:http://www.jianshu.com/p/d507e3514b65 一、自定義View,你真的掌握了嗎? 什麼?你說你掌握了自定義View?來來來,回答老衲如下問題: Google提出View這個概念的目的是什麼?View這個概念與Activtiy、F

Android定義相機 —— 錄影

前面我們已經大致完成了自定義相機的拍照功能,接下來,我們來實現一下錄影的功能。 1、錄影功能簡介 錄影功能的是相對比較簡單,因為步驟是很固定的,google給我們提供的api文件中說的也比較詳細,這裡我們主要用到 MediaRecorder 這個類。

Android定義錄影之錄影功能實現(附demo原始碼)

引言 最近在做一個專案,是有關用手機攝像頭做影象實時識別的。所以裡面需要自定義一個錄影功能。該demo實現了錄影和錄影後文件的儲存檢視,錄影會實時自動對焦(AutoFocus)。根據功能分兩篇講述。這第一篇講述錄影基本實現思路和需要注意的點。後面附有githu

Android 定義View基礎

一:引言 Android的開發中,在移動裝置上展示的所有內容,都是靠一個一個具體的檢視控制元件,按照一定的排列規則展示出來的,這些一個個控制元件,都是系統提供給我們的。但是我們看到,app商店上有些比較炫酷的頁面展示,我們會發現,系統根本沒有提供那些控制元件,

Android 定義View,點,線的繪製

public class PointLine extends View { Paint mLinePaint; Paint mPointPaint; float width; float height; float pointAddress

定義廣播BroadcastReceiver事件 --Android開發

ppc andro xtend 類型 class get receive cli time 本例演示自定義廣播事件。我們需要做的是,在主活動中寫發送廣播的代碼,然後在接收廣播的類中寫接收廣播的代碼。 1、主活動中點擊按鈕後發送廣播 MainActivity.java

android定義相機帶邊框和按鈕

前兩個月專案要求不能呼叫系統的相機,那就只能用自定義的了,查了一些資料,自己再研究了一下,自定義的相機還是有點複雜的,佈局和程式碼中都要用到一個重要的SurfaceView。 一、建立佈局,佈局的背景框可以讓美工給出,這裡姑且就是一個藍色的邊框,然後下面有三個按鈕,我里布局檔案activit

Android學習—簡單定義View

最近手上不忙所以回顧了一下自己今年來所接觸和學習的東西,突然覺得寫部落格真是一個很好的方式,希望自己 可以堅持下去。 自定義View的流程 建立自定義類繼承View,並重寫構造方法,構造方法總共有四種,我們暫時只需要繼承前兩種 public CircleVi

Android 定義Viewinflate()模式

1.建立LayoutInflater例項 有兩種方式 1. LayoutInflater layoutInflater= LayoutInflater.from(MainActivity.this); LayoutInflater layoutInfla

Android 手把手教您定義ViewGroup

最近由於工作的變動,導致的部落格的更新計劃有點被打亂,希望可以儘快脈動回來~今天給大家帶來一篇自定義ViewGroup的教程,說白了,就是教大家如何自定義ViewGroup,如果你對自定義ViewGroup還不是很瞭解,或者正想學習如何自定義,那麼你可以好好看看這篇部落格。1、概述在寫程式碼之前,我必須得

Android定義DataTimePicker日期選擇器

package com.wwj.datetimepicker; import java.text.SimpleDateFormat; import java.util.Calendar; import android.app.Activity; import android.app.AlertDialog

android studio 定義打包apk名稱

在你的build.gradle中的android{}下加入如下程式碼 //配置自定義打包名稱 applicationVariants.all { variant -> variant.outputs.all { def fileN

android定義ProgressBar仿淘寶的載入效果

三種方式實現自定義圓形頁面載入中效果的進度條 To get a ProgressBar in the default theme that is to be used on white/light back ground, use one of the inverse st