developer tip

애플리케이션 내에서 언어 (로케일)를 변경 한 후 활동을 새로 고치는 방법

copycodes 2020. 12. 11. 08:23
반응형

애플리케이션 내에서 언어 (로케일)를 변경 한 후 활동을 새로 고치는 방법


내 애플리케이션 사용자는 앱 설정에서 언어를 변경할 수 있습니다. 일반 언어 설정에 영향을주지 않고 응용 프로그램 내에서 언어를 변경할 수 있습니까? 이 stackoverflow 질문은 나에게 매우 유용하며 시도해 보았습니다. 언어 변경 후 새로 생성 된 활동은 변경된 새 언어로 표시되지만 현재 활동과 일시 중지 상태 인 이전에 생성 된 활동은 업데이트되지 않습니다. 나는 또한 환경 설정 변경을 즉시 적용하기 위해 많은 시간을 보냈지 만 성공하지 못했습니다. 응용 프로그램이 다시 시작되면 모든 활동이 다시 생성되므로 이제 언어가 올바르게 변경되었습니다.

android:configChanges="locale" 

또한 모든 활동에 대한 매니페스트에 추가되었습니다. 또한 모든 화면을 지원합니다. 현재 활동의 onResume () 메서드에서 아무 작업도 수행하지 않았습니다. 활동을 새로 고치거나 업데이트하는 방법이 있습니까 (완료했다가 다시 시작하지 않고)? onResume () 메서드에서 할 일이 없습니까?


언어 변경 후 새로 생성 된 활동은 변경된 새 언어로 표시되지만 현재 활동 및 일시 중지 상태 인 이전에 생성 된 활동은 업데이트되지 않습니다.

Pre API 11 (Honeycomb), 기존 활동을 새 언어로 표시하는 가장 간단한 방법은 다시 시작하는 것입니다. 이런 식으로 각 리소스를 직접 다시로드하지 않아도됩니다.

private void restartActivity() {
    Intent intent = getIntent();
    finish();
    startActivity(intent);
}

언어 기본 설정이 변경된 경우 호출을 OnSharedPreferenceChangeListener등록합니다 . 내 예에서는 PreferenceActivity 만 다시 시작되지만 플래그를 설정하여 활동 재개에 대한 다른 활동을 다시 시작할 수 있어야합니다.onShredPreferenceChanged()restartActivity()

업데이트 (@stackunderflow에게 감사드립니다) : API 11 (Honeycomb) recreate()부터 restartActivity().

public class PreferenceActivity extends android.preference.PreferenceActivity implements
        OnSharedPreferenceChangeListener {

    // ...

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (key.equals("pref_language")) {
            ((Application) getApplication()).setLocale();
            restartActivity();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }
}

이 주제에 대한 자세한 블로그 게시물 이 있지만 중국어로되어 있습니다. 전체 소스 코드는 github : PreferenceActivity.java에 있습니다.


manifest.xmlandroid:configChanges 에서 설정 하고 다음 과 같은 여러 언어에 대해 여러 디렉토리를 생성 한다고 상상했다면 다음 코드를 제안 할 수 있습니다 (In Activity class).values-fr OR values-nl

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

    Button btn = (Button) findViewById(R.id.btn);
    btn.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            // change language by onclick a button
             Configuration newConfig = new Configuration();
             newConfig.locale = Locale.FRENCH;
             onConfigurationChanged(newConfig);
        }
    });
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    getBaseContext().getResources().updateConfiguration(newConfig, getBaseContext().getResources().getDisplayMetrics());
    setContentView(R.layout.main);
    setTitle(R.string.app_name);

    // Checks the active language
    if (newConfig.locale == Locale.ENGLISH) {
        Toast.makeText(this, "English", Toast.LENGTH_SHORT).show();
    } else if (newConfig.locale == Locale.FRENCH){
        Toast.makeText(this, "French", Toast.LENGTH_SHORT).show();
    }
}

이 코드를 테스트했는데 맞습니다.


