先上效果图:
不错吧,最中间那个开关是个CheckBox,中间那个蓝色的是个圆形的拖动条,可以用来显示灯的亮度,而最外面的彩色环形也是可以拖动的,可以用来控制色彩。
彩色环形是自定义View——CirclView,onTouch事件计算如旋转的角度,然后旋转画布,那个环形是UI素材。代码如下:
1 import android.content.Context;
2 import android.graphics.Bitmap;
3 import android.graphics.Canvas;
4 import android.graphics.Color;
5 import android.graphics.drawable.BitmapDrawable;
6 import android.graphics.drawable.Drawable;
7 import android.util.AttributeSet;
8 import android.view.MotionEvent;
9 import android.view.View;
10
11 public class CircleView extends View {
12 public static final int INVALID_ANGLE = Integer.MIN_VALUE;
13
14 private float lastX; //起始X坐标
15 private float lastY; //起始Y坐标
16
17 private float centerX; //圆心X坐标
18 private float centerY; //圆心Y坐标
19
20 double oldAngle; //旋转前角度
21 double angle; //旋转后角度
22
23 private Drawable rotatedPicture; //要旋转的背景图片
24 private Bitmap rotatedBitmap;
25 private float ringRatio;
26
27 private int maxAngle = INVALID_ANGLE;
28 private int minAngle = INVALID_ANGLE;
29
30 private OnAngleChangedListener anglelistener;
31 private OnColorChangedListener colorListener;
32
33 public CircleView(Context context) {
34 this(context, null);
35 }
36
37 public CircleView(Context context, AttributeSet attrs) {
38 this(context, attrs, 0);
39 }
40
41 public CircleView(Context context, AttributeSet attrs, int defStyle) {
42 super(context, attrs, defStyle);
43 }
44
45 /**
46 *
47 * @param lastPointX 一次ActionMove的起始X坐标
48 * @param lastPointY 一次ActionMove的起始Y坐标
49 * @param pointX 一次ActionMove的终止X坐标
50 * @param pointY 一次ActionMove的终止Y坐标
51 * @return 返回一次ActionMove转过的角度
52 */
53 private double computeAngle(float lastPointX, float lastPointY, float pointX, float pointY) {
54 //坐标系换算,原坐标系为以屏幕左上角为原点的直角坐标系,新坐标系为以圆心位置(centerX,centerY)为原点的直角坐标系
55 //没有delta前缀的都是原坐标系的值,delta前缀标识的为换算后新坐标系的值
56 float deltaLastPointX = lastPointX - centerX;
57 float deltaLastPointY = centerY - lastPointY;
58
59 float deltaPointX = pointX - centerX;
60 float deltaPointY = centerY - pointY;
61
62 //gradient表示斜率,如果deltaPonitX的值为零,则gradient为无穷大,否则为deltaPointY / deltaPointX
63 float gradient = deltaPointX != 0 ? deltaPointY / deltaPointX : Integer.MAX_VALUE;
64 float lastGradient = deltaLastPointX != 0 ? deltaLastPointY / deltaLastPointX : Integer.MAX_VALUE;
65 //如果斜率相同,则返回0
66 if (gradient == lastGradient)
67 return 0;
68
69 //lastPointSidePow,pointSidePow表示对应点到圆心的距离平方
70 float lastPointSidePow = deltaLastPointX * deltaLastPointX + deltaLastPointY * deltaLastPointY;
71 float pointSidePow = deltaPointX * deltaPointX + deltaPointY * deltaPointY;
72 //两点之间距离的平方
73 float lastPointPointSidePow = (float) (Math.pow(deltaLastPointY - deltaPointY, 2) + Math.pow(deltaLastPointX
74 - deltaPointX, 2));
75 //下面使用余弦定理求出一次ActionMove所经过角度的余弦值
76 //余弦定理:cosC = (a^2 + b^2 - c^2) / (2·a·b)
77 double cosinCentralAngle = (lastPointSidePow + pointSidePow - lastPointPointSidePow)
78 / (2 * Math.sqrt(lastPointSidePow) * Math.sqrt(pointSidePow));
79 if (cosinCentralAngle > 1 || cosinCentralAngle < -1)
80 return 0;
81 //反余弦:把余弦值换算成对应弧度
82 double acos = Math.acos(cosinCentralAngle);
83 //弧度角度换算:把弧度换算成对应角度
84 double increment = (acos / Math.PI) * 180;
85 //isBellow判断一次ActionMove之后斜率值是否降低
86 boolean isBellow = lastGradient > gradient;
87 //如果斜率降低
88 if (isBellow) {
89 //如果ActionMove之前在第一、四象限
90 if (deltaLastPointX >= 0) {
91 //如果ActionMove之后也在第一、四象限,则旋转的方向为逆时针,角度应为正值
92 if (deltaPointX >= 0) {
93 return increment;
94 //如果ActionMove之后在第二、三象限,则旋转的方向为顺时针,角度为负值
95 } else {
96 return -increment;
97 }
98 //如果ActionMove之前在第二、三象限
99 } else {
100 //如果ActionMove之后也在第二、三象限,则旋转的方向为逆时针,角度应为正值
101 if (deltaPointX <= 0) {
102 return increment;
103 //如果ActionMove之后在第一、四象限,则旋转的方向为顺时针,角度为负值
104 } else {
105 return -increment;
106 }
107 }
108 } else {
109 if (deltaLastPointX >= 0) {
110 if (deltaPointX >= 0) {
111 return -increment;
112 } else {
113 return increment;
114 }
115 } else {
116 if (deltaPointX <= 0) {
117 return -increment;
118 } else {
119 return increment;
120 }
121 }
122 }
123 }
124
125 @Override
126 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
127 centerX = w / 2.0f;
128 centerY = h / 2.0f;
129 rotatedPicture.setBounds(0, 0, w, h);
130 rotatedBitmap = ((BitmapDrawable) rotatedPicture).getBitmap();
131 }
132
133 @Override
134 protected void onDraw(Canvas canvas) {
135 canvas.save();
136 canvas.rotate((float) angle, centerX, centerY);
137 rotatedPicture.draw(canvas);
138 canvas.restore();
139 }
140
141 public boolean isHit(int x, int y) {
142 //getPixel(x, y)方法返回(x,y)坐标位置的像素颜色信息
143 //x,y的取值范围分别为(0...width-1)、(0...height-1),如果超出取值范围则会抛出异常
144 //方法返回值为
145 return (rotatedBitmap.getPixel(x, y) & 0xFF000000) != 0;
146 }
147
148 @Override
149 public boolean onTouchEvent(MotionEvent event) {
150 final float nowX = event.getX();
151 final float nowY = event.getY();
152
153 switch (event.getAction()) {
154 case MotionEvent.ACTION_DOWN:
155 if (!isHit((int) nowX, (int) nowY))
156 return false;
157 lastX = nowX;
158 lastY = nowY;
159 oldAngle = angle;
160 break;
161 case MotionEvent.ACTION_MOVE:
162 double increment = computeAngle(lastX, lastY, nowX, nowY);
163 if (increment != 0) {
164 angle += increment;
165 if (maxAngle != INVALID_ANGLE && angle > maxAngle) {
166 angle = maxAngle;
167 } else if (minAngle != INVALID_ANGLE && angle < minAngle) {
168 angle = minAngle;
169 }
170 invalidate();
171 }
172 lastX = nowX;
173 lastY = nowY;
174 break;
175 case MotionEvent.ACTION_UP:
176 if (anglelistener != null) {
177 anglelistener.onAngleChanged(oldAngle, angle);
178 }
179 if (colorListener != null && ringRatio != 0) {
180 int width = getWidth();
181 int r = (int) (width / 2 - ringRatio * width / 4);
182 int x = 0;
183 int y = 0;
184 double radian = (-angle % 360) * Math.PI / 180;
185 x = (int) (width / 2 + r * Math.sin(radian));
186 y = (int) (width / 2 - r * Math.cos(radian));
187 int color = rotatedBitmap.getPixel(x, y);
188 colorListener.onColorChanged(Color.red(color), Color.green(color), Color.blue(color));
189 }
190 invalidate();
191 break;
192 }
193
194 return true;
195 }
196
197 public static interface OnAngleChangedListener {
198 public void onAngleChanged(double oldAngle, double newAngle);
199 }
200
201 public static interface OnColorChangedListener {
202 public void onColorChanged(int red, int green, int blue);
203 }
204
205 public void OnAngleChangedListener(OnAngleChangedListener listener) {
206 this.anglelistener = listener;
207 }
208
209 public void setOnColorChangedListener(OnColorChangedListener listener) {
210 this.colorListener = listener;
211 }
212
213 public void setRotateDrawable(Drawable rotatedPicture, float ringRatio) {
214 this.rotatedPicture = rotatedPicture;
215 this.ringRatio = ringRatio;
216 }
217
218 public void setRotateDrawable(Drawable rotatedPicture) {
219 this.rotatedPicture = rotatedPicture;
220 }
221
222 public void setMaxRotatedAngle(int maxAngle) {
223 this.maxAngle = maxAngle;
224 }
225
226 public void setMinRotatedAngle(int minAngle) {
227 this.minAngle = minAngle;
228 }
229
230 public void setAngle(double angle) {
231 this.angle = angle;
232 postInvalidate();
233 }
234
235 protected double getAngle() {
236 return angle;
237 }
238 }
那个圆形拖动条则继承自CircleView,然后重写onDraw方法绘制进度。代码如下:
1 import android.content.Context;
2 import android.graphics.Canvas;
3 import android.graphics.Paint;
4 import android.graphics.RectF;
5 import android.graphics.Paint.Cap;
6 import android.graphics.Paint.Style;
7 import android.util.AttributeSet;
8
9 public class ProgressCircleView extends CircleView {
10 private int progressBackground;
11 private int progress;
12
13 private RectF progressRectF;
14 private int progressBgWidth, progressWidth;
15 private Paint progressBgPaint;
16 private Paint progressPaint;
17
18 public ProgressCircleView(Context context) {
19 this(context, null);
20 }
21
22 public ProgressCircleView(Context context, AttributeSet attrs) {
23 this(context, attrs, 0);
24 }
25
26 public ProgressCircleView(Context context, AttributeSet attrs, int defStyle) {
27 super(context, attrs, defStyle);
28 super.setMaxRotatedAngle(360);
29 super.setMinRotatedAngle(0);
30 }
31
32 @Override
33 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
34 super.onSizeChanged(w, h, oldw, oldh);
35 int offset = progressWidth / 2 + 1;
36 progressRectF = new RectF(offset, offset, w - offset, h - offset);
37 }
38
39 @Override
40 protected void onDraw(Canvas canvas) {
41 super.onDraw(canvas);
42 // draw bg
43 canvas.drawArc(progressRectF, 0, 360, false, progressBgPaint);
44 // draw arc
45 canvas.drawArc(progressRectF, -90, (float) getAngle(), false, progressPaint);
46 }
47
48 public void setProgressBackground(int width, int color) {
49 int widthDp = (int) (width * getResources().getDisplayMetrics().density);
50 Paint paint = new Paint();
51 paint.setAntiAlias(true);
52 paint.setAlpha(0xff);
53 paint.setStrokeWidth(widthDp);
54 paint.setStyle(Style.STROKE);
55 paint.setColor(color);
56 progressBgWidth = widthDp;
57 progressBgPaint = paint;
58 }
59
60 public void setProgress(int width, int color) {
61 int widthDp = (int) (width * getResources().getDisplayMetrics().density);
62 Paint paint = new Paint();
63 paint.setAntiAlias(true);
64 paint.setAlpha(0xff);
65 paint.setStrokeWidth(widthDp);
66 paint.setStyle(Style.STROKE);
67 paint.setColor(color);
68 paint.setStrokeCap(Cap.ROUND);
69 progressWidth = widthDp;
70 progressPaint = paint;
71 }
72
73 }
简单使用的Activity:
1 public class MainActivity extends Activity {
2
3 private double oldAngle;
4 boolean isClicked;
5 @Override
6 protected void onCreate(Bundle savedInstanceState) {
7 super.onCreate(savedInstanceState);
8 setContentView(R.layout.color_light_control_panel);
9
10 CircleView cv = (CircleView) findViewById(R.id.colorCircle);
11 cv.setRotateDrawable(getResources().getDrawable(R.drawable.color_light_color_circle), 0.15f);
12 cv.setOnColorChangedListener(new OnColorChangedListener() {
13
14 @Override
15 public void onColorChanged(int red, int green, int blue) {
16 Log.d("onColorChanged", red + " " + green + " " + blue + " ");
17
18 }
19 });
20 // cv.setMaxRotatedAngle(360);
21 // cv.setMinRotatedAngle(0);
22 final ProgressCircleView pcv = (ProgressCircleView) findViewById(R.id.progress);
23 pcv.setRotateDrawable(getResources().getDrawable(R.drawable.color_light_progress));
24 pcv.setProgress(10, 0xFF32CFEB);
25 pcv.setProgressBackground(10, 0xFF3A3A3A);
26
27 pcv.OnAngleChangedListener(new OnAngleChangedListener() {
28
29 @Override
30 public void onAngleChanged(double oldAngle, double newAngle) {
31 Log.d("angle=", "oldAngle=" + oldAngle + " newAngle=" + newAngle);
32 MainActivity.this.oldAngle = oldAngle;
33 }
34 });
35
36 View btn = findViewById(R.id.switch_btn);
37 btn.setOnClickListener(new OnClickListener() {
38
39 @Override
40 public void onClick(View v) {
41 pcv.setAngle(oldAngle);
42 }
43 });
44 }
45 }
最后是布局文件:
1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:background="#ff000000" >
6
7 <com.example.test2.CircleView
8 android:id="@+id/colorCircle"
9 android:layout_width="242dp"
10 android:layout_height="242dp"
11 android:layout_centerInParent="true" >
12 </com.example.test2.CircleView>
13
14 <com.example.test2.ProgressCircleView
15 android:id="@+id/progress"
16 android:layout_width="164dp"
17 android:layout_height="164dp"
18 android:layout_centerInParent="true" >
19 </com.example.test2.ProgressCircleView>
20
21 <View
22 android:id="@+id/switch_btn"
23 android:layout_width="61dp"
24 android:layout_height="61dp"
25 android:background="@drawable/color_light_switch_on"
26 android:layout_centerInParent="true" >
27 </View>
28
29 </RelativeLayout>
一个不错效果的调光调色开关
时间: 2024-10-17 05:33:08