The Use of DataBinding-Implementation of Skin Change Scheme

Posted by burgessm on Sat, 01 Jun 2019 20:31:00 +0200

After learning the Data Binding framework, I think about the application of this scheme in some scenarios, so according to the characteristics of the framework, I write a skin-changing scheme, first look at the effect map:


Data Binding Skin Change

Before looking at the implementation below, I hope you have a basic understanding of the DataBinding framework. If you don't understand, you can refer to my previous article. Android's official framework Data Binding Learning Notes Or reference Official documents.

This skinning scheme supports the switching of font size, color, background color and image, so first create entity class:

public class StyleBean extends BaseObservable
{
    /**
     * Font size level 0-5
     */
    @Bindable
    private int textSizeLivel;
    /**
     * Theme Style Day or Night
     */
    @Bindable
    private boolean isNight;

    public int getTextSizeLivel()
    {
        return textSizeLivel;
    }

    public void setTextSizeLivel(int textSizeLivel)
    {
        this.textSizeLivel = textSizeLivel;
        notifyPropertyChanged(BR.textSizeLivel);
    }

    public boolean isNight()
    {
        return isNight;
    }

    public void setNight(boolean night)
    {
        isNight = night;
        notifyPropertyChanged(BR._all);
    }

    public StyleBean(int textSizeLivel, boolean isNight)
    {
        this.textSizeLivel = textSizeLivel;
        this.isNight = isNight;
    }
}

The BaseObservable class is inherited to automatically update the UI by modifying attributes later.
Then prepare the xml file for font size and color values:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <dimen name="size_level_0">10sp</dimen>

    <dimen name="size_level_1">12sp</dimen>

    <dimen name="size_level_2">14sp</dimen>

    <dimen name="size_level_3">16sp</dimen>

    <dimen name="size_level_4">18sp</dimen>

    <dimen name="size_level_5">20sp</dimen>

    <color name="textColorNight">#222222</color>

    <color name="textColorDay">#aaaaaa</color>

    <color name="backgroundNight">#999999</color>

    <color name="backgroundDay">#eeeeee</color>

</resources>

Initialize these resources directly in the entity class:

    public StyleBean(int textSizeLivel, boolean isNight)
    {
        ···
        initResource();
    }

    private void initResource()
    {
        dimens = new float[6];
        Resources resources = BaseApplication.getContext().getResources();
        dimens[0] = resources.getDimension(R.dimen.size_level_0);
        dimens[1] = resources.getDimension(R.dimen.size_level_1);
        dimens[2] = resources.getDimension(R.dimen.size_level_2);
        dimens[3] = resources.getDimension(R.dimen.size_level_3);
        dimens[4] = resources.getDimension(R.dimen.size_level_4);
        dimens[5] = resources.getDimension(R.dimen.size_level_5);

        mColorDay = resources.getColor(R.color.textColorDay);
        mColorNight = resources.getColor(R.color.textColorNight);

        mBackgroundDay = resources.getColor(R.color.backgroundDay);
        mBackgroundNight = resources.getColor(R.color.backgroundNight);
    }

Then add a custom setter method:

    @BindingAdapter("android:textSize")
    public static void setTextSize(TextView textView, int level)
    {
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, StyleBean.dimens[level]);
    }


    @BindingAdapter("android:textColor")
    public static void setTextColor(TextView textView, boolean isNight)
    {
        textView.setTextColor(isNight ? StyleBean.mColorNight : StyleBean.mColorDay);
    }

    @BindingAdapter("android:background")
    public static void setBackground(View view, boolean isNight)
    {
        view.setBackgroundColor(isNight ? mBackgroundNight : mBackgroundDay);
    }

In this way, when we use expressions to set values in the properties of the control and the tangent expression return type is compatible with our method, we call our method.

Then you can write the code in the layout file:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools">

    <data>

        <import type="com.example.xujiafeng.skinpeeler.StyleBean"/>

        <variable
            name="style"
            type="StyleBean"/>
    </data>

    <RelativeLayout
        android:background="@{style.night}"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="20dp"
        tools:context="com.example.xujiafeng.skinpeeler.activity.MainActivity">

        <LinearLayout
            android:id="@+id/layout1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Font to be set"
                android:textColor="@{style.night}"
                android:textSize="@{style.textSizeLivel}"/>
        </LinearLayout>

        <ImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/layout"
            android:layout_marginTop="10dp"
            android:src="@{style.night ? @drawable/icon_vip_check:@drawable/icon_vip_uncheck}"/>

        <Button
            android:id="@+id/button_text_size"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:text="Set font size"/>

        <Button
            android:id="@+id/button_day"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:text="style"/>
    </RelativeLayout>
