1

I'm setting the TabLayout's tabs into a custom font using the below code:

Locale locale = Locale.getDefault();
if (locale.equals(new Locale("ar"))) {
    for (int i = 0; i < tabs.getTabCount(); i++) {
        @SuppressLint("InflateParams") TextView tv = (TextView)LayoutInflater.from(this).inflate(R.layout.custom_tabview,null);
        tv.setTypeface(Typeface.createFromAsset(getAssets(),"fonts/JannaLT-Regular.ttf"));
        Objects.requireNonNull(tabs.getTabAt(i)).setCustomView(tv);
    }
}

This works fine until I replace my ViewPager in XML with the RtlViewPager library, because Google, for some reason, still hasn't fixed the awkwardly incorrect swiping in RTL layouts.

<com.duolingo.open.rtlviewpager.RtlViewPager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

Before:

<androidx.viewpager.widget.ViewPager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

The font doesn't want to change. By debugging I can tell that the if statement is still being executed, and so is the for-loop along with everything inside it. But it just doesn't change. Simply changing it from ViewPager to RtlViewPager, for some reason, causes the TabLayout to refuse any layout changes. Even trying to set the text color to red doesn't work.

Ali Bdeir
  • 4,151
  • 10
  • 57
  • 117

2 Answers2

1

I think it because that the RTLviewpager use a wrapper for the original adapter and then it notifies a dataset changed twice when the adapter attached to the ViewPager.

So the easy way is to attach your custom data observer to the ViewPager adapter, inside a custom TabLayout and whenever the data is changed, apply the custom font again.

public class MyTabLayout extends TabLayout {


    @Nullable
    private PagerAdapter mPagerAdapter;

    private final DataSetObserver mDataSetObserver = new DataSetObserver() {
        @Override
        public void onChanged() {
            super.onChanged();
            notifyDataSetChanged();
        }
    };

    private final Runnable changeFontRunnable = () -> {
        /* ------ APPLY YOUR FONT HERE TO TAB LAYOUT  --------- */
    };

    public MyTabLayout(Context context) {
        super(context);
    }

    public MyTabLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    public void setupWithViewPager(@Nullable ViewPager viewPager) {
        super.setupWithViewPager(viewPager);

        if (viewPager != null && viewPager.getAdapter() != null) {
            unregisterObserver();
            mPagerAdapter = viewPager.getAdapter();
            registerObserver();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        unregisterObserver();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        unregisterObserver();
        registerObserver();
    }

    private void registerObserver() {
        if (mPagerAdapter != null) {
            mPagerAdapter.registerDataSetObserver(mDataSetObserver);
        }
    }

    private void unregisterObserver() {
        if (mPagerAdapter != null) {
            try {
                // may throw exception when the observer is not registered before
                mPagerAdapter.unregisterDataSetObserver(mDataSetObserver);
            } catch (IllegalStateException ignored) {}
        }
    }

    public void notifyDataSetChanged() {
        post(changeFontRunnable);
    }
}
VnM
  • 163
  • 1
  • 9
0

Check the following example:

1) MainActivity.class

public class MainActivity extends AppCompatActivity {

private final String TAG = MainActivity.class.getSimpleName();
private RelativeLayout rl;
private TabLayout tabLayout;
private RtlViewPager viewPager;
private Button b;
private Context langResContext;
private String language;
private SharedPreferences sharedPreferences;
private final String ARABIC = "ar";
private final String ENGLISH = "en";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    sharedPreferences = getSharedPreferences(getResources().getString(R.string.prefs), MODE_PRIVATE);
    rl = (RelativeLayout) findViewById(R.id.rl);
    viewPager = (RtlViewPager) findViewById(R.id.viewpager);
    /**
     * get saved language and update ui
     */
    language = sharedPreferences.getString(getResources().getString(R.string.language),
            getResources().getString(R.string.language_default));
    b = (Button) findViewById(R.id.b);
    if (language.equals(getResources().getString(R.string.Arabic))) {
        language = getResources().getString(R.string.Arabic);
        langResContext = setLanguage(ARABIC, MainActivity.this);
        b.setText(getResources().getString(R.string.English));
    } else {
        language = getResources().getString(R.string.English);
        langResContext = setLanguage(ENGLISH, MainActivity.this);
        b.setText(getResources().getString(R.string.Arabic));
    }
    CustomPagerAdapter customPagerAdapter = new CustomPagerAdapter(getSupportFragmentManager(), 3, MainActivity.this,
            language, langResContext);
    viewPager.setAdapter(customPagerAdapter);
    tabLayout = (TabLayout) findViewById(R.id.tabLayout);
    viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
    b.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (b.getText().toString().equals(getResources().getString(R.string.Arabic))) {
                language = getResources().getString(R.string.Arabic);
                langResContext = setLanguage(ARABIC, MainActivity.this);
                b.setText(getResources().getString(R.string.English));
            } else {
                language = getResources().getString(R.string.English);
                langResContext = setLanguage(ENGLISH, MainActivity.this);
                b.setText(getResources().getString(R.string.Arabic));
            }
            saveLanguage(language);
            onLanguageChanged(language, langResContext, true);
        }
    });
    onLanguageChanged(language, langResContext, false);
}


