การเปลี่ยนสถานที่ภายในแอพนั้นเอง


197

ผู้ใช้ของฉันสามารถเปลี่ยนภาษาในแอพได้ (พวกเขาอาจต้องการเก็บการตั้งค่าโทรศัพท์เป็นภาษาอังกฤษ แต่อ่านเนื้อหาของแอพของฉันเป็นภาษาฝรั่งเศสดัตช์หรือภาษาอื่น ๆ ... )

เหตุใดจึงทำงานได้ดีอย่างสมบูรณ์ใน 1.5 / 1.6 แต่ไม่ใช่ใน 2.0 อีกต่อไป ???

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch(item.getItemId()) {
    case 201:
        Locale locale2 = new Locale("fr"); 
        Locale.setDefault(locale2);
        Configuration config2 = new Configuration();
        config2.locale = locale2;
        getBaseContext().getResources().updateConfiguration(
            config2, getBaseContext().getResources().getDisplayMetrics());
        // loading data ...
        refresh();
        // refresh the tabs and their content
        refresh_Tab ();   
     break;
     case 201: etc...

ปัญหาคือเมนู "ลดขนาด" มากขึ้นทุกครั้งที่ผู้ใช้ต้องผ่านบรรทัดของรหัสด้านบน ...

นี่คือเมนูที่ย่อขนาด:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    menu.add(0, 100, 1, "REFRESH").setIcon(android.R.drawable.ic_menu_compass);
    SubMenu langMenu = menu.addSubMenu(0, 200, 2, "NL-FR").setIcon(android.R.drawable.ic_menu_rotate);
        langMenu.add(1, 201, 0, "Nederlands");
        langMenu.add(1, 202, 0, "Français");
    menu.add(0, 250, 4, R.string.OptionMenu2).setIcon(android.R.drawable.ic_menu_send);
    menu.add(0, 300, 5, R.string.OptionMenu3).setIcon(android.R.drawable.ic_menu_preferences);
    menu.add(0, 350, 3, R.string.OptionMenu4).setIcon(android.R.drawable.ic_menu_more);
    menu.add(0, 400, 6, "Exit").setIcon(android.R.drawable.ic_menu_delete);

    return super.onCreateOptionsMenu(menu);
}

ฉันควรทำอะไรใน API ระดับ 5 เพื่อให้ทำงานได้อีกครั้ง

นี่คือรหัสเต็มหากคุณต้องการทดสอบสิ่งนี้:

import java.util.Locale;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.Toast;

public class Main extends Activity {
    /** Called when the activity is first created. */


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        SubMenu langMenu = menu.addSubMenu(0, 200, 2, "NL-FR").setIcon(android.R.drawable.ic_menu_rotate);
            langMenu.add(1, 201, 0, "Nederlands");
            langMenu.add(1, 202, 0, "Français");

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()){

        case 201:

            Locale locale = new Locale("nl"); 
            Locale.setDefault(locale);
            Configuration config = new Configuration();
            config.locale = locale;
            getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
            Toast.makeText(this, "Locale in Nederlands !", Toast.LENGTH_LONG).show();
            break;

        case 202:

            Locale locale2 = new Locale("fr"); 
            Locale.setDefault(locale2);
            Configuration config2 = new Configuration();
            config2.locale = locale2;
            getBaseContext().getResources().updateConfiguration(config2, getBaseContext().getResources().getDisplayMetrics());

            Toast.makeText(this, "Locale en Français !", Toast.LENGTH_LONG).show();
            break;  

        }
        return super.onOptionsItemSelected(item);
    }
}

และนี่คือรายการ:

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.cousinHub.ChangeLocale"
          android:versionCode="1"
          android:versionName="1.0">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            <activity android:name=".Main"
                      android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
        <uses-sdk android:minSdkVersion="3" /> 
    </manifest>

นี่คือสิ่งที่ฉันพบ:

<uses-sdk android:minSdkVersion="5" />

=> มันทำงานได้ดีเพียง ...

<uses-sdk android:minSdkVersion="3" />

=> เมนูย่อทุกครั้งที่คุณเปลี่ยนสถานที่ !!!

ตามที่ฉันต้องการให้แอปพลิเคชันของฉันสามารถเข้าถึงได้สำหรับผู้ใช้ที่ 1.5 ฉันควรทำอย่างไร


