【问题标题】:Adjust components basing on devices根据设备调整组件
【发布时间】:2020-06-16 03:48:21
【问题描述】:

有没有办法让容器和字体大小根据安装应用程序的设备的大小进行调整?我开发了一个应用程序,在我的手机(三星 S8+)上,它以我想要的方式出现。当我将它安装在屏幕较小的手机上时,布局发生了变化,组件在小手机上看起来很大。

该页面的 CSS 代码是:

Login-TextFields{
    font-size: 3mm;
    margin: 0.5mm 1mm 0mm 1mm;
    padding: 1mm;
    color: white;
    background: transparent;
    padding: 0mm 0mm 0mm 0mm;
}

Login-Field-Container{
    border: none;
    border-bottom: 0.25mm solid white;
    padding-top: 5mm;
}
LoginFields-Container {
    width: auto;
    height: 2mm;
    border-radius: 3mm;
    padding-top: 8mm;
    margin: 7mm 2mm 0mm 2mm;
    background-color: transparent;
}
LoginForm-Background{
    background-image: url(../assets/background.jpg);
    cn1-background-type: cn1-image-scaled-fill;
    padding: 5mm;
}
Logo-Container{
    background: none;
}
Mask-Button{
    color: white;
}
Login-Button {
    background-color: #0052cc;
    border-radius: 3mm;
    border: none;
    padding: 1mm 2mm 1mm 2mm;
    color: white;
    font-size: 3mm;
    text-align: center;
    margin: 2mm 3mm 2mm 3mm;
}
Forgot-Button{
    text-align: right;
    color: white;
    font-style: italic;
    font-size: 2.5mm;
    margin: 0px;
}
SignUp-Button{
    color: white;
    margin: 1mm 2mm 1mm 0mm;
    text-align: right; 
}
Dont-Have-Account-Label{
    color: #ccffff;
    margin: 1mm 2mm 1mm 0mm;
    text-align: center; 
}
Logo-Area{
    padding: 1mm;
    margin: 2mm 3mm 2mm 3mm;
    text-align: center;
}
Copyright{
    color: white;
}

我想在所有设备上保持第二张图片中的布局。我希望容纳字段的容器根据设备进行调整,同时保持布局和比例。

【问题讨论】:

  • 如果您使用毫米尺寸等,这是非常无缝的。我需要您使用的特定 CSS 示例,以便为您提供更多信息。
  • 在我的一个项目中,我有一个代码可以满足您的要求,但在我发布答案之前,我想确保我正确理解了这个问题。例如:如果在 70 毫米宽的手机上输入了一定数量的文字,您是否希望在 50 毫米宽的手机上输入相同数量的文字?那么文字的大小应该根据屏幕的大小而变化呢?可以?如果是这样,您是否认为方向被阻止?
  • 是的@Francesco。我希望在 50 毫米宽的屏幕上输入与在 70 毫米屏幕上相同数量的文本。我希望文本、容器和字段的大小根据屏幕大小而变化。关于方向,我问的是纵向。
  • @RichardMatovu 好的,看看我的回答

标签: codenameone


【解决方案1】:

根据您的评论Adjust components basing on devices 中的要求,首先将以下Java 类添加到您的项目中。它包含的内容超出您的需要,您可以根据需要对其进行自定义。最重要的是这条线:int defaultScreenWidth = Display.getInstance().convertToPixels(70);。不言自明:它表示参考尺寸,在本例中为 70mm。

import com.codename1.io.Log;
import com.codename1.ui.CN;
import com.codename1.ui.Display;
import com.codename1.ui.Font;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;

/**
 *
 *
 */
public class CSSUtilities {

    // Note: we assume that the only target platforms are native iOS, native Android and Javascript
    public static final boolean isAndroidTheme = UIManager.getInstance().isThemeConstant("textComponentOnTopBool", false);
    public static final boolean isIOSTheme = !isAndroidTheme;

    private static int percentage = calculateAdaptionPercentage();

    /**
     * Call this method if you need to recalculate the adapting percentage
     */
    public static int recalculatePercentage() {
        percentage = calculateAdaptionPercentage();
        return percentage;
    }

