标签:android style class blog c code
先上效果图:
不错吧,最中间那个开关是个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>
标签:android style class blog c code
原文地址:http://www.cnblogs.com/Couch-potato/p/3749420.html