【问题标题】:How to parse a dimension string and convert it to a dimension value如何解析维度字符串并将其转换为维度值
【发布时间】:2012-10-24 13:49:32
【问题描述】:

我正在寻找一种将"30dp" 之类的字符串动态转换为类似于像素数量的 int 的方法。这意味着StaticClass.theMethodImSearchingFor("16px") 将返回16
我的应用程序将动态获取这些字符串,我需要一种方法将其存储为像素值以供以后使用。
我已经看过Android Source code,主要是ResourcesTypedArrayTypedValue这些类,但是我找不到任何有用的东西。

【问题讨论】:

    标签: android android-layout


    【解决方案1】:

    如果您需要 android 资源维度作为 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;
            }
        }
    }
    

    【讨论】:

    • 真的很聪明,+1!我把 C# 端口放在下面。非常感谢,它真的帮助了我。
    • 我相信 String unit = matcher.group(2).toLowerCase();应改为 String unit = matcher.group(3).toLowerCase();
    • @Nick - 我编辑了代码以使用正确的组。不过这很奇怪,因为我在库中的代码是正确的,这是我从中复制的。使用第 2 组会立即使我的库代码崩溃,我在发布之前对其进行了测试。此外,Samus Arin 的代码(这是我的一个端口)也有正确的组,除非他在没有通知我的情况下也修复了错误。相当混乱,但感谢您的提醒。
    • 也看起来像 DIMENSION_PATTERN 不会匹配负值。这个值虽然有效: "^\-?\s*(\d+(\.\d+))\s([a-zA-Z]+)\s*$"
    • @Nick 可能应该是 ^\-?\s*(\d+(\.\d+)*)\s*([a-zA-Z]+)\s*跨度>
    【解决方案3】:

    感谢mindriot,效果很好,是救生员。

    这里是C#

    注意:如果由于某种原因您不能使用整数类型(与 int 相比)(这将是 Mono 中的 Java 整数),我将在 cmets 中使用 C# int 的所有相关代码留下。只需将注释的 int 代码换成您看到的任何未注释的 Integer 代码。

    必须使用 Integer 以便在检查后缀的 Dictionary/Map (TryGetValue) 时确定是否不匹配(在这种情况下它将为 null;如果使用 int 代替,则 out 参数将为 0,对应于 map 的第一个条目,这显然不起作用。太糟糕了 TryGetValue 在不匹配时没有返回负值!?)。

    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】:

      这个link 可能会帮助您确定您的转换,但是由于像素和与密度无关的像素不是 1:1 的匹配,所以会出现一些(轻微的)失真。

      这些单位 (dp) 是相对于 160 dpi 的屏幕,所以 1 dp 就是 1 160 dpi 屏幕上的像素。 dp与像素的比率将随 屏幕密度,但不一定成正比。

      【讨论】:

      • 我在developer.android.com 上读过这篇文章,但是仍然很难从String "12dp" 中提取"dp" 子字符串,验证"dp" 是否有效转换单位,然后转换它。我知道 Android 框架必须能够做到这一点,因为它也可以使用 XML 资源,但似乎没有对非资源字符串开放。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多