    /**
     * Load multiple CSSes, note that "iOS" css is loaded only if the native iOS
     * theme is used, the "Android" css is loaded only if the native Android
     * theme is used, the "WebApp" css is loaded only if the app is running on
     * Javascript; for info about build.xml:
     * https://stackoverflow.com/questions/53480459/multiple-codename-one-css
     *
     * @param css file names, WITHOUT SLASHES AND WITHOUT .css EXTENSION!!!
     */
    public static void loadMultipleCSSes(String... css) {
        try {
            UIManager.initFirstTheme("/" + css[0]);
            Resources resource = Resources.openLayered("/" + css[0]);
            UIManager.getInstance().setThemeProps(adaptTheme(resource.getTheme("Theme")));
            Resources.setGlobalResources(resource);

            Log.p("Loaded " + css[0] + ".css", Log.DEBUG);
            if (isIOSTheme) {
                Log.p("The currently used native theme is iOS", Log.DEBUG);
            }
            if (isAndroidTheme) {
                Log.p("The currently used native theme is Android", Log.DEBUG);
            }

            for (int i = 1; i < css.length; i++) {
                if (css[i].equals("iOS")) {
                    if (!isIOSTheme) {
                        continue;
                    } else {
                        Log.p("Loading CSS for iOS native theme only", Log.DEBUG);
                    }
                }
                if (css[i].equals("Android")) {
                    if (!isAndroidTheme) {
                        continue;
                    } else {
                        Log.p("Loading CSS for Android native theme only", Log.DEBUG);
                    }
                }
                if (css[i].equals("WebApp")) {
                    if (!isJavascript()) {
                        continue;
                    } else {
                        Log.p("Loading CSS for web-app only", Log.DEBUG);
                    }
                }
                Resources res = Resources.openLayered("/" + css[i]);
                if (!css[i].equals("MyController")) {
                    UIManager.getInstance().addThemeProps(adaptTheme(res.getTheme("Theme")));
                } else {
                    UIManager.getInstance().addThemeProps(res.getTheme("Theme"));
                }
                Log.p("Loaded " + css[i] + ".css", Log.DEBUG);
            }
            // Log.p("CssUtilities.loadMultipleCSSes - success, loaded in the order: " + css.toString(), Log.INFO);
        } catch (Exception ex) {
            Log.p("CssUtilities.loadMultipleCSSes - ERROR", Log.ERROR);
            Log.e(ex);
            Log.sendLogAsync();
        }
    }

    /**
     * Calculate the percentage to adapt the font sizes to the screen width. The
     * maximum decrease of the sizes is about 30%, increasing is disabled.
     *
     * @return percentage from -30 to 0
     */
    private static int calculateAdaptionPercentage() {
        int defaultScreenWidth = Display.getInstance().convertToPixels(70);
        int currentScreenWidth = Display.getInstance().getDisplayWidth();
        int currentInMM = currentScreenWidth / Display.getInstance().convertToPixels(1);

        int percentage = currentScreenWidth * 100 / defaultScreenWidth - 100;
        if (percentage < -30) {
            percentage = -30;
        } else if (percentage > 0) {
            percentage = 0;
        }

        Log.p("Estimated screen width: " + currentInMM + " mm", Log.INFO);
        Log.p("Font percentage: " + percentage + "%", Log.INFO);
        return percentage;
    }

    /**
     * Modify a theme changing the font sizes, margins and paddings
     *
     * @param themeProps
     * @return the new theme
     */
    private static Hashtable adaptTheme(Hashtable hashtable) {
        Hashtable<String, Object> result = new Hashtable<>();
        Set<String> keys = hashtable.keySet();
        Iterator<String> itr = keys.iterator();
        String key;
        Object value;
        while (itr.hasNext()) {
            key = itr.next();
            value = hashtable.get(key);
            // Log.p("key: " + key + ", value is: " + value.getClass().getName() + ", " + value.toString());
            if (value instanceof Font && ((Font) value).isTTFNativeFont() && percentage < 0) {
                Font font = (Font) value;
                float newSize = (int) (font.getPixelSize() * (100 + percentage) / 100);
                result.put(key, font.derive(newSize, font.getStyle()));
            } else if (key.endsWith("#margin") || key.endsWith(".margin")
                    || key.endsWith("#padding") || key.endsWith(".padding")) {
                if (value instanceof String) {
                    // Log.p("input:  " + value);
                    // Log.p("output: " + resizeMarginPadding((String) value));
                    result.put(key, resizeMarginPadding((String) value));
                }
            } else {
                result.put(key, value);
            }
        }
        return result;
    }

