Android开发学习之路-图片颜色获取器开发(1)

系列第一篇,从简单的开始,一步一步完成这个小项目。

颜色获取就是通过分析图片中的每个像素的颜色,来分析整个图片的主调颜色,有了主调颜色,我们可以用于图片所在卡片的背景或者标题颜色,这样整体感更加强烈。

有兴趣的可以学习下使用谷歌提供的Palette,也是做同样的工作,博客地址:http://www.cnblogs.com/Fndroid/p/5201236.html

先看效果图:

分析原理比较简单,就是获取图片的所有像素的颜色,然后统计,把统计的数目排序,然后返回给用户。

但是这里要先注意几个问题:

① 获取颜色的过程会不会导致UI线程卡顿;

② 怎么实现排序(如何优化后面研究);

因为考虑到当图片像素较多的时候,分析可能不会马上完成,所以分析过程应该在子线程中完成,避免阻塞UI线程。其次,统计和排序都可以通过Java提供的数据结构来简单实现,暂且不考虑性能因素,实现功能为先。

① 在Android中,图片一般会用Bitmap来表示,而Bitmap中有一个叫做getPixels的方法:

public void getPixels(@ColorInt int[] pixels, int offset, int stride, int x, int y, int width, int height)

参数分别是:

pixiels:存放识别出颜色的数组

offset:数组的起始下标

stride:每行的数据量,比如一下张200*200的图片,在stride设置为200的时候,pixels[200]为第二行的第一个像素颜色,当stride设置为400的时候,pixels[200]为额外的信息,不包含颜色,而第二行的第一个像素的颜色应该在pixels[400]

x、y:开始的x和y

width、height:需要获取颜色的宽度和高度,和x、y构成一个矩形

② 获取完颜色之后,对颜色进行统计,因为得到了一个数组,所以对数组进行统计,如果有相同的要加一,得出每个颜色出现的次数,算法简单如下

HashMap<Integer, Integer> colors = new HashMap<>();
for (int pixel : pixels) {
   Integer num = colors.get(pixel);
   if (num == null) {
       colors.put(pixel, 1);
   } else {
       num += 1;
       colors.put(pixel, num);
   }
}

③ 排序,因为需要返回一个有序的数组,这里方便的可以用TreeMap直接排,要注意TreeMap是对key排序的,而且默认是升序

TreeMap<Integer, Integer> sortedColors = new TreeMap<>();
for (Map.Entry<Integer, Integer> entry : colors.entrySet()) {
    sortedColors.put(entry.getValue(), entry.getKey());
}

④ 返回一个有序数组,这里简单的遍历一下存到ArrayList中即可

ArrayList<Integer> result = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : sortedColors.entrySet()) {
    result.add(entry.getValue());
}

⑤ 如果直接调用函数求,会可能阻塞UI,所以要用子线程来做这个工作,简单的话直接new一个Thread

⑥ 在子线程中如果任务执行完毕,通过Handler发送消息,通知UI更新

下面给出整个类:

public class ColorCaptureUtil {
    private static final String TAG = "ColorCaptureUtil";
    private Handler mHandler;
    public static final int SUCCESS = 1;

    /**
     * Construct a ColorCaptureUtil for analysing the color of the bitmap
     * @param handler When the analysing done, it would be used to send back the result and call for update
     */
    public ColorCaptureUtil(Handler handler) {
        mHandler = handler;
    }

    /**
     * Capture the bitmap‘s colors by counting every pixels, use the constructor‘s Handler to send back
     * message, the ArrayList which contains the colors and sorted by the color quantity would be send
     * back with the message in the Message.obj
     * @param bitmap The Bitmap for analysing
     */
    public void getBitmapColors(Bitmap bitmap){
        getBitmapColors(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight());
    }

