顏色選擇器【ColorPickerPreference】自定義Preference 對話框

在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);
    }
}
完。      
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章