คุณหมายถึงอะไรโดย "หดตัว"?
CommonsWare

มันเล็กลงทุกครั้ง ฉันควรจะใช้สิ่งอื่นนอกเหนือจาก getBaseContext หรือไม่
ฮิวเบิร์ต

อาจจะไม่ใช่เส้นเหล่านี้ที่สร้างปัญหาเหมือนตอนที่ฉันทำแอพพื้นฐานที่ทำแค่การเปลี่ยนแปลงใน Locale ฉันไม่มี "การลดขนาดเมนู" เหมือนกัน ฉันคิดว่าอาจเป็นเพราะกิจกรรมของฉันเป็นจริง TabActivity แต่ถึงอย่างนั้นฉันก็ไม่สามารถสร้างปัญหาขึ้นมาใหม่ได้ ฉันจะต้องตรวจสอบเพิ่มเติมว่าอะไรคือสาเหตุที่แท้จริงของข้อผิดพลาดนี้ ... ไม่ต้องค้นหาเพิ่มเติม ฉันจะโพสต์คำตอบที่นี่เมื่อฉันพบมัน Cheers, H.
Hubert

ฉันแก้ไขข้อความแรกของฉันโดยให้ตัวอย่างในการสร้างปัญหา และฉันสังเกตเห็นว่าในความเป็นจริงเมื่อฉันเปลี่ยนบรรทัด <uses-sdk android: minSdkVersion = "5" /> เป็น "3" ... แน่นอนปัญหาปรากฏขึ้น!
Hubert

1
คุณสามารถใช้ไลบรารีต่อไปนี้ซึ่งมีรายการภาษาการตั้งค่าสำหรับหน้าจอการตั้งค่าของคุณและแทนที่ภาษาในแอปพลิเคชันของคุณ: github.com/delight-im/Android-Languages
caw

คำตอบ:


151

ผ่านคำถามเดิมไม่ได้เกี่ยวกับสถานที่เกิดเหตุตัวเองทุกคำถามที่เกี่ยวข้องกับสถานที่เกิดเหตุทั้งหมดมีการอ้างอิงถึงคำถามนี้ นั่นเป็นเหตุผลที่ฉันต้องการชี้แจงปัญหาที่นี่ ฉันใช้คำถามนี้เป็นจุดเริ่มต้นสำหรับรหัสการเปลี่ยนตำแหน่งที่ตั้งของฉันเองและพบว่าวิธีการนั้นไม่ถูกต้อง มันใช้งานได้ แต่จนกว่าจะมีการเปลี่ยนแปลงการกำหนดค่าใด ๆ (เช่นการหมุนหน้าจอ) และเฉพาะในกิจกรรมนั้น เล่นกับรหัสในขณะที่ฉันได้ลงเอยด้วยวิธีการต่อไปนี้:

ฉันได้ขยาย android.app.Application และเพิ่มรหัสต่อไปนี้:

public class MyApplication extends Application
{
    private Locale locale = null;

    @Override
    public void onConfigurationChanged(Configuration newConfig)
    {
        super.onConfigurationChanged(newConfig);
        if (locale != null)
        {
            newConfig.locale = locale;
            Locale.setDefault(locale);
            getBaseContext().getResources().updateConfiguration(newConfig, getBaseContext().getResources().getDisplayMetrics());
        }
    }

    @Override
    public void onCreate()
    {
        super.onCreate();

        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);

        Configuration config = getBaseContext().getResources().getConfiguration();

        String lang = settings.getString(getString(R.string.pref_locale), "");
        if (! "".equals(lang) && ! config.locale.getLanguage().equals(lang))
        {
            locale = new Locale(lang);
            Locale.setDefault(locale);
            config.locale = locale;
            getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
        }
    }
}

รหัสนี้ทำให้มั่นใจได้ว่าทุกกิจกรรมจะมีชุดภาษาที่กำหนดเองและจะไม่ถูกรีเซ็ตในการหมุนและกิจกรรมอื่น ๆ

ฉันได้ใช้เวลามากมายในการพยายามเปลี่ยนการตั้งค่าที่จะนำมาใช้ทันที แต่ไม่ประสบความสำเร็จ: ภาษาเปลี่ยนไปอย่างถูกต้องในการรีสตาร์ทกิจกรรม แต่รูปแบบตัวเลขและคุณสมบัติโลแคลอื่น ๆ จะไม่ถูกนำไปใช้

