Android进阶——自定义View之自己绘制彩虹圆环调色板引言前面几篇文章都是关于通过继承系统View和组合现有View来实现自定义View的,刚好由于项目需要实现一个滑动切换LED彩灯颜色的功能,所以需要一个类似调色板的功能,随着手在调色板有效区域滑动,LED彩灯随即显示相应的颜色,也可以通过左右的按钮,按顺序切换显示一组颜色,同时都随着亮度的改变LED彩灯的亮度随即变化,这篇基本上把继承View重绘实现自定义控件的大部分知识总结了下(当然还有蛮多没有涉及到,比如说自适应布局等),源码在Github上一、继承View绘制自定义控件的通用步骤自定义属性和继承View重写onDraw方法实现构造方法,其中public RainbowPalette(Context context, AttributeSet attrs) 必须实现,否则无法通过xml引用,public RainbowPalette(Context context) ,public RainbowPalette(Context context, AttributeSet attrs, int defStyleAttr)可选,通常在构造方法中完成属性和其他成员变量的初始化重写onMeasure方法,否则在xml中有些设置布局参数无效@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(width, height);//重新设置View的位置,若不重写的话,则不会布局,即使设置centerInParent为true也无效//setMeasuredDimension(width,height);}手动调用invalidate或者postInvalidateon方法完成界面刷新重写onTouchEvent方法实现基本的交互定义回调接口供外部调用二、彩虹圆环调色板设计思想1、UI构成首先从整个形状来看是个圆环,系统自有的控件明显没具有这个功能,只能是继承View重写OnDraw来完成绘制工作。
前面Android入门——利用Canvas完成绘制点、圆、直线、路径、椭圆、多边形等2D图形可以知道通过Paint可以在Canvas绘制任何图形,包括圆环,于是整体的结构就出来了,中心实体小圆作为指示当前颜色的标记,外圈渐变色圆环作为调色板的取色区域(可以通过给Paint传入Shader shader = new SweepGradient(0, 0, gradientColors, null)来绘制渐变色),最外圈的光环可以绘制多个圆环,而作为指示器标记的小圆点(也可以传入图片资源)也是一个圆形,如此一来UI方面的结构基本明了。
2、交互设计一般来说自定义View的人机交互都是通过回调的方式的来实现的。
滑动选择颜色:自定义控件的滑动自然是重写onTouchEvent,然后调用invalidate手动触发View重绘(即重新调用onDraw)颜色指示器的显示的位置:手动触发invalidate重新调用onDraw绘制中心圆形自动同步选中的颜色:手动触发invalidate重新调用onDraw绘制仅在圆环处滑动选择颜色:如果面积小于外圆大于内圆的就认为是有效滑动/*** 是否是有效Touch即是否是按在圆环之上的* @return*/private boolean isEfectiveTouch(float x, float y, float outRadius, float inRadius){ double outCircle = Math.PI * outRadius * outRadius;double inCircle = Math.PI * inRadius * inRadius;double fingerCircle = Math.PI * (x * x + y * y);if(fingerCircle < outCircle && fingerCircle > inCircle) {return true;}else {return false;}}三、实现彩虹圆环调色板1、自定义属性attr.xml<declare-styleable name="rainbow_palette"><attr name="palette_color" format="color"/><!-- 可滑动小球的颜色--><attr name="indicator_color" format="color" /><!--中间指示当前选中颜色值的圆--><attr name="center_circle_defcolor" format="color"/><!-- 外圈圆环的半径--><attr name="out_circle_radius" format="dimension" /><!-- 调色环的外圈圆环的半径--><attr name="palette_radius" format="dimension" /><!-- 中心圆环的半径--><attr name="center_radius" format="dimension" /><!-- 调色环的宽度--><attr name="palette_width" format="dimension" /><!-- 可滑动小球的半径--><attr name="indicator_radius" format="dimension" /><!--用其他图片来代替绘制的指示器的小圆--><attr name="ic_indicator" format="reference"/></declare-styleable>2、重写Viewimport com.xiaoi.app.zkSmartHome.R;/*** auther: Crazy.Mo* Date: 2016/12/13* Time:10:27* Des:自定义的调色板,可以绘制圆形指示器,也可以自定义图标指示器,但是指示器会超出边界*/public class RainbowPalette extends View {private Context context;private Paint borderPaint;//色环外圈的彩虹圆环private Paint palettePaint;//渐变色环的画笔private Paint centerPaint;//中间圆画笔,用于显示当前选中的颜色private Paint indictorPaint; // 可移动小球画笔private int indictorColor;private int[] gradientColors;//渐变色环颜色private int centerCircleColor;private int width;//当前调色板的宽度private int height;//当前调色板的高度private float paletteRadius;//色环半径,整个环形外径,直接决定这个调色板的整体的大小,画渐变环可以看成画一个完整的圆形再挖掉一个小圆private float centerRadius;//中心圆半径private float paletteWidth;//色环的宽度private float indictorRadius;//小球的半径private Point indictorPosition;// 小球当前位置private Point centerPosition;//圆心的位置,色环圆心的位置private Bitmap indicatorBitmap; // 指示器小球的图标private int indictorResId;//指示器图标的idprivate RainbowPalette.OnColorChangedListen listen;private static boolean isShowIndictor=true;private final static int BORDER_WIDTH=2;private final static int PALETTE_WIDTH=100;private final static int CENTER_CIRCLE_WIDTH=5;private final static int INDICTOR_WIDTH=5;private final static int DEF_INDICTOR_COLOR=0xFFc9f5f1;//设置指示小圆形的颜色private final static int DEF_CIRCLE_COLOR=0xFF0511FB;//设置中间圆的默认颜色public RainbowPalette(Context context) {super(context);}public RainbowPalette(Context context, AttributeSet attrs) {super(context, attrs);this.context = context;init(attrs);}public RainbowPalette(Context context, AttributeSet attrs, int defStyleAttr){super(context, attrs, defStyleAttr);this.context=context;init(attrs);}private void init(AttributeSet attrs){setPaletteSize();initAttrColor(attrs);initPaint();initPosition();initRadiusWidth(attrs);}/*** 用于设置中间圆的颜色* @param color*/public void setCenterPaint(int color){centerPaint.setColor(color);}/*** 设置调色板的尺寸*/private void setPaletteSize(){WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);//获取WM对象int height = (int) (manager.getDefaultDisplay().getHeight() * 0.5f);//获取屏幕的高度*0.5int width = (int) (manager.getDefaultDisplay().getWidth() * 0.7f);//获取屏幕宽度的0.7this.height = height - 36;this.width = width;setMinimumHeight(height - 36);setMinimumWidth(width);}/*** 设置颜色指示器的位置* @param point*/public void setIndictorPosition(Point point){if(point!=null) {this.indictorPosition.x = point.x;this.indictorPosition.y = point.y;}}/*** 设置是否显示颜色指示器* @param isShow*/public static void setIndictorShow(boolean isShow){RainbowPalette.isShowIndictor=isShow;}/*** 设置指示器小球Color的默认值* @param color*/public void setBallColor(int color){this.indictorColor=color;}/*** 初始化各种Paint对象*/private void initPaint(){setGradientColors();Shader shader = new SweepGradient(0, 0, gradientColors, null);//SweepGradient渐变//外层彩虹光环borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//在画图的时候,图片如果旋转或缩放之后,总是会出现那些华丽的锯齿。