200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > Android上基于HSV颜色模型实现取色盘功能

Android上基于HSV颜色模型实现取色盘功能

时间:2024-01-02 20:16:46

相关推荐

Android上基于HSV颜色模型实现取色盘功能

HSV色彩空间

HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)。

色调H

用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;

饱和度S

饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。

明度V

明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。

RGB和CMY颜色模型都是面向硬件的,而HSV(Hue Saturation Value)颜色模型是面向用户的。

HSV模型的三维表示从RGB立方体演化而来。设想从RGB沿立方体对角线的白色顶点向黑色顶点观察,就可以看到立方体的六边形外形。六边形边界表示色彩,水平轴表示纯度,明度沿垂直轴测量。

六棱锥

H参数表示色彩信息,即所处的光谱颜色的位置。该参数用一HSV颜色空间模型角度量来表示,红、绿、蓝分别相隔120度。互补色分别相差180度。

纯度S为一比例值,范围从0到1,它表示成所选颜色的纯度和该颜色最大的纯度之间的比率。S=0时,只有灰度。

V表示色彩的明亮程度,范围从0到1。有一点要注意:它和光强度之间并没有直接的联系.

取色盘,这样的去色盘,其实是V为1时,HSV颜色模型中最上面一层;

HSV

如上图的取色盘,相当于HSV颜色模型中的最顶层,相当于Value为1时的情况;那么,我们可将该平面看做一个二维坐标系,通过触摸点坐标与圆心坐标,利用反三角函数,可计算出任意一点与X轴,Y轴之间的夹角,该夹角及为HSV颜色模型中的Hue;同时通过触摸点坐标与圆心可计算出当前点距离圆心的距离,该距离与圆盘半径的比值即为HSV颜色模型中的Saturation ;

那么通过上面小圆坐标和大圆圆心位置,即可得到HSV颜色模型中的Hue和Saturation,而Value前面说过,始终是1,因此可通过java的Color类的静态方法,HSV2Color(HSV[])得到对应的点的color,同时也可转成RGB值;

代码实现如下:

1.自定义ColorPickerView,实现取色功能