    /**
     * Returns a resized dimension (like a width or height)
     *
     * @param size, the unit of measurement (px, mm, pt, etc.) does not matter
     * @return
     */
    public static int getResized(int size) {
        return size * (100 + percentage) / 100;
    }

    /**
     * Returns a resized dimension (like a width or height)
     *
     * @param size, the unit of measurement (px, mm, pt, etc.) does not matter
     * @return
     */
    public static float getResized(double size) {
        return (float) (size * (100 + percentage) / 100);
    }

    /**
     * Returns a resized dimension (like a width or height)
     *
     * @param size, the unit of measurement (px, mm, pt, etc.) does not matter
     * @param convertToPx if true, convert the given size from mm to pixels
     * @return
     */
    public static int getResized(int size, boolean convertToPx) {
        if (!convertToPx) {
            return getResized(size);
        } else {
            return getResized(Display.getInstance().convertToPixels(size));
        }
    }

    /**
     * Resizes the given margin or the padding
     *
     * @param input in a form like 0.0,1.0,0.9,15.0
     * @return the given input if it's not a valid margin or padding, or a new
     * String with the margins or paddings recalculated
     */
    private static String resizeMarginPadding(String input) {
        String result = "";

        StringTokenizer st = new StringTokenizer(input, ",");
        // Do we have 4 numbers?
        if (st.countTokens() == 4) {
            while (st.hasMoreTokens()) {
                // Is this token a number like 1.5, 0.0, etc.?
                String token = st.nextToken();
                try {
                    Float number = Float.valueOf(token);
                    number = getResized(number);
                    number = ((int) (number * 10)) / 10.0f;
                    result += number;
                    if (st.countTokens() != 0) {
                        result += ",";
                    }
                } catch (NumberFormatException e) {
                    return input;
                }
            }
        } else {
            return input;
        }

        return result;
    }

    /**
     * Returns a resized dimension (like a width or height)
     *
     * @param size, the unit of measurement (px, mm, pt, etc.) does not matter
     * @param convertToPx if true, convert the given size from mm to pixels
     * @return
     */
    public static double getResized(double size, boolean convertToPx) {
        if (!convertToPx) {
            return getResized(size);
        } else {
            return getResized(Display.getInstance().convertToPixels((float) size));
        }
    }

    /**
     * Returns true if the app is running in the CN1 Simulator
     *
     * @return
     */
    public static boolean isSimulator() {
        return Display.getInstance().isSimulator();
    }

    /**
     * Returns true if the app is running as native Android app
     *
     * @return
     */
    public static boolean isAndroidNative() {
        return !isSimulator() && "and".equals(CN.getPlatformName());
    }

    /**
     * Returns true if the app is running as native iOS app
     *
     * @return
     */
    public static boolean isiOSNative() {
        return !isSimulator() && "ios".equals(CN.getPlatformName());
    }

    /**
     * Returns true if the app is running as Javascript port
     *
     * @return
     */
    public static boolean isJavascript() {
        return !isSimulator() && "HTML5".equals(CN.getPlatformName());
    }
}

然后,在您的主类中,注释 theme = UIManager.initFirstTheme("/theme"); 行,将其替换为:

// theme = UIManager.initFirstTheme("/theme");
// We assume that CSS support is enabled
CSSUtilities.loadMultipleCSSes("theme");

就是这样。这是一个使用示例:

Form hi = new Form("Hi World", BoxLayout.y());
hi.add(new Label("(Recognized) screen width: " + (hi.getWidth() / CN.convertToPixels(1)) + " mm"));
hi.add(new SpanLabel("This text enters a line on a 70mm screen. Do tests."));
hi.add(FontImage.createMaterial(FontImage.MATERIAL_LOCAL_RESTAURANT, "Label", CSSUtilities.getResized(10.0f)));
hi.show();

请注意,识别的宽度不是实际宽度,可能会有所不同:但是,我的代码确保即使在 Codename One 无法正确检测宽度的情况下,文本也会按照您的要求进行调整。