문자열 리소스가 기존 로케일에 대해 이미로드되었으므로 이미 열린 활동은 새 로케일의 문자열을 사용하여 자동으로 표시되지 않습니다. 이를 해결하는 유일한 방법은 모든 문자열을 다시로드하고 뷰에서 다시 설정하는 것입니다. 일반적으로를 호출하면 setContentView(...)이를 처리 할 수 ​​있지만 (활동 구조에 따라 다름) 물론 뷰 상태를 잃는 부작용이 있습니다.

public void onResume() {
    super.onResume();
    ...
    if (localeHasChanged) {
        setContentView(R.layout.xxx);
    }
    ...
}

에서 매번 뷰를 다시로드하지 않고 onResume()로케일이 변경된 경우에만 뷰를 다시로드하고 싶을 것입니다 . 뷰를 업데이트 할시기 (예 :)를 확인 localeHasChanged하는 것은 로케일 변경 이벤트를 이전 활동에 전파하는 문제입니다. 이것은 정적 싱글 톤과 같은 상태를 사용하거나이 이벤트를 스토리지에 유지하는 것과 같은 다양한 방법으로 수행 될 수 있습니다.

로케일을 변경할 수있을 때 열 수있는 활동의 수를 최소화 할 수도 있습니다 (예 : 초기 화면 중 하나에서 선택하도록 함).


Android 4.2 (API 17)의 경우 android:configChanges="locale|layoutDirection"AndroidManifest.xml에서 사용해야 합니다. onConfigurationchanged가 jellybean (4.2.1)을 통해 호출되지 않음을 참조하십시오.


우리가 한 방식은 브로드 캐스트를 사용하는 것입니다.

  1. 사용자가 언어를 변경할 때마다 브로드 캐스트를 보냅니다.
  2. 브로드 캐스트 수신기를에 AppActivity.onCreate()등록하고 등록을 취소합니다.AppActivity.onDestroy()
  3. 에서 BroadcastReceiver.onReceive()바로 활동을 다시 시작합니다.

AppActivity 다른 모든 활동이 하위 클래스로 분류하는 상위 활동입니다.


아래는 프로젝트 외부에서 테스트되지 않은 내 코드의 스 니펫이지만 좋은 아이디어를 제공해야합니다.

사용자가 언어를 변경할 때

sendBroadcast(new Intent("Language.changed"));

그리고 부모 활동에서

public class AppActivity extends Activity {

    /**
     * The receiver that will handle the change of the language.
     */
    private BroadcastReceiver mLangaugeChangedReceiver;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ...
        // Other code here
        // ...

        // Define receiver
        mLangaugeChangedReceiver = new BroadcastReceiver() {

            @Override
            public void onReceive(final Context context, final Intent intent) {
                startActivity(getIntent());
                finish();
            }
        };

        // Register receiver
        registerReceiver(mLangaugeChangedReceiver, new IntentFilter("Language.changed"));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // ...
        // Other cleanup code here
        // ...

        // Unregister receiver
        if (mLangaugeChangedReceiver != null) {
            try {
                unregisterReceiver(mLangaugeChangedReceiver);
                mLangaugeChangedReceiver = null;
            } catch (final Exception e) {}
        }
    }
}

또한 언어를 변경 한 활동을 새로 고칩니다 (위 활동을 하위 클래스로 분류하는 경우).

이로 인해 데이터가 손실되지만 중요한 경우 Actvity.onSaveInstanceState()Actvity.onRestoreInstanceState()(또는 이와 유사한)를 사용하여 이미 처리 했어야 합니다.

이것에 대한 당신의 생각을 알려주세요.

건배!


recreate();언어 변경시 활동을 다시 시작 하는 사용할 수 있습니다 .

언어가 변경되면 다음 코드를 사용하여 활동을 다시 시작합니다.

SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
Configuration config = getBaseContext().getResources().getConfiguration();

String lang = settings.getString("lang_list", "");

if (! "".equals(lang) && ! config.locale.getLanguage().equals(lang)) {
      recreate();  //this is used for recreate activity
      Locale locale = new Locale(lang);
      Locale.setDefault(locale);
      config.locale = locale;
      getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
}

이 코드로 내 문제를 해결했습니다.

