多屏下字体自动取色
<p>需求:多屏场景下,设置同一系列屏保,屏保中间组件字体颜色需要动态读取背后壁纸主色亮度,根据背后亮度动态设置字体颜色</p><p>偏亮的=黑色,偏暗的=白色</p>
<p>1、取色</p>
<p>读取亮度需要先对bitmap解码,通过 Color.colorToHSV 方法读取亮度值</p>
<div class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_23b084a1-04af-4c51-9afb-e5b482f6ad80" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_23b084a1-04af-4c51-9afb-e5b482f6ad80" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun generate(newMap: Bitmap): FloatArray {
val hsvColorArray </span>= FloatArray(3<span style="color: rgba(0, 0, 0, 1)">)
Palette.from(newMap).clearFilters().generate().apply {
val dominantColor </span>=<span style="color: rgba(0, 0, 0, 1)"> getDominantColor(Color.BLACK)
Color.colorToHSV(dominantColor, hsvColorArray)
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> hsvColorArray
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>需要注意的是,如果是黑白纯色的bitmap,Palette中的mSwatches是空的</p>
<div class="cnblogs_code"><img id="code_img_closed_50693cf8-6d11-47ab-b554-e66cec3d57e4" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_50693cf8-6d11-47ab-b554-e66cec3d57e4" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_50693cf8-6d11-47ab-b554-e66cec3d57e4" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Palette generate() {
TimingLogger logger </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
List</span><Swatch><span style="color: rgba(0, 0, 0, 1)"> swatches;
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.mBitmap != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
Bitmap bitmap </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.scaleBitmapDown(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mBitmap);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (logger != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
logger.addSplit(</span>"Processed Bitmap"<span style="color: rgba(0, 0, 0, 1)">);
}
Rect region </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mRegion;
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (bitmap != <span style="color: rgba(0, 0, 255, 1)">this</span>.mBitmap && region != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">double</span> scale = (<span style="color: rgba(0, 0, 255, 1)">double</span>)bitmap.getWidth() / (<span style="color: rgba(0, 0, 255, 1)">double</span>)<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mBitmap.getWidth();
region.left </span>= (<span style="color: rgba(0, 0, 255, 1)">int</span>)Math.floor((<span style="color: rgba(0, 0, 255, 1)">double</span>)region.left *<span style="color: rgba(0, 0, 0, 1)"> scale);
region.top </span>= (<span style="color: rgba(0, 0, 255, 1)">int</span>)Math.floor((<span style="color: rgba(0, 0, 255, 1)">double</span>)region.top *<span style="color: rgba(0, 0, 0, 1)"> scale);
region.right </span>= Math.min((<span style="color: rgba(0, 0, 255, 1)">int</span>)Math.ceil((<span style="color: rgba(0, 0, 255, 1)">double</span>)region.right *<span style="color: rgba(0, 0, 0, 1)"> scale), bitmap.getWidth());
region.bottom </span>= Math.min((<span style="color: rgba(0, 0, 255, 1)">int</span>)Math.ceil((<span style="color: rgba(0, 0, 255, 1)">double</span>)region.bottom *<span style="color: rgba(0, 0, 0, 1)"> scale), bitmap.getHeight());
}
ColorCutQuantizer quantizer </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> ColorCutQuantizer(<span style="color: rgba(0, 0, 255, 1)">this</span>.getPixelsFromBitmap(bitmap), <span style="color: rgba(0, 0, 255, 1)">this</span>.mMaxColors, <span style="color: rgba(0, 0, 255, 1)">this</span>.mFilters.isEmpty() ? <span style="color: rgba(0, 0, 255, 1)">null</span> : (Filter[])<span style="color: rgba(0, 0, 255, 1)">this</span>.mFilters.toArray(<span style="color: rgba(0, 0, 255, 1)">new</span> Filter[<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mFilters.size()]));
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (bitmap != <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mBitmap) {
bitmap.recycle();
}
swatches </span>=<span style="color: rgba(0, 0, 0, 1)"> quantizer.getQuantizedColors();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (logger != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
logger.addSplit(</span>"Color quantization completed"<span style="color: rgba(0, 0, 0, 1)">);
}
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.mSwatches == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> AssertionError();
}
swatches </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mSwatches;
}
Palette p </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> Palette(swatches, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mTargets);
p.generate();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (logger != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
logger.addSplit(</span>"Created Palette"<span style="color: rgba(0, 0, 0, 1)">);
logger.dumpToLog();
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> p;
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>源码中的swatches是通过ColorCutQuantizer类的getQuantizedColors方法获取的,可以看到swatches = quantizer.getQuantizedColors();</p>
<div class="cnblogs_code"><img id="code_img_closed_653bc432-8b62-4ff2-8c68-b521480dc201" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_653bc432-8b62-4ff2-8c68-b521480dc201" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_653bc432-8b62-4ff2-8c68-b521480dc201" class="cnblogs_code_hide">
<pre> <span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* Constructor.
*
* </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> pixels histogram representing an image's pixel data
* </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> maxColors The maximum number of colors that should be in the result palette.
* </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> filters Set of filters to use in the quantization stage
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@SuppressWarnings(</span>"NullAway") <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> mTimingLogger initialization and access guarded by LOG_TIMINGS.</span>
ColorCutQuantizer(<span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">int</span>[] pixels, <span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">int</span> maxColors, <span style="color: rgba(0, 0, 255, 1)">final</span><span style="color: rgba(0, 0, 0, 1)"> Palette.Filter[] filters) {
mTimingLogger </span>= LOG_TIMINGS ? <span style="color: rgba(0, 0, 255, 1)">new</span> TimingLogger(LOG_TAG, "Creation") : <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
mFilters </span>=<span style="color: rgba(0, 0, 0, 1)"> filters;
</span><span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">int</span>[] hist = mHistogram = <span style="color: rgba(0, 0, 255, 1)">new</span> <span style="color: rgba(0, 0, 255, 1)">int</span>;
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i < pixels.length; i++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">int</span> quantizedColor =<span style="color: rgba(0, 0, 0, 1)"> quantizeFromRgb888(pixels);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Now update the pixel value to the quantized value</span>
pixels =<span style="color: rgba(0, 0, 0, 1)"> quantizedColor;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> And update the histogram</span>
hist++<span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (LOG_TIMINGS) {
mTimingLogger.addSplit(</span>"Histogram created"<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Now let's count the number of distinct colors</span>
<span style="color: rgba(0, 0, 255, 1)">int</span> distinctColorCount = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> color = 0; color < hist.length; color++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (hist > 0 &&<span style="color: rgba(0, 0, 0, 1)"> shouldIgnoreColor(color)) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> If we should ignore the color, set the population to 0</span>
hist = 0<span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (hist > 0<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> If the color has population, increase the distinct color count</span>
distinctColorCount++<span style="color: rgba(0, 0, 0, 1)">;
}
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (LOG_TIMINGS) {
mTimingLogger.addSplit(</span>"Filtered colors and distinct colors counted"<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Now lets go through create an array consisting of only distinct colors</span>
<span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">int</span>[] colors = mColors = <span style="color: rgba(0, 0, 255, 1)">new</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">int</span> distinctColorIndex = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> color = 0; color < hist.length; color++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (hist > 0<span style="color: rgba(0, 0, 0, 1)">) {
colors =<span style="color: rgba(0, 0, 0, 1)"> color;
}
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (LOG_TIMINGS) {
mTimingLogger.addSplit(</span>"Distinct colors copied into array"<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (distinctColorCount <=<span style="color: rgba(0, 0, 0, 1)"> maxColors) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The image has fewer colors than the maximum requested, so just return the colors</span>
mQuantizedColors = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList<><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> color : colors) {
mQuantizedColors.add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Palette.Swatch(approximateToRgb888(color), hist));
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (LOG_TIMINGS) {
mTimingLogger.addSplit(</span>"Too few colors present. Copied to Swatches"<span style="color: rgba(0, 0, 0, 1)">);
mTimingLogger.dumpToLog();
}
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> We need use quantization to reduce the number of colors</span>
mQuantizedColors =<span style="color: rgba(0, 0, 0, 1)"> quantizePixels(maxColors);
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (LOG_TIMINGS) {
mTimingLogger.addSplit(</span>"Quantized colors computed"<span style="color: rgba(0, 0, 0, 1)">);
mTimingLogger.dumpToLog();
}
}
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>上面方法在mQuantizedColors添加swatche时存在过滤操作 shouldIgnoreColor</p>
<div class="cnblogs_code"><img id="code_img_closed_e3354a9c-218e-42dc-895c-625a986008b7" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_e3354a9c-218e-42dc-895c-625a986008b7" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_e3354a9c-218e-42dc-895c-625a986008b7" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span> shouldIgnoreColor(<span style="color: rgba(0, 0, 255, 1)">int</span> rgb, <span style="color: rgba(0, 0, 255, 1)">float</span><span style="color: rgba(0, 0, 0, 1)">[] hsl) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (mFilters != <span style="color: rgba(0, 0, 255, 1)">null</span> && mFilters.length > 0<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0, count = mFilters.length; i < count; i++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">mFilters.isAllowed(rgb, hsl)) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}
}
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>而最终调用的是Palette中的DEFAULT_FILTER,而这个filter在Palette.from方法中就被添加到mFilters</p>
<div class="cnblogs_code"><img id="code_img_closed_6dfb2457-242d-4451-8341-c99a0b06aca2" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_6dfb2457-242d-4451-8341-c99a0b06aca2" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_6dfb2457-242d-4451-8341-c99a0b06aca2" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Builder(@NonNull Bitmap bitmap) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (bitmap != <span style="color: rgba(0, 0, 255, 1)">null</span> && !<span style="color: rgba(0, 0, 0, 1)">bitmap.isRecycled()) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mFilters.add(Palette.DEFAULT_FILTER);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.mBitmap =<span style="color: rgba(0, 0, 0, 1)"> bitmap;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.mSwatches = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mTargets.add(Target.LIGHT_VIBRANT);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mTargets.add(Target.VIBRANT);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mTargets.add(Target.DARK_VIBRANT);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mTargets.add(Target.LIGHT_MUTED);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mTargets.add(Target.MUTED);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mTargets.add(Target.DARK_MUTED);
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> IllegalArgumentException("Bitmap is not valid"<span style="color: rgba(0, 0, 0, 1)">);
}
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>不过类中有提供mFilters.clear()操作,需要在build时手动调用一下clearFilters方法</p>
<p>如果没调用这个方法,黑白纯色会导致getDominantColor主色返回0,导致取色不准</p>
<p> </p>
<p>随后直接根据亮度值返回对应颜色</p>
<div class="cnblogs_code"><img id="code_img_closed_f516595a-db21-4ff3-89ba-9203291de5dd" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_f516595a-db21-4ff3-89ba-9203291de5dd" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_f516595a-db21-4ff3-89ba-9203291de5dd" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> hsvColorArray</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> fun getColor(num: Float = 0f) = <span style="color: rgba(0, 0, 255, 1)">if</span> (num >= 0.7<span style="color: rgba(0, 0, 0, 1)">) {
CommonUtils.COLOR_BLACK
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
CommonUtils.COLOR_WHITE
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>2、裁剪</p>
<p>取色的bitmap是字体背后对应的区域,而不是整个壁纸,所以需要进行计算裁剪</p>
<div class="cnblogs_code"><img id="code_img_closed_1a3d1184-2578-4e3b-90ba-d39b4c3b60d0" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_1a3d1184-2578-4e3b-90ba-d39b4c3b60d0" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_1a3d1184-2578-4e3b-90ba-d39b4c3b60d0" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createRectBitmap(bitmap: Bitmap, isDim: Boolean): Bitmap {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (isDim) {
Bitmap.createBitmap(
bitmap,
dimWidth </span>/ 2 - rectWidth / 2, dimHeight / 2 - rectHeight / 2<span style="color: rgba(0, 0, 0, 1)">,
rectWidth, rectHeight
)
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
Bitmap.createBitmap(
bitmap, mWidth </span>/ 2 - rectWidth / 2<span style="color: rgba(0, 0, 0, 1)">, rectTopMargin, rectWidth, rectHeight
)
}
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>不同屏幕尺寸不一样,布局不一样,所以对应要裁剪的区域也不一样,这里需要统一适配</p>
<p>bitmap建议使用 Glide 加载,效果更高</p>
<p>对bitmap进行裁剪取色都属于耗时操作,需要放在子线程处理,缓存使用map存放,key使用屏幕的displayId</p>
<p>3、预加载</p>
<p>每个系列好几张图片,每个DHU可能有多个屏幕(比如CSD屏存在主副驾屏),加上DIM屏属于QNX系统,不太方便实现该功能,所以也需要CSD去实现同步</p>
<p>基于上面要求,缓存量很大,所以需要在初始化时使用多线程处理</p>
<div class="cnblogs_code"><img id="code_img_closed_83098f62-a82b-4be4-a157-9838f68ec74f" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_83098f62-a82b-4be4-a157-9838f68ec74f" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_83098f62-a82b-4be4-a157-9838f68ec74f" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 0, 1)">object ColorPickManager {
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val TAG = "ColorPickManager"
<span style="color: rgba(0, 0, 255, 1)">private</span> val suitMap = hashMapOf<Int, MutableList<String>><span style="color: rgba(0, 0, 0, 1)">()
</span><span style="color: rgba(0, 0, 255, 1)">private</span> val suitIdObserver = Observer<Int><span style="color: rgba(0, 0, 0, 1)"> {
loadSuitData(it)
}
@JvmStatic
fun init() {
Log.d(TAG, </span>"$TAG init ${VehicleManager.getVehicleType()}"<span style="color: rgba(0, 0, 0, 1)">)
initSuitData()
LocalResourceManager.observeUsageSuitId(suitIdObserver)
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun initSuitData() {
val list </span>= mutableListOf<DisplayParameter><span style="color: rgba(0, 0, 0, 1)">()
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (VehicleManager.isA()) {
list.add(DisplayParameter.DISPLAY_PSD)
list.add(DisplayParameter.DISPLAY_CSD)
list.add(DisplayParameter.DISPLAY_TV)
list.add(DisplayParameter.DISPLAY_DIM)
} </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (VehicleManager.isB()) {
list.add(DisplayParameter.DISPLAY_CONSOLE)
list.add(DisplayParameter.DISPLAY_CSD)
list.add(DisplayParameter.DISPLAY_TV)
list.add(DisplayParameter.DISPLAY_DIM)
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
Log.w(TAG, </span>"initSuitData 不支持的车型 ${VehicleManager.getVehicleType()}"<span style="color: rgba(0, 0, 0, 1)">)
}
list.forEach {
Log.d(TAG, </span>"initSuitData ${it.displayName} suitMap[${it.displayId}]"<span style="color: rgba(0, 0, 0, 1)">)
suitMap </span>=<span style="color: rgba(0, 0, 0, 1)"> mutableListOf()
}
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> 当前屏保套系 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun loadSuitData(suitId: Int) {
Log.d(TAG, </span>"loadSuitData suitId=$suitId"<span style="color: rgba(0, 0, 0, 1)">)
MainScope().launch(Dispatchers.IO) {
LocalResourceManager.getSuitsWithPicturesById(suitId)</span>?<span style="color: rgba(0, 0, 0, 1)">.apply {
Log.d(TAG, </span>"loadSuitData suitMapSize=${suitMap.size}"<span style="color: rgba(0, 0, 0, 1)">)
suitMap.keys.forEach { addCacheData(it, </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">) }
}
}
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun addCacheData(displayId: Int, suitsWithPictures: SuitWithPictures) {
val suit </span>=<span style="color: rgba(0, 0, 0, 1)"> suitsWithPictures.suit
val pictures </span>=<span style="color: rgba(0, 0, 0, 1)"> suitsWithPictures.pictures
suitMap</span>?<span style="color: rgba(0, 0, 0, 1)">.clear()
val pathList </span>= mutableListOf<String><span style="color: rgba(0, 0, 0, 1)">()
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (ResourceLoadManager.isDynamic(suit.type)) {
pathList.add(suit.path)
} </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (ResourceLoadManager.TYPE_CUSTOM !=<span style="color: rgba(0, 0, 0, 1)"> suit.type) {
pictures.forEach { pathList.add(convertPath(displayId, it.path)) }
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
pictures.forEach {
val path </span>=<span style="color: rgba(0, 0, 0, 1)"> when (displayId) {
DisplayParameter.DISPLAY_TV.displayId </span>-><span style="color: rgba(0, 0, 0, 1)"> {
ResourceLoadManager.convertPath2Tv(it.path)
}
DisplayParameter.DISPLAY_PSD.displayId </span>-><span style="color: rgba(0, 0, 0, 1)"> {
ResourceLoadManager.convertPath2Psd(it.path)
}
</span><span style="color: rgba(0, 0, 255, 1)">else</span> -><span style="color: rgba(0, 0, 0, 1)"> ResourceLoadManager.convertPath2Csd(it.path)
}
pathList.add(convertPath(displayId, path))
}
}
pathList.forEach {
Log.d(TAG, </span>"addCacheData suitMap[$displayId] add $it"<span style="color: rgba(0, 0, 0, 1)">)
}
suitMap</span>?<span style="color: rgba(0, 0, 0, 1)">.addAll(pathList)
loadScreensaversFontColorData(displayId)
loadAmbientLightData(displayId)
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun convertPath(displayId: Int, path: String): String {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (displayId ==<span style="color: rgba(0, 0, 0, 1)"> DisplayParameter.DISPLAY_DIM.displayId) {
ResourceLoadManager.convertPath2Dim(path)
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> path
}
@JvmStatic
fun syncScreensaversColor(displayId: Int, index: Int) {
AmbientLightColorPickManager.setAmbientLight(displayId, index)
FontColorPickManager.syncScreensaversFontColor(displayId, index)
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun loadScreensaversFontColorData(displayId: Int) {
val list </span>=<span style="color: rgba(0, 0, 0, 1)"> suitMap
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (list.isNullOrEmpty()) {
Log.w(TAG, </span>"loadScreensaversFontColorData suitMap[$displayId] value is null"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}
FontColorPickManager.loadScreensaversFontColorData(displayId, list)
}
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>针对不同车型缓存对应屏幕,监听屏保变化,如果中途切换需要动态更新</p>
<p>4、同步</p>
<p>缓存处理好后,屏保通过计时器轮播,每次轮播从缓存中读取对应的颜色值同步到字体颜色,实现字体动态跟随屏保壁纸切换</p>
<p>5、多屏适配</p>
<p>不同屏幕尺寸不一样,动态获取背后矩形区域不利于提前缓存处理,因为需要布局加载完后才能拿到坐标宽高,而等进入屏保后在获取会导致字体默认显示颜色跟实际取色不一致,进入屏保导致字体颜色切换过程,如果提前缓存便没有这个过程</p>
<p>实现方案:拿到bitmap后拉伸到基准宽高(设计稿给的宽高),然后参考设计稿裁剪矩形区域,这样无论什么尺寸的屏幕,裁剪的区域都一样,颜色也一样,同步屏保时就可以保证没有偏差</p>
<p>优化:图片过大导致内存占比耗时过高等,将所有基准降低三倍处理(不能太低,否则主色会有偏差,导致颜色不对</p>
<p>6、多屏预览</p>
<p>预览页要求可以预览所有屏幕的屏保,也包含组件字体颜色的预览,并且主副驾屏幕可以同时进入详情(同一个DHU)</p>
<div class="cnblogs_code"><img id="code_img_closed_770b4782-1b0e-4327-8ba6-aaba9821fddb" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_770b4782-1b0e-4327-8ba6-aaba9821fddb" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_770b4782-1b0e-4327-8ba6-aaba9821fddb" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.graphics.Bitmap
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.graphics.Color
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.util.Log
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> androidx.core.graphics.scale
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> androidx.palette.graphics.Palette
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> com.blankj.utilcode.util.GsonUtils
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> com.bumptech.glide.request.target.Target
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> com.jeremyliao.liveeventbus.LiveEventBus
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.util.concurrent.ConcurrentHashMap
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.util.concurrent.CopyOnWriteArrayList
object FontColorPickManager {
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val TAG = "FontColorPickManager"
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val PREVIEW_BASE_DISPLAY_ID = 1000
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val SCALE_NUM = 3
<span style="color: rgba(0, 0, 255, 1)">private</span> val maps = ConcurrentHashMap<String, CopyOnWriteArrayList<FontColorBean>><span style="color: rgba(0, 0, 0, 1)">()
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 3200 * 2000 得机型
* 以下都以前排屏为基准,无论其它什么显示屏,都缩放至统一大小,进行主色掉取值,保证前后排,其它显示器上色调完全一致.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val WIDTH = 3200 /<span style="color: rgba(0, 0, 0, 1)"> SCALE_NUM
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val HEIGHT = 2000 /<span style="color: rgba(0, 0, 0, 1)"> SCALE_NUM
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val DIM_WIDTH = 2560 /<span style="color: rgba(0, 0, 0, 1)"> SCALE_NUM
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val DIM_HEIGHT = 538 /<span style="color: rgba(0, 0, 0, 1)"> SCALE_NUM
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val RECT_WIDTH = 500 /<span style="color: rgba(0, 0, 0, 1)"> SCALE_NUM
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val RECT_HEIGHT = 280 /<span style="color: rgba(0, 0, 0, 1)"> SCALE_NUM
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val RECT_TOP_MARGIN = 388 /<span style="color: rgba(0, 0, 0, 1)"> SCALE_NUM
@JvmStatic
fun syncScreensaversFontColor(displayId: Int, index: Int) {
val key </span>= "$displayId"<span style="color: rgba(0, 0, 0, 1)">
syncFontColor(key, index, CommonUtils.SCREEN_SYNC_FONT_COLOR)
}
fun syncDimFontColor(index: Int) {
val dimKey </span>=<span style="color: rgba(0, 0, 0, 1)"> DisplayParameter.DISPLAY_DIM.displayId.toString()
val list </span>=<span style="color: rgba(0, 0, 0, 1)"> maps
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (list.isNullOrEmpty()) {
Log.w(TAG, </span>"key=$dimKey | syncDimFontColor list is null"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (index < 0 || index >=<span style="color: rgba(0, 0, 0, 1)"> list.size) {
Log.w(TAG, </span>"key=$dimKey | syncDimFontColor 索引异常"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}
val color </span>=<span style="color: rgba(0, 0, 0, 1)"> list.color
MultiScreenSyncManager.setDimFontColor(color)
}
@JvmStatic
fun syncPreviewFontColor(displayId: Int, index: Int) {
ScreenPreviewType.values().forEach {
val key </span>=<span style="color: rgba(0, 0, 0, 1)"> getPreviewKey(displayId, it)
syncFontColor(key, index, getPreviewObserverId(key))
}
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 每一个套系id 对应一组图片,每一组图片就是一套切换图,动态屏保一个视频, 主副驾多了一个displayId
* 每个显示屏 对应一套屏保,通过切换的索引去定位当前播放到哪一张了
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun syncFontColor(displayId: String, index: Int, key: String) {
val list </span>=<span style="color: rgba(0, 0, 0, 1)"> maps
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (list.isNullOrEmpty()) {
Log.w(TAG, </span>"key=$displayId | syncFontColor list is null"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (index < 0 || index >=<span style="color: rgba(0, 0, 0, 1)"> list.size) {
Log.w(TAG, </span>"key=$displayId | syncFontColor 索引异常"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}
val color </span>=<span style="color: rgba(0, 0, 0, 1)"> list.color
log(displayId, </span>"syncFontColor post key=$key,color=$color index = $index"<span style="color: rgba(0, 0, 0, 1)">)
LiveEventBus.get</span><String><span style="color: rgba(0, 0, 0, 1)">(key).post(color)
}
@JvmStatic
fun loadScreensaversFontColorData(displayId: Int, pictures: List</span><String><span style="color: rgba(0, 0, 0, 1)">) {
val key </span>= "$displayId"<span style="color: rgba(0, 0, 0, 1)">
loadData(key, pictures) {
syncScreensaversFontColor(displayId, MultiScreenSyncManager.getIndex())
syncDimFontColor(MultiScreenSyncManager.getIndex())
}
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 不同于屏保,这个displayId 只作为变量,配合屏幕类型, 小窗口预览类型(screen)
* 比如List<String> dimpaths , csdpaths7张原图 -> 对应7张dim 图,7张csd图 有不同的屏幕窗口预览图.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@JvmStatic
fun loadPreviewFontColorData(displayId: Int, screen: ScreenPreviewType, pictures: List</span><Any><span style="color: rgba(0, 0, 0, 1)">) {
val key </span>=<span style="color: rgba(0, 0, 0, 1)"> getPreviewKey(displayId, screen)
loadData(key, pictures) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">最终更新得不一定是0号位.
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> syncFontColor(key, 0, getPreviewObserverId(key))</span>
<span style="color: rgba(0, 0, 0, 1)"> }
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span> fun loadData(key: String, pictures: List<Any>, callBack: () -><span style="color: rgba(0, 0, 0, 1)"> Unit) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (pictures.isEmpty()) {
Log.w(TAG, </span>"key=$key | pictures is empty"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (maps.containsKey(key)) {
maps</span>?<span style="color: rgba(0, 0, 0, 1)">.clear()
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
maps </span>= CopyOnWriteArrayList<FontColorBean><span style="color: rgba(0, 0, 0, 1)">()
}
val list </span>= mutableListOf<FontColorBean><span style="color: rgba(0, 0, 0, 1)">()
val isDim </span>=<span style="color: rgba(0, 0, 0, 1)">
key.contains(ScreenPreviewType.DIM.key) </span>|| key ==<span style="color: rgba(0, 0, 0, 1)"> DisplayParameter.DISPLAY_DIM.displayId.toString()
</span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (picture in pictures) {
val color </span>=<span style="color: rgba(0, 0, 0, 1)"> createFontColor(isDim, key, picture)
list.add(color)
}
maps</span>?<span style="color: rgba(0, 0, 0, 1)">.addAll(list)
callBack.invoke()
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createFontColor(
isDim: Boolean,
key: String,
obj: Any,
positionInfo: PositionInfo</span>? = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">
): FontColorBean {
var bean </span>=<span style="color: rgba(0, 0, 0, 1)"> FontColorBean(getColor())
runCatching {
val bitmap </span>=<span style="color: rgba(0, 0, 0, 1)"> createSourceBitmap(obj, isDim, positionInfo)
val rectMap </span>=<span style="color: rgba(0, 0, 0, 1)"> createRectBitmap(bitmap, isDim)
val hsvColorArray </span>=<span style="color: rgba(0, 0, 0, 1)"> generate(rectMap)
val result </span>= getColor(hsvColorArray)
log(key, </span>"createFontColor Brightness=${hsvColorArray},result=$resultpath = $obj"<span style="color: rgba(0, 0, 0, 1)">)
bean </span>=<span style="color: rgba(0, 0, 0, 1)"> FontColorBean(result)
}.getOrElse {
Log.e(TAG, </span>"key=$key | createFontColor ${GsonUtils.toJson(obj)}\n$it"<span style="color: rgba(0, 0, 0, 1)">)
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> bean
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* test creteFontColor
* 请子线程中调用,否则会异常
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> fun testCreateFontColor(
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> isDim: Boolean,
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> obj: Any,
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> positionInfo: PositionInfo? = null
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ): FontColorBean {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> var bean = FontColorBean(getColor())
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> runCatching {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> val bitmap = createSourceBitmap(obj, isDim, positionInfo)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> val rectMap = createRectBitmap(bitmap, isDim)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> val hsvColorArray = generate(rectMap)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> val result = getColor(hsvColorArray)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> log("test_createFontColor", "createFontColor Brightness=${hsvColorArray},result=$result path = $obj")
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> bean = FontColorBean(result)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> }.getOrElse {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ex -> Log.e(TAG, "test_createFontColor 异常了 = $ex")
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> }
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> return bean
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> }</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> fun getColor(num: Float = 0f) = <span style="color: rgba(0, 0, 255, 1)">if</span> (num >= 0.7<span style="color: rgba(0, 0, 0, 1)">) {
CommonUtils.COLOR_BLACK
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
CommonUtils.COLOR_WHITE
}
@JvmStatic
fun updateFontColor(
displayId: Int,
screen: ScreenPreviewType,
obj: Any,
index: Int,
positionInfo: PositionInfo</span>? = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">
) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (obj is PictureData) {
val key </span>=<span style="color: rgba(0, 0, 0, 1)"> getPreviewKey(displayId, screen)
val isDim </span>=<span style="color: rgba(0, 0, 0, 1)"> key.contains(ScreenPreviewType.DIM.key)
val bean </span>=<span style="color: rgba(0, 0, 0, 1)"> createFontColor(isDim, key, obj, positionInfo)
maps</span>?<span style="color: rgba(0, 0, 0, 1)">.let {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (index < 0 || index >=<span style="color: rgba(0, 0, 0, 1)"> it.size) {
Log.w(TAG, </span>"updateFontColor invalid index $index size ${it.size}"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}
it</span>?.let { fontColorBean -><span style="color: rgba(0, 0, 0, 1)">
fontColorBean.color </span>=<span style="color: rgba(0, 0, 0, 1)"> bean.color
syncFontColor(key, index, getPreviewObserverId(key))
}
}
}
}
@JvmStatic
fun createFontColor(bitmap: Bitmap, x: Int, y: Int, width: Int, height: Int): String {
var color </span>=<span style="color: rgba(0, 0, 0, 1)"> getColor()
runCatching {
val rectMap </span>=<span style="color: rgba(0, 0, 0, 1)"> Bitmap.createBitmap(bitmap, x, y, width, height)
val hsvColorArray </span>=<span style="color: rgba(0, 0, 0, 1)"> generate(rectMap)
val result </span>= getColor(hsvColorArray)
log(</span>"createFontColor Brightness=${hsvColorArray},result=$result"<span style="color: rgba(0, 0, 0, 1)">)
color </span>=<span style="color: rgba(0, 0, 0, 1)"> result
}.getOrElse {
Log.e(TAG, </span>"createFontColor $it"<span style="color: rgba(0, 0, 0, 1)">)
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> color
}
@JvmStatic
fun loadImageAsBitmap(path: String): Bitmap {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> GlideCacheUtils.loadImageAsBitmap(path, Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createSourceBitmap(
obj: Any,
isDim: Boolean,
positionInfo: PositionInfo</span>? = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">
): Bitmap {
val width </span>= <span style="color: rgba(0, 0, 255, 1)">if</span> (isDim) DIM_WIDTH <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> WIDTH
val height </span>= <span style="color: rgba(0, 0, 255, 1)">if</span> (isDim) DIM_HEIGHT <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> HEIGHT
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (obj is PictureData) {
LogUtil.d(TAG, </span>"createFontColor = obj is PictureData"<span style="color: rgba(0, 0, 0, 1)">)
PreviewDataManager.readBitmap(obj, positionInfo).scale(width, height)
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
GlideCacheUtils.loadImageAsBitmap(obj as String, width, height)
}
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createRectBitmap(bitmap: Bitmap, isDim: Boolean): Bitmap {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (isDim) {
Bitmap.createBitmap(
bitmap,
DIM_WIDTH </span>/ 2 - RECT_WIDTH / 2, DIM_HEIGHT / 2 - RECT_HEIGHT / 2<span style="color: rgba(0, 0, 0, 1)">,
RECT_WIDTH, RECT_HEIGHT
)
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
Bitmap.createBitmap(
bitmap, WIDTH </span>/ 2 - RECT_WIDTH / 2<span style="color: rgba(0, 0, 0, 1)">, RECT_TOP_MARGIN, RECT_WIDTH, RECT_HEIGHT
)
}
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun generate(newMap: Bitmap): FloatArray {
val hsvColorArray </span>= FloatArray(3<span style="color: rgba(0, 0, 0, 1)">)
Palette.from(newMap).clearFilters().generate().apply {
val dominantColor </span>=<span style="color: rgba(0, 0, 0, 1)"> getDominantColor(Color.BLACK)
Color.colorToHSV(dominantColor, hsvColorArray)
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> hsvColorArray
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun clear(displayId: String) {
log(displayId, </span>"$TAG clear"<span style="color: rgba(0, 0, 0, 1)">)
maps.remove(displayId)
}
@JvmStatic
fun clearPreviewData(displayId: Int) {
ScreenPreviewType.values().forEach {
clear(getPreviewKey(displayId, it))
}
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun getPreviewKey(displayId: Int, screen: ScreenPreviewType): String {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> "${PREVIEW_BASE_DISPLAY_ID + displayId}_${screen.key}"<span style="color: rgba(0, 0, 0, 1)">
}
@JvmStatic
fun getPreviewObserverId(displayId: Int, screen: ScreenPreviewType): String {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> getPreviewObserverId(getPreviewKey(displayId, screen))
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">CommonUtils.PREVIEW_SYNC_FONT_COLOR +</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> fun getPreviewObserverId(key: String) = CommonUtils.PREVIEW_SYNC_FONT_COLOR +<span style="color: rgba(0, 0, 0, 1)"> key
</span><span style="color: rgba(0, 0, 255, 1)">private</span> fun log(displayId: String, str: String) = Log.d(TAG, "key=$displayId | $str"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">private</span> fun log(str: String) =<span style="color: rgba(0, 0, 0, 1)"> Log.d(TAG, str)
data </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> FontColorBean(
var color: String,
)
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p> </p>
<p><img alt="" width="157" height="35" loading="lazy" src="https://img2024.cnblogs.com/blog/583064/202506/583064-20250627180917100-183581068.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/583064/202506/583064-20250627180927976-999645137.png"></p>
<p>字体会自动跟随壁纸亮度切换颜色</p>
<p> </p><br><br>
来源:https://www.cnblogs.com/LiuZhen/p/18948838
頁:
[1]