    /**
     * Capture the bitmap‘s colors by counting every pixels, use the constructor‘s Handler to send back
     * message, the ArrayList which contains the colors and sorted by the color quantity would be send
     * back with the message in the Message.obj
     * @param bitmap The Bitmap for analysing
     * @param fromX  The start X in the bitmap, can not be negative
     * @param fromY  The start Y in the bitmap, can not be negative
     * @param toX    The end X in the bitmap, can not less than fromX
     * @param toY    The end Y in the bitmap, can not less than fromY
     */
    public void getBitmapColors(Bitmap bitmap, int fromX, int fromY, int toX, int
            toY) {
        new Thread(new MyRunnable(bitmap,fromX,fromY,toX,toY,mHandler)).start();
    }

    private class MyRunnable implements Runnable{
        private Bitmap bitmap;
        private int fromX,fromY,toX,toY;
        private Handler mHandler;

        public MyRunnable(Bitmap bitmap, int fromX, int fromY, int toX, int toY, Handler handler) {
            this.bitmap = bitmap;
            this.fromX = fromX;
            this.fromY = fromY;
            this.toX = toX;
            this.toY = toY;
            this.mHandler = handler;
        }

        @Override
        public void run() {
            int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
            HashMap<Integer, Integer> colors = new HashMap<>();
            TreeMap<Integer, Integer> sortedColors = new TreeMap<>();
            ArrayList<Integer> result = new ArrayList<>();
            bitmap.getPixels(pixels, 0, bitmap.getWidth(), fromX, fromY, toX - fromX, toY - fromY);
            for (int pixel : pixels) {
                Integer num = colors.get(pixel);
                if (num == null) {
                    colors.put(pixel, 1);
                } else {
                    num += 1;
                    colors.put(pixel, num);
                }
            }
            for (Map.Entry<Integer, Integer> entry : colors.entrySet()) {
                sortedColors.put(entry.getValue(), entry.getKey());
            }
            for (Map.Entry<Integer, Integer> entry : sortedColors.entrySet()) {
                result.add(entry.getValue());
                Log.d(TAG, "run: color:"+entry.getValue()+",count:"+entry.getKey());
            }
            Message msg = new Message();
            msg.obj = result;
            msg.what = SUCCESS;
            mHandler.sendMessage(msg);
        }
    }
}

可以看到,在构造的时候需要一个Handler,当我们处理完图片颜色数据的时候,就是通过这个Handler来通知UI线程,然后把统计的数据存到Message.obj中,在主线程中直接取出即可得到颜色,数据是按升序排列的,如果想得到最多的颜色,需要取出size-1下标的值

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == ColorCaptureUtil.SUCCESS) {
                ArrayList<Integer> colors = (ArrayList<Integer>) msg.obj;
                color.setBackgroundColor(colors.get(colors.size() - 1));
            }
        }
    };

第一部分的内容比较简单,正是因为比较简单,所以接下来还是有一些问题需要解决的

① 解析速度问题,考虑是否要使用其他排序算法?是否需要处理全部的像素点?能否进行局部采样?

② 当图片像效果图中的第四张(如下)一样的时候,统计出现最多的像素颜色是否合理?

③ 直接使用new Thread是否合理?当有大量图片需要处理的时候会不会出现问题?

尽快更新下篇,欢迎指正交流。

时间: 2024-10-24 15:44:03

Android开发学习之路-图片颜色获取器开发(1)的相关文章

android开发学习之路——连连看之游戏逻辑(五)

GameService组件则是整个游戏逻辑实现的核心,而且GameService是一个可以复用的业务逻辑类. (一)定义GameService组件接口 根据前面程序对GameService组件的依赖,程序需要GameService组件包含如下方法.   ·start():初始化游戏状态,开始游戏的方法.     ·Piece[][] getPieces():返回表示游戏状态的Piece[][]数组.     ·boolean hasPieces():判断Pieces[][]数组中是否还剩Piec

Android开发学习之路--网络编程之xml、json

一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的.常用的就是xml和json了.在此先要搭建个简单的服务器吧,首先呢下载xampp,然后安装之类的就不再多讲了,参考http://cnbin.github.io/blog/2015/06/05/mac-an-zhuang-he-shi-yong-xampp/.安装好后,启动xampp,之后在浏览器输入localhost或者127.0.0.1就可以看到如下所示了: 这个就

