zoukankan      html  css  js  c++  java
  • 一个不错效果的调光调色开关

      先上效果图:

      不错吧,最中间那个开关是个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>
  • 相关阅读:
    执行shell脚本的四种方式(转)
    linux free命令详解(一)
    linux TOP命令各参数详解【转载】
    grep命令
    vim常用命令
    IntelliJ Idea注释模板--类注释、方法注释
    [Chrome]抓请求直接生成可执行代码
    记录Markdown工具Typora
    VSCODE 配置远程开发环境
    [Boost::Polygon]多边形相减得到新的多边形序列
  • 原文地址:https://www.cnblogs.com/Couch-potato/p/3749420.html
Copyright © 2011-2022 走看看