简体   繁体   中英

How to get the id of a string resource associated with a TextView?

I can access the TextView instance, get it's resource ID and get it's resource name via getResources().getResourceEntryName() , but I can't seem to find a way to get the id of the string associated with it.

How can I dynamically get the id of the string resource associated with a TextView ?

You can't. A TextView doesn't store the id of the String resources if you call setText(int resid) . All this overloaded method does is get the String from the resources and call the setText(CharSequence text) method.

The only workaround I could was to match the +id of the TextView instances in the layout xml with the name of the string, then use Resources to get pair the two:

/**
     * finds a string id matching a text view id name and returns the value
     * @param textView - text view
     * @param context  - the context
     * @param locale   - the locale string
     * @return  String - resource string if found, empty string otherwise
     */
    public  static String getTextViewStringFromResources(TextView textView,Context context,String locale){
        String result = "";
        final int textViewID = textView.getId();

        if(textViewID <= 0){
            Log.e(TAG,"invalid id for textView " + textView + " text = " + textView.getText());
            result = textView.getText().toString();
            return result;
        }
        //get resources (re-usable)
        final Resources resources = getLocalizedResources(context,locale);
        // get textViews id
        final String entryName = resources.getResourceEntryName(textView.getId());
        // get the string id !! must match the textview id !!
        final int stringID = resources.getIdentifier(entryName,"string",context.getPackageName());
        //return string value for string id found by textview id
        try{
            result = resources.getString(stringID);
        }catch (Resources.NotFoundException e){
            Log.e(TAG,"couldn't find a string id for " + entryName);
            Log.e(TAG,e.getMessage());
        }
        return result;
    }

//!! requires minSDK 17
    @NonNull
    public static Resources getLocalizedResources(Context context, String locale) {
        Locale desiredLocale = new Locale(locale);
        Configuration conf = context.getResources().getConfiguration();
        conf.setLocale(desiredLocale);
        Context localizedContext = context.createConfigurationContext(conf);
        return localizedContext.getResources();
    }

The swapping is partially based on Gunhan Sancar's method of swapping text field.

This works but it pretty much a hack.

Eventually I needed to also change the Google Handwriting Input language programmatically and for that Juuso's ADB Change Language app came in super handy.

For reference, here's a function using Juuso's approach:

final String TAG = "ChangeLanguage"
//change language utils "kindly borrowed" from net.sanapeli.adbchangelanguage: brilliant little app!

    /**
     * updates a Configuration with a list of Locales
     * @param configuration - a configuration to updated
     * @param arrayList - an ArrayList of Locale instances
     * @return the updated configuration
     */
    @TargetApi(24)
    public static Configuration addLocalesToConfiguration(Configuration configuration, ArrayList<Locale> arrayList) {
        configuration.setLocales(new LocaleList((Locale[]) arrayList.toArray(new Locale[arrayList.size()])));
        return configuration;
    }

    /**
     * Change the system language
     * @param lanaguageList - a list of Locale instances to change to
     */
    public static void changeLanguages(ArrayList<Locale> lanaguageList){
        try {
            Class cls = Class.forName("android.app.ActivityManagerNative");
            Method method = cls.getMethod("getDefault", new Class[0]);
            method.setAccessible(true);
            Object invoke = method.invoke(cls, new Object[0]);
            method = cls.getMethod("getConfiguration", new Class[0]);
            method.setAccessible(true);
            Configuration configuration = (Configuration) method.invoke(invoke, new Object[0]);
            configuration.getClass().getField("userSetLocale").setBoolean(configuration, true);
            if (VERSION.SDK_INT >= 24) {
                configuration = addLocalesToConfiguration(configuration, lanaguageList);
            } else {
                configuration.locale = (Locale) lanaguageList.get(0);
            }
            Method method2 = cls.getMethod("updateConfiguration", new Class[]{Configuration.class});
            method2.setAccessible(true);
            method2.invoke(invoke, new Object[]{configuration});
            Log.d(TAG,"locale updated to" + lanaguageList.get(0).toString());
        } catch (Exception e) {
            Log.e(TAG,"error changing locale (double check you're granted permissions to the app first: pm grant your.app.package.here android.permission.CHANGE_CONFIGURATION )");
            e.printStackTrace();
        }
    }

Remember to grant CHANGE_CONFIGURATION to your app.

This is tested with Android 7.1.1 but bare in mind that it will fail on Android 8.0 (and potentially upwards) as getConfiguration is not found (the API is different) under certain build conditions.

The method expects an ArrayList of Locale objects with the language you want to change to on top: eg

ArrayList<Locale> fr = new ArrayList<Locale>();
fr.add(new Locale("fr"));
fr.add(new Locale("en"));

ArrayList<Locale> en = new ArrayList<Locale>();
en.add(new Locale("en"));
en.add(new Locale("fr"));

Make sure the Languages are installed on the devices as well.

If you have other improvement suggestions I'd be more than happy to update the answer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM