16

I'm using ViewPager in my application and I want to support RTL, but when I cahnge my device language to RTl it seems it does'nt working.

I want the tabs to start from the right side (the first fragment in the right).

Please help me to solve it.

EDIT: Here is a picture of it: Tabs start from the left side

Here is my code:

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include
        layout="@layout/app_bar_main"
        android:id="@+id/toolbar_actionbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:openDrawer="start"
        android:layout_below="@id/toolbar_actionbar">

        <android.support.design.widget.CoordinatorLayout
            android:id="@+id/fragment_viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <android.support.v4.view.ViewPager
                android:id="@+id/viewpager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="start"
                app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

            <com.google.android.gms.ads.AdView
                android:id="@+id/adView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                ads:adSize="BANNER"
                ads:adUnitId="@string/banner_ad_unit_id"
                android:layout_gravity="center_horizontal|bottom"
                android:layout_alignParentBottom="true"
                android:layout_centerHorizontal="true">
            </com.google.android.gms.ads.AdView>

        </android.support.design.widget.CoordinatorLayout>

        <android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="250dp"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:menu="@menu/activity_main_drawer"/>

    </android.support.v4.widget.DrawerLayout>

</RelativeLayout>

</android.support.design.widget.CoordinatorLayout>

MainActivity.java:

package ...;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.NavigationView;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;

import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.InterstitialAd;

import java.util.ArrayList;
import java.util.List;


public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

InterstitialAd mInterstitialAd;

@Override
protected void onCreate(Bundle savedInstanceState) {
    setTheme(R.style.AppTheme_NoActionBar);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    setTitle(getString(R.string.app_name));

    ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
    setupViewPager(viewPager);

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
    tabLayout.setupWithViewPager(viewPager);

    mInterstitialAd = new InterstitialAd(this);
    mInterstitialAd.setAdUnitId(getString(R.string.interstitial_ad_unit_id));

    mInterstitialAd.setAdListener(new AdListener() {
        @Override
        public void onAdClosed() {
            requestNewInterstitial();
        }
    });

    requestNewInterstitial();

    AdView mAdView = (AdView) findViewById(R.id.adView);
    AdRequest adRequest = new AdRequest.Builder().build();
    mAdView.loadAd(adRequest);


    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.setDrawerListener(toggle);
    toggle.syncState();

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);
}

private void setupViewPager(ViewPager viewPager) {
    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFragment(new Intro(), getString(R.string.Introduction));
    adapter.addFragment(new S1(), getString(R.string.stage1));
    adapter.addFragment(new S2(), getString(R.string.stage2));
    adapter.addFragment(new S3(), getString(R.string.stage3));
    adapter.addFragment(new S4(), getString(R.string.stage4));
    adapter.addFragment(new S5(), getString(R.string.stage5));
    adapter.addFragment(new S6(), getString(R.string.stage6));
    adapter.addFragment(new S7(), getString(R.string.stage7));
    viewPager.setAdapter(adapter);
}

class ViewPagerAdapter extends FragmentPagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }

    public void addFragment(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }
}


@Override
public void onBackPressed() {
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.START)) {
        drawer.closeDrawer(GravityCompat.START);
    } else {
        super.onBackPressed();
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    switch (id) {
        case R.id.action_contact_us: {
            Intent browserIntent1 = new Intent(Intent.ACTION_VIEW,
                    Uri.parse(getString(R.string.Contact_us_URL)));
            startActivity(browserIntent1);
            return true;
        }
        case R.id.action_share: {
            Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
            sharingIntent.setType("text/plain");
            String shareBody = getString(R.string.shareText);
            sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody);
            startActivity(Intent.createChooser(sharingIntent, getString(R.string.share_via)));
            return true;
        }
        case R.id.action_rate_us: {
            Intent browserIntent2 = new Intent(Intent.ACTION_VIEW,
                    Uri.parse(getString(R.string.Rate_us_URL)));
            startActivity(browserIntent2);
            return true;
        }
        case R.id.action_help: {
            final Intent intent1 = new Intent(MainActivity.this, HelpActivity.class);
            if (mInterstitialAd.isLoaded()) {
                mInterstitialAd.show();
                mInterstitialAd.setAdListener(new AdListener() {
                    public void onAdClosed() {
                        requestNewInterstitial();
                        startActivity(intent1);
                    }
                });
            }
            else {
                startActivity(intent1);
            }
            return true;
        }
        case R.id.action_about_us: {
            final Intent intent2 = new Intent(MainActivity.this, AboutActivity.class);
            if (mInterstitialAd.isLoaded()) {
                mInterstitialAd.show();
                mInterstitialAd.setAdListener(new AdListener() {
                    public void onAdClosed() {
                        requestNewInterstitial();
                        startActivity(intent2);
                    }
                });
            }
            else {
                startActivity(intent2);
            }
            return true;
        }
        case R.id.action_settings: {
            Intent intent3 = new Intent(MainActivity.this, SettingsActivity.class);
            startActivity(intent3);
            return true;
        }
    }
    return super.onOptionsItemSelected(item);
}