</layout>

Since I wrote the custom setter in StyleBean, I had to import this class.

In order to achieve the consistency of App style, the StyleBean implementation class is placed in the Application, and the method of setting style is added:

public class BaseApplication extends Application
{
    @Override
    public void onCreate()
    {
        super.onCreate();
        sContext = this;
        style = new StyleBean(2, false);
    }

    private static StyleBean style;

    public static StyleBean getStyle()
    {
        return style;
    }

    public static Context sContext;

    public static Context getContext()
    {
        return sContext;
    }

    public static void setTextSize(int level)
    {
        style.setTextSizeLivel(level);
    }

    public static void setNight(boolean isNight)
    {
        style.setNight(isNight);
    }

    public static int getTextSize()
    {
        return style.getTextSizeLivel();
    }

    public static boolean getNight()
    {
        return style.isNight();
    }
}

Then when you set the style in Activity, use the global style in Application. The complete code is as follows:

public class MainActivity extends AppCompatActivity
{

    private ActivityMainBinding mBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mBinding.setStyle(BaseApplication.getStyle());
    }
}

Add a method to modify style in StyleBean for buttons to add events:

public class StyleBean extends BaseObservable
{

    public void changeTextSize(View view)
    {
        final String[] items = {"0", "1", "2", "3", "4", "5"};
        new AlertDialog.Builder(view.getContext()).setItems(items, new DialogInterface.OnClickListener()
        {
            @Override
            public void onClick(DialogInterface dialogInterface, int i)
            {
                BaseApplication.setTextSize(Integer.valueOf(items[i]));
            }
        }).create().show();
    }
    public void changeNight(View view)
    {
        final String[] items = {"At night", "Day"};
        new AlertDialog.Builder(view.getContext()).setItems(items, new DialogInterface.OnClickListener()
        {
            @Override
            public void onClick(DialogInterface dialogInterface, int i)
            {
                BaseApplication.setNight(i == 0);
            }
        }).create().show();
    }
}

        <Button
            android:id="@+id/button_text_size"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:onClick="@{(view)->style.changeTextSize(view)}"
            android:text="Set font size"/>

        <Button
            android:id="@+id/button_day"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:onClick="@{(view)->style.changeNight(view)}"
            android:text="style"/>

This allows you to choose font size by clicking on the button.

GitHub address of the project
After learning the Data Binding framework, I think about the application of this scheme in some scenarios, so according to the characteristics of the framework, I write a skin-changing scheme, first look at the effect map:


Data Binding Skin Change

Before looking at the implementation below, I hope you have a basic understanding of the DataBinding framework. If you don't understand, you can refer to my previous article. Android's official framework Data Binding Learning Notes Or reference Official documents.

This skinning scheme supports the switching of font size, color, background color and image, so first create entity class:

public class StyleBean extends BaseObservable
{
    /**
     * Font size level 0-5
     */
    @Bindable
    private int textSizeLivel;
    /**
     * Theme Style Day or Night
     */
    @Bindable
    private boolean isNight;

    public int getTextSizeLivel()
    {
        return textSizeLivel;
    }

    public void setTextSizeLivel(int textSizeLivel)
    {
        this.textSizeLivel = textSizeLivel;
        notifyPropertyChanged(BR.textSizeLivel);
    }

    public boolean isNight()
    {
        return isNight;
    }

    public void setNight(boolean night)
    {
        isNight = night;
        notifyPropertyChanged(BR._all);
    }

    public StyleBean(int textSizeLivel, boolean isNight)
    {
        this.textSizeLivel = textSizeLivel;
        this.isNight = isNight;
    }
}

The BaseObservable class is inherited to automatically update the UI by modifying attributes later.
Then prepare the xml file for font size and color values:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <dimen name="size_level_0">10sp</dimen>

    <dimen name="size_level_1">12sp</dimen>

    <dimen name="size_level_2">14sp</dimen>

    <dimen name="size_level_3">16sp</dimen>

    <dimen name="size_level_4">18sp</dimen>

    <dimen name="size_level_5">20sp</dimen>

    <color name="textColorNight">#222222</color>

    <color name="textColorDay">#aaaaaa</color>

    <color name="backgroundNight">#999999</color>

    <color name="backgroundDay">#eeeeee</color>

</resources>

