Hướng dẫn hỗ trợ đa màn hình trong lập trình Android

android fragmentation

Trong quá trình phát triển Android thì mình nhận ra một vấn đề rất lớn của nền tảng mobile có thị phần lớn nhất thế giới đó chính là sự phân mảnh thiết bị, cộng với vô số các kích thước màn hình. Với một newbie thì đây thực sự là cơn ác mộng, đó cũng là lý do các bạn thực tập thường ngại làm với UI/UX.

Vậy thì làm thế nào để có thể hỗ trợ nhiều độ phân giải màn hình cho điện thoại Android, bạn có thể tham khảo bài viết bên dưới nhé.

Bước 1: Tạo file multiscreen.gradle nằm trong thư mục chính của Project

task convertDimens() {
    def xmlRoot = "${project.rootDir}/app/src/main/res/values"
    def xmlDimenFileName = "dimens.xml"
    def xmlFileDefault = xmlRoot + "/" + xmlDimenFileName
    def xmlFile360 = xmlRoot + "-sw360dp/" + xmlDimenFileName
    def xmlFile480 = xmlRoot + "-sw480dp/" + xmlDimenFileName
    def xmlFile540 = xmlRoot + "-sw540dp/" + xmlDimenFileName
    def xmlFile600 = xmlRoot + "-sw600dp/" + xmlDimenFileName
    def xmlFile640 = xmlRoot + "-sw640dp/" + xmlDimenFileName
    def xmlFile720 = xmlRoot + "-sw720dp/" + xmlDimenFileName
    def xmlFile800 = xmlRoot + "-sw800dp/" + xmlDimenFileName

    makeFolder(xmlFile360, xmlFile480, xmlFile540, xmlFile600, xmlFile640, xmlFile720, xmlFile800)

    createOrCloneDefaultData(xmlFileDefault, xmlFile360)

    def xmlOriginal = new XmlParser().parse(xmlFile360)

    def xml480 = xmlOriginal.clone()
    def xml540 = xmlOriginal.clone()
    def xml600 = xmlOriginal.clone()
    def xml640 = xmlOriginal.clone()
    def xml720 = xmlOriginal.clone()
    def xml800 = xmlOriginal.clone()

    xml480.dimen.each { dimen ->
        def value = dimen.text()
        if (isSkipCheck(value)) {
            //skip
        } else if (value.contains("px")) {
            def newValue = value.replace("px", "").toFloat()
            newValue = round((480 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "px"
        } else {
            def newValue = value.replace("dp", "").toFloat()
            newValue = round((480 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "dp"
        }
    }

    xml540.dimen.each { dimen ->
        def value = dimen.text()
        if (isSkipCheck(value)) {
            //skip
        } else if (value.contains("px")) {
            def newValue = value.replace("px", "").toFloat()
            newValue = round((540 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "px"
        } else {
            def newValue = value.replace("dp", "").toFloat()
            newValue = round((540 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "dp"
        }
    }
    xml600.dimen.each { dimen ->
        def value = dimen.text()
        if (isSkipCheck(value)) {
            //skip
        } else if (value.contains("px")) {
            def newValue = value.replace("px", "").toFloat()
            newValue = round((600 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "px"
        } else {
            def newValue = value.replace("dp", "").toFloat()
            newValue = round((600 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "dp"
        }
    }
    xml640.dimen.each { dimen ->
        def value = dimen.text()
        if (isSkipCheck(value)) {
            //skip
        } else if (value.contains("px")) {
            def newValue = value.replace("px", "").toFloat()
            newValue = round((640 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "px"
        } else {
            def newValue = value.replace("dp", "").toFloat()
            newValue = round((640 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "dp"
        }
    }
    xml720.dimen.each { dimen ->
        def value = dimen.text()
        if (isSkipCheck(value)) {
            //skip
        } else if (value.contains("px")) {
            def newValue = value.replace("px", "").toFloat()
            newValue = round((720 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "px"
        } else {
            def newValue = value.replace("dp", "").toFloat()
            newValue = round((720 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "dp"
        }
    }
    xml800.dimen.each { dimen ->
        def value = dimen.text()
        if (isSkipCheck(value)) {
            //skip
        } else if (value.contains("px")) {
            def newValue = value.replace("px", "").toFloat()
            newValue = round((800 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "px"
        } else {
            def newValue = value.replace("dp", "").toFloat()
            newValue = round((800 / 360 * newValue).toFloat(), 1)
            dimen.value = newValue + "dp"
        }
    }

    new XmlNodePrinter(new PrintWriter(new FileWriter(xmlFile480))).print(xml480)
    new XmlNodePrinter(new PrintWriter(new FileWriter(xmlFile540))).print(xml540)
    new XmlNodePrinter(new PrintWriter(new FileWriter(xmlFile600))).print(xml600)
    new XmlNodePrinter(new PrintWriter(new FileWriter(xmlFile640))).print(xml640)
    new XmlNodePrinter(new PrintWriter(new FileWriter(xmlFile720))).print(xml720)
    new XmlNodePrinter(new PrintWriter(new FileWriter(xmlFile800))).print(xml800)
}

private static boolean isSkipCheck(String value) {
    return value.contains("sp") || value.contains("@dimen/")
}

private static void createOrCloneDefaultData(String xmlFileDefault, String xmlFile360) {
    def fileDefault = new File(xmlFileDefault)
    if (!fileDefault.exists()) {
        fileDefault.createNewFile()
        StringBuilder content = new StringBuilder("<resources>\n")
        for (int i = 2; i <= 100; i += 2) {
            content.append("\t<dimen name=\"dp" + i + "\">" + i + "dp</dimen>\n")
        }
        content.append("</resources>")
        BufferedWriter bw = new BufferedWriter(new FileWriter(xmlFileDefault))
        bw.write(content.toString())
        bw.close()
    }

    def file360 = new File(xmlFile360)
    if (fileDefault.exists()) {
        copyFileUsingStream(fileDefault, file360)
    }
}

static float round(float d, int decimalPlace) {
    BigDecimal bd = new BigDecimal(Float.toString(d))
    bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP)
    return bd.floatValue()
}

static void makeFolder(String... paths) {
    for (int i = 0; i < paths.length; i++) {
        def folder = new File(new File(paths[i]).getParent())
        if (!folder.exists()) {
            folder.mkdirs()
        }
    }
}

static void copyFileUsingStream(File source, File dest) throws IOException {
    InputStream is = null
    OutputStream os = null
    try {
        is = new FileInputStream(source)
        os = new FileOutputStream(dest)
        byte[] buffer = new byte[1024]
        int length
        while ((length = is.read(buffer)) > 0) {
            os.write(buffer, 0, length)
        }
    } finally {
        if (is != null) {
            is.close()
        }
        if (os != null) {
            os.close()
        }
    }
}

Bước 2: Import vào module app

apply from: '../multiscreen.gradle'

android {
   clean.dependsOn convertDimens
}

Chúc mừng bạn, việc tích hợp đã hoàn thành!

Kết quả sau khi tích hợp:

Script sẽ tự generate các giá trị tương ứng với từng độ phân giải màn hình. Công thức tính dựa theo độ phân giải 360dp

Với những giá trị bạn không muốn script can thiệp, bạn có thể bỏ qua bằng cách tạo thêm file fixed_dimens.xml trong folder values là được nha.

Đây là những kinh nghiệm trong quá trình làm việc mà mình và các đồng nghiệp tại tập thể công ty Cổ phần Segu đã tìm ra. Rất mong các bạn ủng hộ.

Loading

Là một người thích chia sẻ, tôi tạo ra blog này để mọi người - đặc biệt là các bạn mới vào nghề biết thêm được những kiến thức hữu ích. Rất mong nó sẽ có ích với bạn.

One thought on “Hướng dẫn hỗ trợ đa màn hình trong lập trình Android

  1. It s so hard for me personally at this age and stage of my life to pin point what is normal and what could possibly be a side effect priligy canada Sacubitril valsartan improved congestion to a greater extent than did enalapril

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top