Skip to content
王集鹄 edited this page Sep 13, 2013 · 1 revision

组件代码

package com.renrousousuo.ace;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.RadialGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.view.View;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by zswang on 13-9-10.
 */
public class AceHeatmap extends View {
    private static final String LOG_TAG = "AceHeatmap";
    /**
     * 调色板
     */
    private int[] mColorPalette;
    /**
     * 内半径
     */
    private int mRadiusIn = 15;
    /**
     * 外半径
     */
    private int mRadiusOut = 40;
    /**
     * 亮度图
     */
    private Bitmap mBitmapAlpha;
    /**
     * 作色图
     */
    private Bitmap mBitmapMap;

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (mBitmapAlpha != null) {
            mBitmapAlpha.recycle();
        }
        mBitmapAlpha = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

        if (mBitmapMap != null) {
            mBitmapMap.recycle();
        }
        mBitmapMap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        drawAll();
    }

    private void drawAll() {
        mBitmapAlpha.eraseColor(Color.TRANSPARENT);
        mBitmapMap.eraseColor(Color.TRANSPARENT);
        int left = getWidth();
        int top = getHeight();
        int right = 0;
        int bottom = 0;
        for (Point point : points.values()) {
            drawAlpha(point.x, point.y);
            left = Math.min(point.x - mRadiusOut, left);
            top = Math.min(point.y - mRadiusOut, top);
            right = Math.max(point.x + mRadiusOut, right);
            bottom = Math.max(point.y + mRadiusOut, bottom);
        }
        if (left < right && top < bottom) {
            colorize(left, top, right - left, bottom - top);
        }
    }

    /**
     * 获取调色板
     * @return 返回调色板的颜色集合
     */
    public static int[] colorPalette() {
        int[] colors = new int[] {
                Color.parseColor("#0000ff"),
                Color.parseColor("#00ffff"),
                Color.parseColor("#00ff00"),
                Color.parseColor("#ffff00"),
                Color.parseColor("#ff0000")
        };

        float[] positions = new float[] {
                0.45f,
                0.55f,
                0.65f,
                0.95f,
                1.00f
        };

        return colorPalette(colors, positions);
    }

    private void drawAlpha(int x, int y) {
        if (mBitmapAlpha == null) {
            return;
        }
        String key = String.format("%s,%s", x, y);
        Point point = points.get(key);
        if (point == null) {
            return;
        }
        double rate = point.count / (mFixedMax < 0.0001f ? mMax : mFixedMax);
        Canvas canvas = new Canvas(mBitmapAlpha);

        int[] colors = new int[] {
                Color.argb((int)(rate * 256), 0, 0, 0),
                Color.argb(0, 0, 0, 0)
        };

        float[] positions = new float[] {
                0,
                1,
        };

        RadialGradient radialGradient;
        radialGradient = new RadialGradient(x, y, mRadiusIn, colors, positions, Shader.TileMode.CLAMP);
        float left = x - mRadiusOut;
        float top = y - mRadiusOut;
        float mul = 2 * mRadiusOut;
        float bottom = top + mul;
        float right = left + mul;
        Paint paint = new Paint();
        paint.setShader(radialGradient);

        canvas.drawRect(left, top, right, bottom, paint);

        radialGradient = new RadialGradient(x, y, mRadiusOut, colors, positions, Shader.TileMode.CLAMP);
        paint.setShader(radialGradient);
        canvas.drawRect(left, top, right, bottom, paint);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mBitmapMap != null) {
            canvas.drawBitmap(mBitmapMap, 0, 0, null);
        }
    }

    /**
     * 获取调色板
     * @param colors 色阶列表
     * @param positions 色阶偏移
     * @return 返回调色板的颜色集合
     */
    public static int[] colorPalette(int[] colors, float[] positions) {
        Bitmap bitmap = Bitmap.createBitmap(1, 256, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        LinearGradient linearGradient;
        linearGradient = new LinearGradient(0f, 0f, 1f, 256f, colors, positions, Shader.TileMode.CLAMP);
        Paint paint = new Paint();
        paint.setShader(linearGradient);
        canvas.drawRect(0f, 0f, 1f, 256f, paint);
        int[] result = new int[256];
        bitmap.getPixels(result, 0, 1, 0, 0, 1, 256);
        bitmap.recycle();
        return result;
    }

    public class Point {
        public double count;
        public int x;
        public int y;
        public Point(int x, int y, double count) {
            this.count = count;
            this.x = x;
            this.y = y;
        }
    }

    private Map<String, Point> points = new HashMap<String, Point>();
    private double mMax;
    private double mFixedMax = 0f;
    private void put(int x, int y, double count) {
        String key = String.format("%s,%s", x, y);
        Point point = points.get(key);
        if (point == null) {
            point = new Point(x, y, count);
            points.put(key, point);
        } else {
            point.count += count;
        }
        mMax = Math.max(mMax, point.count);
    }
    private boolean mPapercut;
    private void colorize(int x, int y, int width, int height) {
        if (mBitmapAlpha == null) {
            return;
        }
        x = Math.max(0, x);
        y = Math.max(0, y);
        int currentWidth = getWidth();
        int currentHeight = getHeight();
        width = Math.min(width, currentWidth - x);
        height = Math.min(height, currentHeight - y);
        int[] pixels = new int[width * height];
        mBitmapAlpha.getPixels(pixels, 0, width, x, y, width, height);
        int A;
        int R;
        int G;
        int B;
        for (int i = 0; i < pixels.length; i++) {
            A = (pixels[i] >> 24) & 0xff;
            if (mPapercut){
                R = G = B = 0;
                A = 255 - A;
            } else if (A > 0) {
                int offset = A;
                int color = mColorPalette[A];
                R = (color >> 16) & 0xff;
                G = (color >> 8) & 0xff;
                B = color & 0xff;
            } else {
                R = G = B = 0;
            }
            pixels[i] = Color.argb(A, R, G, B);
        }
        mBitmapMap.setPixels(pixels, 0, width, x, y, width, height);
    }

    /**
     * 添加一个热点
     * @param x x坐标
     * @param y y坐标
     * @param count 热力值,同一位置会叠加
     */
    public void add(int x, int y, double count) {
        double oldMax = mMax;
        put(x, y, count);
        if (mBitmapAlpha == null) {
            return;
        }
        if (oldMax == mMax) {
            drawAlpha(x, y);
            colorize(x - mRadiusOut, y - mRadiusOut, mRadiusOut * 2, mRadiusOut * 2);
            //postInvalidate(x - mRadiusOut, y - mRadiusOut, mRadiusOut * 2, mRadiusOut * 2);
            postInvalidate();
        } else {
            drawAll();
            postInvalidate();
        }
    }
    private void clear(boolean invalidate) {
        points.clear();
        mBitmapAlpha.eraseColor(Color.TRANSPARENT);
        mBitmapMap.eraseColor(Color.TRANSPARENT);
        if (invalidate) {
            postInvalidate();
        }
    }
    public void clear() {
        clear(true);
    }
    public void setData(Point[] data) {
        points.clear();
        for (Point point : data) {
            put(point.x, point.y, point.count);
        }
        drawAll();
        postInvalidate();
    }

    public AceHeatmap(Context context) {
        super(context);
        mColorPalette = colorPalette();
    }


}

调用示例

    private AceHeatmap mAceHeadmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAceHeadmap = new AceHeatmap(this);
        setContentView(mAceHeadmap);
        mAceHeadmap.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                switch (motionEvent.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_MOVE:
                        mAceHeadmap.add((int) motionEvent.getX(), (int) motionEvent.getY(), 1);
                        break;
                }
                return true;
            }
        });

Clone this wiki locally