钮钴禄斐金 發表於 2025-6-27 18:12:00

多屏下字体自动取色

<p>需求:多屏场景下,设置同一系列屏保,屏保中间组件字体颜色需要动态读取背后壁纸主色亮度,根据背后亮度动态设置字体颜色</p>
<p>偏亮的=黑色,偏暗的=白色</p>
<p>1、取色</p>
<p>读取亮度需要先对bitmap解码,通过&nbsp;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>&lt;Swatch&gt;<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 &amp;&amp; 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&nbsp;=&nbsp;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 &lt; 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 &lt; hist.length; color++<span style="color: rgba(0, 0, 0, 1)">) {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (hist &gt; 0 &amp;&amp;<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 &gt; 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 &lt; hist.length; color++<span style="color: rgba(0, 0, 0, 1)">) {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (hist &gt; 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 &lt;=<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&lt;&gt;<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时存在过滤操作&nbsp;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> &amp;&amp; mFilters.length &gt; 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 &lt; 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> &amp;&amp; !<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>&nbsp;</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 &gt;= 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&lt;Int, MutableList&lt;String&gt;&gt;<span style="color: rgba(0, 0, 0, 1)">()

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val suitIdObserver = Observer&lt;Int&gt;<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&lt;DisplayParameter&gt;<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&lt;String&gt;<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>-&gt;<span style="color: rgba(0, 0, 0, 1)"> {
                        ResourceLoadManager.convertPath2Tv(it.path)
                  }

                  DisplayParameter.DISPLAY_PSD.displayId </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> {
                        ResourceLoadManager.convertPath2Psd(it.path)
                  }

                  </span><span style="color: rgba(0, 0, 255, 1)">else</span> -&gt;<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&lt;String, CopyOnWriteArrayList&lt;FontColorBean&gt;&gt;<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 &lt; 0 || index &gt;=<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 &lt; 0 || index &gt;=<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>&lt;String&gt;<span style="color: rgba(0, 0, 0, 1)">(key).post(color)
    }

    @JvmStatic
    fun loadScreensaversFontColorData(displayId: Int, pictures: List</span>&lt;String&gt;<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&lt;String&gt; dimpaths , csdpaths7张原图 -&gt; 对应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>&lt;Any&gt;<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&lt;Any&gt;, callBack: () -&gt;<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&lt;FontColorBean&gt;<span style="color: rgba(0, 0, 0, 1)">()
      }
      val list </span>= mutableListOf&lt;FontColorBean&gt;<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 -&gt; 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 &gt;= 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 &lt; 0 || index &gt;=<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 -&gt;<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>&nbsp;</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>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/LiuZhen/p/18948838
頁: [1]
查看完整版本: 多屏下字体自动取色