@TargetApi(Build.VERSION_CODES.KITKAT)
private void onLanguageChanged(String language, Context langResContext, boolean recreate) {
    if (language.equals(getResources().getString(R.string.Arabic))) {
        rl.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
    } else {
        rl.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
    }
    for (int i = 0; i < tabLayout.getTabCount(); i++) {
        tabLayout.getTabAt(i).setCustomView(null);
        @SuppressLint("InflateParams")
        TextView tv = (TextView) LayoutInflater.from(this).inflate(R.layout.custom_tabview, null);
        Objects.requireNonNull(tabLayout.getTabAt(i)).setCustomView(tv);
        if (language.equals(getResources().getString(R.string.Arabic))) {
            tv.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/JannaLT-Regular.ttf"));
            tv.setTextColor(Color.BLUE);
        } else {
            tv.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/SrabiScript.ttf"));
            tv.setTextColor(Color.GREEN);
        }
        switch (i) {
            case 0:
                tv.setText(langResContext.getResources().getString(R.string.page1));
                break;
            case 1:
                tv.setText(langResContext.getResources().getString(R.string.page2));
                break;
            case 2:
                tv.setText(langResContext.getResources().getString(R.string.page3));
                break;
        }
        CustomPagerAdapter customPagerAdapter = (CustomPagerAdapter) viewPager.getAdapter();
        if (customPagerAdapter != null) {
            customPagerAdapter.setLanguage(language, langResContext);
            CustomFragment customFragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag(customPagerAdapter.getFragmentTag(i, viewPager.getId()));
            if (customFragment != null) {
                customFragment.setLanguage(language, langResContext);
                customFragment.applyLanguageChanges();
            }
        }
    }
    if(recreate){
        MainActivity.this.recreate();
    }
}

private void saveLanguage(String language) {
    SharedPreferences.Editor editor = sharedPreferences.edit();
    editor.putString(getResources().getString(R.string.language), language);
    editor.apply();
}

/**
 * https://stackoverflow.com/questions/2900023/change-app-language-programmatically-in-android
 *
 * Thanks to Zoe's Answer
 */
/**
 * * Full locale list: https://stackoverflow.com/questions/7973023/what-is-the-list-of-supported-languages-locales-on-android
 *
 * @param lang language code (e.g. en_US)
 * @return the context
 * PLEASE READ: This method can be changed for usage outside an Activity. Simply add a COntext to the arguments
 */
public Context setLanguage(String lang, Context c) {
    int API = Build.VERSION.SDK_INT;
    if (API >= 17) {
        return setLanguage17(lang, c);
    } else {
        return setLanguageLegacy(lang, c);
    }
}

/**
 * Set language for API 17
 *
 * @param lang
 * @param c
 * @return
 */
@TargetApi(17)
public Context setLanguage17(String lang, Context c) {
    Configuration overrideConfiguration = c.getResources().getConfiguration();
    Locale locale = new Locale(lang);
    Locale.setDefault(locale);
    overrideConfiguration.setLocale(locale);
    //the configuration can be used for other stuff as well
    Context context = createConfigurationContext(overrideConfiguration);//"local variable is redundant" if the below line is uncommented, it is needed
    //Resources resources = context.getResources();//If you want to pass the resources instead of a Context, uncomment this line and put it somewhere useful
    return context;
}

public Context setLanguageLegacy(String lang, Context c) {
    Resources res = c.getResources();
    // Change locale settings in the app.
    DisplayMetrics dm = res.getDisplayMetrics();//Utility line
    android.content.res.Configuration conf = res.getConfiguration();

    conf.locale = new Locale(lang);//setLocale requires API 17+ - just like createConfigurationContext
    Locale.setDefault(conf.locale);
    res.updateConfiguration(conf, dm);

    //Using this method you don't need to modify the Context itself. Setting it at the start of the app is enough. As you
    //target both API's though, you want to return the context as you have no clue what is called. Now you can use the Context
    //supplied for both things
    return c;
}

}

2) CustomPagerAdapter.class