.myapplication;importandroid.content.Context;importandroid.content.res.TypedArray;importandroid.graphics.Bitmap;importandroid.graphics.BitmapFactory;importandroid.graphics.Canvas;importandroid.graphics.Color;poseShader;importandroid.graphics.Paint;importandroid.graphics.Point;importandroid.graphics.PorterDuff;importandroid.graphics.RadialGradient;importandroid.graphics.Shader;importandroid.graphics.SweepGradient;importandroid.support.annotation.Nullable;importandroid.util.AttributeSet;importandroid.util.Log;importandroid.view.MotionEvent;importandroid.view.View;publicclassColorPickerViewextendsView{privatestaticfinalfloatBASE_RADIO=1.0f;//饱和度倍数,0-1,饱和度越高颜色越艳丽,可根据具体需求调整privateContextmContext;privateintmBigCircle;//外圈半径privateintmRudeRadius;//可移动小球的半径privateintmCenterColor;//可移动小球的颜色privateintmRudeStrokeWidth=5;privateBitmapmBitmapBack;//背景图片privatePaintmPaint;//背景画笔privatePaintmCenterPaint;//可移动小球画笔privatePointmCenterPoint;//中心位置privatePointmRockPosition;//小球当前位置privateOnColorChangedListenermListener;//小球移动的监听privatedoublelength;//小球到中心位置的距离privatedoublemHue=0.0;//0-360.0//色相privatedoublemBrightness=1.0;//0-1.0//亮度privatedoublemSaturation=1.0;//0-1.0//饱和度privatefloat[]mHSV=newfloat[3];publicColorPickerView(Contextcontext){super(context);}publicColorPickerView(Contextcontext,@NullableAttributeSetattrs){super(context,attrs);this.mContext=context;init(attrs);}publicColorPickerView(Contextcontext,@NullableAttributeSetattrs,intdefStyleAttr){super(context,attrs,defStyleAttr);this.mContext=context;init(attrs);}/***@paramattrs*/privatevoidinit(AttributeSetattrs){//获取自定义组件的属性TypedArraytypes=mContext.obtainStyledAttributes(attrs,R.styleable.color_picker);try{mBigCircle=types.getDimensionPixelOffset(R.styleable.color_picker_circle_radius,100);mRudeRadius=types.getDimensionPixelOffset(R.styleable.color_picker_center_radius,10);mCenterColor=types.getColor(R.styleable.color_picker_center_color,Color.WHITE);}finally{types.recycle();//TypeArray用完需要recycle}//此处根据UI要求,可使用UI图片代替绘制取色盘//将背景图片大小设置为属性设置的直径//mBitmapBack=BitmapFactory.decodeResource(getResources(),//R.drawable.hsb_circle_hard);//mBitmapBack=Bitmap.createScaledBitmap(mBitmapBack,mBigCircle*2,//mBigCircle*2,false);//中心位置坐标mCenterPoint=newPoint(mBigCircle,mBigCircle);mRockPosition=newPoint(mCenterPoint);//初始化背景画笔和可移动小球的画笔mPaint=newPaint();mPaint.setAntiAlias(true);mBitmapBack=createColorWheelBitmap(mBigCircle*2,mBigCircle*2);mCenterPaint=newPaint();mCenterPaint.setColor(mCenterColor);mCenterPaint.setStrokeWidth(mRudeStrokeWidth);mCenterPaint.setStyle(Paint.Style.STROKE);}/***绘制取色盘,可根据UI需求,使用资源图片代替*@paramwidth*@paramheight*@return*/privateBitmapcreateColorWheelBitmap(intwidth,intheight){Bitmapbitmap=Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888);intcolorCount=12;intcolorAngleStep=360/colorCount;intcolors[]=newint[colorCount+1];floathsv[]=newfloat[]{0f,1f,1f};for(inti=0;i<colors.length;i++){hsv[0]=360-(i*colorAngleStep)%360;colors[i]=Color.HSVToColor(hsv);}colors[colorCount]=colors[0];SweepGradientsweepGradient=newSweepGradient(width/2,height/2,colors,null);RadialGradientradialGradient=newRadialGradient(width/2,height/2,mBigCircle,0xFFFFFFFF,0x00FFFFFF,Shader.TileMode.CLAMP);ComposeShadercomposeShader=newComposeShader(sweepGradient,radialGradient,PorterDuff.Mode.SRC_OVER);mPaint.setShader(composeShader);Canvascanvas=newCanvas(bitmap);canvas.drawCircle(width/2,height/2,mBigCircle,mPaint);returnbitmap;}@OverrideprotectedvoidonDraw(Canvascanvas){super.onDraw(canvas);//画背景图片canvas.drawBitmap(mBitmapBack,0,0,mPaint);//画中心小球canvas.drawCircle(mRockPosition.x,mRockPosition.y,mRudeRadius,mCenterPaint);}publicvoidsetOnColorChangedListener(OnColorChangedListenerlistener){this.mListener=listener;}//颜色发生变化的回调接口publicinterfaceOnColorChangedListener{voidonColorChange(float[]hsb);}@OverridepublicbooleanonTouchEvent(MotionEventevent){switch(event.getAction()){caseMotionEvent.ACTION_DOWN:caseMotionEvent.ACTION_UP:caseMotionEvent.ACTION_MOVE:length=getLength(event.getX(),event.getY(),mCenterPoint.x,mCenterPoint.y);if(length<=mBigCircle-mRudeRadius){mRockPosition.set((int)event.getX(),(int)event.getY());}else{mRockPosition=getBorderPoint(mCenterPoint,newPoint((int)event.getX(),(int)event.getY()),(mBigCircle-mRudeRadius-5));}floatcX=mCenterPoint.x;floatcY=mCenterPoint.y;floatpX=event.getX();floatpY=event.getY();mHue=getHue(cX,cY,pX,pY);if(mHue<0)mHue+=360.0;doubledeltaX=Math.abs(cX-pX),deltaY=(cY-pY);mSaturation=Math.pow(deltaX*deltaX+deltaY*deltaY,0.5)/mBigCircle*BASE_RADIO;if(mSaturation<=0)mSaturation=0;if(mSaturation>=1.0)mSaturation=1.0;break;default:break;}finaldoublehue=mHue,//360.0,sat=mSaturation,brt=mBrightness;mHSV[0]=(float)hue;mHSV[1]=(float)sat;mHSV[2]=(float)brt;Log.d("niexu","onTouchEvent:mHSV[0]="+mHSV[0]+"mHSB[1]="+mHSV[1]+"mHSB[2]=="+mHSV[2]);mListener.onColorChange(mHSV);invalidate();returntrue;}@OverrideprotectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){//视图大小设置为直径setMeasuredDimension(mBigCircle*2,mBigCircle*2);}/***@paramx1*@paramy1*@paramx2*@paramy2*@return*/privatedoublegetLength(floatx1,floaty1,floatx2,floaty2){returnMath.sqrt(Math.pow(x1-x2,2)+Math.pow(y1-y2,2));}/***@parama*@paramb*@paramcutRadius*@return*/privatePointgetBorderPoint(Pointa,Pointb,intcutRadius){floatradian=getRadian(a,b);returnnewPoint(a.x+(int)(cutRadius*Math.cos(radian)),a.x+(int)(cutRadius*Math.sin(radian)));}/***@parama*@paramb*@return*/privatefloatgetRadian(Pointa,Pointb){floatlenA=b.x-a.x;floatlenB=b.y-a.y;floatlenC=(float)Math.sqrt(lenA*lenA+lenB*lenB);floatang=(float)Math.acos(lenA/lenC);ang=ang*(b.y<a.y?-1:1);returnang;}/***计算角度,即HSB中的H**@paramrockXrockY小圆point*@paramcenterXcenterY圆心point*@return*/privatedoublegetHue(floatcenterX,floatcenterY,floatrockX,floatrockY){doublehue=0.0;doubledeltaA=Math.abs(rockX-centerX);doubledeltaB=Math.abs(rockY-centerY);doubledeltaC=getLength(centerX,centerY,rockX,rockY);if(centerX==rockX&&centerY==rockY){return0;}if(centerX==rockX){//在Y轴上if(centerY>rockY){hue=90;}else{hue=270;}returnhue;}if(centerY==rockY){//在X轴上if(centerX>rockX){hue=180;}else{hue=0;}returnhue;}if(rockX>centerX&&centerY>rockY){//第一象限hue=Math.asin(deltaB/deltaC)*180/Math.PI;}elseif(rockX<centerX&&rockY<centerY){//第二象限hue=Math.asin(deltaA/deltaC)*180/Math.PI+90;}elseif(rockX<centerX&&rockY>centerY){//第三象限hue=Math.asin(deltaB/deltaC)*180/Math.PI+180;}elseif(rockX>centerX&&rockY>centerY){//第四象限hue=Math.asin(deltaA/deltaC)*180/Math.PI+270;}Log.d("niexu","getHue:hue="+hue);returnhue;}}

2.自定义属性

<?xmlversion="1.0"encoding="utf-8"?><resources><declare-styleablename="color_picker"><!--背景圆的半径--><attrname="circle_radius"format="dimension"/><!--可滑动小球的半径--><attrname="center_radius"format="dimension"/><!--可滑动小球的颜色--><attrname="center_color"format="color"/></declare-styleable></resources>

3.布局中使用ColorPickerView

<LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Buttonandroid:id="@+id/button1"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="button1"/><.myapplication.ColorPickerViewandroid:id="@+id/color_picker_view"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"color_picker:circle_radius="100dp"color_picker:center_radius="10dp"color_picker:center_color="#ffffff"/></LinearLayout>

4.java代码中监听颜色改变,做相应的业务逻辑。

publicclassColorPickerTestActivityextendsAppCompatActivity{//privateRecyclerViewmRecyclerView;privateColorPickerViewmColorPickerView;privateButtonmButton;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.color_picker_test_layout);Toolbartoolbar=findViewById(R.id.toolbar);setSupportActionBar(toolbar);mColorPickerView=findViewById(R.id.color_picker_view);mButton=findViewById(R.id.button1);mColorPickerView.setOnColorChangedListener(newColorPickerView.OnColorChangedListener(){@OverridepublicvoidonColorChange(float[]hsv){mButton.setBackgroundColor(Color.HSVToColor(hsv));}});}

效果测试

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。