@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
    // Handle navigation view item clicks here.
    int id = item.getItemId();

    switch (id){

        case R.id.action_contact_us:
            Intent browserIntent1 = new Intent(Intent.ACTION_VIEW,
                    Uri.parse(getString(R.string.Contact_us_URL)));
            startActivity(browserIntent1);
            break;

        case R.id.action_share:
            Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
            sharingIntent.setType("text/plain");
            String shareBody = getString(R.string.shareText);
            sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody);
            startActivity(Intent.createChooser(sharingIntent, getString(R.string.share_via)));
            break;

        case R.id.action_rate_us:
            Intent browserIntent2 = new Intent(Intent.ACTION_VIEW,
                    Uri.parse(getString(R.string.Rate_us_URL)));
            startActivity(browserIntent2);
            break;

        case R.id.action_help:
            final Intent intent1 = new Intent(MainActivity.this, HelpActivity.class);
            if (mInterstitialAd.isLoaded()) {
                mInterstitialAd.show();
                mInterstitialAd.setAdListener(new AdListener() {
                    public void onAdClosed() {
                        requestNewInterstitial();
                        startActivity(intent1);
                    }
                });
            }
            else {
                startActivity(intent1);
            }
            break;

        case R.id.action_about_us:
            final Intent intent2 = new Intent(MainActivity.this, AboutActivity.class);
            if (mInterstitialAd.isLoaded()) {
                mInterstitialAd.show();
                mInterstitialAd.setAdListener(new AdListener() {
                    public void onAdClosed() {
                        requestNewInterstitial();
                        startActivity(intent2);
                    }
                });
            }
            else {
                startActivity(intent2);
            }
            break;

        case R.id.action_settings:
            Intent intent3 = new Intent(this, SettingsActivity.class);
            startActivity(intent3);
            break;
    }
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
    }

private void requestNewInterstitial() {
    AdRequest adRequest = new AdRequest.Builder().build();
    mInterstitialAd.loadAd(adRequest);
}

}

Thank you!!!

Itiel Maimon
  • 834
  • 2
  • 9
  • 26

9 Answers9

17

After a lot of research I realized how to do it. What I needed to do is to reverse the order of the fragments (when initialize them to the ViewPager) and do viewPager.setCurrentItem(ViewPagerSize). But it's has to happen only if the locale is RTL so I used a method to determine if it is.

This is the code:

 public static boolean isRTL() {
    return isRTL(Locale.getDefault());
 }

 public static boolean isRTL(Locale locale) {
    final int directionality = Character.getDirectionality(locale.getDisplayName().charAt(0));
    return directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT ||
            directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC;
 }

private void setupViewPager(ViewPager viewPager) {
    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
    if (isRTL()) {
        // The view has RTL layout
        adapter.addFragment(new S7(), getString(R.string.stage7));
        adapter.addFragment(new S6(), getString(R.string.stage6));
        adapter.addFragment(new S5(), getString(R.string.stage5));
        adapter.addFragment(new S4(), getString(R.string.stage4));
        adapter.addFragment(new S3(), getString(R.string.stage3));
        adapter.addFragment(new S2(), getString(R.string.stage2));
        adapter.addFragment(new S1(), getString(R.string.stage1));
        adapter.addFragment(new Intro(), getString(R.string.Introduction));
    } else {
        // The view has LTR layout
        adapter.addFragment(new Intro(), getString(R.string.Introduction));
        adapter.addFragment(new S1(), getString(R.string.stage1));
        adapter.addFragment(new S2(), getString(R.string.stage2));
        adapter.addFragment(new S3(), getString(R.string.stage3));
        adapter.addFragment(new S4(), getString(R.string.stage4));
        adapter.addFragment(new S5(), getString(R.string.stage5));
        adapter.addFragment(new S6(), getString(R.string.stage6));
        adapter.addFragment(new S7(), getString(R.string.stage7));
    }
    viewPager.setAdapter(adapter);
}

And for the tabs I had to set the direction to LTR (It's looks messy when it's RTL).