เปลี่ยนเป็น AndroidManifest.xml

อย่าลืมเพิ่มandroid:configChanges="layoutDirection|locale"ในทุกกิจกรรมที่ AndroidManifest รวมandroid:name=".MyApplication"ถึง<application>องค์ประกอบ


1
กิจกรรมที่คุณชอบถูกเรียกจากกิจกรรมหลักหรือไม่? คุณสามารถวางสิ่งนี้ไว้ในประวัติย่อ ... @Void ป้องกันโมฆะ onResume () {ถ้า (! (PreferenceManager.getDefaultSharedPreferences (getApplicationContext ()). getString ("listLanguage", "en") .equal (langPreference)) รีเฟรช (); } super.onResume (); } การรีเฟรชโมฆะส่วนตัว () {เสร็จสิ้น (); Intent myIntent = new Intent (Main.this, Main.class); startActivity (myIntent); }
trgraglia

1
นี่คือสิ่งที่ฉันต้องการจริง ๆ แล้วฉันเคยเรียกว่า updateConfiguration () ใน MainActivity แต่เมนูไม่ได้อัปเดตเมื่อแอปพลิเคชันยุติลงและเริ่มทำงาน วิธีการแก้ปัญหาของคุณถูกต้อง หากคุณต้องการรีเฟรชสตริงทันทีฉันคิดว่าคุณยังคงต้องรีเซ็ตสตริงด้วยตนเอง (เช่นป้ายปุ่มรีเซ็ตหรือเมนูสร้างใหม่)
emeraldhieu

สิ่งนี้รบกวนระบบภาษาของ Android หรือไม่
Kostadin

17
โปรดทราบว่าคุณต้องไม่แก้ไขวัตถุการกำหนดค่าที่ Android ให้ ( configและnewConfig) คุณควรทำสำเนาconfig = new Configuration(config);ก่อน ฉันใช้เวลาหนึ่งชั่วโมงในการดีบักโค้ดนี้ก่อนที่จะค้นพบปัญหา (มันทำให้กิจกรรมกระพริบอย่างไม่รู้จบ)
imgx64

1
เทคนิคนี้มีความผิดพลาดใน Android 7+ เมื่อเริ่มต้นแอพตั้งแต่เริ่มเย็นโดยมีการตั้งค่าขนาดตัวอักษรของระบบเป็นใหญ่ Google ได้เปลี่ยนวิธีการupdateConfiguration()ทำงานดังนั้นบางครั้งกิจกรรมของคุณจะปรากฏใน 2 ภาษาที่แตกต่างกัน (โดยเฉพาะในกล่องโต้ตอบ) ข้อมูลเพิ่มเติม: stackoverflow.com/questions/39705739/…
Mr-IDE

61

หลังจากนอนหลับฝันดีฉันพบคำตอบบนเว็บ (การค้นหาโดย Google แบบง่าย ๆ ในบรรทัดต่อไปนี้ "getBaseContext().getResources().updateConfiguration(mConfig, getBaseContext().getResources().getDisplayMetrics()); ") นี่คือ:

ข้อความลิงค์ => ลิงค์นี้ยังแสดงให้เห็นscreenshotsสิ่งที่เกิดขึ้น!

ความหนาแน่นเป็นปัญหาที่นี่ฉันต้องมีสิ่งนี้ใน AndroidManifest.xml

<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:anyDensity="true"
/>

ที่สำคัญที่สุดคือAndroid: anyDensity = "true""true"

อย่าลืมเพิ่มสิ่งต่อไปนี้ในAndroidManifest.xmlทุกกิจกรรม (สำหรับ Android 4.1 และด้านล่าง):

android:configChanges="locale"

รุ่นนี้จำเป็นต้องใช้เมื่อคุณสร้างคำอธิบาย Android 4.2 (API ระดับ 17) ที่นี่ :

android:configChanges="locale|layoutDirection"

3
ฉันมี Android: anyDensity = "false" เนื่องจากคำแนะนำของ Google (กลยุทธ์สำหรับแอปพลิเคชันรุ่นเก่า - จุดที่ 9: developer.android.com/intl/fr/guide/practices/ ...... ) ระวังตัวด้วย!
Hubert

