在Api Demo里面有一个叫ColorPickerDialog的对话框,该对话框扩展了Dialog 的功能,使其具备颜色选择器的功能。具体可以参考Api Demo源代码,路径为:android-sdk-windows\samples\android-7\ApiDemos\src\com\example\android\apis\graphics\ColorPickerDialog.java
本功能是基于上述的颜色选择器对话框进行扩展,模仿PreferceActivity 组件的实现方式,新建一个名为ColorPickerPreference 的类使其继承自DialogPreference 并实现其内部功能菜单,如图:
在Api Demo里面的颜色选择器是不具备有黑色和白色的选择的,这里我们虽然使用api Demo 里面的颜色选择器但其内部我们稍稍改造了一下。使其支持黑色和白色的选择,如上图中间的一条颜色条,头部和尾部分别代表黑色和白色。
为了显示的友好性,如果用户选择了颜色应该应该会有一个内容窗口或者一个文本对用户的选择做出相应的预览效果,。我们这里使用了一个TextView 做为颜色的预览效果,我们知道,Preference 的summary 只支持字符串的操作,类似下面的图:
如上图,只支持类型类型为CharSequence和字符所在的资源ID位置,那么我们这里如何使其支持颜色的显示和动态更换颜色呢?
这一切都在神奇的回调函数,onCreateView 里面这个方法先于 onBindView 执行,在里面初始化视图并且返回视图。具体处理见下面代码:
@Override
protected
View onCreateView(ViewGroup parent) {
//
TODO Auto-generated method stub
View view
=
LayoutInflater.from(getContext()).inflate(
R.layout.preference,
null
);
TextView title
=
(TextView) view.findViewById(R.id.title);
title.setText(getTitle());
summary
=
(TextView) view.findViewById(R.id.summary);
summary.setText(getSummary());
SharedPreferences prefs
=
getPreferenceManager().getSharedPreferences();
mInitialColor
=
prefs.getInt(getKey(), Color.LTGRAY);
summary.setTextColor(mInitialColor);
return
view;
}
上面代码,我们通过LayoutInflater 函数引入一个外部的布局文件,然后设置其title 和summary 并初始其颜色,通过SharedPreferences 类调用保存后的颜色值,布局文件如下:
<?
xml version="1.0" encoding="UTF-8"
?>
<
LinearLayout
android:id
="@+id/LinearLayout01"
android:layout_width
="fill_parent"
android:layout_height
="fill_parent"
xmlns:android
="http://schemas.android.com/apk/res/android"
>
<
RelativeLayout
android:layout_width
="fill_parent"
android:gravity
="center"
android:layout_height
="fill_parent"
>
<
TextView
android:id
="@+id/title"
android:layout_width
="wrap_content"
android:layout_marginLeft
="15dp"
android:textAppearance
="?android:attr/textAppearanceLarge"
android:layout_height
="wrap_content"
></
TextView
>
<
ImageView
android:src
="@drawable/ic_dialog_menu_generic"
android:layout_marginRight
="15dp"
android:layout_alignParentRight
="true"
android:layout_width
="wrap_content"
android:layout_height
="wrap_content"
></
ImageView
>
<
TextView
android:id
="@+id/summary"
android:layout_below
="@id/title"
android:layout_marginLeft
="15dp"
android:layout_width
="wrap_content"
android:layout_height
="wrap_content"
></
TextView
>
</
RelativeLayout
>
</
LinearLayout
>
实始化对话框的回调函数里面我们为其添加一个ColorPickerDialog 本身的颜色改变的事件监听并为其初始化颜色值,代码如下:
@Override
protected
void
onPrepareDialogBuilder(Builder builder) {
super.onPrepareDialogBuilder(builder);
OnColorChangedListener l
=
new
OnColorChangedListener() {
public
void
colorChanged(
int
color) {
mCurrentColor
=
color;
onDialogClosed(
true
);
getDialog().dismiss();
}
};
LinearLayout layout
=
new
LinearLayout(getContext());
layout.setPadding(
20
,
20
,
20
,
20
);
layout.setOrientation(LinearLayout.VERTICAL);
mCPView
=
new
ColorPickerView(getContext(), l, mInitialColor);
LinearLayout.LayoutParams params1
=
new
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params1.gravity
=
Gravity.CENTER;
mCPView.setLayoutParams(params1);
layout.addView(
this
.mCPView);
layout.setId(android.R.id.widget_frame);
builder.setView(layout);
}
当对话框关闭时,即选择完颜色后,我们就要马上回去更新文本的颜色和将颜色值保存到键值里面,代码如下:
@Override
protected
void
onDialogClosed(boolean positiveResult) {
if
(positiveResult) {
mCurrentColor
=
mCPView.getColor();
summary.setTextColor(mCurrentColor);
SharedPreferences.Editor editor
=
getEditor();
editor.putInt(getKey(), mCurrentColor);
editor.commit();
callChangeListener(
new
Integer(mCurrentColor));
}
}
通过重写,onDialogClosed 回调函数,到这一步整个的封装就己结束,在XML可以通过如下使用:
<
com.terry.util.ColorPickerPreference
android:key
="colorpiker"
android:persistent
="true"
android:summary
="@string/app_name"
android:dialogTitle
="@string/str_fontscolor"
android:title
="@string/str_fontscolor"
/>
Tip:自己封装控件不论是自定义控件也好,preference 也好在XML里面都是无法智能提示的,只要自己把握好输入的属性准确无误就行。
完整代码如下:
package com.terry.util;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.terry.eBook.R;
public
class
ColorPickerPreference extends DialogPreference {
private
int
mInitialColor;
private
int
mCurrentColor;
private
ColorPickerView mCPView;
private
TextView summary;
private
static
class
ColorPickerView extends View {
private
Paint mPaint;
private
Paint mCenterPaint;
private
Paint mHSVPaint;
private
final
int
[] mColors;
private
int
[] mHSVColors;
private
boolean mRedrawHSV;
private
OnColorChangedListener mListener;
ColorPickerView(Context c, OnColorChangedListener l,
int
color) {
super(c);
mListener
=
l;
mColors
=
new
int
[] {
0xFFFF0000
,
0xFFFF00FF
,
0xFF0000FF
,
0xFF00FFFF
,
0xFF00FF00
,
0xFFFFFF00
,
0xFFFF0000
};
Shader s
=
new
SweepGradient(
0
,
0
, mColors,
null
);
mPaint
=
new
Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setShader(s);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(
55
);
mCenterPaint
=
new
Paint(Paint.ANTI_ALIAS_FLAG);
mCenterPaint.setColor(color);
mCenterPaint.setStrokeWidth(
5
);
mHSVColors
=
new
int
[] {
0xFF000000
, color,
0xFFFFFFFF
};
mHSVPaint
=
new
Paint(Paint.ANTI_ALIAS_FLAG);
mHSVPaint.setStrokeWidth(
10
);
mRedrawHSV
=
true
;
}
private
boolean mTrackingCenter;
private
boolean mHighlightCenter;
public
int
getColor() {
return
mCenterPaint.getColor();
}
@Override
protected
void
onDraw(Canvas canvas) {
float
r
=
CENTER_X
-
mPaint.getStrokeWidth()
*
0.5f
;
canvas.translate(CENTER_X, CENTER_X);
int
c
=
mCenterPaint.getColor();
if
(mRedrawHSV) {
mHSVColors[
1
]
=
c;
mHSVPaint.setShader(
new
LinearGradient(
-
100
,
0
,
100
,
0
,
mHSVColors,
null
, Shader.TileMode.CLAMP));
}
canvas.drawOval(
new
RectF(
-
r,
-
r, r, r), mPaint);
canvas.drawCircle(
0
,
0
, CENTER_RADIUS, mCenterPaint);
canvas.drawRect(
new
RectF(
-
100
,
130
,
100
,
110
), mHSVPaint);
if
(mTrackingCenter) {
mCenterPaint.setStyle(Paint.Style.STROKE);
if
(mHighlightCenter) {
mCenterPaint.setAlpha(
0xFF
);
}
else
{
mCenterPaint.setAlpha(
0x80
);
}
canvas.drawCircle(
0
,
0
, CENTER_RADIUS
+
mCenterPaint.getStrokeWidth(), mCenterPaint);
mCenterPaint.setStyle(Paint.Style.FILL);
mCenterPaint.setColor(c);
}
mRedrawHSV
=
true
;
}
@Override
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
setMeasuredDimension(CENTER_X
*
2
, (CENTER_Y
+
25
)
*
2
);
}
private
static
final
int
CENTER_X
=
100
;
private
static
final
int
CENTER_Y
=
100
;
private
static
final
int
CENTER_RADIUS
=
30
;
private
int
ave(
int
s,
int
d,
float
p) {
return
s
+
java.lang.Math.round(p
*
(d
-
s));
}
private
int
interpColor(
int
colors[],
float
unit) {
if
(unit
<=
0
) {
return
colors[
0
];
}
if
(unit
>=
1
) {
return
colors[colors.length
-
1
];
}
float
p
=
unit
*
(colors.length
-
1
);
int
i
=
(
int
) p;
p
-=
i;
//
now p is just the fractional part [0...1) and i is the index
int
c0
=
colors[i];
int
c1
=
colors[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);
return
Color.argb(a, r, g, b);
}
private
static
final
float
PI
=
3.1415926f
;
@Override
public
boolean onTouchEvent(MotionEvent
event
) {
float
x
=
event
.getX()
-
CENTER_X;
float
y
=
event
.getY()
-
CENTER_Y;
boolean inCenter
=
java.lang.Math.sqrt(x
*
x
+
y
*
y)
<=
CENTER_RADIUS;
switch
(
event
.getAction()) {
case
MotionEvent.ACTION_DOWN:
mTrackingCenter
=
inCenter;
if
(inCenter) {
mHighlightCenter
=
true
;
invalidate();
break
;
}
case
MotionEvent.ACTION_MOVE:
if
(mTrackingCenter) {
if
(mHighlightCenter
!=
inCenter) {
mHighlightCenter
=
inCenter;
invalidate();
}
}
else
if
((x
>=
-
100
&
x
<=
100
)
&&
(y
<=
130
&&
y
>=
110
))
//
see
//
if
//
we're
//
in
//
the
//
hsv
//
slider
{
int
a, r, g, b, c0, c1;
float
p;
//
set the center paint to this color
if
(x
<
0
) {
c0
=
mHSVColors[
0
];
c1
=
mHSVColors[
1
];
p
=
(x
+
100
)
/
100
;
}
else
{
c0
=
mHSVColors[
1
];
c1
=
mHSVColors[
2
];
p
=
x
/
100
;
}
a
=
ave(Color.alpha(c0), Color.alpha(c1), p);
r
=
ave(Color.red(c0), Color.red(c1), p);
g
=
ave(Color.green(c0), Color.green(c1), p);
b
=
ave(Color.blue(c0), Color.blue(c1), p);
mCenterPaint.setColor(Color.argb(a, r, g, b));
mRedrawHSV
=
false
;
invalidate();
}
else
{
float
angle
=
(
float
) java.lang.Math.atan2(y, x);
//
need to turn angle [-PI ... PI] into unit [0....1]
float
unit
=
angle
/
(
2
*
PI);
if
(unit
<
0
) {
unit
+=
1
;
}
mCenterPaint.setColor(interpColor(mColors, unit));
invalidate();
}
break
;
case
MotionEvent.ACTION_UP:
if
(mTrackingCenter) {
if
(inCenter) {
mListener.colorChanged(mCenterPaint.getColor());
}
mTrackingCenter
=
false
;
//
so we draw w/o halo
invalidate();
}
break
;
}
return
true
;
}
}
public
interface
OnColorChangedListener {
void
colorChanged(
int
color);
}
public
ColorPickerPreference(Context contex) {
this
(contex,
null
);
}
public
ColorPickerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public
ColorPickerPreference(Context context, AttributeSet attrs,
int
defStyle) {
super(context, attrs, defStyle);
}
@Override
protected
void
onDialogClosed(boolean positiveResult) {
if
(positiveResult) {
mCurrentColor
=
mCPView.getColor();
summary.setTextColor(mCurrentColor);
SharedPreferences.Editor editor
=
getEditor();
editor.putInt(getKey(), mCurrentColor);
editor.commit();
callChangeListener(
new
Integer(mCurrentColor));
}
}
@Override
protected
View onCreateView(ViewGroup parent) {
//
TODO Auto-generated method stub
View view
=
LayoutInflater.from(getContext()).inflate(
R.layout.preference,
null
);
TextView title
=
(TextView) view.findViewById(R.id.title);
title.setText(getTitle());
summary
=
(TextView) view.findViewById(R.id.summary);
summary.setText(getSummary());
SharedPreferences prefs
=
getPreferenceManager().getSharedPreferences();
mInitialColor
=
prefs.getInt(getKey(), Color.LTGRAY);
summary.setTextColor(mInitialColor);
return
view;
}
@Override
protected
void
onPrepareDialogBuilder(Builder builder) {
super.onPrepareDialogBuilder(builder);
OnColorChangedListener l
=
new
OnColorChangedListener() {
public
void
colorChanged(
int
color) {
mCurrentColor
=
color;
onDialogClosed(
true
);
getDialog().dismiss();
}
};
LinearLayout layout
=
new
LinearLayout(getContext());
layout.setPadding(
20
,
20
,
20
,
20
);
layout.setOrientation(LinearLayout.VERTICAL);
mCPView
=
new
ColorPickerView(getContext(), l, mInitialColor);
LinearLayout.LayoutParams params1
=
new
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params1.gravity
=
Gravity.CENTER;
mCPView.setLayoutParams(params1);
layout.addView(
this
.mCPView);
layout.setId(android.R.id.widget_frame);
builder.setView(layout);
}
}
完。