Blame view

src/com/ectrip/trips/view/CheckSwitchButton.java 10.6 KB
3c2353cd   杜方   1、畅游通核销app源码提交;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
  package com.ectrip.trips.view;
  
  import android.content.Context;
  import android.content.res.Resources;
  import android.graphics.Bitmap;
  import android.graphics.BitmapFactory;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.Paint;
  import android.graphics.PorterDuff;
  import android.graphics.PorterDuffXfermode;
  import android.graphics.RectF;
  import android.util.AttributeSet;
  import android.view.MotionEvent;
  import android.view.ViewConfiguration;
  import android.view.ViewParent;
  import android.widget.CheckBox;
  
  import com.ectrip.trips.check.R;
  /**
   * @describe 使用了第三方的 SwitchButton,添加了相关属性的注释,更容易看懂
   * @author https://github.com/Issacw0ng/SwitchButton
   *
   */
  public class CheckSwitchButton extends CheckBox {
  	/** 画笔 */
  	private Paint mPaint;
  	private ViewParent mParent;
  	private Bitmap mBottom;
  	private Bitmap mCurBtnPic;
  	private Bitmap mBtnPressed;
  	private Bitmap mBtnNormal;
  	/** 整体框架 */
  	private Bitmap mFrame;
  	/** 阴影层,不能点击的时候显示 */
  	private Bitmap mMask;
  	/** 保存布局的矩阵 */
  	private RectF mSaveLayerRectF;
  	/** 布局之间叠层,好比背景和背景上的图片效果 */
  	private PorterDuffXfermode mXfermode;
  	/** 首次按下的Y */
  	private float mFirstDownY;
  	/** 首次按下的X */
  	private float mFirstDownX;
  	/** 图片的绘制位置 */
  	private float mRealPos;
  	/** 按钮的位置 */
  	private float mBtnPos;
  	/** 开关打开的位置 */
  	private float mBtnOnPos;
  	/** 开关关闭的位置 */
  	private float mBtnOffPos;
  	/** 阴影的宽度 */
  	private float mMaskWidth;
  	/** 阴影的高度 */
  	private float mMaskHeight;
  	/** 开关圆形按钮的宽度 */
  	private float mBtnWidth;
  	/** 开关初始坐标 */
  	private float mBtnInitPos;
  
  	private int mClickTimeout;
  
  	private int mTouchSlop;
  	/** 最大透明度,就是不透明 */
  	private final int MAX_ALPHA = 255;
  	/** 当前透明度,这里主要用于如果控件的enable属性为false时候设置半透明 ,即不可以点击 */
  	private int mAlpha = MAX_ALPHA;
  
  	private boolean mChecked = false;
  
  	private boolean mBroadcasting;
  
  	private boolean mTurningOn;
  
  	private PerformClick mPerformClick;
  	/** 开关状态切换监听接口 */
  	private OnCheckedChangeListener mOnCheckedChangeListener;
  
  	private OnCheckedChangeListener mOnCheckedChangeWidgetListener;
  	/** 判断是否在进行动画  */
  	private boolean mAnimating;
  
  	private final float VELOCITY = 350;
  	/** 滑动速度 */
  	private float mVelocity;
  
  	private final float EXTENDED_OFFSET_Y = 15;
  	/** Y轴方向扩大的区域,增大点击区域 */
  	private float mExtendOffsetY;
  
  	private float mAnimationPosition;
  
  	private float mAnimatedVelocity;
  
  	public CheckSwitchButton(Context context, AttributeSet attrs) {
  		this(context, attrs, android.R.attr.checkboxStyle);
  	}
  
  	public CheckSwitchButton(Context context) {
  		this(context, null);
  	}
  
  	public CheckSwitchButton(Context context, AttributeSet attrs, int defStyle) {
  		super(context, attrs, defStyle);
  		initView(context);
  	}
  
  	private void initView(Context context) {
  		mPaint = new Paint();
  		mPaint.setColor(Color.WHITE);
  		Resources resources = context.getResources();
  
  		// get viewConfiguration
  		mClickTimeout = ViewConfiguration.getPressedStateDuration()
  				+ ViewConfiguration.getTapTimeout();
  		mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
  
  		// get Bitmap
  		mBottom = BitmapFactory.decodeResource(resources, R.drawable.checkswitch_bottom);
  		mBtnPressed = BitmapFactory.decodeResource(resources,R.drawable.checkswitch_btn_pressed);
  		mBtnNormal = BitmapFactory.decodeResource(resources,R.drawable.checkswitch_btn_unpressed);
  		mFrame = BitmapFactory.decodeResource(resources, R.drawable.checkswitch_frame);
  		mMask = BitmapFactory.decodeResource(resources, R.drawable.checkswitch_mask);
  		mCurBtnPic = mBtnNormal;
  
  		mBtnWidth = mBtnPressed.getWidth();
  		mMaskWidth = mMask.getWidth();
  		mMaskHeight = mMask.getHeight();
  
  		mBtnOffPos = mBtnWidth / 2;
  		mBtnOnPos = mMaskWidth - mBtnWidth / 2;
  		// 判断起始位置,如果设定了mChecked为true,起始位置为 mBtnOnPos
  		mBtnPos = mChecked ? mBtnOnPos : mBtnOffPos;
  		mRealPos = getRealPos(mBtnPos);
  		// density 密度
  		final float density = getResources().getDisplayMetrics().density;// 方法是获取资源密度(Density)
  		mVelocity = (int) (VELOCITY * density + 0.5f);
  		mExtendOffsetY = (int) (EXTENDED_OFFSET_Y * density + 0.5f);
  		// 创建一个新的矩形与指定的坐标。
  		mSaveLayerRectF = new RectF(0, mExtendOffsetY, mMask.getWidth(),
  				mMask.getHeight() + mExtendOffsetY);
  		mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);// PorterDuff.Mode.SRC_IN
  		// :这个属性代表
  		// 取两层绘制交集。显示上层。
  	}
  
  	@Override
  	public void setEnabled(boolean enabled) {
  		mAlpha = enabled ? MAX_ALPHA : MAX_ALPHA / 2;
  		super.setEnabled(enabled);
  	}
  
  	public boolean isChecked() {
  		return mChecked;
  	}
  
  	/** 自动判断切换至相反的属性 : true -->false ;false -->true */
  	public void toggle() {
  		setChecked(!mChecked);
  	}
  
  	/**
  	 * 内部调用此方法设置checked状态,此方法会延迟执行各种回调函数,保证动画的流畅度
  	 *
  	 * @param checked
  	 */
  	private void setCheckedDelayed(final boolean checked) {
  		this.postDelayed(new Runnable() {
  
  			@Override
  			public void run() {
  				setChecked(checked);
  			}
  		}, 10);
  	}
  
  	/**
  	 * <p>
  	 * Changes the checked state of this button.
  	 * </p>
  	 *
  	 * @param checked
  	 *            true to check the button, false to uncheck it
  	 */
  	public void setChecked(boolean checked) {
  
  		if (mChecked != checked) {
  			mChecked = checked;
  
  			mBtnPos = checked ? mBtnOnPos : mBtnOffPos;
  			mRealPos = getRealPos(mBtnPos);
  			invalidate();
  
  			// Avoid infinite recursions if setChecked() is called from a
  			// listener
  			if (mBroadcasting) {
  				return;
  			}
  
  			mBroadcasting = true;
  			if (mOnCheckedChangeListener != null) {
  				mOnCheckedChangeListener.onCheckedChanged(CheckSwitchButton.this,mChecked);
  			}
  			if (mOnCheckedChangeWidgetListener != null) {
  				mOnCheckedChangeWidgetListener.onCheckedChanged(CheckSwitchButton.this, mChecked);
  			}
  
  			mBroadcasting = false;
  		}
  	}
  
  	/**
  	 * Register a callback to be invoked when the checked state of this button
  	 * changes.
  	 *
  	 * @param listener
  	 *            the callback to call on checked state change
  	 */
  	public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
  		mOnCheckedChangeListener = listener;
  	}
  
  	/**
  	 * Register a callback to be invoked when the checked state of this button
  	 * changes. This callback is used for internal purpose only.
  	 *
  	 * @param listener
  	 *            the callback to call on checked state change
  	 * @hide
  	 */
  	void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) {
  		mOnCheckedChangeWidgetListener = listener;
  	}
  
  	@Override
  	public boolean onTouchEvent(MotionEvent event) {
  		int action = event.getAction();
  		float x = event.getX();
  		float y = event.getY();
  		float deltaX = Math.abs(x - mFirstDownX);
  		float deltaY = Math.abs(y - mFirstDownY);
  		switch (action) {
  			case MotionEvent.ACTION_DOWN:
  				attemptClaimDrag();
  				mFirstDownX = x;
  				mFirstDownY = y;
  				mCurBtnPic = mBtnPressed;
  				mBtnInitPos = mChecked ? mBtnOnPos : mBtnOffPos;
  				break;
  			case MotionEvent.ACTION_MOVE:
  				// 拖动着的时间
  				float time = event.getEventTime() - event.getDownTime();
  				// 当前按钮的位置
  				mBtnPos = mBtnInitPos + event.getX() - mFirstDownX;
  				if (mBtnPos >= mBtnOffPos) {
  					mBtnPos = mBtnOffPos;
  				}
  				if (mBtnPos <= mBtnOnPos) {
  					mBtnPos = mBtnOnPos;
  				}
  				mTurningOn = mBtnPos > (mBtnOffPos - mBtnOnPos) / 2 + mBtnOnPos;
  
  				mRealPos = getRealPos(mBtnPos);
  				break;
  			case MotionEvent.ACTION_UP:
  				mCurBtnPic = mBtnNormal;
  				time = event.getEventTime() - event.getDownTime();
  				if (deltaY < mTouchSlop && deltaX < mTouchSlop
  						&& time < mClickTimeout) {
  					if (mPerformClick == null) {
  						mPerformClick = new PerformClick();
  					}
  					if (!post(mPerformClick)) {
  						performClick();
  					}
  				} else {
  					startAnimation(!mTurningOn);
  				}
  				break;
  		}
  
  		invalidate();
  		return isEnabled();
  	}
  
  	private final class PerformClick implements Runnable {
  		public void run() {
  			performClick();
  		}
  	}
  
  	@Override
  	public boolean performClick() {
  		startAnimation(!mChecked);
  		return true;
  	}
  
  	/**
  	 * 通知父类不要拦截touch事件 Tries to claim the user's drag motion, and requests
  	 * disallowing any ancestors from stealing events in the drag.
  	 */
  	private void attemptClaimDrag() {
  		mParent = getParent();
  		if (mParent != null) {
  			// 通知父类不要拦截touch事件
  			mParent.requestDisallowInterceptTouchEvent(true);
  		}
  	}
  
  	/**
  	 * btnPos转换成RealPos
  	 *
  	 * @param btnPos
  	 * @return
  	 */
  	private float getRealPos(float btnPos) {
  		return btnPos - mBtnWidth / 2;
  	}
  
  	@Override
  	protected void onDraw(Canvas canvas) {
  		canvas.saveLayerAlpha(mSaveLayerRectF, mAlpha, Canvas.MATRIX_SAVE_FLAG
  				| Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
  				| Canvas.FULL_COLOR_LAYER_SAVE_FLAG
  				| Canvas.CLIP_TO_LAYER_SAVE_FLAG);
  		// 绘制蒙板
  		canvas.drawBitmap(mMask, 0, mExtendOffsetY, mPaint);
  		mPaint.setXfermode(mXfermode);
  
  		// 绘制底部图片
  		canvas.drawBitmap(mBottom, mRealPos, mExtendOffsetY, mPaint);
  		mPaint.setXfermode(null);
  		// 绘制边框
  		canvas.drawBitmap(mFrame, 0, mExtendOffsetY, mPaint);
  
  		// 绘制按钮
  		canvas.drawBitmap(mCurBtnPic, mRealPos, mExtendOffsetY, mPaint);
  		canvas.restore();
  	}
  
  	@Override
  	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  		setMeasuredDimension((int) mMaskWidth,
  				(int) (mMaskHeight + 2 * mExtendOffsetY));
  	}
  
  	private void startAnimation(boolean turnOn) {
  		mAnimating = true;
  		mAnimatedVelocity = turnOn ? -mVelocity : mVelocity;
  		mAnimationPosition = mBtnPos;
  
  		new SwitchAnimation().run();
  	}
  
  	private void stopAnimation() {
  		mAnimating = false;
  	}
  
  	private final class SwitchAnimation implements Runnable {
  
  		@Override
  		public void run() {
  			if (!mAnimating) {
  				return;
  			}
  			doAnimation();
  			FrameAnimationController.requestAnimationFrame(this);
  		}
  	}
  
  	private void doAnimation() {
  		mAnimationPosition += mAnimatedVelocity * FrameAnimationController.ANIMATION_FRAME_DURATION / 1000;
  		if (mAnimationPosition <= mBtnOnPos) {
  			stopAnimation();
  			mAnimationPosition = mBtnOnPos;
  			setCheckedDelayed(true);
  		} else if (mAnimationPosition >= mBtnOffPos) {
  			stopAnimation();
  			mAnimationPosition = mBtnOffPos;
  			setCheckedDelayed(false);
  		}
  		moveView(mAnimationPosition);
  	}
  
  	private void moveView(float position) {
  		mBtnPos = position;
  		mRealPos = getRealPos(mBtnPos);
  		invalidate();
  	}
  }