前一篇文章,只是简单了分享了如何自定义一个色盘。
这个显得太low了。这里加入滑动获取颜色的功能,同时增加一个滑动的小球。
作为升级版,先上效果图:
考虑到要动态判断圆心到小球的举例,就不能和上一篇那样处理。将色盘圆心作作为变量处理。先上初始化的代码:
private Paint mDiskPaint;//色盘画笔
private float mDiskPaintWidth;//色盘宽度
private float mDiskPoint;//色盘圆点
private Paint mPointPaint;//圆点画笔
private Shader shader; // 色环颜色的发散位置
private Paint bgPaint;//背景画笔
private float r_bgPaint;//背景圆半径
private float r_shaderPaint;//
private float centerX;// 圆心X
private float centerY;// 圆心Y
private int mColor;//当前选择颜色
//渐变色环颜色
private int[] mCircleColors = new int[] { 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00,
0xFFFF0000 };
private float[] colorPotXY = new float[2]; // 白点坐标
private float markPointX;//当前滑动点x坐标
private float markPointY;//当前滑动点Y坐标
public ColorDiskView(Context context) {
super(context);
init();
}
public ColorDiskView(Context context, AttributeSet attrs) {
//this(context, attrs,0);
super(context,attrs);
init();
}
public ColorDiskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mDiskPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
mDiskPaint.setStyle(Paint.Style.STROKE);
bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
LinearGradient gradient = new LinearGradient(0, 0, 700, 700, 0XFF222222, 0xFF909090, Shader.TileMode.CLAMP);
bgPaint.setShader(gradient);
mPointPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
mPointPaint.setColor(Color.WHITE);
}
初始化工作是比较简单的。这里依旧重新写onMeasure方法。和之前一样。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode=MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize=MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize=MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){
setMeasuredDimension(500,500);
}else if(widthSpecMode==MeasureSpec.AT_MOST){
setMeasuredDimension(500,heightSpecSize);
}else if (heightSpecMode==MeasureSpec.AT_MOST){
setMeasuredDimension(widthSpecSize,500);
}
}
重点来了,就是onDraw的方法要注意,先上代码:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final int paddingLeft=getPaddingLeft();
final int paddingRight=getPaddingRight();
final int paddingTop=getPaddingTop();
final int paddingBottom=getPaddingBottom();
int width=getWidth()-paddingLeft-paddingRight;
int height=getHeight()-paddingTop-paddingBottom;
//平移,将画布的坐标原点向左右方向移动width / 2,向上下方向移动height / 2
canvas.translate(width / 2, height / 2);
centerX=Math.min(width,height)/2;
centerY= height / 2;
r_bgPaint=centerX;
mDiskPaintWidth=r_bgPaint/12*5;
mDiskPoint=mDiskPaintWidth/5;
r_shaderPaint=r_bgPaint-mDiskPaintWidth/2;
mDiskPaint.setStrokeWidth(mDiskPaintWidth);
colorPotXY[0] =centerX -mDiskPaintWidth/2;
colorPotXY[1] = 0;
//圆心确定再填充
shader=new SweepGradient(0,0,mCircleColors,null);
mDiskPaint.setShader(shader);
canvas.drawCircle(0,0,r_bgPaint,bgPaint); //外圆大小
canvas.drawCircle(0,0, r_shaderPaint, mDiskPaint);//色盘大小
if (markPointX==0&&markPointY==0){
markPointX=colorPotXY[0];
markPointY=colorPotXY[1];
}
canvas.drawCircle(markPointX, markPointY,mDiskPoint,mPointPaint);
}
注意:为什么要有: canvas.translate(width / 2, height / 2);
看canvas的几个方法:
1.translate(x,y):平移,将画布的坐标原点向左右方向移动x,向上下方向移动y.canvas的默认位置是在(0,0)。
2.scale(x,y):扩大。x为水平方向的放大倍数,y为竖直方向的放大倍数。
3.rotate(angel):旋转.angle指旋转的角度,顺时针旋转。
4.transform():切变。所谓切变,其实就是把图像的顶部或底部推到一边。
因为我上面自定义的坐标起点都是0,0,,所以要移到中间去。
重点看这几行代码:
if (markPointX==0&&markPointY==0){
markPointX=colorPotXY[0];
markPointY=colorPotXY[1];
}
canvas.drawCircle(markPointX, markPointY,mDiskPoint,mPointPaint);
}
这里就牵扯到滑动的处理了,滑动在变,markPointX, markPointY在变。
@Override
public boolean onTouchEvent(MotionEvent event) {
float x=event.getX();
float y=event.getY();
//限制小圆球的滑动范围
if (Math.pow(x-centerX,2)+Math.pow(y-centerY,2)>Math.pow(centerX-mDiskPoint*2/3,2)||
Math.pow(x-centerX,2)+Math.pow(y-centerY,2)<Math.pow(centerX-mDiskPaintWidth+mDiskPoint*2/3,2)){
return true;
}
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
moved(x, y);
break;
case MotionEvent.ACTION_MOVE:
moved(x, y);
break;
case MotionEvent.ACTION_UP:
moved(x, y);
//获取角度
float angle=calculateAngle(x,y);
//获取选中的颜色
mColor=calculateColor(angle);
break;
}
return true;
}
最后还剩几个方法:全贴出来。都不难:
private float calculateAngle(float x, float y) {
return (float) Math.atan2(y - centerY, x - centerX);
}
private int calculateColor(float angle) {
float unit = (float) (angle / (2 * Math.PI));
if (unit < 0) {
unit += 1;
}
if (unit <= 0) {
mColor = mCircleColors[0];
return mCircleColors[0];
}
if (unit >= 1) {
mColor = mCircleColors[mCircleColors.length - 1];
return mCircleColors[mCircleColors.length - 1];
}
float p = unit * (mCircleColors.length - 1);
int i = (int) p;
p -= i;
int c0 = mCircleColors[i];
int c1 = mCircleColors[i + 1];
int a = ave(Color.alpha(c0), Color.alpha(c1), p);
int r = ave(Color.red(c0), Color.red(c1), p);
int g = ave(Color.green(c0), Color.green(c1), p);
int b = ave(Color.blue(c0), Color.blue(c1), p);
Log.e("rgb","rgb"+r+":"+g+":"+b);
mColor = Color.argb(a, r, g, b);
return Color.argb(a, r, g, b);
}
private int ave(int s, int d, float p) {
return s + Math.round(p * (d - s));
}
private void moved(float x, float y) {
//根据三角函数整切定理计算得到X.Y的坐标
markPointX =x-this.getWidth()/2 ;/*(float) ((r_bgPaint-mDiskPaintWidth/2)
* Math.cos(Math.atan2(x - centerX, centerY - y) - (Math.PI / 2)));*/
markPointY =y-this.getHeight()/2 ;/*(float) ((r_bgPaint-mDiskPaintWidth/2)
* Math.sin(Math.atan2(x - centerX, centerY - y) - (Math.PI / 2)));*/
invalidate();
}
记得,moved方法中要invalidate()。
当然了,这些都是在自定义里面写的,你的Activity界面,或者fragment界面,肯定需要获取颜色来作进一步处理,你可以选择写接口或者公共方法就好。我这里直接给了一个方法。
public int getColor(){
return mColor;
}
好了,今天就是这么多了。