SwitchView(滑动按钮)

最近写了个滑动按钮,分享给大家,写的不太好,多多包涵。有问题联系我 QQ157688302


最底下提供下载


废话不多说,先上效果图:

             


先介绍几个特性:

1. 显示的文字可以自己配置

2. 点击控件会自动更改状态(可以通过closeclickevent 函数关闭此功能)。

3. 拖动开关可以更改状态

4. 可通过OnSwitchListener获取事件更改.

5. 拖动开关至边界处,会产生抖动效果.


缺点:

1. 开关的背景图可以自己更换,本人美工不好,图片不是很好看.

2. 代码编写并不是很规范,不好之处还请多多体谅.

3. 没有禁止选择的函数(以后考虑添加)


使用方法:

1. 可以在布局中使用,如果在布局中使用,请务必不要写成wrap_content. 可以自己指定大小或者fill_parent( or match).

xml 实例:

 <com.xxx.SwitchView
        android:id="@+id/switch_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentBottom="true"
        android:layout_margin="10dp"
        />



好了,其他废话不多说,上代码:

package com.clw.signalenhancement;

import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

/**
 * 滑动按钮
 * 
 * @author Chaos(Thanks chenwen3k to help solve a serious problem)
 * @date 2013-05-25
 */
public class SwitchView extends FrameLayout {
	/**
	 * LeftSwitch
	 */
	private static final String TEXT_LEFT_SWITCH = "开";
	/**
	 * RightSwitch
	 */
	private static final String TEXT_RIGHT_SWITCH = "关";
	/**
	 * TYPE - LEFT
	 */
	public static final int TYPE_LEFT = 0;
	/**
	 * TYPE - Right
	 */
	public static final int TYPE_RIGHT = 1;
	/**
	 * The best width
	 */
	private static final int MAX_WIDTH = 300;
	/**
	 * The best height
	 */
	private static final int MAX_HEIGHT = 80;
	/**
	 * In order to trick your eyes
	 */
	private static final int FRAMES = 15;
	/**
	 * Used to describe the degree of shaking
	 */
	private static final int ARGUMENT_SHAKE = 2;
	/**
	 * The offset for the upper view
	 */
	private int mOffsetX = 0;
	/**
	 * Total width of the floating layer (but I think that may not be required)
	 */
	private int mFloatingLayerWidth;
	/**
	 * The total width of the view (but I think that may not be required)
	 */
	private int mTotalWidth;
	/**
	 * Description of the currently checked type
	 */
	private int mNowType = TYPE_LEFT;
	/**
	 * In order to listen for checked events
	 */
	private OnSwitchListener mSwitchListener;
	/**
	 * Necessary object
	 */
	private TextView mLeftSwitch;
	private TextView mRightSwitch;
	private ImageView mFloatingLayer;
	private LinearLayout mSwitchParent;
	/**
	 * Touchevent interceptor
	 */
	private GestureDetector mDetector = null;
	/**
	 * The click switch event flag
	 */
	private boolean isOpenClick = true;

	public SwitchView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public SwitchView(Context context) {
		super(context);
		init();
	}

	private void init() {
		// build parent
		mSwitchParent = new LinearLayout(getContext());
		// Ready to work
		mSwitchParent.setOrientation(LinearLayout.HORIZONTAL);
		setBackgroundResource(R.drawable.usage_list_dark);
		// step.1: add floating layer in bottom(maybe called 'sediment layer')
		mFloatingLayer = new ImageView(getContext());
		mFloatingLayer.setBackgroundResource(R.drawable.usage_list_green);
		LayoutParams flowLayoutParams = new LayoutParams(
				LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
		addView(mFloatingLayer, flowLayoutParams);
		// step.2: init children
		mLeftSwitch = new TextView(getContext());
		mRightSwitch = new TextView(getContext());
		// step.3: load res for the textView
		mLeftSwitch.setText(TEXT_LEFT_SWITCH);
		mRightSwitch.setText(TEXT_RIGHT_SWITCH);
		// step.4: whitewash and relocation the text
		mLeftSwitch.setBackgroundColor(Color.TRANSPARENT);
		mRightSwitch.setBackgroundColor(Color.TRANSPARENT);
		mLeftSwitch.setTextColor(Color.BLACK);
		mRightSwitch.setTextColor(Color.BLACK);
		mLeftSwitch.setGravity(Gravity.CENTER);
		mRightSwitch.setGravity(Gravity.CENTER);
		// step.5: build children LayoutParams
		LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
				LinearLayout.LayoutParams.MATCH_PARENT,
				LinearLayout.LayoutParams.MATCH_PARENT);
		lp.weight = 1;
		mLeftSwitch.setLayoutParams(lp);
		mRightSwitch.setLayoutParams(lp);
		// step.6: add children
		mSwitchParent.addView(mLeftSwitch);
		mSwitchParent.addView(mRightSwitch);
		// step.7 : setup parent
		addView(mSwitchParent, LayoutParams.MATCH_PARENT,
				LayoutParams.MATCH_PARENT);
		// step.8 : handle flow
		mDetector = new GestureDetector(getContext(), new FlowGesture());

	}