public void setLocale(String lang) {

        myLocale = new Locale(lang);
        Resources res = getResources();
        DisplayMetrics dm = res.getDisplayMetrics();
        Configuration conf = res.getConfiguration();
        conf.locale = myLocale;
        res.updateConfiguration(conf, dm);

        onConfigurationChanged(conf);
    }



    @Override
    public void onConfigurationChanged(Configuration newConfig) 
    {
        iv.setImageDrawable(getResources().getDrawable(R.drawable.keyboard));
        greet.setText(R.string.greet);
        textView1.setText(R.string.langselection);

        super.onConfigurationChanged(newConfig);

    }

이 메서드를 호출하여 앱 로캘을 변경합니다.

public void settingLocale(Context context, String language) {

    Locale locale;

    Configuration config = new Configuration();

     if(language.equals(LANGUAGE_ENGLISH)) {

        locale = new Locale("en");

        Locale.setDefault(locale);

        config.locale = locale;

    }else if(language.equals(LANGUAGE_ARABIC)){

        locale = new Locale("hi");

        Locale.setDefault(locale);

        config.locale = locale;

    }

    context.getResources().updateConfiguration(config, null);

    // Here again set the text on view to reflect locale change

    // and it will pick resource from new locale

    tv1.setText(R.string.one); //tv1 is textview in my activity

}

참고 : 값 및 값 폴더에 문자열을 넣으십시오.


이 접근 방식은 모든 API 수준 장치에서 작동합니다.

  1. attachBaseContext에 대한 기본 활동을 사용하여 로케일 언어를 설정하고 모든 활동에 대해이 활동을 확장하십시오.

    open class  BaseAppCompactActivity() : AppCompatActivity() {
    
        override fun attachBaseContext(newBase: Context) {
            super.attachBaseContext(LocaleHelper.onAttach(newBase))    
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)           
        }
    }
    
  2. 애플리케이션 attachBaseContext onConfigurationChanged를 사용하고 로케일 언어 설정

    public class MyApplication extends Application {
        private static MyApplication application;
    
        @Override
        public void onCreate() {
            super.onCreate();
        }
    
        public static MyApplication getApplication() {
            return application;
        }
    
        /**
         * overide to change local sothat language can be chnaged from android device  nogaut and above
         */
        @Override
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(LocaleHelper.INSTANCE.onAttach(base));
        }
    
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            setLanguageFromNewConfig(newConfig);
            super.onConfigurationChanged(newConfig);
        }
    
        /*** also handle chnage  language if  device language chnaged **/
        private void setLanguageFromNewConfig(Configuration newConfig){
            Prefs.putSaveLocaleLanguage(this,  selectedLocaleLanguage );
            LocaleHelper.INSTANCE.onAttach(this);
        }
    
  3. Use Locale Helper for handling language changes, this approach work on all device

    object LocaleHelper {
        private var defaultLanguage  :String = KycUtility.KYC_LANGUAGE.ENGLISH.languageCode
    
        fun onAttach(context: Context, defaultLanguage: String): Context {
            return setLocale(context, defaultLanguage)
        }
    
    
    
        fun setLocale(context: Context, language: String): Context {
            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                updateResources(context, language)
            } else updateResourcesLegacy(context, language)
    
        }
    
    
        @TargetApi(Build.VERSION_CODES.N)
        private fun updateResources(context: Context, language: String): Context {
            val locale = Locale(language)
            Locale.setDefault(locale)
    
            val configuration = context.getResources().getConfiguration()
            configuration.setLocale(locale)
            configuration.setLayoutDirection(locale)
    
            return context.createConfigurationContext(configuration)
        }
    
        private fun updateResourcesLegacy(context: Context, language: String): Context {
            val locale = Locale(language)
            Locale.setDefault(locale)
    
            val resources = context.getResources()
    
            val configuration = resources.getConfiguration()
            configuration.locale = locale
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                configuration.setLayoutDirection(locale)
            }
            resources.updateConfiguration(configuration, resources.getDisplayMetrics())
            return context
        }
    }
    

참고URL : https://stackoverflow.com/questions/8049207/how-to-refresh-activity-after-changing-language-locale-inside-application

반응형