11月杂记(二)——SVG解析,RecyclerView删除列表,List统计+去重,RGB与HSB互转,获取图片像素RGB与图片主颜色

一,前期基础知识储备

11月下旬已经走了一大半了,因为本月事情较多,还未来得及记录。这两天整理了一下过去一个月不完全懂的东西,分为两部分杂记,此为第二篇。

11月杂记(一)——String拼接,Json读写,Xml读写,Hashmap使用,File存储

《11月杂记(二)——SVG解析,RecyclerView删除列表,List统计+去重,RGB与HSB互转,获取图片像素RGB与图片主颜色》

二,上代码,具体实现

1.SVG解析

在博主之前的文章《有趣的自定义View —— SVG区域点击交互》中使用Document的方式解析过SVG图片,上篇文章是先将SVG图片转为Vector标签后,然后使用Document的方式解析。接下来,直接对SVG进行解析,不再进行转换。

以下是需要被解析的SVG信息:

<g id="block">
    <path
        style="fill: #e2b0b0"
        d="M678.76,477l-.71,8.66,7.91,5.62c6.91-10.35,9.05-21.76,9.88-33.55l-1.43-1.36L668,473l3.67,5.73Z" />
    <path
        style="fill: #e2b0b0"
        d="M672.24,510.17l11-16.58H675.8l-2.06,7.83-1.47.64-3.66-5.9-6.54,4.8Z" />
    <path
        style="fill: #e2b0b0"
        d="M692.49,499.56c4.9-6.06,8.84-12.49,10.24-20.26,1.59-8.83,3.23-17.66,4.74-26.51a13.34,13.34,0,0,0-.36-3.57l-1.58-.31c-2,5.19-5.18,10.25-5.77,15.6-1.68,15.18-6.88,28.29-18.05,39-4.09,3.93-3.81,9.14-.95,12.1C684.76,510.1,688.38,504.63,692.49,499.56Z" />
    <path
        style="fill: #e2b0b0"
        d="M641.79,469.59c13.07.73,25.87.75,36.83-8.5,4.08-3.44,9.35-5.48,14.07-8.17,2.46-1.4,4.9-2.84,7.35-4.26l-.92-1.81c-8.39,3.24-17.3,5.58-25.05,10-10.09,5.68-19.94,10.6-31.92,9.1-1-.12-2.1,1-3.16,1.54C639.91,468.18,640.81,469.54,641.79,469.59Z" />
</g>    

接着是解析方法:将SVG视为标准XML,然后使用XmlPullParser的方式进行解析。