	/**
	 * Register a callback to be invoked when this view is checked.
	 * 
	 * @param sl
	 *            The callback that will run
	 */
	public void setOnSwitchListener(OnSwitchListener sl) {
		this.mSwitchListener = sl;
	}

	/**
	 * Change the status of the current(will Slide)
	 */
	public void switching() {
		int type = getType();
		if (type == TYPE_LEFT) {
			sliding(TYPE_RIGHT);
		} else {
			sliding(TYPE_LEFT);
		}
	}

	/**
	 * Change the status of the current(maybe Slide)
	 * 
	 * @param type
	 * @see #TYPE_LEFT
	 * @see #TYPE_RIGHT
	 */
	public void switching(int type) {
		sliding(type);
	}

	/**
	 * Change the status of the current(maybe Slide)
	 * 
	 * @param isCheckedLeft
	 */
	public void switching(boolean isCheckedLeft) {
		System.out.println(isCheckedLeft);
		sliding(isCheckedLeft ? TYPE_LEFT : TYPE_RIGHT);
	}

	private int getType() {
		if (mOffsetX == 0) {
			return TYPE_LEFT;
		} else {
			return TYPE_RIGHT;
		}
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int width = MeasureSpec.getSize(widthMeasureSpec);
		int wmode = MeasureSpec.getMode(widthMeasureSpec);
		int height = MeasureSpec.getSize(heightMeasureSpec);
		int hmode = MeasureSpec.getMode(heightMeasureSpec);

		width = getBestWidth(width);
		height = getBestHeight(height);

		mTotalWidth = width;

		int wSpec = MeasureSpec.makeMeasureSpec(width, wmode);
		int hSpec = MeasureSpec.makeMeasureSpec(height, hmode);

		mFloatingLayerWidth = getFlowWidth(width);

		mFloatingLayer.measure(
				MeasureSpec.makeMeasureSpec(mFloatingLayerWidth, wmode), hSpec);
		mSwitchParent.measure(wSpec, hSpec);
		setMeasuredDimension(wSpec, hSpec);
	}

	private int getBestWidth(int width) {
		return width > MAX_WIDTH ? MAX_WIDTH : width;
	}

	private int getBestHeight(int height) {
		return height > MAX_HEIGHT ? MAX_HEIGHT : height;
	}

