Подтвердить что ты не робот

Как разбирать строку измерения и преобразовывать ее в значение измерения

Я ищу способ динамически преобразовать String как "30dp" в int, который напоминает количество пикселей. Это означало бы, что StaticClass.theMethodImSearchingFor("16px") вернет 16.
Мое приложение будет получать эти строки динамически, и мне нужен способ сохранить его как значение пикселя для использования позже.
Я уже смотрел Исходный код Android, в основном классы Resources, TypedArray и TypedValue, но я не мог найти что-нибудь полезное.

4b9b3361

Ответ 1

Если вам нужно, чтобы ресурс android dimen был как int, вы можете сделать это в своем коде:

context.getResources().getDimensionPixelSize(R.dimen.your_dimen_res);

Ответ 2

Мне тоже это нужно, поэтому я написал класс для его обработки. Весь код в этом ответе лицензируется под Apache License 2.0. Наслаждайтесь.

Существует два статических метода, которые имитируют два метода TypedValue. DimensionConverter.stringToDimension() имитирует TypedValue.complexToDimension. DimensionConverter.stringToDimensionPixelSize() имитирует TypedValue.complexToDimensionPixelSize.

Поддерживает все текущие единицы. Будут принимать строки измерений, такие как "33sp", "44 dp" и выдают исключение для плохих форматов.

Прост в использовании:

String dimension = "38dp";
Log.i(TAG, "Testing: " + dimension);
try {
    Log.i(TAG, "Converts to: " + DimensionConverter.stringToDimension(dimension, resources.getDisplayMetrics()));
} catch (NumberFormatException exception) {
    Log.i(TAG, "Unable to convert.");
}

Класс здесь:

public class DimensionConverter {

    // -- Initialize dimension string to constant lookup.
    public static final Map<String, Integer> dimensionConstantLookup = initDimensionConstantLookup();
    private static Map<String, Integer> initDimensionConstantLookup() {
        Map<String, Integer> m = new HashMap<String, Integer>();  
        m.put("px", TypedValue.COMPLEX_UNIT_PX);
        m.put("dip", TypedValue.COMPLEX_UNIT_DIP);
        m.put("dp", TypedValue.COMPLEX_UNIT_DIP);
        m.put("sp", TypedValue.COMPLEX_UNIT_SP);
        m.put("pt", TypedValue.COMPLEX_UNIT_PT);
        m.put("in", TypedValue.COMPLEX_UNIT_IN);
        m.put("mm", TypedValue.COMPLEX_UNIT_MM);
        return Collections.unmodifiableMap(m);  
    }
    // -- Initialize pattern for dimension string.
    private static final Pattern DIMENSION_PATTERN = Pattern.compile("^\\s*(\\d+(\\.\\d+)*)\\s*([a-zA-Z]+)\\s*$");

    public static int stringToDimensionPixelSize(String dimension, DisplayMetrics metrics) {
        // -- Mimics TypedValue.complexToDimensionPixelSize(int data, DisplayMetrics metrics).
        InternalDimension internalDimension = stringToInternalDimension(dimension);
        final float value = internalDimension.value;
        final float f = TypedValue.applyDimension(internalDimension.unit, value, metrics);
        final int res = (int)(f+0.5f);
        if (res != 0) return res;
        if (value == 0) return 0;
        if (value > 0) return 1;
        return -1;
    }

    public static float stringToDimension(String dimension, DisplayMetrics metrics) {
        // -- Mimics TypedValue.complexToDimension(int data, DisplayMetrics metrics).
        InternalDimension internalDimension = stringToInternalDimension(dimension);
        return TypedValue.applyDimension(internalDimension.unit, internalDimension.value, metrics);
    }

    private static InternalDimension stringToInternalDimension(String dimension) {
        // -- Match target against pattern.
        Matcher matcher = DIMENSION_PATTERN.matcher(dimension);
        if (matcher.matches()) {
            // -- Match found.
            // -- Extract value.
            float value = Float.valueOf(matcher.group(1)).floatValue();
            // -- Extract dimension units.
            String unit = matcher.group(3).toLowerCase();
            // -- Get Android dimension constant.
            Integer dimensionUnit = dimensionConstantLookup.get(unit);
            if (dimensionUnit == null) {
                // -- Invalid format.
                throw new NumberFormatException();
            } else {
                // -- Return valid dimension.
                return new InternalDimension(value, dimensionUnit);
            }
        } else {
            // -- Invalid format.
            throw new NumberFormatException();
        }        
    }

    private static class InternalDimension {
        float value;
        int unit;

        public InternalDimension(float value, int unit) {
            this.value = value;
            this.unit = unit;
        }
    }
}

Ответ 3

Благодаря mindriot, отлично работает и является спасателем.

Здесь он находится в С#

note. Если по какой-то причине вы не можете использовать типы Integer (vs int) (которые будут Java-целыми в моно), я оставил код, который использует С# int в комментариях везде, где это связано. Просто замените прокомментированный код int для недоказанного целочисленного кода везде, где вы его видите.