Initialize these resources directly in the entity class:

    public StyleBean(int textSizeLivel, boolean isNight)
    {
        ···
        initResource();
    }

    private void initResource()
    {
        dimens = new float[6];
        Resources resources = BaseApplication.getContext().getResources();
        dimens[0] = resources.getDimension(R.dimen.size_level_0);
        dimens[1] = resources.getDimension(R.dimen.size_level_1);
        dimens[2] = resources.getDimension(R.dimen.size_level_2);
        dimens[3] = resources.getDimension(R.dimen.size_level_3);
        dimens[4] = resources.getDimension(R.dimen.size_level_4);
        dimens[5] = resources.getDimension(R.dimen.size_level_5);

        mColorDay = resources.getColor(R.color.textColorDay);
        mColorNight = resources.getColor(R.color.textColorNight);

        mBackgroundDay = resources.getColor(R.color.backgroundDay);
        mBackgroundNight = resources.getColor(R.color.backgroundNight);
    }

Then add a custom setter method:

    @BindingAdapter("android:textSize")
    public static void setTextSize(TextView textView, int level)
    {
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, StyleBean.dimens[level]);
    }


    @BindingAdapter("android:textColor")
    public static void setTextColor(TextView textView, boolean isNight)
    {
        textView.setTextColor(isNight ? StyleBean.mColorNight : StyleBean.mColorDay);
    }

    @BindingAdapter("android:background")
    public static void setBackground(View view, boolean isNight)
    {
        view.setBackgroundColor(isNight ? mBackgroundNight : mBackgroundDay);
    }

In this way, when we use expressions to set values in the properties of the control and the tangent expression return type is compatible with our method, we call our method.

Then you can write the code in the layout file:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools">

    <data>

        <import type="com.example.xujiafeng.skinpeeler.StyleBean"/>

        <variable
            name="style"
            type="StyleBean"/>
    </data>

    <RelativeLayout
        android:background="@{style.night}"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="20dp"
        tools:context="com.example.xujiafeng.skinpeeler.activity.MainActivity">

        <LinearLayout
            android:id="@+id/layout1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Font to be set"
                android:textColor="@{style.night}"
                android:textSize="@{style.textSizeLivel}"/>
        </LinearLayout>

        <ImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/layout"
            android:layout_marginTop="10dp"
            android:src="@{style.night ? @drawable/icon_vip_check:@drawable/icon_vip_uncheck}"/>

        <Button
            android:id="@+id/button_text_size"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:text="Set font size"/>

        <Button
            android:id="@+id/button_day"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:text="style"/>
    </RelativeLayout>
</layout>

Since I wrote the custom setter in StyleBean, I had to import this class.

In order to achieve the consistency of App style, the StyleBean implementation class is placed in the Application, and the method of setting style is added:

public class BaseApplication extends Application
{
    @Override
    public void onCreate()
    {
        super.onCreate();
        sContext = this;
        style = new StyleBean(2, false);
    }

    private static StyleBean style;

    public static StyleBean getStyle()
    {
        return style;
    }

    public static Context sContext;

    public static Context getContext()
    {
        return sContext;
    }

    public static void setTextSize(int level)
    {
        style.setTextSizeLivel(level);
    }

    public static void setNight(boolean isNight)
    {
        style.setNight(isNight);
    }

    public static int getTextSize()
    {
        return style.getTextSizeLivel();
    }

    public static boolean getNight()
    {
        return style.isNight();
    }
}

Then when you set the style in Activity, use the global style in Application. The complete code is as follows:

public class MainActivity extends AppCompatActivity
{

    private ActivityMainBinding mBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mBinding.setStyle(BaseApplication.getStyle());
    }
}

Add a method to modify style in StyleBean for buttons to add events:

public class StyleBean extends BaseObservable
{

    public void changeTextSize(View view)
    {
        final String[] items = {"0", "1", "2", "3", "4", "5"};
        new AlertDialog.Builder(view.getContext()).setItems(items, new DialogInterface.OnClickListener()
        {
            @Override
            public void onClick(DialogInterface dialogInterface, int i)
            {
                BaseApplication.setTextSize(Integer.valueOf(items[i]));
            }
        }).create().show();
    }
    public void changeNight(View view)
    {
        final String[] items = {"At night", "Day"};
        new AlertDialog.Builder(view.getContext()).setItems(items, new DialogInterface.OnClickListener()
        {
            @Override
            public void onClick(DialogInterface dialogInterface, int i)
            {
                BaseApplication.setNight(i == 0);
            }
        }).create().show();
    }
}

        <Button
            android:id="@+id/button_text_size"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:onClick="@{(view)->style.changeTextSize(view)}"
            android:text="Set font size"/>

        <Button
            android:id="@+id/button_day"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:onClick="@{(view)->style.changeNight(view)}"
            android:text="style"/>

This allows you to choose font size by clicking on the button.

GitHub address of the project

Topics: Android xml encoding github