Цветовая палитра материала

Google разработала цветную палитру . Учитывая цвет, я хочу динамически создавать палитру в Android.

На сайте Графический дизайн был похож вопрос и с открытым исходным кодом javascript solution, который генерирует подобную цветовую палитру. Факторы для каждого цвета найдены здесь, а функция, используемая для создания цвета, находится в qaru.site/info/10785/....

Я использовал этот ответ и проект для создания палитры, похожей на Google. Тем не менее, я хочу, чтобы алгоритм возвращал точные значения, которые генерировал Google (см. Первую ссылку).

Вопрос: Как Google вычисляет цвета палитры для дизайна материалов?


То, что я пробовал до сих пор:

Основываясь на приведенной выше информации, я создал этот пример, чтобы показать, как я создал подобную палитру. Опять же, мне нужны точные значения.

import android.app.Activity;
import android.app.AlertDialog;
import android.graphics.Color;
import android.os.AsyncTask;
import android.view.Gravity;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;

import java.lang.ref.WeakReference;
import java.util.LinkedList;
import java.util.List;

/**
 * @author Jared Rummler <[email protected]>
 */
public class PaletteTask extends AsyncTask<Integer, Void, List<PaletteTask.Shade>> {

  private static int shadeColor(int color, double percent) {
    return shadeColor(String.format("#%06X", (0xFFFFFF & color)), percent);
  }

  private static int shadeColor(String color, double percent) {
    long f = Long.parseLong(color.substring(1), 16);
    double t = percent < 0 ? 0 : 255;
    double p = percent < 0 ? percent * -1 : percent;
    long R = f >> 16;
    long G = f >> 8 & 0x00FF;
    long B = f & 0x0000FF;
    int red = (int) (Math.round((t - R) * p) + R);
    int green = (int) (Math.round((t - G) * p) + G);
    int blue = (int) (Math.round((t - B) * p) + B);
    return Color.rgb(red, green, blue);
  }

  private final WeakReference<Activity> activityWeakReference;

  private final List<Shade> shades = new LinkedList<>();

  {
    shades.add(new Shade(0.9, "50"));
    shades.add(new Shade(0.7, "100"));
    shades.add(new Shade(0.5, "200"));
    shades.add(new Shade(0.333, "300"));
    shades.add(new Shade(0.166, "400"));
    shades.add(new Shade(0, "500"));
    shades.add(new Shade(-0.125, "600"));
    shades.add(new Shade(-0.25, "700"));
    shades.add(new Shade(-0.375, "800"));
    shades.add(new Shade(-0.5, "900"));
    shades.add(new Shade(0.7, "A100"));
    shades.add(new Shade(0.5, "A200"));
    shades.add(new Shade(0.166, "A400"));
    shades.add(new Shade(-0.25, "A700"));
  }

  public PaletteTask(Activity activity) {
    activityWeakReference = new WeakReference<>(activity);
  }

  @Override protected List<Shade> doInBackground(Integer... colors) {

    for (Shade shade : shades) {
      shade.color = shadeColor(colors[0], shade.percent);
    }

    return shades;
  }

  @Override protected void onPostExecute(List<Shade> shades) {
    Activity activity = activityWeakReference.get();
    if (activity == null || activity.isFinishing()) {
      return;
    }

    // Create a dialog that shows our generated colors:

    ScrollView scrollView = new ScrollView(activity);
    LinearLayout linearLayout = new LinearLayout(activity);
    linearLayout.setOrientation(LinearLayout.VERTICAL);

    int width, height;
    width = LinearLayout.LayoutParams.MATCH_PARENT;
    height = (int) (30/*dp*/ * (activity.getResources().getDisplayMetrics().densityDpi / 160f));

    // add each color
    for (Shade shade : shades) {
      LinearLayout layoutColor = new LinearLayout(activity);
      TextView textView = new TextView(activity);

      layoutColor.setLayoutParams(new LinearLayout.LayoutParams(width, height));
      layoutColor.setBackgroundColor(shade.color);
      layoutColor.setGravity(Gravity.CENTER);

      textView.setText(shade.name + "    " + String.format("#%06X", (0xFFFFFF & shade.color)));

      layoutColor.addView(textView);
      linearLayout.addView(layoutColor);
    }

    scrollView.addView(linearLayout);

    new AlertDialog.Builder(activity).setView(scrollView).show();
  }