Android开发学习之路-RecyclerView滑动删除和拖动排序

Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开发学习之路-下拉刷新怎么做? 本篇是接着上面三篇之后的一个对RecyclerView的介绍,这里多说两句,如果你还在使用ListView的话,可以放弃掉ListView了.RecyclerView自动帮我们缓存Item视图(ViewHolder),允许我们自定义各种动作的动画和分割线,允许我们对It

Android的学习之路(一)开发环境的搭建

一直以来都是做IOS开发的,但是有时候需要将IOS的应用搬到Android平台上.但是自身的功底并不是很好,所以就得学习学习了,今天我就和大家一块来学习下Android的环境搭建. 1>下载并且安装Eclipse. Eclipse下载地址:http://pan.baidu.com/s/1pJtwLib 2>下载并且安装ADT插件 ADT插件下载地址:http://dl.google.com/android/ADT-22.3.0.zip 3>下载安装Android SDK Android

Android的学习之路(二)项目中原生文件的使用场景和文件介绍

1.src文件:java源代码存放目录 2.gen 文件:自动生成所有由android开发工具自动生成的文件,目录中最重要的就是R.java文件,这个文件由android开 发工具自动产生的.android开发工具会自动根据你存放res目录的资源,同步更新修稿R.java文件,正因为 R.java文件是由开发工具自动生成的,所以我们应避免手工修改R.java.R.java文件在应用中起到了字典的作 用,它包含了各种资源的ID,通过R.java,应用可以很方便的找到对应资源, 2.1R.java 

我的Android 4 学习系列之开始入手:配置开发环境与理解Hello World!

p { padding-left: 10px; } 目录 如何安装Android SDK.创建开发环境和调试项目 移动设计中一些注意事项 使用Android虚拟设备.模拟器和其他开发工具 如何安装Android SDK.创建开发环境和调试项目 下载和安装Android SDK : 我的是window7系统,当然下载 SDK starter package 最合适了: http://developer.android.com/sdk/index.html 下载完打开压缩包如下: 然后把这个包解压到

Android的学习之路(三)项目的启动过程和安装过程详解

应用的安装和启动过程: 安装:第一步:java的编译器会把这个.java文件编译成.class文件 第二部:Android的SDK提供了一个dx工具,这个工具把.class文件转义成.dex文件 第三部:打包操作,把.dex文件和资源文件进行打包,打包成一个压缩文件,然后进行签名.最后就打包成为了.apk文件 第四部:调用adb指令:adb install c:/x.apk安装到模拟器 具体过程:.JAVA---->.class--.dx-->.dex--->打包签名--->.ap

Android开发学习之路--Content Provider之初体验

天气说变就变,马上又变冷了,还好空气不错,阳光也不错,早起上班的车上的人也不多,公司来的同事和昨天一样一样的,可能明天会多一些吧,那就再来学习android吧.学了两个android的组件,这里学习下第三个android的组件,Content Provider内容提供器. Content Provider向我们提供了在不同应用程序之间的数据共享,比如微信啊,支付宝啊,想要获取手机联系人的信息,而手机联系人是另一个应用程序,那么这时候就需要用到Content Provider了.Content P

SSH学习之路(一).Net开发与Java Web开发

摘要:一直再使用.net mvc5开发,期间学习过java web相关的知识,现如今想要进阶一下便开始学习ssh框架. 1..net web开发 对于微软的东西,开发者考虑的东西不需要太多,你新建一个项目几乎可以就可以跑起来,因为微软大大已经简化了所有较为复杂的配置处理,你基本上做很少的配置就可以开发一个项目,当然这个是针对于保证能跑起来就行.而对于高级web开发中,.net 开发也是属于较为有难度的,虽然入门容易但是深入真的需要花费很多时间,因为简单的入门导致很多开发者已经适应了ASP.NET