一些注意事项:

  • 此代码有限制,需要垂直锁定方向,仅适用于智能手机屏幕(无平板电脑);

  • 记住Android和iOS的原生字体是不一样的;

  • 此代码会自动调整您在 css 中指定的尺寸,涉及文本、边距和填充(使用 mm 或 pt,1pt 约为 0.35mm);

  • 调整大小不是自动的,在示例代码中我向您展示了如何自动调整图像;

  • 代码被限制为最多减少30%的文本,在我的测试中这很好,前提是默认大小是70mm;

  • 代码永远不会增加文本的大小:同样,我根据我的测试决定了这种行为,你可以随心所欲。

  • 更准确地说,这段代码对我有帮助,尤其是在那些没有正确报告其屏幕密度并因此“愚弄”代号一的设备上。

我希望这对你有用。它在真实设备上比在模拟器上更有用。

【讨论】:

  • 我已经实现了你的课程,但我无法读取我在我的 css 文件中加载的图像。它在控制台上显示它成功加载了 Android.css,但它在我从 css 获取图像的所有点都抛出了一个空异常。请多多指教。
  • 另外,我要求澄清,所以我创建多个 css 文件并将它们命名为 Android.css 然后 iOS.css 等等,然后在我的主类中我将它们全部添加到主题(如您的代码所示)或者我是否在我的主类中维护单词主题,但我相应地命名我的 css 文件?
  • 我的 Java 类是一个自定义示例,因为它基于我的需要。你不需要像我一样使用多个 CSS,你可以删除关于加载更多 CSS 的部分。您最感兴趣的是我如何使用private static Hashtable adaptTheme(Hashtable hashtable) 方法。从 CSS 加载图像对我有用,如下所述:github.com/shannah/cn1-css/wiki/Images
  • 关于“theme.css”,我的代码假定您不应该使用“主题”这个词,并且您修改了 build.xml 以支持您的单个 css 或多个 css。 “主题”这个词的问题在于它被 Codename One 用来支持在不重新编译项目的情况下即时重新设计 UI,但该功能“可能”与我的代码不兼容:我不确定,我没有检查它们是否兼容。
  • 我对代码进行了一些更改以使其适应我的项目。我将 loadMultipleCSSes(String... css) 方法从 void 更改为 Resources 以返回加载的主题。然后在我的主要课程中,我用theme=CSSUtilities.loadMultipleCSSes("Android","iOS"); 代替了theme = UIManager.initFirstTheme("/theme");,它对我有用。
【解决方案2】:

我的方法更容易实现,但也很有效。我执行以下操作来调整我的应用以适应不同的屏幕尺寸:

捕捉屏幕尺寸:

int height = Display.getInstance().getDisplayHeight();

然后我使用(部分)结果值来根据容器中的组件定义边距和填充。它需要一点改进,但​​适用于所有设备:

label.getAllStyles().setMargin(height/20,5,30,30);
label.getAllStyles().setPadding(height/30,height/30,25,25);

捕捉高度并将其除以一个固定的整数,使大型设备的边距/填充变大,而对于较小的屏幕,边距/填充变小。

你可以对字体做同样的事情:

int fontSize = Display.getInstance().convertToPixels(height/30);

您甚至可以将 CSS 设置与此方法结合使用,但 setMargin() 或 setPadding() 方法必须在 setUIID() 之后才能覆盖 CSS 设置。

sn-p 可能如下所示:

int height = Display.getInstance().getDisplayHeight();
int fontSize = Display.getInstance().convertToPixels(height/30);
Font redHatFont = Font.createTrueTypeFont("Red Hat Text Medium", "RedHatText-Medium.ttf").derive(fontSize, Font.STYLE_PLAIN);
label.setUIID("MyLabel");
label.getAllStyles().setMargin(height/20,5,30,30);
label.getAllStyles().setPadding(height/30,displayHeight/30,25,25);
label.getUnselectedStyle().setFont(redHatFont);

【讨论】:

  • 有趣 :) 我们有解决相同问题的想法,但方法不同。
  • 感谢@FrancescoGalgani .... 我一直在寻找最简单的可用方法。我也很感兴趣地研究了你的:-)
猜你喜欢
  • 1970-01-01
  • 2017-08-12
  • 1970-01-01
  • 2019-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-17
相关资源
最近更新 更多