android中图片色调识别探究

2023-10-28 03:59

本文主要是介绍android中图片色调识别探究,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

项目背景

最近项目组要做这样的一件事,通过访问网站拿到网站的favicon来根据favicon匹配它的颜色,色调然后调用我们自己的绘图板去绘制符合当前网站的图标,当时也在github上面找了下,看看有没有现成能借鉴的,但是失败了,但是无意间发现了android的v7下的palette包,貌似可以实现我想要的效果,但是项目里面不可能引入其他的依赖包,因为v7引用到了v4,一想到我的apk又要增加几十k果断的不干了,所以我就把v7的里面的源码拿出来改了改,然后v4里面的需要的也单独的剥离出来,这样即达到了我需要的效果,也没有给我的apk增加太多的大小。

效果

我们这里先看看我要实现的效果,这里写图片描述这里写图片描述我们要提取这两个icon里面的主色调,然后看看我们提取的效果,这里写图片描述
fuck,你在逗我?怎么会有红色呢?不急不急,听我慢慢道来。

实现

首先我们要做的就是移植v7这个包下面的pattern包,看看我的工程目录,需要移植的就是这几个类。这里写图片描述
我这里也不墨迹了,直接上代码吧,google的注释都是很到位的,也很nice我也不说了,我做的工作就是把这几个类整合提取出来,然后屏蔽一些兼容的问题。这里我先给出这几个类的代码。

