代码改变世界

eBook 功能模块一之颜色选择器【ColorPickerPreference】自定义Preference 对话框

2010-09-18 19:22 by Terry_龙, ... 阅读, ... 评论, 收藏, 编辑

 

     在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(
20202020);
        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[] { 0xFFFF00000xFFFF00FF0xFF0000FF,
                    
0xFF00FFFF0xFF00FF000xFFFFFF000xFFFF0000 };
            Shader s 
= new SweepGradient(00, 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(-10001000,
                        mHSVColors, 
null, Shader.TileMode.CLAMP));
            }

            canvas.drawOval(
new RectF(-r, -r, r, r), mPaint);
            canvas.drawCircle(
00, CENTER_RADIUS, mCenterPaint);
            canvas.drawRect(
new RectF(-100130100110), mHSVPaint);

            
if (mTrackingCenter) {
                mCenterPaint.setStyle(Paint.Style.STROKE);

                
if (mHighlightCenter) {
                    mCenterPaint.setAlpha(
0xFF);
                } 
else {
                    mCenterPaint.setAlpha(
0x80);
                }
                canvas.drawCircle(
00, 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(
20202020);
        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);
    }
}

完。