  public static class Shade {

    final double percent;
    final String name;
    int color;

    public Shade(double percent, String name) {
      this.percent = percent;
      this.name = name;
    }
  }

}

Вызов AsynTask:

int materialRed500 = 0xFFF44336;
new PaletteTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, materialRed500);

Диалог, созданный из приведенного выше кода:

диалог цветовой палитры

Ответ 1

В нижней строке вы не можете создать палитру, используя один цвет, который всегда будет точно соответствовать соответствующей палитре свойств материала Google.

Это потому, что все палитры следуют за другой прогрессией цветов. Например, красная палитра создается с использованием следующей прогрессии (JS-код с использованием TinyColor.js, но вы все еще можете видеть изменения HSL):

return [
            { hex : tinycolor( hex ).lighten( 37.7 ).saturate( 10.4 ).spin( -13 ).toHexString(), name : '50' },
            { hex : tinycolor( hex ).lighten( 31.8 ).saturate( 10.4 ).spin( -9.5 ).toHexString(), name : '100' },
            { hex : tinycolor( hex ).lighten( 18.7 ).desaturate( 17 ).spin( -3.9 ).toHexString(), name : '200' },
            { hex : tinycolor( hex ).lighten( 9.1 ).desaturate( 20.9 ).spin( -4 ).toHexString(), name : '300' },
            { hex : tinycolor( hex ).lighten( 4.1 ).desaturate( 6.6 ).spin( -3 ).toHexString(), name : '400' },
            { hex : hex, name : '500' },
            { hex : tinycolor( hex ).darken( 3.1 ).desaturate( 12.4 ).spin( -2.7 ).toHexString(), name: '600' },
            { hex : tinycolor( hex ).darken( 7.8 ).desaturate( 24.5 ).spin( -4 ).toHexString(), name: '700' },
            { hex : tinycolor( hex ).darken( 11.7 ).desaturate( 23.2 ).spin( -4 ).toHexString(), name: '800' },
            { hex : tinycolor( hex ).darken( 17 ).desaturate( 16.1 ).spin( -4 ).toHexString(), name: '900' },
            { hex : tinycolor( hex ).lighten( 16.7 ).saturate( 10.4 ).spin( 0.6 ).toHexString(), name: 'A100' },
            { hex : tinycolor( hex ).lighten( 7.7 ).saturate( 10.4 ).spin( -4 ).toHexString(), name: 'A200' },
            { hex : tinycolor( hex ).darken( 3.9 ).saturate( 10.4 ).spin( -15.5 ).toHexString(), name: 'A400' },
            { hex : tinycolor( hex ).darken( 16.6 ).saturate( 10.4 ).spin( -4 ).toHexString(), name: 'A700' }
        ];

Однако, когда вы применяете ту же самую прогрессию к базовому цвету Indigo (500), вы можете видеть, что палитры не совпадают. На следующем изображении крайняя левая палитра является стандартной палитрой MD, а вторая слева - палитрой, сгенерированной с указанной выше прогрессией. Эти две палитры точно совпадают. Когда я загружаю в палитру MD Indigo (третья палитра), а затем создаю палитру с использованием значения Indigo 500 и кода прогрессии красной палитры, он создает 4-ю палитру. Как вы можете видеть, в то время как эта прогрессия точна для красного, это путь, путь для других цветов:

введите описание изображения здесь

Подробнее об этой теме можно найти здесь. Скорее всего, цвета, выбранные Google, выбраны дизайнером, а не программно сгенерированы.

EDIT: Кроме того, код для этого MCG был полностью пересмотрен. Новая логика цветов можно найти здесь, и она использует tinycolor. js для функций модификации.