	private int getFlowWidth(int total) {
		// expression (can make all the changes)
		return total / 2 + total / 15;
	}

	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		setup(mFloatingLayer, mOffsetX);
	}

	private void setup(View child, int offset) {
		child.layout(offset, 0, offset + child.getMeasuredWidth(),
				child.getMeasuredHeight());
	}

	public void closeClickEvent() {
		isOpenClick = false;
	}

	public void openClickEvent() {
		isOpenClick = true;
	}

	private void sliding(int type) {
		final int nowPosX = mOffsetX;
		final int targetX = getX(type);

		if (nowPosX == targetX) {
			// no meaning
			return;
		}

		final int distance = targetX - nowPosX;

		final FakeAnimation fake = new FakeAnimation(distance, 150);
		new Handler().post(new Runnable() {
			@Override
			public void run() {
				if (fake.isEnd()) {
					mOffsetX = targetX;
					requestLayout();
				} else {
					mOffsetX = fake.getValue() + mOffsetX;
					requestLayout();
					postDelayed(this, FRAMES);
				}
			}
		});

		// set current type
		mNowType = type;
		performOnCheck(mNowType);
	}

	private int getX(int type) {
		if (type == TYPE_LEFT) {
			return 0;
		} else if (type == TYPE_RIGHT) {
			return mTotalWidth - mFloatingLayerWidth;
		} else {
			return 0;
		}
	}

	/**
	 * Call this view's OnSwitchListener
	 */
	private void performOnCheck(int type) {
		if (mSwitchListener == null) {
			return;
		}
		if (type == TYPE_LEFT) {
			mSwitchListener.onCheck(this, true, false);
		} else if (type == TYPE_RIGHT) {
			mSwitchListener.onCheck(this, false, true);
		} else {
			// i don't know really will run here?
			mSwitchListener.onCheck(this, false, false);
		}
	}

	public TextView getLeftView() {
		return mLeftSwitch;
	}

	public TextView getRightView() {
		return mRightSwitch;
	}

	private int getFlowXWithLimit(double nowX, double flowX) {
		if (nowX < 0) {
			shake(TYPE_LEFT);
			return 0;
		} else if (nowX > (mTotalWidth - mFloatingLayerWidth)) {
			shake(TYPE_RIGHT);
			return (mTotalWidth - mFloatingLayerWidth);
		}
		return (int) flowX;
	}

	private void shake(int type) {
		int amplitude;
		if (type == TYPE_LEFT) {
			amplitude = ARGUMENT_SHAKE;
		} else if (type == TYPE_RIGHT) {
			amplitude = -ARGUMENT_SHAKE;
		} else {
			amplitude = 0;
		}
		mOffsetX = mOffsetX + amplitude;
		requestLayout();
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		super.onTouchEvent(event);
		final int x = (int) event.getX();

		if (event.getAction() == MotionEvent.ACTION_UP) {
			// if the action equal UP, must reset the flow view;
			int type = x > mTotalWidth / 2 ? TYPE_RIGHT : TYPE_LEFT;
			sliding(type);
		}
		return mDetector.onTouchEvent(event);
	}

	class FlowGesture extends SimpleOnGestureListener {

		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2,
				float distanceX, float distanceY) {
			// common scroll
			mOffsetX = getFlowXWithLimit(mOffsetX, mOffsetX - distanceX);
			requestLayout();
			return true;
		}

		@Override
		public boolean onDown(MotionEvent e) {
			final int x = (int) e.getX();
			final int y = (int) e.getY();

			Rect hitRect = new Rect();
			mFloatingLayer.getHitRect(hitRect);
			if (!hitRect.contains(x, y)) {
				if (isOpenClick) {
					switching();
				}
				return false;
			}
			return true;
		}

		@Override
		public boolean onSingleTapUp(MotionEvent e) {
			if (isOpenClick) {
				switching();
			}
			return super.onSingleTapUp(e);
		}

	}

	/**
	 * ha, cheat you!
	 * 
	 * @author Chaos
	 */
	private static class FakeAnimation {

		private int times;
		private int nowTimes;
		private int ceil;

		FakeAnimation(int distance, long time) {
			times = (int) (time / FRAMES);
			ceil = distance / times;
			nowTimes = 0;
		}

		public boolean isEnd() {
			return nowTimes == times;
		}

		public int getValue() {
			nowTimes++;
			return ceil;
		}
	}

	/**
	 * Interface definition for a callback to be invoked when a view is checked.
	 */
	public static interface OnSwitchListener {
		/**
		 * Called when a view has been checked.
		 * 
		 * @param sv
		 *            The view that was checked.
		 * @param checkLeft
		 *            will be true if the leftSwitch was checked , otherwise
		 *            return false.
		 * @param checkRight
		 *            will be true if the rightSwitch was checked , otherwise
		 *            return false.
		 */
		public void onCheck(SwitchView sv, boolean checkLeft, boolean checkRight);
	}
}




点我下载






评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值