2
ข้อความบนปุ่มไม่เปลี่ยนแปลงหลังจากฉันเปลี่ยนสถานที่ ฉันสงสัยว่าจะมีวิธีใดบ้างที่จะรีเฟรชวิดเจ็ตทั้งหมดที่มีรีซอร์สสตริง ตอนนี้ฉันกำลังเขียนข้อความใหม่โดยใช้ setText (getString (R.string.button_label)) ใน OnRestart () (แน่นอนว่ามันจะเปลี่ยนไปหลังจากรีสตาร์ทแอพพลิเคชั่น)
emeraldhieu

คุณสามารถเรียกกิจกรรมสร้างใหม่activity.recreate()
ถึงกระ

59

ใน Android M ทางออกที่ดีที่สุดจะไม่ทำงาน ฉันได้เขียนคลาสผู้ช่วยเพื่อแก้ไขสิ่งที่คุณควรเรียกจากคลาสแอปพลิเคชันของคุณและกิจกรรมทั้งหมด (ฉันขอแนะนำให้สร้าง BaseActivity แล้วทำให้กิจกรรมทั้งหมดสืบทอดมาจากมัน

หมายเหตุ : สิ่งนี้จะสนับสนุนทิศทางโครงร่าง RTL อย่างถูกต้อง

ระดับผู้ช่วย:

public class LocaleUtils {

    private static Locale sLocale;

    public static void setLocale(Locale locale) {
        sLocale = locale;
        if(sLocale != null) {
            Locale.setDefault(sLocale);
        }
    }

    public static void updateConfig(ContextThemeWrapper wrapper) {
        if(sLocale != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Configuration configuration = new Configuration();
            configuration.setLocale(sLocale);
            wrapper.applyOverrideConfiguration(configuration);
        }
    }

    public static void updateConfig(Application app, Configuration configuration) {
        if (sLocale != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            //Wrapping the configuration to avoid Activity endless loop
            Configuration config = new Configuration(configuration);
            // We must use the now-deprecated config.locale and res.updateConfiguration here,
            // because the replacements aren't available till API level 24 and 17 respectively.
            config.locale = sLocale;
            Resources res = app.getBaseContext().getResources();
            res.updateConfiguration(config, res.getDisplayMetrics());
        }
    }
}

การประยุกต์ใช้:

public class App extends Application {
    public void onCreate(){
        super.onCreate();

        LocaleUtils.setLocale(new Locale("iw"));
        LocaleUtils.updateConfig(this, getBaseContext().getResources().getConfiguration());
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        LocaleUtils.updateConfig(this, newConfig);
    }
}

BaseActivity:

public class BaseActivity extends Activity {
    public BaseActivity() {
        LocaleUtils.updateConfig(this);
    }
}

11
คำตอบนี้ควรได้รับการยอมรับ ตรวจสอบให้แน่ใจ updateConfig ควรเรียกจากคอนสตรัคไม่ได้มาจาก onCreate ของชั้นเรียนกิจกรรมที่ผมทำผิดพลาดนี้ ..
Hitesh Sahu

1
ขึ้นอยู่กับว่าคุณใช้สิ่งนี้ในโปรแกรมของคุณอย่างไร หากคุณมี overriden onConfigurationChanged () คุณไม่จำเป็นต้องโทร. updateConfig () และนั่นเป็นสาเหตุที่คุณได้รับข้อผิดพลาดเพราะมันถูกเรียกสองครั้ง หากคุณกำลังบอกว่าการโทร. setLocale () ไม่ทำงานนี่เป็นเพราะคุณต้องรีเฟรช UI ของคุณไม่ว่าจะโดยการเรียก recreate () บนกิจกรรมหรือวิธีการของคุณเองในกิจกรรม / ชิ้นส่วน (ฉันชอบแบบเก่าแม้ว่า มันไม่ราบรื่น แต่คุณจะไม่ต้องกังวลกับปัญหา)
RJFares

3
// นำเข้า android.support.v7.view.ContextThemeWrapper; นำเข้า android.view.ContextThemeWrapper; // แก้ไข namespace
Mehdi Rostami

1
คอนBaseActivity()สตรัคเตอร์ไม่ควรเรียกsuper()หรือ
LarsH

1
การแก้ปัญหานี้โดยนัยรวมถึงการใส่<application android:name=".App">ในรายการหรือไม่ วิธีเพิ่มเกี่ยวกับandroid:configChanges="layoutDirection|locale"ทุกกิจกรรมในไฟล์ Manifest?
LarsH

10

นี่คือความคิดเห็นของฉันเกี่ยวกับคำตอบของ Andrey แต่ฉันไม่สามารถใส่รหัสในความคิดเห็นได้

กิจกรรมที่คุณชอบถูกเรียกจากกิจกรรมหลักหรือไม่? คุณสามารถวางสิ่งนี้ในประวัติย่อ ...

@Override
protected void onResume() {
    if (!(PreferenceManager.getDefaultSharedPreferences(
            getApplicationContext()).getString("listLanguage", "en")
            .equals(langPreference))) {
        refresh();
    }
    super.onResume();
}

private void refresh() {
    finish();
    Intent myIntent = new Intent(Main.this, Main.class);
    startActivity(myIntent);
}

2
คุณหมายถึงถ้าฉันต้องการเปลี่ยนภาษาที่รันไทม์ฉันต้องทำกิจกรรมนั้นให้เสร็จและเริ่มกิจกรรมนั้นอีกครั้งเพื่อรับการเปลี่ยนแปลง? มีวิธีอื่นที่จะทำให้มันเปลี่ยนไปเพราะไม่ดีทุกครั้งที่ทำกิจกรรมเสร็จแล้วเริ่มใหม่อีกครั้ง
Shreyash Mahajan

1
ทำได้ดีกว่าactivity.recreate()
ถึงกระ

@trgradlia ฉันต้องการใช้รหัสด้านบน โปรดบอกฉันว่า langPreference คืออะไร ฉันคิดว่าเรากำลังเปรียบเทียบ lang ที่เปลี่ยนไปล่าสุดกับ lang ก่อนหน้านี้
Mahen

1
กิจกรรมมีrecreate()วิธีการใช้recreate()แทนrefresh()รหัสของคุณ วิธีนี้จะช่วยประหยัดรหัส 3 บรรทัดของคุณในrefresh()วิธีการ
Inzimam Tariq IT

1
@InzimamTariqIT ขอบคุณสำหรับเคล็ดลับ ... คำตอบคือเกือบ 7 ปีดังนั้นฉันจะปล่อยมันตามที่เป็นอยู่และผู้คนสามารถค้นหาการปรับปรุงของคุณที่นี่ในความคิดเห็น
trgraglia

6

ฉันไม่สามารถใช้ android ได้: anyDensity = "true" เพราะวัตถุในเกมของฉันจะอยู่ในตำแหน่งที่แตกต่างไปจากเดิมอย่างสิ้นเชิง ...

// การสร้างสถานที่
Locale locale2 = Locale ใหม่ (loc); 
Locale.setDefault (locale2);
การกำหนดค่า config2 = การกำหนดค่าใหม่ ();
config2.locale = locale2;

// อัปเดตสถานที่
mContext.getResources (). updateConfiguration (config2, null);

วิธีแก้ปัญหานี้ดีที่สุดสำหรับฉันแค่ใช้สิ่งนี้code Locale.setDefault(mLocale); Configuration config = getBaseContext().getResources().getConfiguration(); if (!config.locale.equals(mLocale)) { config.locale = mLocale; getBaseContext().getResources().updateConfiguration(config, null); }
Huds0nHawk

1

หากคุณต้องการมีผลกับตัวเลือกเมนูสำหรับเปลี่ยนสถานที่ทันทีคุณต้องทำเช่นนี้

//onCreate method calls only once when menu is called first time.
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    //1.Here you can add your  locale settings . 
    //2.Your menu declaration.
}
//This method is called when your menu is opend to again....
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
    menu.clear();
    onCreateOptionsMenu(menu);
    return super.onMenuOpened(featureId, menu);
}

แต่วิธีนี้จะไม่ล้างเมนูในทุกเมนูที่เปิดหรือไม่ ฉันonMenuOpenedควรจะเรียกเพียงครั้งเดียวหลังจากเปลี่ยนสถานที่เกิดเหตุ
trante
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.