public class CustomPagerAdapter extends FragmentPagerAdapter {

private int numberOfPages = 0;
private Context context;
private String language;
private Context langResContext;
private int[] color = new int[]{Color.RED, Color.MAGENTA, Color.GREEN};

public CustomPagerAdapter(FragmentManager fm, int numberOfPages, Context context,
                          String language, Context langResContext) {
    super(fm);
    this.numberOfPages = numberOfPages;
    this.context = context;
    this.language = language;
    this.langResContext = langResContext;
}

@Override
public Fragment getItem(int position) {
    CustomFragment customFragment = new CustomFragment(context,
            language, langResContext);
    Bundle extras = new Bundle();
    extras.putInt(CustomFragment.NUMBER, position);
    extras.putInt(CustomFragment.COLOR, color[position]);
    customFragment.setArguments(extras);
    return customFragment ;
}

@Override
public int getCount() {
    return this.numberOfPages;
}

public String getFragmentTag(int pos, int vpId) {
    return "android:switcher:" + vpId + ":" + pos;
}

public void setLanguage(String language, Context langResContext) {
    this.language = language;
    this.langResContext = langResContext;
}
}

3) CustomFragment.class

public class CustomFragment extends Fragment {

private int number = 0;
private int color = Color.BLACK;

public static final String NUMBER = "number";
public static final String COLOR = "color";
public static final String LANGUAGE = "language";

private Context context;
private String language;
private Context langResContext;

public CustomFragment(Context context, String language, Context langResContext) {
    super();
    this.context = context;
    this.langResContext = langResContext;
    this.language = language;

}

public CustomFragment() {
    super();
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View layout = (View) inflater.inflate(R.layout.custom_fragment, container, false);
    return layout;
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if (getArguments() != null) {
        Bundle extra = getArguments();
        number = extra.getInt(NUMBER, 0);
        color = extra.getInt(COLOR, Color.BLACK);
    }
    applyLanguageChanges();
}

public void applyLanguageChanges() {
    View view = getView();
    Context langResContext  = this.langResContext != null ? this.langResContext : this.context;
    if (getArguments() != null && view != null) {
        Bundle extra = getArguments();
        number = extra.getInt(NUMBER, 0);
        color = extra.getInt(COLOR, Color.WHITE);
        TextView tv = (TextView) view.findViewById(R.id.tv);
        String text = "0";
        switch (number) {
            case 0:
                text = langResContext.getResources().getString(R.string.page1);
                break;
            case 1:
                text = langResContext.getResources().getString(R.string.page2);
                break;
            case 2:
                text = langResContext.getResources().getString(R.string.page3);
                break;
        }
        tv.setText(text);
        tv.setBackgroundColor(this.color);
    }
}

public void setLanguage(String language, Context langResContext){
    this.language = language;
    this.langResContext = langResContext;
}

}

4) activity_main.xml

<com.google.android.material.tabs.TabLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/tabLayout"
    android:layout_alignParentTop="true"
    android:background="@color/colorPrimary"
    app:tabSelectedTextColor="@android:color/white"
    app:tabTextColor="@android:color/black">

    <com.google.android.material.tabs.TabItem
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/page1"
        android:text="@string/page1">
    </com.google.android.material.tabs.TabItem>

    <com.google.android.material.tabs.TabItem
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/page2"
        android:text="@string/page2">
    </com.google.android.material.tabs.TabItem>

    <com.google.android.material.tabs.TabItem
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/page3"
        android:text="@string/page3">
    </com.google.android.material.tabs.TabItem>

</com.google.android.material.tabs.TabLayout>

<com.duolingo.open.rtlviewpager.RtlViewPager
    android:id="@+id/viewpager"
    android:layout_below="@id/tabLayout"
    android:layout_width="match_parent"
    android:layout_above="@id/b"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:layout_height="match_parent"/>

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:text="@string/English"
    android:id="@+id/b">
</Button>

</RelativeLayout>

5) custom_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:textSize="45sp"
    android:gravity="center"
    android:id="@+id/tv"
    android:textColor="@android:color/white"
    android:text="Page">
</TextView>

</LinearLayout>

6) custom_tabview.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tv"
android:gravity="center"
xmlns:android="http://schemas.android.com/apk/res/android">
</TextView>

7) strings.xml (English)

<resources>
<string name="page1">Page 1</string>
<string name="page2">Page 2</string>
<string name="page3">Page 3</string>

<string name="prefs">prefs</string>
<string name="language">language_pref</string>
<string name="language_default">English</string>
<string name="English">English</string>
<string name="Arabic">العربية</string>

</resources>

8) strings.xml (Arabic)

<string name="page1">صفحة رقم ١</string>
<string name="page2">صفحة رقم ٢</string>
<string name="page3">صفحة رقم ٣</string>

<string name="prefs">prefs</string>
<string name="language">language_pref</string>
<string name="language_default">English</string>
<string name="English">English</string>
<string name="Arabic">العربية</string>

</resources> 

9) Output

output

Brainnovo
  • 1,749
  • 2
  • 12
  • 17