zoukankan      html  css  js  c++  java
  • 自己定义滑动开关控件的实现与使用

    在IPhone中,滑动开关控件很常见,并且效果也很好,可是在Android平台下,却没有自带的这样的控件,仅仅有功能类似的ToggleButton控件。本篇文章主要介绍自己定义的滑动开关控件的实现与使用。在实现的过程中,也參考了其它类似自己定义控件的实现,同一时候对代码进行了优化。

    首先看实现的效果图


    以下解说这个自己定义控件怎样实现

    /**
     * 滑动控件
     * 
     * @Time 2014-6-17 下午2:35:17
     */
    public class SlipSwitch extends View implements OnTouchListener {
    
    	// 开关开启时的背景,关闭时的背景,滑动button
    	private Bitmap switch_on_bg, switch_off_bg, slip_Btn;
    	// 是否正在滑动
    	private boolean isSlipping = false;
    	// 当前开关状态,true为开启,false为关闭
    	private boolean isSwitchOn = false;
    	// 手指按下时的水平坐标X,当前的水平坐标X
    	private float previousX, currentX;
    	// 开关监听器
    	private OnSwitchListener onSwitchListener;
    	// 是否设置了开关监听器
    	private boolean isSwitchListenerOn = false;
    	// 矩阵
    	private Matrix matrix = new Matrix();
    	// 画笔
    	private Paint paint = new Paint();
    	// 滑动button的左边坐标
    	private float left_SlipBtn;
    	// 松开前开关的状态
    	private boolean previousSwitchState;
    
    	public SlipSwitch(Context context) {
    		super(context);
    		init();
    	}
    
    	public SlipSwitch(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		init();
    	}
    	//初始化
    	protected void init() {
    		setOnTouchListener(this);
    		setSwitchState(true);
    	}
    
    	@Override
    	protected void onDraw(Canvas canvas) {
    		super.onDraw(canvas);
    		// 手指滑动到左半边的时候表示开关为关闭状态,滑动到右半边的时候表示开关为开启状态
    		if (currentX < (switch_on_bg.getWidth() / 2)) {
    			canvas.drawBitmap(switch_off_bg, matrix, paint);
    		} else {
    			canvas.drawBitmap(switch_on_bg, matrix, paint);
    		}
    
    		// 推断当前是否正在滑动
    		if (isSlipping) {
    			if (currentX > switch_on_bg.getWidth()) {
    				left_SlipBtn = switch_on_bg.getWidth() - slip_Btn.getWidth();
    			} else {
    				left_SlipBtn = currentX - slip_Btn.getWidth() / 2;
    			}
    		} else {
    			// 依据当前的开关状态设置滑动button的位置
    			if (isSwitchOn) {
    				left_SlipBtn = switch_off_bg.getWidth();
    			} else {
    				left_SlipBtn = 0;
    			}
    		}
    
    		// 对滑动button的位置进行异常推断
    		if (left_SlipBtn < 0) {
    			left_SlipBtn = 0;
    		} else if (left_SlipBtn > switch_on_bg.getWidth() - slip_Btn.getWidth()) {
    			left_SlipBtn = switch_on_bg.getWidth() - slip_Btn.getWidth();
    		}
    
    		canvas.drawBitmap(slip_Btn, left_SlipBtn, 0, paint);
    	}
    
    	// 告诉父控件,要占多大的空间
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		setMeasuredDimension(switch_on_bg.getWidth(), switch_on_bg.getHeight());
    	}
    
    	@Override
    	public boolean onTouch(View v, MotionEvent event) {
    		switch (event.getAction()) {
    		// 滑动
    		case MotionEvent.ACTION_MOVE:
    			currentX = event.getX();
    			break;
    		// 按下
    		case MotionEvent.ACTION_DOWN:
    			isSlipping = true;
    			previousX = event.getX();
    			currentX = previousX;
    			break;
    		// 松开
    		case MotionEvent.ACTION_UP:
    			isSlipping = false;
    			previousSwitchState = isSwitchOn;
    			if (event.getX() >= (switch_on_bg.getWidth() / 2)) {
    				isSwitchOn = true;
    			} else {
    				isSwitchOn = false;
    			}
    			// 假设设置了监听器,则调用此方法
    			if (isSwitchListenerOn && (previousSwitchState != isSwitchOn)) {
    				onSwitchListener.onSwitched(isSwitchOn);
    			}
    			break;
    		}
    		// 又一次绘制控件
    		invalidate();
    		return true;
    	}
    
    	protected void setImageResource(int switchOnBkg, int switchOffBkg,
    			int slipBtn) {
    		switch_on_bg = BitmapFactory
    				.decodeResource(getResources(), switchOnBkg);
    		switch_off_bg = BitmapFactory.decodeResource(getResources(),
    				switchOffBkg);
    		slip_Btn = BitmapFactory.decodeResource(getResources(), slipBtn);
    
    	}
    
    	protected void setSwitchState(boolean switchState) {
    		isSwitchOn = switchState;
    	}
    
    	protected boolean getSwitchState() {
    		return isSwitchOn;
    	}
    
    	protected void updateSwitchState(boolean switchState) {
    		isSwitchOn = switchState;
    		invalidate();
    	}
    
    	public void setOnSwitchListener(OnSwitchListener listener) {
    		onSwitchListener = listener;
    		isSwitchListenerOn = true;
    	}
    
    	// 监听器接口
    	public interface OnSwitchListener {
    		abstract void onSwitched(boolean isSwitchOn);
    	}
    }

    在这个实现过程中,有几个方法至关重要。

    一个是onMeasure(),这种方法主要是告诉父控件,自己定义控件要占多大的控件,我们把背景图片的宽高设置就可以。

    另外一个onDraw(),这种方法负责自己定义界面的绘制,当手指按下、滑动、松开的时候,这种方法都须要对更改后的界面进行又一次的绘制。

    最后一个方法便是onTouch(),由于自己定义控件实现了OnTouchListener接口,所以要重写这种方法。当手指在屏幕点击和滑动的时候,就会出发这个事件,我们须要依据用户操作的不同,对按下、放开、滑动等事件,进行不一样的处理。可是不管怎样处理,在方法的最后,我们都要调用invalidate();方法,对界面进行刷新,我们能够看到这种方法的介绍

    Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. This must be called from a UI thread. To call from a non-UI thread, call postInvalidate().

    意思就是说,假设控件可见,我们在调用这种方法之后,系统会调用onDraw方法进行界面的刷新,并且这种方法必须在主线程调用,假设在非主线程想完毕界面刷新的功能,我们能够调用postInvalidate()这种方法实现。

    并且onTouch()的返回值为true,我们能够看一下这种方法的介绍

    True if the listener has consumed the event, false otherwise. 就是说,假设返回的是true,那么这个触摸事件就不会继续往下传递,这个事件就被当前的监听器消耗了,也就是吃掉了。

    好了,通过这几个方法,我们就实现了一个简单的自己定义的滑动开关控件,以下我们看一下怎样使用这个自己定义的控件。

    布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@android:color/white"
        android:gravity="center"
        android:orientation="vertical" >
    
        <edu.qust.SlipSwitch
            android:id="@+id/main_myslipswitch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
    
    </LinearLayout>

    Activity中的代码

    public class MainActivity extends Activity {
    
    	private SlipSwitch slipswitch;
    
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
    
    		slipswitch = (SlipSwitch) findViewById(R.id.main_myslipswitch);
    		slipswitch.setImageResource(R.drawable.bkg_switch,
    				R.drawable.bkg_switch, R.drawable.btn_slip);
    		slipswitch.setOnSwitchListener(new OnSwitchListener() {
    
    			@Override
    			public void onSwitched(boolean isSwitchOn) {
    				if (isSwitchOn) {
    					Toast.makeText(MainActivity.this, "开关已经开启",
    							Toast.LENGTH_SHORT).show();
    				} else {
    					Toast.makeText(MainActivity.this, "开关已经关闭",
    							Toast.LENGTH_SHORT).show();
    				}
    			}
    		});
    
    	}
    }

    使用很easy,只是多介绍了

    使用到的素材文件


  • 相关阅读:
    计算机科学导论第二周学习总结
    计算机科学概论2,3章小结
    2019—2020第一学期20202430王凯欣《网空概论》第四第五章学习小结
    2020—2021年第一学期20202430王凯欣《计算机科学概论》第一次笔记
    2020-2021第一学期20202410《计算机科学概论》第二次学习总结
    2020-2021第一学期20202410《计算机科学概论》第一次学习总结
    网页登录上网账号
    python从socket做个websocket的聊天室server
    windows下python 编码问题
    由css属性:vertial-align想到的。。
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/3799789.html
Copyright © 2011-2022 走看看