package com.bobo.picdis.utils;/** Copyright 2014 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**       http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import android.graphics.Bitmap;
import android.graphics.Color;
import android.util.SparseIntArray;import com.bobo.picdis.utils.Palette.Swatch;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;/*** An color quantizer based on the Median-cut algorithm, but optimized for picking out distinct* colors rather than representation colors.** The color space is represented as a 3-dimensional cube with each dimension being an RGB* component. The cube is then repeatedly divided until we have reduced the color space to the* requested number of colors. An average color is then generated from each cube.** What makes this different to median-cut is that median-cut divided cubes so that all of the cubes* have roughly the same population, where this quantizer divides boxes based on their color volume.* This means that the color space is divided into distinct colors, rather than representative* colors.*/
final class ColorCutQuantizer {private static final String LOG_TAG = ColorCutQuantizer.class.getSimpleName();private final float[] mTempHsl = new float[3];private static final float BLACK_MAX_LIGHTNESS = 0.05f;private static final float WHITE_MIN_LIGHTNESS = 0.95f;private static final int COMPONENT_RED = -3;private static final int COMPONENT_GREEN = -2;private static final int COMPONENT_BLUE = -1;private final int[] mColors;private final SparseIntArray mColorPopulations;private final List<Swatch> mQuantizedColors;/*** Factory-method to generate a {@link ColorCutQuantizer} from a {@link Bitmap} object.** @param bitmap Bitmap to extract the pixel data from* @param maxColors The maximum number of colors that should be in the result palette.*/static ColorCutQuantizer fromBitmap(Bitmap bitmap, int maxColors) {final int width = bitmap.getWidth();final int height = bitmap.getHeight();final int[] pixels = new int[width * height];bitmap.getPixels(pixels, 0, width, 0, 0, width, height);return new ColorCutQuantizer(new ColorHistogram(pixels), maxColors);}/*** Private constructor.** @param colorHistogram histogram representing an image's pixel data* @param maxColors The maximum number of colors that should be in the result palette.*/private ColorCutQuantizer(ColorHistogram colorHistogram, int maxColors) {final int rawColorCount = colorHistogram.getNumberOfColors();final int[] rawColors = colorHistogram.getColors();final int[] rawColorCounts = colorHistogram.getColorCounts();// First, lets pack the populations into a SparseIntArray so that they can be easily// retrieved without knowing a color's indexmColorPopulations = new SparseIntArray(rawColorCount);for (int i = 0; i < rawColors.length; i++) {mColorPopulations.append(rawColors[i], rawColorCounts[i]);}// Now go through all of the colors and keep those which we do not want to ignoremColors = new int[rawColorCount];int validColorCount = 0;for (int color : rawColors) {if (!shouldIgnoreColor(color)) {mColors[validColorCount++] = color;}}if (validColorCount <= maxColors) {// The image has fewer colors than the maximum requested, so just return the colorsmQuantizedColors = new ArrayList<Swatch>();for (final int color : mColors) {mQuantizedColors.add(new Swatch(color, mColorPopulations.get(color)));}} else {// We need use quantization to reduce the number of colorsmQuantizedColors = quantizePixels(validColorCount - 1, maxColors);}}/*** @return the list of quantized colors*/List<Swatch> getQuantizedColors() {return mQuantizedColors;}private List<Swatch> quantizePixels(int maxColorIndex, int maxColors) {// Create the priority queue which is sorted by volume descending. This means we always// split the largest box in the queuefinal PriorityQueue<Vbox> pq = new PriorityQueue<Vbox>(maxColors, VBOX_COMPARATOR_VOLUME);// To start, offer a box which contains all of the colorspq.offer(new Vbox(0, maxColorIndex));// Now go through the boxes, splitting them until we have reached maxColors or there are no// more boxes to splitsplitBoxes(pq, maxColors);// Finally, return the average colors of the color boxesreturn generateAverageColors(pq);}/*** Iterate through the {@link java.util.Queue}, popping* {@link ColorCutQuantizer.Vbox} objects from the queue* and splitting them. Once split, the new box and the remaining box are offered back to the* queue.** @param queue {@link java.util.PriorityQueue} to poll for boxes* @param maxSize Maximum amount of boxes to split*/private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) {while (queue.size() < maxSize) {final Vbox vbox = queue.poll();if (vbox != null && vbox.canSplit()) {// First split the box, and offer the resultqueue.offer(vbox.splitBox());// Then offer the box backqueue.offer(vbox);} else {// If we get here then there are no more boxes to split, so returnreturn;}}}private List<Swatch> generateAverageColors(Collection<Vbox> vboxes) {ArrayList<Swatch> colors = new ArrayList<Swatch>(vboxes.size());for (Vbox vbox : vboxes) {Swatch color = vbox.getAverageColor();if (!shouldIgnoreColor(color)) {// As we're averaging a color box, we can still get colors which we do not want, so// we check again herecolors.add(color);}}return colors;}/*** Represents a tightly fitting box around a color space.*/private class Vbox {// lower and upper index are inclusiveprivate int mLowerIndex;private int mUpperIndex;private int mMinRed, mMaxRed;private int mMinGreen, mMaxGreen;private int mMinBlue, mMaxBlue;Vbox(int lowerIndex, int upperIndex) {mLowerIndex = lowerIndex;mUpperIndex = upperIndex;fitBox();}int getVolume() {return (mMaxRed - mMinRed + 1) * (mMaxGreen - mMinGreen + 1) *(mMaxBlue - mMinBlue + 1);}boolean canSplit() {return getColorCount() > 1;}int getColorCount() {return mUpperIndex - mLowerIndex + 1;}/*** Recomputes the boundaries of this box to tightly fit the colors within the box.*/void fitBox() {// Reset the min and max to opposite valuesmMinRed = mMinGreen = mMinBlue = 0xFF;mMaxRed = mMaxGreen = mMaxBlue = 0x0;for (int i = mLowerIndex; i <= mUpperIndex; i++) {final int color = mColors[i];final int r = Color.red(color);final int g = Color.green(color);final int b = Color.blue(color);if (r > mMaxRed) {mMaxRed = r;}if (r < mMinRed) {mMinRed = r;}if (g > mMaxGreen) {mMaxGreen = g;}if (g < mMinGreen) {mMinGreen = g;}if (b > mMaxBlue) {mMaxBlue = b;}if (b < mMinBlue) {mMinBlue = b;}}}/*** Split this color box at the mid-point along it's longest dimension** @return the new ColorBox*/Vbox splitBox() {if (!canSplit()) {throw new IllegalStateException("Can not split a box with only 1 color");}// find median along the longest dimensionfinal int splitPoint = findSplitPoint();Vbox newBox = new Vbox(splitPoint + 1, mUpperIndex);// Now change this box's upperIndex and recompute the color boundariesmUpperIndex = splitPoint;fitBox();return newBox;}/*** @return the dimension which this box is largest in*/int getLongestColorDimension() {final int redLength = mMaxRed - mMinRed;final int greenLength = mMaxGreen - mMinGreen;final int blueLength = mMaxBlue - mMinBlue;if (redLength >= greenLength && redLength >= blueLength) {return COMPONENT_RED;} else if (greenLength >= redLength && greenLength >= blueLength) {return COMPONENT_GREEN;} else {return COMPONENT_BLUE;}}/*** Finds the point within this box's lowerIndex and upperIndex index of where to split.** This is calculated by finding the longest color dimension, and then sorting the* sub-array based on that dimension value in each color. The colors are then iterated over* until a color is found with at least the midpoint of the whole box's dimension midpoint.** @return the index of the colors array to split from*/int findSplitPoint() {final int longestDimension = getLongestColorDimension();// We need to sort the colors in this box based on the longest color dimension.// As we can't use a Comparator to define the sort logic, we modify each color so that// it's most significant is the desired dimensionmodifySignificantOctet(longestDimension, mLowerIndex, mUpperIndex);// Now sort... Arrays.sort uses a exclusive toIndex so we need to add 1Arrays.sort(mColors, mLowerIndex, mUpperIndex + 1);// Now revert all of the colors so that they are packed as RGB againmodifySignificantOctet(longestDimension, mLowerIndex, mUpperIndex);final int dimensionMidPoint = midPoint(longestDimension);for (int i = mLowerIndex; i <= mUpperIndex; i++)  {final int color = mColors[i];switch (longestDimension) {case COMPONENT_RED:if (Color.red(color) >= dimensionMidPoint) {return i;}break;case COMPONENT_GREEN:if (Color.green(color) >= dimensionMidPoint) {return i;}break;case COMPONENT_BLUE:if (Color.blue(color) > dimensionMidPoint) {return i;}break;}}return mLowerIndex;}/*** @return the average color of this box.*/Swatch getAverageColor() {int redSum = 0;int greenSum = 0;int blueSum = 0;int totalPopulation = 0;for (int i = mLowerIndex; i <= mUpperIndex; i++) {final int color = mColors[i];final int colorPopulation = mColorPopulations.get(color);totalPopulation += colorPopulation;redSum += colorPopulation * Color.red(color);greenSum += colorPopulation * Color.green(color);blueSum += colorPopulation * Color.blue(color);}final int redAverage = Math.round(redSum / (float) totalPopulation);final int greenAverage = Math.round(greenSum / (float) totalPopulation);final int blueAverage = Math.round(blueSum / (float) totalPopulation);return new Swatch(redAverage, greenAverage, blueAverage, totalPopulation);}/*** @return the midpoint of this box in the given {@code dimension}*/int midPoint(int dimension) {switch (dimension) {case COMPONENT_RED:default:return (mMinRed + mMaxRed) / 2;case COMPONENT_GREEN:return (mMinGreen + mMaxGreen) / 2;case COMPONENT_BLUE:return (mMinBlue + mMaxBlue) / 2;}}}/*** Modify the significant octet in a packed color int. Allows sorting based on the value of a* single color component.** @see Vbox#findSplitPoint()*/private void modifySignificantOctet(final int dimension, int lowerIndex, int upperIndex) {switch (dimension) {case COMPONENT_RED:// Already in RGB, no need to do anythingbreak;case COMPONENT_GREEN:// We need to do a RGB to GRB swap, or vice-versafor (int i = lowerIndex; i <= upperIndex; i++) {final int color = mColors[i];mColors[i] = Color.rgb((color >> 8) & 0xFF, (color >> 16) & 0xFF, color & 0xFF);}break;case COMPONENT_BLUE:// We need to do a RGB to BGR swap, or vice-versafor (int i = lowerIndex; i <= upperIndex; i++) {final int color = mColors[i];mColors[i] = Color.rgb(color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF);}break;}}private boolean shouldIgnoreColor(int color) {ColorUtils.RGBtoHSL(Color.red(color), Color.green(color), Color.blue(color), mTempHsl);return shouldIgnoreColor(mTempHsl);}private static boolean shouldIgnoreColor(Swatch color) {return shouldIgnoreColor(color.getHsl());}private static boolean shouldIgnoreColor(float[] hslColor) {return isWhite(hslColor) || isBlack(hslColor) || isNearRedILine(hslColor);}/*** @return true if the color represents a color which is close to black.*/private static boolean isBlack(float[] hslColor) {return hslColor[2] <= BLACK_MAX_LIGHTNESS;}/*** @return true if the color represents a color which is close to white.*/private static boolean isWhite(float[] hslColor) {return hslColor[2] >= WHITE_MIN_LIGHTNESS;}/*** @return true if the color lies close to the red side of the I line.*/private static boolean isNearRedILine(float[] hslColor) {return hslColor[0] >= 10f && hslColor[0] <= 37f && hslColor[1] <= 0.82f;}/*** Comparator which sorts {@link Vbox} instances based on their volume, in descending order*/private static final Comparator<Vbox> VBOX_COMPARATOR_VOLUME = new Comparator<Vbox>() {@Overridepublic int compare(Vbox lhs, Vbox rhs) {return rhs.getVolume() - lhs.getVolume();}};}

然后是ColorHistogram这个类

package com.bobo.picdis.utils;
/** Copyright 2014 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**       http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import java.util.Arrays;/*** Class which provides a histogram for RGB values.*/
final class ColorHistogram {private final int[] mColors;private final int[] mColorCounts;private final int mNumberColors;/*** A new {@link ColorHistogram} instance.** @param pixels array of image contents*/ColorHistogram(final int[] pixels) {// Sort the pixels to enable counting belowArrays.sort(pixels);// Count number of distinct colorsmNumberColors = countDistinctColors(pixels);// Create arraysmColors = new int[mNumberColors];mColorCounts = new int[mNumberColors];// Finally count the frequency of each colorcountFrequencies(pixels);}/*** @return number of distinct colors in the image.*/int getNumberOfColors() {return mNumberColors;}/*** @return an array containing all of the distinct colors in the image.*/int[] getColors() {return mColors;}/*** @return an array containing the frequency of a distinct colors within the image.*/int[] getColorCounts() {return mColorCounts;}private static int countDistinctColors(final int[] pixels) {if (pixels.length < 2) {// If we have less than 2 pixels we can stop herereturn pixels.length;}// If we have at least 2 pixels, we have a minimum of 1 color...int colorCount = 1;int currentColor = pixels[0];// Now iterate from the second pixel to the end, counting distinct colorsfor (int i = 1; i < pixels.length; i++) {// If we encounter a new color, increase the populationif (pixels[i] != currentColor) {currentColor = pixels[i];colorCount++;}}return colorCount;}private void countFrequencies(final int[] pixels) {if (pixels.length == 0) {return;}int currentColorIndex = 0;int currentColor = pixels[0];mColors[currentColorIndex] = currentColor;mColorCounts[currentColorIndex] = 1;if (pixels.length == 1) {// If we only have one pixel, we can stop herereturn;}// Now iterate from the second pixel to the end, population distinct colorsfor (int i = 1; i < pixels.length; i++) {if (pixels[i] == currentColor) {// We've hit the same color as before, increase populationmColorCounts[currentColorIndex]++;} else {// We've hit a new color, increase indexcurrentColor = pixels[i];currentColorIndex++;mColors[currentColorIndex] = currentColor;mColorCounts[currentColorIndex] = 1;}}}}

过后是ColorUtils这个类,这个类有些事在v4里面的包里面,需要自己去修改和剔除来。

package com.bobo.picdis.utils;/** Copyright 2014 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**       http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import android.graphics.Color;final class ColorUtils {private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10;private static final int MIN_ALPHA_SEARCH_PRECISION = 10;private ColorUtils() {}/*** Composite two potentially translucent colors over each other and returns the result.*/private static int compositeColors(int fg, int bg) {final float alpha1 = Color.alpha(fg) / 255f;final float alpha2 = Color.alpha(bg) / 255f;float a = (alpha1 + alpha2) * (1f - alpha1);float r = (Color.red(fg) * alpha1) + (Color.red(bg) * alpha2 * (1f - alpha1));float g = (Color.green(fg) * alpha1) + (Color.green(bg) * alpha2 * (1f - alpha1));float b = (Color.blue(fg) * alpha1) + (Color.blue(bg) * alpha2 * (1f - alpha1));return Color.argb((int) a, (int) r, (int) g, (int) b);}/*** Returns the luminance of a color.** Formula defined here: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef*/private static double calculateLuminance(int color) {double red = Color.red(color) / 255d;red = red < 0.03928 ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4);double green = Color.green(color) / 255d;green = green < 0.03928 ? green / 12.92 : Math.pow((green + 0.055) / 1.055, 2.4);double blue = Color.blue(color) / 255d;blue = blue < 0.03928 ? blue / 12.92 : Math.pow((blue + 0.055) / 1.055, 2.4);return (0.2126 * red) + (0.7152 * green) + (0.0722 * blue);}/*** Returns the contrast ratio between two colors.** Formula defined here: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef*/private static double calculateContrast(int foreground, int background) {if (Color.alpha(background) != 255) {throw new IllegalArgumentException("background can not be translucent");}if (Color.alpha(foreground) < 255) {// If the foreground is translucent, composite the foreground over the backgroundforeground = compositeColors(foreground, background);}final double luminance1 = calculateLuminance(foreground) + 0.05;final double luminance2 = calculateLuminance(background) + 0.05;// Now return the lighter luminance divided by the darker luminancereturn Math.max(luminance1, luminance2) / Math.min(luminance1, luminance2);}/*** Finds the minimum alpha value which can be applied to {@code foreground} so that is has a* contrast value of at least {@code minContrastRatio} when compared to background.** @return the alpha value in the range 0-255.*/private static int findMinimumAlpha(int foreground, int background, double minContrastRatio) {if (Color.alpha(background) != 255) {throw new IllegalArgumentException("background can not be translucent");}// First lets check that a fully opaque foreground has sufficient contrastint testForeground = modifyAlpha(foreground, 255);double testRatio = calculateContrast(testForeground, background);if (testRatio < minContrastRatio) {// Fully opaque foreground does not have sufficient contrast, return errorreturn -1;}// Binary search to find a value with the minimum value which provides sufficient contrastint numIterations = 0;int minAlpha = 0;int maxAlpha = 255;while (numIterations <= MIN_ALPHA_SEARCH_MAX_ITERATIONS &&(maxAlpha - minAlpha) > MIN_ALPHA_SEARCH_PRECISION) {final int testAlpha = (minAlpha + maxAlpha) / 2;testForeground = modifyAlpha(foreground, testAlpha);testRatio = calculateContrast(testForeground, background);if (testRatio < minContrastRatio) {minAlpha = testAlpha;} else {maxAlpha = testAlpha;}numIterations++;}// Conservatively return the max of the range of possible alphas, which is known to pass.return maxAlpha;}static int getTextColorForBackground(int backgroundColor, float minContrastRatio) {// First we will check white as most colors will be darkfinal int whiteMinAlpha = ColorUtils.findMinimumAlpha(Color.WHITE, backgroundColor, minContrastRatio);if (whiteMinAlpha >= 0) {return ColorUtils.modifyAlpha(Color.WHITE, whiteMinAlpha);}// If we hit here then there is not an translucent white which provides enough contrast,// so check blackfinal int blackMinAlpha = ColorUtils.findMinimumAlpha(Color.BLACK, backgroundColor, minContrastRatio);if (blackMinAlpha >= 0) {return ColorUtils.modifyAlpha(Color.BLACK, blackMinAlpha);}// This should not happen!return -1;}static void RGBtoHSL(int r, int g, int b, float[] hsl) {final float rf = r / 255f;final float gf = g / 255f;final float bf = b / 255f;final float max = Math.max(rf, Math.max(gf, bf));final float min = Math.min(rf, Math.min(gf, bf));final float deltaMaxMin = max - min;float h, s;float l = (max + min) / 2f;if (max == min) {// Monochromatich = s = 0f;} else {if (max == rf) {h = ((gf - bf) / deltaMaxMin) % 6f;} else if (max == gf) {h = ((bf - rf) / deltaMaxMin) + 2f;} else {h = ((rf - gf) / deltaMaxMin) + 4f;}s =  deltaMaxMin / (1f - Math.abs(2f * l - 1f));}hsl[0] = (h * 60f) % 360f;hsl[1] = s;hsl[2] = l;}static int HSLtoRGB (float[] hsl) {final float h = hsl[0];final float s = hsl[1];final float l = hsl[2];final float c = (1f - Math.abs(2 * l - 1f)) * s;final float m = l - 0.5f * c;final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f));final int hueSegment = (int) h / 60;int r = 0, g = 0, b = 0;switch (hueSegment) {case 0:r = Math.round(255 * (c + m));g = Math.round(255 * (x + m));b = Math.round(255 * m);break;case 1:r = Math.round(255 * (x + m));g = Math.round(255 * (c + m));b = Math.round(255 * m);break;case 2:r = Math.round(255 * m);g = Math.round(255 * (c + m));b = Math.round(255 * (x + m));break;case 3:r = Math.round(255 * m);g = Math.round(255 * (x + m));b = Math.round(255 * (c + m));break;case 4:r = Math.round(255 * (x + m));g = Math.round(255 * m);b = Math.round(255 * (c + m));break;case 5:case 6:r = Math.round(255 * (c + m));g = Math.round(255 * m);b = Math.round(255 * (x + m));break;}r = Math.max(0, Math.min(255, r));g = Math.max(0, Math.min(255, g));b = Math.max(0, Math.min(255, b));return Color.rgb(r, g, b);}/*** Set the alpha component of {@code color} to be {@code alpha}.*/static int modifyAlpha(int color, int alpha) {return (color & 0x00ffffff) | (alpha << 24);}}

最后就是我们的重要部分了,对外接口的类Palette

package com.bobo.picdis.utils;/** Copyright 2014 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**       http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.AsyncTask;import java.util.Arrays;
import java.util.Collections;
import java.util.List;/*** A helper class to extract prominent colors from an image.* <p>* A number of colors with different profiles are extracted from the image:* <ul>*     <li>Vibrant</li>*     <li>Vibrant Dark</li>*     <li>Vibrant Light</li>*     <li>Muted</li>*     <li>Muted Dark</li>*     <li>Muted Light</li>* </ul>* These can be retrieved from the appropriate getter method.** <p>* Instances can be created with the synchronous factory methods {@link #generate(Bitmap)} and* {@link #generate(Bitmap, int)}.* <p>* These should be called on a background thread, ideally the one in* which you load your images on. Sometimes that is not possible, so asynchronous factory methods* have also been provided: {@link #generateAsync(Bitmap, PaletteAsyncListener)} and* {@link #generateAsync(Bitmap, int, PaletteAsyncListener)}. These can be used as so:** <pre>* Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {*     public void onGenerated(Palette palette) {*         // Do something with colors...*     }* });* </pre>*/
public final class Palette {/*** Listener to be used with {@link #generateAsync(Bitmap, PaletteAsyncListener)} or* {@link #generateAsync(Bitmap, int, PaletteAsyncListener)}*/public interface PaletteAsyncListener {/*** Called when the {@link Palette} has been generated.*/void onGenerated(Palette palette);}private static final int CALCULATE_BITMAP_MIN_DIMENSION = 100;private static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16;private static final float TARGET_DARK_LUMA = 0.26f;private static final float MAX_DARK_LUMA = 0.45f;private static final float MIN_LIGHT_LUMA = 0.55f;private static final float TARGET_LIGHT_LUMA = 0.74f;private static final float MIN_NORMAL_LUMA = 0.3f;private static final float TARGET_NORMAL_LUMA = 0.5f;private static final float MAX_NORMAL_LUMA = 0.7f;private static final float TARGET_MUTED_SATURATION = 0.3f;private static final float MAX_MUTED_SATURATION = 0.4f;private static final float TARGET_VIBRANT_SATURATION = 1f;private static final float MIN_VIBRANT_SATURATION = 0.35f;private static final float WEIGHT_SATURATION = 3f;private static final float WEIGHT_LUMA = 6f;private static final float WEIGHT_POPULATION = 1f;private static final float MIN_CONTRAST_TITLE_TEXT = 3.0f;private static final float MIN_CONTRAST_BODY_TEXT = 4.5f;private final List<Swatch> mSwatches;private final int mHighestPopulation;private Swatch mVibrantSwatch;private Swatch mMutedSwatch;private Swatch mDarkVibrantSwatch;private Swatch mDarkMutedSwatch;private Swatch mLightVibrantSwatch;private Swatch mLightMutedColor;/*** Generate a {@link Palette} from a {@link Bitmap} using the default number of colors.*/public static Palette generate(Bitmap bitmap) {return generate(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS);}/*** Generate a {@link Palette} from a {@link Bitmap} using the specified {@code numColors}.* Good values for {@code numColors} depend on the source image type.* For landscapes, a good values are in the range 12-16. For images which are largely made up* of people's faces then this value should be increased to 24-32.** @param numColors The maximum number of colors in the generated palette. Increasing this*                  number will increase the time needed to compute the values.*/public static Palette generate(Bitmap bitmap, int numColors) {checkBitmapParam(bitmap);checkNumberColorsParam(numColors);// First we'll scale down the bitmap so it's shortest dimension is 100pxfinal Bitmap scaledBitmap = scaleBitmapDown(bitmap);// Now generate a quantizer from the BitmapColorCutQuantizer quantizer = ColorCutQuantizer.fromBitmap(scaledBitmap, numColors);// If created a new bitmap, recycle itif (scaledBitmap != bitmap) {scaledBitmap.recycle();}// Now return a ColorExtractor instancereturn new Palette(quantizer.getQuantizedColors());}/*** Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)}* will be called with the created instance. The resulting {@link Palette} is the same as* what would be created by calling {@link #generate(Bitmap)}.** @param listener Listener to be invoked when the {@link Palette} has been generated.** @return the {@link android.os.AsyncTask} used to asynchronously generate the instance.*/public static AsyncTask<Bitmap, Void, Palette> generateAsync(Bitmap bitmap, PaletteAsyncListener listener) {return generateAsync(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS, listener);}/*** Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)}* will be called with the created instance. The resulting {@link Palette} is the same as what* would be created by calling {@link #generate(Bitmap, int)}.** @param listener Listener to be invoked when the {@link Palette} has been generated.** @return the {@link android.os.AsyncTask} used to asynchronously generate the instance.*/public static AsyncTask<Bitmap, Void, Palette> generateAsync(final Bitmap bitmap, final int numColors, final PaletteAsyncListener listener) {checkBitmapParam(bitmap);checkNumberColorsParam(numColors);checkAsyncListenerParam(listener);AsyncTask<Bitmap, Void, Palette> task  = new AsyncTask<Bitmap, Void, Palette>() {@Overrideprotected Palette doInBackground(Bitmap... params) {return generate(params[0], numColors);}@Overrideprotected void onPostExecute(Palette colorExtractor) {super.onPostExecute(colorExtractor);listener.onGenerated(colorExtractor);}};task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, bitmap);
//        return AsyncTaskCompat.executeParallel(
//                new AsyncTask<Bitmap, Void, Palette>() {
//                    @Override
//                    protected Palette doInBackground(Bitmap... params) {
//                        return generate(params[0], numColors);
//                    }
//
//                    @Override
//                    protected void onPostExecute(Palette colorExtractor) {
//                        listener.onGenerated(colorExtractor);
//                    }
//                }, bitmap);return task;}private Palette(List<Swatch> swatches) {mSwatches = swatches;mHighestPopulation = findMaxPopulation();mVibrantSwatch = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);mLightVibrantSwatch = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);mDarkVibrantSwatch = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);mMutedSwatch = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);mLightMutedColor = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);mDarkMutedSwatch = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);// Now try and generate any missing colorsgenerateEmptySwatches();}/*** Returns all of the swatches which make up the palette.*/public List<Swatch> getSwatches() {return Collections.unmodifiableList(mSwatches);}/*** Returns the most vibrant swatch in the palette. Might be null.*/public Swatch getVibrantSwatch() {return mVibrantSwatch;}/*** Returns a light and vibrant swatch from the palette. Might be null.*/public Swatch getLightVibrantSwatch() {return mLightVibrantSwatch;}/*** Returns a dark and vibrant swatch from the palette. Might be null.*/public Swatch getDarkVibrantSwatch() {return mDarkVibrantSwatch;}/*** Returns a muted swatch from the palette. Might be null.*/public Swatch getMutedSwatch() {return mMutedSwatch;}/*** Returns a muted and light swatch from the palette. Might be null.*/public Swatch getLightMutedSwatch() {return mLightMutedColor;}/*** Returns a muted and dark swatch from the palette. Might be null.*/public Swatch getDarkMutedSwatch() {return mDarkMutedSwatch;}/*** Returns the most vibrant color in the palette as an RGB packed int.** @param defaultColor value to return if the swatch isn't available*/public int getVibrantColor(int defaultColor) {return mVibrantSwatch != null ? mVibrantSwatch.getRgb() : defaultColor;}/*** Returns a light and vibrant color from the palette as an RGB packed int.** @param defaultColor value to return if the swatch isn't available*/public int getLightVibrantColor(int defaultColor) {return mLightVibrantSwatch != null ? mLightVibrantSwatch.getRgb() : defaultColor;}/*** Returns a dark and vibrant color from the palette as an RGB packed int.** @param defaultColor value to return if the swatch isn't available*/public int getDarkVibrantColor(int defaultColor) {return mDarkVibrantSwatch != null ? mDarkVibrantSwatch.getRgb() : defaultColor;}/*** Returns a muted color from the palette as an RGB packed int.** @param defaultColor value to return if the swatch isn't available*/public int getMutedColor(int defaultColor) {return mMutedSwatch != null ? mMutedSwatch.getRgb() : defaultColor;}/*** Returns a muted and light color from the palette as an RGB packed int.** @param defaultColor value to return if the swatch isn't available*/public int getLightMutedColor(int defaultColor) {return mLightMutedColor != null ? mLightMutedColor.getRgb() : defaultColor;}/*** Returns a muted and dark color from the palette as an RGB packed int.** @param defaultColor value to return if the swatch isn't available*/public int getDarkMutedColor(int defaultColor) {return mDarkMutedSwatch != null ? mDarkMutedSwatch.getRgb() : defaultColor;}/*** @return true if we have already selected {@code swatch}*/private boolean isAlreadySelected(Swatch swatch) {return mVibrantSwatch == swatch || mDarkVibrantSwatch == swatch ||mLightVibrantSwatch == swatch || mMutedSwatch == swatch ||mDarkMutedSwatch == swatch || mLightMutedColor == swatch;}private Swatch findColor(float targetLuma, float minLuma, float maxLuma,float targetSaturation, float minSaturation, float maxSaturation) {Swatch max = null;float maxValue = 0f;for (Swatch swatch : mSwatches) {final float sat = swatch.getHsl()[1];final float luma = swatch.getHsl()[2];if (sat >= minSaturation && sat <= maxSaturation &&luma >= minLuma && luma <= maxLuma &&!isAlreadySelected(swatch)) {float thisValue = createComparisonValue(sat, targetSaturation, luma, targetLuma,swatch.getPopulation(), mHighestPopulation);if (max == null || thisValue > maxValue) {max = swatch;maxValue = thisValue;}}}return max;}/*** Try and generate any missing swatches from the swatches we did find.*/private void generateEmptySwatches() {if (mVibrantSwatch == null) {// If we do not have a vibrant color...if (mDarkVibrantSwatch != null) {// ...but we do have a dark vibrant, generate the value by modifying the lumafinal float[] newHsl = copyHslValues(mDarkVibrantSwatch);newHsl[2] = TARGET_NORMAL_LUMA;mVibrantSwatch = new Swatch(ColorUtils.HSLtoRGB(newHsl), 0);}}if (mDarkVibrantSwatch == null) {// If we do not have a dark vibrant color...if (mVibrantSwatch != null) {// ...but we do have a vibrant, generate the value by modifying the lumafinal float[] newHsl = copyHslValues(mVibrantSwatch);newHsl[2] = TARGET_DARK_LUMA;mDarkVibrantSwatch = new Swatch(ColorUtils.HSLtoRGB(newHsl), 0);}}}/*** Find the {@link Swatch} with the highest population value and return the population.*/private int findMaxPopulation() {int population = 0;for (Swatch swatch : mSwatches) {population = Math.max(population, swatch.getPopulation());}return population;}@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}Palette palette = (Palette) o;if (mSwatches != null ? !mSwatches.equals(palette.mSwatches) : palette.mSwatches != null) {return false;}if (mDarkMutedSwatch != null ? !mDarkMutedSwatch.equals(palette.mDarkMutedSwatch): palette.mDarkMutedSwatch != null) {return false;}if (mDarkVibrantSwatch != null ? !mDarkVibrantSwatch.equals(palette.mDarkVibrantSwatch): palette.mDarkVibrantSwatch != null) {return false;}if (mLightMutedColor != null ? !mLightMutedColor.equals(palette.mLightMutedColor): palette.mLightMutedColor != null) {return false;}if (mLightVibrantSwatch != null ? !mLightVibrantSwatch.equals(palette.mLightVibrantSwatch): palette.mLightVibrantSwatch != null) {return false;}if (mMutedSwatch != null ? !mMutedSwatch.equals(palette.mMutedSwatch): palette.mMutedSwatch != null) {return false;}if (mVibrantSwatch != null ? !mVibrantSwatch.equals(palette.mVibrantSwatch): palette.mVibrantSwatch != null) {return false;}return true;}@Overridepublic int hashCode() {int result = mSwatches != null ? mSwatches.hashCode() : 0;result = 31 * result + (mVibrantSwatch != null ? mVibrantSwatch.hashCode() : 0);result = 31 * result + (mMutedSwatch != null ? mMutedSwatch.hashCode() : 0);result = 31 * result + (mDarkVibrantSwatch != null ? mDarkVibrantSwatch.hashCode() : 0);result = 31 * result + (mDarkMutedSwatch != null ? mDarkMutedSwatch.hashCode() : 0);result = 31 * result + (mLightVibrantSwatch != null ? mLightVibrantSwatch.hashCode() : 0);result = 31 * result + (mLightMutedColor != null ? mLightMutedColor.hashCode() : 0);return result;}/*** Scale the bitmap down so that it's smallest dimension is* {@value #CALCULATE_BITMAP_MIN_DIMENSION}px. If {@code bitmap} is smaller than this, than it* is returned.*/private static Bitmap scaleBitmapDown(Bitmap bitmap) {final int minDimension = Math.min(bitmap.getWidth(), bitmap.getHeight());if (minDimension <= CALCULATE_BITMAP_MIN_DIMENSION) {// If the bitmap is small enough already, just return itreturn bitmap;}final float scaleRatio = CALCULATE_BITMAP_MIN_DIMENSION / (float) minDimension;return Bitmap.createScaledBitmap(bitmap,Math.round(bitmap.getWidth() * scaleRatio),Math.round(bitmap.getHeight() * scaleRatio),false);}private static float createComparisonValue(float saturation, float targetSaturation,float luma, float targetLuma,int population, int highestPopulation) {return weightedMean(invertDiff(saturation, targetSaturation), WEIGHT_SATURATION,invertDiff(luma, targetLuma), WEIGHT_LUMA,population / (float) highestPopulation, WEIGHT_POPULATION);}/*** Copy a {@link Swatch}'s HSL values into a new float[].*/private static float[] copyHslValues(Swatch color) {final float[] newHsl = new float[3];System.arraycopy(color.getHsl(), 0, newHsl, 0, 3);return newHsl;}/*** Returns a value in the range 0-1. 1 is returned when {@code value} equals the* {@code targetValue} and then decreases as the absolute difference between {@code value} and* {@code targetValue} increases.** @param value the item's value* @param targetValue the value which we desire*/private static float invertDiff(float value, float targetValue) {return 1f - Math.abs(value - targetValue);}private static float weightedMean(float... values) {float sum = 0f;float sumWeight = 0f;for (int i = 0; i < values.length; i += 2) {float value = values[i];float weight = values[i + 1];sum += (value * weight);sumWeight += weight;}return sum / sumWeight;}private static void checkBitmapParam(Bitmap bitmap) {if (bitmap == null) {throw new IllegalArgumentException("bitmap can not be null");}if (bitmap.isRecycled()) {throw new IllegalArgumentException("bitmap can not be recycled");}}private static void checkNumberColorsParam(int numColors) {if (numColors < 1) {throw new IllegalArgumentException("numColors must be 1 of greater");}}private static void checkAsyncListenerParam(PaletteAsyncListener listener) {if (listener == null) {throw new IllegalArgumentException("listener can not be null");}}/*** Represents a color swatch generated from an image's palette. The RGB color can be retrieved* by calling {@link #getRgb()}.*/public static final class Swatch {private final int mRed, mGreen, mBlue;private final int mRgb;private final int mPopulation;private boolean mGeneratedTextColors;private int mTitleTextColor;private int mBodyTextColor;private float[] mHsl;Swatch(int rgbColor, int population) {mRed = Color.red(rgbColor);mGreen = Color.green(rgbColor);mBlue = Color.blue(rgbColor);mRgb = rgbColor;mPopulation = population;}Swatch(int red, int green, int blue, int population) {mRed = red;mGreen = green;mBlue = blue;mRgb = Color.rgb(red, green, blue);mPopulation = population;}/*** @return this swatch's RGB color value*/public int getRgb() {return mRgb;}/*** Return this swatch's HSL values.*     hsv[0] is Hue [0 .. 360)*     hsv[1] is Saturation [0...1]*     hsv[2] is Lightness [0...1]*/public float[] getHsl() {if (mHsl == null) {// Lazily generate HSL values from RGBmHsl = new float[3];ColorUtils.RGBtoHSL(mRed, mGreen, mBlue, mHsl);}return mHsl;}/*** @return the number of pixels represented by this swatch*/public int getPopulation() {return mPopulation;}/*** Returns an appropriate color to use for any 'title' text which is displayed over this* {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.*/public int getTitleTextColor() {ensureTextColorsGenerated();return mTitleTextColor;}/*** Returns an appropriate color to use for any 'body' text which is displayed over this* {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.*/public int getBodyTextColor() {ensureTextColorsGenerated();return mBodyTextColor;}private void ensureTextColorsGenerated() {if (!mGeneratedTextColors) {mTitleTextColor = ColorUtils.getTextColorForBackground(mRgb,MIN_CONTRAST_TITLE_TEXT);mBodyTextColor = ColorUtils.getTextColorForBackground(mRgb,MIN_CONTRAST_BODY_TEXT);mGeneratedTextColors = true;}}@Overridepublic String toString() {return new StringBuilder(getClass().getSimpleName()).append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']').append(" [HSL: ").append(Arrays.toString(getHsl())).append(']').append(" [Population: ").append(mPopulation).append(']').append(" [Title Text: #").append(Integer.toHexString(mTitleTextColor)).append(']').append(" [Body Text: #").append(Integer.toHexString(mBodyTextColor)).append(']').toString();}@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}Swatch swatch = (Swatch) o;return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb;}@Overridepublic int hashCode() {return 31 * mRgb + mPopulation;}}}

讲解

需要注意的我修改的一个比较重要的地方,Palette里面的generateAsync方法,它返回的这一段代码
return AsyncTaskCompat.executeParallel(
// new AsyncTask<Bitmap, Void, Palette>() {
// @Override
// protected Palette doInBackground(Bitmap... params) {
// return generate(params[0], numColors);
// }
//
// @Override
// protected void onPostExecute(Palette colorExtractor) {
// listener.onGenerated(colorExtractor);
// }
// }, bitmap);

被我修改成了

  AsyncTask<Bitmap, Void, Palette> task  = new AsyncTask<Bitmap, Void, Palette>() {@Overrideprotected Palette doInBackground(Bitmap... params) {return generate(params[0], numColors);}@Overrideprotected void onPostExecute(Palette colorExtractor) {super.onPostExecute(colorExtractor);listener.onGenerated(colorExtractor);}};task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, bitmap);

这个貌似不兼容api11以下,11以下可以直接调用task.execute方法,然后传入参数就可以了,我也是看了源码修改的,因为我的需求里面不需要兼容11的api,下面就看看使用的效果吧。上我们的Activity的代码。

ublic class MainActivity extends Activity implements Palette.PaletteAsyncListener {LinearLayout mRoot;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mRoot = new LinearLayout(this);mRoot.setOrientation(LinearLayout.VERTICAL);setContentView(mRoot);
//导入一个图片Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.examlist_naeti);Palette palette = Palette.generate(bitmap);//获取色调DarkMuted颜色 没有获取到这个模式下的颜色默认给一个red的红色
//        int darkMultedColor = palette.getDarkMutedColor(Color.RED);
//        View view = new View(this);LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 100);params.bottomMargin = 5;
//        view.setBackgroundColor(darkMultedColor);
//        mRoot.addView(view, params);
// 获取色调DarkMuted颜色
//        int darkcolorExtractor = palette.getDarkVibrantColor(Color.RED);
//        view = new View(this);
//        view.setLayoutParams(params);
//        view.setBackgroundColor(darkcolorExtractor);
//        mRoot.addView(view, params);
//
//获取色调DarkMuted颜色
//        int lightMuteColor = palette.getLightMutedColor(Color.RED);
//        view = new View(this);
//        view.setLayoutParams(params);
//        view.setBackgroundColor(lightMuteColor);
//        mRoot.addView(view, params);//获取所有的图片分析结果List<Palette.Swatch> lists = palette.getSwatches();for (Palette.Swatch swatch : lists) {int color = swatch.getRgb();View view = new View(this);view.setLayoutParams(params);view.setBackgroundColor(color);mRoot.addView(view, params);}//导入第二张图片异步去分析它Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.examlist_cmep);//需要注意的是颜色的个数必须要大于1而且lisenler也不能为空。AsyncTask<Bitmap, Void, Palette> palette1 = Palette.generateAsync(bitmap1, 3, this);}@Overridepublic void onGenerated(Palette palette) {int darkMultedColor = palette.getDarkMutedColor(Color.RED);View view = new View(this);LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 100);view.setBackgroundColor(darkMultedColor);mRoot.addView(view, params);int darkcolorExtractor = palette.getDarkVibrantColor(Color.RED);view = new View(this);view.setLayoutParams(params);view.setBackgroundColor(darkcolorExtractor);mRoot.addView(view, params);int lightMuteColor = palette.getLightMutedColor(Color.RED);view = new View(this);view.setLayoutParams(params);view.setBackgroundColor(lightMuteColor);mRoot.addView(view, params);}
}

用法跟google提供的调色板palette是一样的,其中很多色调的颜色可以根据自己的需要去提取,也可以提取所有的颜色,这样提取到了当前图片的色调颜色,就可以很容易的匹配和选择我们需要的颜色了。源码地址:传送门

这篇关于android中图片色调识别探究的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/290549

相关文章

JAVA读取MongoDB中的二进制图片并显示在页面上

1:Jsp页面: <td><img src="${ctx}/mongoImg/show"></td> 2:xml配置: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c

android 带与不带logo的二维码生成

该代码基于ZXing项目,这个网上能下载得到。 定义的控件以及属性: public static final int SCAN_CODE = 1;private ImageView iv;private EditText et;private Button qr_btn,add_logo;private Bitmap logo,bitmap,bmp; //logo图标private st

Android多线程下载见解

通过for循环开启N个线程,这是多线程,但每次循环都new一个线程肯定很耗内存的。那可以改用线程池来。 就以我个人对多线程下载的理解是开启一个线程后: 1.通过HttpUrlConnection对象获取要下载文件的总长度 2.通过RandomAccessFile流对象在本地创建一个跟远程文件长度一样大小的空文件。 3.通过文件总长度/线程个数=得到每个线程大概要下载的量(线程块大小)。

Linux系统稳定性的奥秘:探究其背后的机制与哲学

在计算机操作系统的世界里,Linux以其卓越的稳定性和可靠性著称,成为服务器、嵌入式系统乃至个人电脑用户的首选。那么,是什么造就了Linux如此之高的稳定性呢?本文将深入解析Linux系统稳定性的几个关键因素,揭示其背后的技术哲学与实践。 1. 开源协作的力量Linux是一个开源项目,意味着任何人都可以查看、修改和贡献其源代码。这种开放性吸引了全球成千上万的开发者参与到内核的维护与优化中,形成了

时间服务器中,适用于国内的 NTP 服务器地址,可用于时间同步或 Android 加速 GPS 定位

NTP 是什么?   NTP 是网络时间协议(Network Time Protocol),它用来同步网络设备【如计算机、手机】的时间的协议。 NTP 实现什么目的?   目的很简单,就是为了提供准确时间。因为我们的手表、设备等,经常会时间跑着跑着就有误差,或快或慢的少几秒,时间长了甚至误差过分钟。 NTP 服务器列表 最常见、熟知的就是 www.pool.ntp.org/zo

高仿精仿愤怒的小鸟android版游戏源码

这是一款很完美的高仿精仿愤怒的小鸟android版游戏源码,大家可以研究一下吧、 为了报复偷走鸟蛋的肥猪们,鸟儿以自己的身体为武器,仿佛炮弹一样去攻击肥猪们的堡垒。游戏是十分卡通的2D画面,看着愤怒的红色小鸟,奋不顾身的往绿色的肥猪的堡垒砸去,那种奇妙的感觉还真是令人感到很欢乐。而游戏的配乐同样充满了欢乐的感觉,轻松的节奏,欢快的风格。 源码下载