Придется использовать Integer так, чтобы его можно было определить, если нет совпадения при проверке суффиксов Dictionary/Map (TryGetValue) (в этом случае это будет null, если вместо этого используются ints, тогда параметр out 0, что соответствует первой записи карты, которая, очевидно, не работает. Слишком плохо TryGetValue не возвращал значение negeative без соответствия!?).

public class DimensionConverter
{
    // -- Initialize dimension string to constant lookup.     

    //public static readonly Dictionary<string, int> dimensionConstantLookup = initDimensionConstantLookup();
    public static readonly Dictionary<string, Integer> dimensionConstantLookup = initDimensionConstantLookup();

    //private static Dictionary<string, int> initDimensionConstantLookup()
    private static Dictionary<string, Integer> initDimensionConstantLookup()
    {
        //Dictionary<string, int> m = new Dictionary<string, int>();
        Dictionary<string, Integer> m = new Dictionary<string, Integer>();

        m.Add("px", (Integer)((int)ComplexUnitType.Px));
        m.Add("dip", (Integer)((int)ComplexUnitType.Dip));
        m.Add("dp", (Integer)((int)ComplexUnitType.Dip));
        m.Add("sp", (Integer)((int)ComplexUnitType.Sp));
        m.Add("pt", (Integer)((int)ComplexUnitType.Pt));
        m.Add("in", (Integer)((int)ComplexUnitType.In));
        m.Add("mm", (Integer)((int)ComplexUnitType.Mm));

        /*m.Add("px", (int)ComplexUnitType.Px);
        m.Add("dip", (int)ComplexUnitType.Dip);
        m.Add("dp", (int)ComplexUnitType.Dip);
        m.Add("sp", (int)ComplexUnitType.Sp);
        m.Add("pt", (int)ComplexUnitType.Pt);
        m.Add("in", (int)ComplexUnitType.In);
        m.Add("mm", (int)ComplexUnitType.Mm);*/

        return m;
    }

    // -- Initialize pattern for dimension string.     

    private static Regex DIMENSION_PATTERN = new Regex("^\\s*(\\d+(\\.\\d+)*)\\s*([a-zA-Z]+)\\s*$");

    public static int stringToDimensionPixelSize(string dimension, DisplayMetrics metrics)
    {
        // -- Mimics TypedValue.complexToDimensionPixelSize(int data, DisplayMetrics metrics).         

        InternalDimension internalDimension = stringToInternalDimension(dimension);

        float value = internalDimension.value;
        //float f = TypedValue.ApplyDimension((ComplexUnitType)internalDimension.unit, value, metrics);
        float f = TypedValue.ApplyDimension((ComplexUnitType)(int)internalDimension.unit, value, metrics);
        int res = (int)(f + 0.5f);

        if (res != 0) return res;
        if (value == 0) return 0;
        if (value > 0) return 1;

        return -1;
    }

    public static float stringToDimension(String dimension, DisplayMetrics metrics)
    {
        // -- Mimics TypedValue.complexToDimension(int data, DisplayMetrics metrics).         

        InternalDimension internalDimension = stringToInternalDimension(dimension);

        //return TypedValue.ApplyDimension((ComplexUnitType)internalDimension.unit, internalDimension.value, metrics);
        return TypedValue.ApplyDimension((ComplexUnitType)(int)internalDimension.unit, internalDimension.value, metrics);
    }

    private static InternalDimension stringToInternalDimension(String dimension)
    {
        // -- Match target against pattern.         

        MatchCollection matches = DIMENSION_PATTERN.Matches(dimension);

        if (matches.Count > 0)
        {
            Match matcher = matches[0];

            // -- Match found.             
            // -- Extract value.             
            float value = Float.ValueOf(matcher.Groups[1].Value).FloatValue();

            // -- Extract dimension units.             
            string unit = matcher.Groups[3].ToString().ToLower();

            // -- Get Android dimension constant.             
            //int dimensionUnit;

            Integer dimensionUnit;
            dimensionConstantLookup.TryGetValue(unit, out dimensionUnit);

            //if (dimensionUnit == ????)
            if (dimensionUnit == null)
            {
                // -- Invalid format.                 
                throw new NumberFormatException();
            }
            else
            {
                // -- Return valid dimension.                 
                return new InternalDimension(value, dimensionUnit);
            }
        }
        else
        {
            // -- Invalid format.             
            throw new NumberFormatException();
        }
    }

    private class InternalDimension
    {
        public float value;
        //public int unit;
        public Integer unit;

        //public InternalDimension(float value, int unit)
        public InternalDimension(float value, Integer unit)
        {
            this.value = value;
            this.unit = unit;
        }
    }
}

Ответ 4

Эта ссылка может помочь вам разобраться в вашем преобразовании, однако, поскольку пиксели и независимые от плотности пиксели не соответствуют 1:1, ожидайте некоторые (незначительные) искажения.

Эти единицы (dp) относятся к экрану с разрешением 160 точек на дюйм, поэтому один dp является одним пикселей на экране с разрешением 160 точек на дюйм. Отношение dp-to-pixel будет изменяться с плотность экрана, но не обязательно в прямой пропорции.