在高端设备上绘制Canvas时出现问题

5

我希望在画布上以编程方式绘制一个 SeekBar。 我编写了根据设备密度设置 SeekBar 的 LayoutParams 的代码。 我正在使用基于设备密度的 switch case,例如:

final DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
if(metrics.densityDpi <= DisplayMetrics.DENSITY_LOW){
        zoomBarParams = new LinearLayout.LayoutParams(18,
                LayoutParams.FILL_PARENT);
        } else if (metrics.densityDpi <= DisplayMetrics.DENSITY_MEDIUM){
            zoomBarParams = new LinearLayout.LayoutParams(24,
                    LayoutParams.FILL_PARENT);

        }else if (metrics.densityDpi <= DisplayMetrics.DENSITY_HIGH){
            zoomBarParams = new LinearLayout.LayoutParams(24,
                    LayoutParams.FILL_PARENT);

        }else if (metrics.densityDpi <= DisplayMetrics.DENSITY_XHIGH){
            zoomBarParams = new LinearLayout.LayoutParams(31,
                    LayoutParams.FILL_PARENT);


        }else if (metrics.densityDpi <= DisplayMetrics.DENSITY_XXHIGH){
            zoomBarParams = new LinearLayout.LayoutParams(60,
                    LayoutParams.FILL_PARENT);

        }else if (metrics.densityDpi <= DisplayMetrics.DENSITY_XXXHIGH){
            zoomBarParams = new LinearLayout.LayoutParams(60,
                    LayoutParams.FILL_PARENT);

        } else {
            zoomBarParams = new LinearLayout.LayoutParams(60,
                    LayoutParams.FILL_PARENT);

        }

但是在像三星Note 5、Galaxy S6 Edge这样的高端设备上却不起作用。我相信这些设备属于XXXHIGH密度范围,那为什么它不起作用呢?在画布上绘制时,设备密度和屏幕大小之间有关系吗?非常感谢任何帮助。


我认为你的问题在于densityDpiValue不能保证完全等于DisplayMetrics常量指定的密度之一。比如说,如果你得到了密度值为321,它将不会触发任何一个情况。考虑检查范围。 - Dmitry Zaytsev
作为一个次要问题 - 你试图用这样的 switch 解决什么问题?为什么不使用 dp 值呢? - Dmitry Zaytsev
@DmitryZaitsev 是的。我更新了我的代码,像检查范围一样。在三星Note 5和Galaxy S6 Edge中问题得到了解决。但是,在S6 Edge +中问题仍然存在。请查看我在问题中更新的代码。 - Jas
} else if (metrics.densityDpi <= DisplayMetrics.DENSITY_XXXHIGH){ zoomBarParams = new LinearLayout.LayoutParams(60, LayoutParams.FILL_PARENT); 这将在S6 Edge触发。 - Jas
1
那么,到底是什么出了问题?你原本期望它如何工作? - Dmitry Zaytsev
显示剩余3条评论
3个回答

2
问题在于没有针对此问题的通用代码。
如果您想要原始密度,则可以使用metrics.xdpimetrics.ydpi分别表示水平和垂直密度。
或者按照以下代码操作:
public static String getDensity(Resources resources) {
    switch (resources.getDisplayMetrics().densityDpi) {
        case DisplayMetrics.DENSITY_LOW:
            return "ldpi";
        case DisplayMetrics.DENSITY_MEDIUM:
            return "mdpi";
        case DisplayMetrics.DENSITY_HIGH:
            return "hdpi";
        case DisplayMetrics.DENSITY_XHIGH:
            return "xhdpi";
        case DisplayMetrics.DENSITY_XXHIGH:
            return "xxhdpi";
        case DisplayMetrics.DENSITY_XXXHIGH:
            return "xxxhdpi";
        case DisplayMetrics.DENSITY_TV:
            return "tvdpi";
        default:
            return "unknown";
    }

这个话题的一个非常深入的解释是由这个人提供的:

https://dev59.com/2FsX5IYBdhLWcg3wf_n2#33789580

还要查看 Android 开发者文档中的 DisplayMetrics 部分。

2

为什么不尝试摆脱所有的if else情况,做一些通用的东西呢?由于需要根据屏幕大小放置不同的像素值,因此可以使用dp代替。

您可以从设备的屏幕密度中获取px并在LayoutParams中使用它。

public static float convertDpToPixel(float dp, Context context){
    Resources resources = context.getResources();
    DisplayMetrics metrics = resources.getDisplayMetrics();
    float px = dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT); // You can cache "((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT)" to avoid re-calculation.
    return px;
}

所以您需要创建以下这样的param
zoomBarParams = new LinearLayout.LayoutParams(convertDpToPixel(DP_VALUE, context),
                LayoutParams.FILL_PARENT);

DP_VALUE将在所有设备上保持不变。

我希望这可以解决您的问题。


1
如果您正在使用画布,请只与像素一起玩耍。而不是dpi,您可以根据需要使用百分比宽度和高度。这样最适合并且适用于每个设备。例如,如果您需要一个600 x 128px的进度条,屏幕大小为800 x 1280 px。那么它就像是宽度的75%和高度的10%。现在通过代码应用此百分比-
DisplayMetrics dm = getResources().getDisplayMetrics();
int seekBarWidth= dm.widthPixels * 75 / 100;
int seekBarHeight= dm.heightPixels * 10 / 100;

这将完美地适用于所有屏幕尺寸。如果需要进一步协助,请告诉我 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接