Search code examples
androidandroid-layoutandroid-design-libraryandroiddesignsupport

Android UI scale


I know that the Android system provides a way to scale up the UI on the whole system, but I want something similar but just for my app. Say that in Settings I have an option to scale the UI, and using a slider the UI for my app scales up or down only on my app. Any ideas to do this the easy way?

UPDATE:

I've tried this from onCreate:

private void setScale(float screenRatio) {
    Resources resources = getResources();
    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);

    Configuration configuration = resources.getConfiguration();
    configuration.densityDpi = (int) (metrics.densityDpi * screenRatio);
    metrics.scaledDensity = metrics.scaledDensity * screenRatio;
    resources.updateConfiguration(configuration, resources.getDisplayMetrics());
}

but there are three downsides:

  1. I need to restart the app in order for the changes to have effect
  2. It only scales the view and not the font size
  3. The changes are not visible in real time

This are the same 260dpi screen but with scaled UI.

https://i.sstatic.net/wkzZb.png

https://i.sstatic.net/WF5rY.png

https://i.sstatic.net/GMYRA.png

https://i.sstatic.net/hTREN.png


Solution

  • So this is the solution that I came to and here is a working example of how to implement it: https://github.com/thelong1EU/ScaleUI

    1. Create a ContextWraper object that uses the custom Configuration object

    As I was guessing it all had to to with the context object. The first step is to create a ContextWraper object that contains the modifications you want. You can change the locale and all other qualifiers or device properties from here. See the full list here: https://developer.android.com/reference/android/content/res/Configuration.html#lfields

    public class ScaledContextWrapper extends ContextWrapper {
    
        public ScaledContextWrapper(Context base) {
            super(base);
        }
    
        @SuppressWarnings("deprecation")
        public static ScaledContextWrapper wrap(Context context) {
            Resources resources = context.getResources();
            Configuration configuration = resources.getConfiguration();
            DisplayMetrics metrics = resources.getDisplayMetrics();
    
            configuration.densityDpi = (int) (metrics.densityDpi * sScaleRatio);
            configuration.fontScale = sScaleRatio;
    
            if (SDK_INT > 17) {
                context = context.createConfigurationContext(configuration);
            } else {
                resources.updateConfiguration(configuration, resources.getDisplayMetrics());
            }
    
            return new ScaledContextWrapper(context);
        }
    
    }
    

    2. Setup the new context where you want the modifications to take place

    If you want to change the scale of the entire Activity you need to override the attachBaseContext() and pass the modified Context object:

    @Override
    protected void attachBaseContext(Context newBase) {
        Context context = ScaledContextWrapper.wrap(newBase);
        super.attachBaseContext(context);
    }
    

    If you want to change the scale just for one fragment or even one view you need to use a LayoutInflater object constructed using the modified Context. This is an example for scaling the fragment:

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        Context scaledContext = ScaledContextWrapper.wrap(getActivity());
        LayoutInflater scaledInflater = LayoutInflater.from(scaledContext);
    
        return scaledInflater.inflate(R.layout.fragment_layout, container, false);
    }