private void parseSvgXml() {

        XmlPullParserFactory factory = null;
        InputStream is = null;

        try {
            is = getAssets().open("block_path.xml"); //将xml文件导入输入流
            factory = XmlPullParserFactory.newInstance(); //构造工厂实例
            factory.setNamespaceAware(true); //设置xml命名空间为true
            XmlPullParser xmlPullParser = factory.newPullParser();  //创建解析对象
            xmlPullParser.setInput(is, "UTF-8"); //设置输入流和编码方式
            int evtType = xmlPullParser.getEventType(); // 产生第一个时间
            List<Path> pathList = new ArrayList<>();
            List<String> styleList = new ArrayList<>();
            List<String> dList = new ArrayList<>();
            HashMap<Path, Boolean> pathInCenterMap = new HashMap<>();
            /*END_DOCUMENT:XmlPullParser 定义的int常量,表示文档结束;*/
            while (evtType != XmlPullParser.END_DOCUMENT) {
                switch (evtType) {
                    case XmlPullParser.START_TAG:
                        String tagName = xmlPullParser.getName(); // getName():获取标签的名字,返回字符串,例如question;
                        if (tagName.equals("path")) {
                            int tagCount = xmlPullParser.getAttributeCount();
                            if (tagCount == 2) {
                                /*g - block 添加id时 第一次读到的是style 第二次是d*/
                                String q1 = xmlPullParser.getAttributeName(0);
                                String q2 = xmlPullParser.getAttributeValue(0);
                                Log.d(TAG, "parseSvgXml: style,," + tagCount + ",,," + q1 + ",," + q2);
                                styleList.add(q2);
                                String q3 = xmlPullParser.getAttributeName(1);
                                String q4 = xmlPullParser.getAttributeValue(1);
                                dList.add(q4);
                                Log.d(TAG, "parseSvgXml: d,," + tagCount + ",," + q3 + ",," + q4);
                                @SuppressLint("RestrictedApi")
                                Path path = PathParser.createPathFromPathData(q4);
                                pathList.add(path);
                            }
                        }
                        break;
                    case XmlPullParser.END_TAG:
                        break;
                }
                evtType = xmlPullParser.next();
            }
            this.styleList = styleList;
            this.dList = dList;
            this.pathList = pathList;
            handleFalsePath(pathList);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

解析结果如下:

2.RecyclerView删除列表项

Activity内进行删除控制;

    public void removeList(int position){
            //删除数据源,移除集合中当前下标的数据
            imgList.remove(position);
    	    //刷新被删除的地方
            adapter.notifyItemRemoved(position);
            //刷新被删除数据,以及其后面的数据
            adapter.notifyItemRangeChanged(position, imgList.size()-pos);
    }

 

Adapter内进行删除控制;

   public void removeList(int position){
            imgList.remove(position);//删除数据源,移除集合中当前下标的数据
            notifyItemRemoved(position);//刷新被删除的地方
            notifyItemRangeChanged(position, getItemCount()); //刷新被删除数据,以及其后面的数据
    }

推荐文章《Android-RecyclerView实现Item添加和删除

3.List统计重复元素数量和去除重复元素

① 统计List元素种类数和各个元素的数量

    private Map<String, Integer> allColorMap; // 装载所有颜色及对应颜色的个数

    allColorMap = new HashMap<>();

    allColorMap = ColorTypeUtils.frequencyOfListElements(colorBlockList);

    /**
     * java统计List集合中每个元素出现的次数
     * 例如frequencyOfListElements(["111","111","222"])
     * 则返回Map {"111"=2,"222"=1}
     */
    public static Map<String, Integer> frequencyOfListElements(List<String> items) {
        if (items == null || items.size() == 0) return null;
        Map<String, Integer> map = new HashMap<String, Integer>();
        for (String temp : items) {
            Integer count = map.get(temp);
            map.put(temp, (count == null) ? 1 : count + 1);
        }
        return map;
    }

② 对List进行去重,去除重复的元素数;

    public static List<String> removeDuplicate(List<String> list)
    {
        Set set = new LinkedHashSet<String>();
        set.addAll(list);
        list.clear();
        list.addAll(set);
        return list;
    }

推荐文章:《Android 去除list集合中重复项的几种方法

需要注意:利用HashSet进行去重时,可能会出现元素顺序打乱的情况,博主实际项目中出现过,引起了很大的麻烦。

4.RGB与HSB互转

HSB又称HSV,表示一种颜色模式:在HSB模式中,H(hues)表示色相,S(saturation)表示饱和度,B(brightness)表示亮度HSB模式对应的媒介是人眼。

HSB模式中S和B呈现的数值越高,饱和度明度越高,页面色彩强烈艳丽,对视觉刺激是迅速的,醒目的效果,但不益于长时间的观看。

色相(H,hue):在0~360°的标准色轮上,色相是按位置度量的。在通常的使用中,色相是由颜色名称标识的,比如红、绿或橙色。黑色和白色无色相。

饱和度(S,saturation):表示色彩的纯度,为0时为灰色。白、黑和其他灰色色彩都没有饱和度的。在最大饱和度时,每一色相具有最纯的色光。取值范围0~100%。

亮度(B,brightness或V,value):是色彩的明亮度。为0时即为黑色。最大亮度是色彩最鲜明的状态。取值范围0~100%

① RGB转HSB;

public float[] rgb2hsb(int rgbR, int rgbG, int rgbB) {
        assert 0 <= rgbR && rgbR <= 255;
        assert 0 <= rgbG && rgbG <= 255;
        assert 0 <= rgbB && rgbB <= 255;
        int[] rgb = new int[]{rgbR, rgbG, rgbB};
        Arrays.sort(rgb);
        int max = rgb[2];
        int min = rgb[0];

        float hsbB = max / 255.0f;
        float hsbS = max == 0 ? 0 : (max - min) / (float) max;

        float hsbH = 0;
        if (max == rgbR && rgbG >= rgbB) {
            hsbH = (rgbG - rgbB) * 60f / (max - min) + 0;
        } else if (max == rgbR && rgbG < rgbB) {
            hsbH = (rgbG - rgbB) * 60f / (max - min) + 360;
        } else if (max == rgbG) {
            hsbH = (rgbB - rgbR) * 60f / (max - min) + 120;
        } else if (max == rgbB) {
            hsbH = (rgbR - rgbG) * 60f / (max - min) + 240;
        }

        return new float[]{hsbH, hsbS, hsbB};
    }

 

② HSB转RGB; 

public int[] hsb2rgb(float h, float s, float v) {
        assert Float.compare(h, 0.0f) >= 0 && Float.compare(h, 360.0f) <= 0;
        assert Float.compare(s, 0.0f) >= 0 && Float.compare(s, 1.0f) <= 0;
        assert Float.compare(v, 0.0f) >= 0 && Float.compare(v, 1.0f) <= 0;

        float r = 0, g = 0, b = 0;
        int i = (int) ((h / 60) % 6);
        float f = (h / 60) - i;
        float p = v * (1 - s);
        float q = v * (1 - f * s);
        float t = v * (1 - (1 - f) * s);
        switch (i) {
            case 0:
                r = v;
                g = t;
                b = p;
                break;
            case 1:
                r = q;
                g = v;
                b = p;
                break;
            case 2:
                r = p;
                g = v;
                b = t;
                break;
            case 3:
                r = p;
                g = q;
                b = v;
                break;
            case 4:
                r = t;
                g = p;
                b = v;
                break;
            case 5:
                r = v;
                g = p;
                b = q;
                break;
            default:
                break;
        }
        return new int[]{(int) (r * 255.0), (int) (g * 255.0),
                (int) (b * 255.0)};
    }

注意,RGB数组和HSB数组精确度是不一样的。

int[] rgb
float[] newHsb

Arrays.toString(newHsb) - 查看hsb数组

5.获取图片某像素点位置的色值

            // 确定像素点的位置
            int xCoor = Integer.parseInt(pointBean.getxCor());
            int yCoor = Integer.parseInt(pointBean.getyCor());

            // 将此位置对应到Bitmap上,然后获取int类型的颜色数值
            int color = bitmap.getPixel((int) (xCoor * ratio), (int) (yCoor * ratio));
            
            // 将int类型的颜色值分别转换 得到R G B值
            int r = Color.red(color);
            int g = Color.green(color);
            int b = Color.blue(color);

            // 得到十六位进制的颜色值,如#ffggkk
            int colorInfo = Color.rgb(r, g, b);
            String colorRgb = ColorUtils.int2RgbString(colorInfo); // 使用Blankj工具类直接处理

注意最后一步中,将RGB值转为十六位进制的色值时,不要使用以下方法:

            String r1 = Integer.toHexString(r); // 把R、G、B、A转为16进制
            String g1 = Integer.toHexString(g);
            String b1 = Integer.toHexString(b);
            String colorStr = r1 + g1 + b1;    // 十六进制的颜色字符串 b为单值时需要手动拼接一位数0

该方法最后一位值容易漏掉0,是需要自己判断然后手动拼接的。这里依旧是推荐Blankj的工具类进行处理。

6.获取图片的主颜色

根据图片的颜色显示不同的背景颜色,即显示图片的主色调。

① 使用谷歌官方的api中提供的方法Palette来实现;

推荐文章《Android Lollipop:使用Palette抽取图片主色调

Github上亦有提供和图片加载结合的library使用起来比较简单;

PicassoPalette

GlidePalette

② 遍历整张图片的像素点,将整个像素点的颜色值(去掉白色和纯黑色值)保存下来,选出颜色值最多的一个做为背景色;

推荐文章《Android 获取图片颜色

7. 截取View做为图片

测试了三种方法,做了一个工具类,使用时,传入需要截取的View即可,返回的是一个Bitmap。

public class SaveStatusUtils {

    /**
     * 系统API 截取View 作为Bitmap返回
     * 原View过大时崩溃
     * @param v
     * @return
     */
    public static Bitmap getViewBp(View v) {
        if (null == v) {
            return null;
        }
        v.setDrawingCacheEnabled(true);
        v.buildDrawingCache();
        v.measure(View.MeasureSpec.makeMeasureSpec(v.getWidth(),
                View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(
                v.getHeight(), View.MeasureSpec.EXACTLY));
        v.layout((int) v.getX(), (int) v.getY(),
                (int) v.getX() + v.getMeasuredWidth(),
                (int) v.getY() + v.getMeasuredHeight());
        Bitmap b = Bitmap.createBitmap(v.getDrawingCache(), 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());

        v.setDrawingCacheEnabled(false);
        v.destroyDrawingCache();
        return b;
    }

    /**
     * 自己在Canvas上画图,并得到Canvas上的Bitmap
     * 原View过大时 图片尺寸也很大 但图片内容显示正常
     * @param v
     * @return
     */
    public static Bitmap loadBitmapFromView(View v) {
        if (v == null) {
            return null;
        }
        Bitmap screenshot;
        screenshot = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_4444);
        Canvas canvas = new Canvas(screenshot);
        canvas.translate(-v.getScrollX(), -v.getScrollY());//我们在用滑动View获得它的Bitmap时候,获得的是整个View的区域(包括隐藏的),如果想得到当前区域,需要重新定位到当前可显示的区域
        v.draw(canvas);// 将 view 画到画布上
        return screenshot;
    }

    /**
     * 获取一个 View 的缓存视图
     */
    public static Bitmap getCacheBitmapFromView(View v) {
        //第一种方案   返回的bitmap不为空
        if (v.getLayoutParams().width <= 0 || v.getLayoutParams().height <= 0) {
            return null;
        }
        Bitmap b = Bitmap.createBitmap(v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_4444);
        Canvas c = new Canvas(b);
        v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
        v.draw(c);
        return b;
    }
}

注意,一共有三种实现方式,第一种是使用系统API,图片本身过大时,会发生崩溃;第二第三种是借用Canvas进行绘制的方法,此种方法不会有崩溃发生。同时,只要控制好ImageView的大小和ScalType,截取View生成的图片是表现良好的。

另外就是,这是一个耗时操作,需要放入到线程中执行。

    public void getViewBitmap(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                /*耗时操作*/
                final Bitmap bitmap1 = SaveStatusUtils.getViewBp(setImageView);
                appCompatImageView.post(new Runnable() {
                    @Override
                    public void run() {
                        appCompatImageView.setImageBitmap(bitmap1); 
                    }
                });
            }
        }).start();
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值