So I used this code (It's only available in API 17+):

 TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        tabLayout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
    }

I could'nt find a way to do this on pre API 17.

Itiel Maimon
  • 834
  • 2
  • 9
  • 26
16

this library RtlViewPager Does the job cleanly

only requires replacing

<android.support.v4.view.ViewPager

with

<com.duolingo.open:rtl-viewpager

in the XML

it also supports tabLayout

using this approach is cleaner since it doesn't require reversing the Adapter logic

Also see this issue report for google

Mohammad Yahia
  • 483
  • 9
  • 19
10

My solution is adding android:rotationY="@integer/view_pager_rotation" attribute to viewPager in xml.

values/integers contains <integer name="view_pager_rotation">0</integer>

values-ldrtl/integers contains <integer name="view_pager_rotation">180</integer>

Note that the content is also get rotated.

Update 02/11/2019

Now it's supported with viewpager2 .

Levon Petrosyan
  • 8,815
  • 8
  • 54
  • 65
8

After a long research I found a very simple solution. Without reverse adapters, rotation child in their classes and etc.

open class RtlViewPager : ViewPager {

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

    override fun onRtlPropertiesChanged(layoutDirection: Int) {
        super.onRtlPropertiesChanged(layoutDirection)
        if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
            rotationY = 180f
        }
    }

    override fun onViewAdded(child: View?) {
        if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
            child?.rotationY = 180f
        }
        super.onViewAdded(child)
    }

}

Rewrite with Java :

public class RtlViewPager extends ViewPager {
    public RtlViewPager(@NonNull Context context) {
        super(context);
    }

    public RtlViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void onRtlPropertiesChanged(int layoutDirection) {
        super.onRtlPropertiesChanged(layoutDirection);
        if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
            setRotationY(180f);
        }
    }

    @Override
    public void onViewAdded(View child) {
        if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
            child.setRotationY(180);
        }
        super.onViewAdded(child);
    }
}
Sana Ebadi
  • 6,656
  • 2
  • 44
  • 44
Valery Boretsky
  • 190
  • 2
  • 9
2

Use this library

https://github.com/duolingo/rtl-viewpager

implementation 'com.android.support:support-core-ui:<choose_your_version>'
implementation 'com.duolingo.open:rtl-viewpager:1.0.3'
younes
  • 742
  • 7
  • 8
1

In the screenshot you gave us , not only the ViewPager is not drawing itseft RTL, nothing is already RTL. How can be that if you set in the application tag supportsRtl="true" ?? You have to set RTL in the your phone Settings under Development options. If I am not wrong you have to set nothing to support RTL automatically in ViewPager After getting your ViewPager propterly adjusted to RTL you can set which fragment you want to be the default fragment putting the following inside onCreate():

viewPager.setCurrentItem(number) 

where number indicates the number of the fragment in a zero based index. For example to select the first one, set 0, for the second set 1, and so on.

Carlos Hernández Gil
  • 1,853
  • 2
  • 22
  • 30
1

it worked for me:

ViewCompat.setLayoutDirection(binding.pager, ViewCompat.LAYOUT_DIRECTION_RTL);
Parisa Baastani
  • 1,713
  • 14
  • 31
0

There is another easy way to do so is by applying rotation to ViewPager and it's child view as follows:

Step 1. Change the rotation of the View pager

if(LocaleUtil.isRTL()) viewPager.setRotationY(180);

Step 2. Then again change the direction of the fragment container which is the child of ViewPager,

if(LocaleUtil.isRTL()) rootView.setRotationY(180);

In my case i had another fragment as child of view pager, so after inflating the layout i changed the direction of rootView.

LocaleUtil.java

public class LocaleUtil {

    // other functions

    public static boolean isRTL() {
        return isRTL(Locale.getDefault());
    }

    private static boolean isRTL(Locale locale) {
        final int directionality = Character.getDirectionality(locale.getDisplayName().charAt(0));
        return directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT ||
            directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC;
    }
}

UPDATE

You can use ViewPager2 it has right-to-left(RTL) support and this will be enabled automatically based on app locale.

Nauman Zubair
  • 1,208
  • 15
  • 27
0

No need of any third party library. Android recently added new UI component called ViewPager2 which supports RTL Layout.

https://developer.android.com/jetpack/androidx/releases/viewpager2

Please find below link to an answer. The answer talks about Migration guide and sample github app to support RTL view pager.

https://stackoverflow.com/a/59558911/651377

Alok Gupta
  • 1,806
  • 22
  • 21