android适配右到左布局注意事项

  呜呼,伊朗的项目终于做完了,大部分都是在整理右到左布局的需求。好在android sdk 从API17(Android4.2)开始支持右到左布局的需求,但是会有很多坑需要去填。
  Android中的大部分组件是支持右到左布局的,只需要在Androidmanifest中配置如下:

    <application
    	 ....
        android:supportsRtl="true">
    </application>

我们先看一个demo,MainActivity对应的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.test.activity.MainActivity">
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        android:background="@color/fastlane_background"
        />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical"
        android:text="hello world "
        android:background="@color/selected_background"/>
</LinearLayout>

我们可以动态设置系统的语言来模仿用户环境,如下将app的配置设置为波斯语,当然,最简单的是到手机设置中直接设置当前语言为波斯语(设置有风险,语言需谨慎~~):


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Locale locale = new Locale("fa_IR");//fa_IR是波斯语(伊朗)的代码
        Locale.setDefault(locale);
        Configuration config = new Configuration();
        config.locale = locale;
        DisplayMetrics dm = getResources().getDisplayMetrics();
        getResources().updateConfiguration(config, dm);
        setContentView(R.layout.activity_main);
    }

以下是没有加android:supportsRtl="true"的布局,很正常不过的布局吧?
在这里插入图片描述
那么加了之后呢?
在这里插入图片描述
显然toolbar的内容已经反过来了。且慢,textview好像没有任何变化?_?,难道textview不支持右到左布局?

1、textview gravity的右到左布局适配

大家知道textview有一个gravity的属性,其中可以通过以下两种方式指定textview的内容在左边还是在右边
  1) left,right:绝对位置,指定内容显示在textview的左边还是右边,这个比较好理解
  2) start,end:这个大家应该见过,该值作用的结果与系统布局方向有关,如下所示:
在这里插入图片描述
  举一反三,drawableStart,drawableEnd和layout_marginStart,layout_marginEnd以及layout_toStartOf,layout_toEndOf等也是同理,都是相对布局方向而言的。
  有同学马上就会想到,直接指定textview的gravity为start不就解决上面的问题了嘛!修改后的textview如下:

    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical|start"
        android:text="hello world "
        android:background="@color/selected_background"/>

效果如下:
在这里插入图片描述
不对啊,这不按套路出牌呀。内容怎么还是在左边。。。
同学们发现没有,内容是英文的,而伊朗是用波斯语言,会不会与语言有关呢?我们再加一个textView显示波斯语言的:


    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/fti_left_color"
        android:gravity="center_vertical|start"
        android:text="فارسی" />

结果如下:
在这里插入图片描述
是不是反过来了?由于波斯语言是右到左显示的,所以其内容也是默认居右边的。这说明,textview 的内容布局位置与文字显示的方向相关的。为了与伊朗用户的用户习惯保持一致,就算textview的内容是英文的,也应该居右边显示,那么如何做到呢?
Android提供了Java代码动态获取当前布局方向,如下:

    /**
    * 
    * @param context
    * @return 如果是右到左布局,返回true
    */
   public boolean isRtl(Context context) {
       return context.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
   }

然后我们可以通过该方法设置textView的gravity

        textView.setGravity(isRtl(this) ? Gravity.RIGHT:Gravity.LEFT);

结果大家显然都知道了,这里就不列出来了。
但是这样还是觉得很麻烦,有没有更便捷的属性?有!

namedescription描述
android:layoutDirectionattribute for setting the direction of a component’s layout设置组件的布局排列方向
android:textDirectionattribute for setting the direction of a component’s text设置组件的文字排列方向
android:textAlignmentattribute for setting the alignment of a component’s text设置文字的对齐方式
getLayoutDirectionFromLocale()method for getting the Locale-specified direction获取指定地区的惯用布局方式
其中,android:layoutDirection和android:textDirection的值有:
valuetypedescription
----------------
inheritintHorizontal layout direction is inherited
继承水平方向
rtlintHorizontal layout direction is from Right to Left
布局方向是右到左
ltrintHorizontal layout direction is from Left to Right
布局方向是左到右
localeintHorizontal layout direction is deduced from the default language script for the locale
从区域设置的默认语言脚本推导出水平布局方向

android:textAlignment的值有:

ConstantvalueDescription
center4Center the paragraph, for example: ALIGN_CENTER.
居中
gravity1Default for the root view. The gravity determines the alignment, ALIGN_NORMAL, ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s text direction.
由view通过gravity属性定义对齐方式
inherit0Default.
默认
textEnd3Align to the end of the paragraph, for example: ALIGN_OPPOSITE.
与段落的末尾对齐
textStart2Align to the start of the paragraph, for example: ALIGN_NORMAL.
与段落的开头对齐
viewEnd6Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved layoutDirection is LTR, and ALIGN_LEFT otherwise.
与view的末尾对齐,
viewStart5Align to the start of the view, which is ALIGN_LEFT if the view’s resolved layoutDirection is LTR, and ALIGN_RIGHT otherwise.
与view的开头对齐
还是原来的布局,这里多加一行
  <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/selected_background"
        android:gravity="center_vertical|start"
        android:textDirection="locale"
        android:text="hello world " />

效果如下
在这里插入图片描述
要给辣么多个textview添加该属性,多麻烦!没事,有全局的设置~~

<resources>

    <style name="AppTheme" parent="@style/Theme.AppCompat.Light.NoActionBar" >
        <item name="android:textViewStyle">@style/TextDirection</item>
    </style>
    <style name="TextDirection" parent="android:Widget.TextView">
        <item name="android:textDirection">locale</item>
    </style>
</resources>

然后在Androidmanifest文件中引用该style


    <application
        ......
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        .......
    </application>

类似的还有EditText


    <style name="AppTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
		 .....
        <item name="editTextStyle">@style/EditTextStyle</item>
    </style>
    
    <style name="EditTextStyle" parent="@android:style/Widget.EditText">
        <item name="android:textAlignment">viewStart</item>
        <item name="android:gravity">start</item>
        <item name="android:textDirection">locale</item>
    </style>

2、组件之间的相对位置

在处理组件之间的相对位置,一般会用到layout_marginLeft,layout_marginRight或者layout_toLeftOf,layout_toRightOf等。那么如果你的app需要兼顾右到左布局的时候,这些布局约束就不合适了,需要用Start,End来代替Left,Right。天呐,如果有几十上百个布局文件,不是要改死程序员了么?别怕,Android studio已经为我们提供了一键适配右到左布局的需求,如下图所示
在这里插入图片描述
然后会弹出一个对话框,直接点击“run”
在这里插入图片描述
接着会提示你哪些需要添加适配属性的,直接点击“Do Refactor”
在这里插入图片描述
如果有不需要修改的,可以选中不修改的文件,或者某一行代码。选择Remove即可
在这里插入图片描述
这样我们的布局文件就自定添加start、end属性。
如果布局中使用了drawableStart或者drawableEnd属性就需要注意了,需要考虑图标是否需要跟随布局方向变化而变化,例如作为方向标示的就不应该变化位置,因此需要依旧使用drawableLeft和drawableRight

3、布局约束的右到左布局适配

在开发中,有时需要动态移动view的位置,一般的做法是修改view的layout,设置x,y轴或者设置marginLayoutParams等,当然还有一般的动画。
还是我们的mainActivity,对应的布局有两个textview1,textview2,点击textview2会修改textview1的位置,对应的代码如下:

    private TextView textview1, textview2;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        Locale locale = new Locale("fa_IR");
//        Locale.setDefault(locale);
//        Configuration config = new Configuration();
//        config.locale = locale;
//        DisplayMetrics dm = getResources().getDisplayMetrics();
//        getResources().updateConfiguration(config, dm);
        setContentView(R.layout.activity_main);
        textview1 = findViewById(R.id.textview1);
        textview2 = findViewById(R.id.textview2);
    }
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.textview2) {
            ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) textview1.getLayoutParams();
            marginLayoutParams.leftMargin += 20 ;
            marginLayoutParams.topMargin += 10;
            textview1.setLayoutParams(marginLayoutParams);
        }
    }

效果如果,我们成功的移动了textview1
在这里插入图片描述
我们再解除被注释掉的代码,再试试看:
在这里插入图片描述
怎么没有往右下角移动?_? 不是已经设置了leftMargin了么,怎么只有topMargin有效?
在右到左布局下,Android的会从右到左依次绘制view,虽然我们设置了leftMargin,但是,系统在绘制view的时候会以rightMargin约束条件为准,如下图所示,所以就导致了leftMargin无效的问题。
需要注意的是,在右到左布局下,系统坐标并没有改变,getLeft,getTop,getX,getY等值没有发生变化。
在这里插入图片描述
所以,我们需要稍作修改

  @Override
    public void onClick(View v) {
        if (v.getId() == R.id.textview2) {
            ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) textview1.getLayoutParams();
            marginLayoutParams.rightMargin += 20 ;
            marginLayoutParams.topMargin += 10;
            textview1.setLayoutParams(marginLayoutParams);
        }
    }

结果如下:
在这里插入图片描述
那如果在右到左布局下,textview的移动动画与左到右布局下一致呢?很简单,同时设置leftMargin和rightMargin即可:

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.textview2) {
            int screenWidth = getWindowManager().getDefaultDisplay().getWidth() ;
            ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) textview1.getLayoutParams();
            marginLayoutParams.leftMargin += 20;
            marginLayoutParams.rightMargin = screenWidth
                - textview1.getWidth()-marginLayoutParams.leftMargin ;
            marginLayoutParams.topMargin += 10;
            textview1.setLayoutParams(marginLayoutParams);
        }
    }

效果如下
在这里插入图片描述

总结

1、如果使用到drawableLeft、drawableRight属性,请确定图标是否需要跟随系统布局方法改变而改变,如果是,请修改为drawableStart、drawableEnd;反之不用
2、在代码中需要动态修改view的布局约束,例如marginLayoutParams,layoutParams.setMargins()等,需要同时考虑left和right,或者采用修改layout的方式。
3、获取view的位置可以通过以下代码:

     int[] location = new int[2];
     view.getLocationOnScreen(location);
     float x = location[0];
     float y = location[1]

4、在relativelayout中添加layout rule的时候,用START_OF
、END_OF代替LEFT_OF、RIGHT_OF。
5、在右到左布局下,其坐标布局方式不变,getLeft,getRight等不变。变的是布局约束,view的绘制受right(start)约束。
6、一些方向图标,重新做一个相对方向的放到 drawable-ldrtl-xxxhdpi 包下
7、textview,editText需要对内容做RTL处理,设置style,详情见第一节
8、在显示文件路径时候,如果包含波斯语,会导致路径显示错误的情况,需要在string的前后加上\u202D 和\u202C

        String content = "sdcard\\فارسی\\3D\\فارسی.mp3";
        textview1.setText("\u202D"+content+"\u202C");

或者使用如下代码


    public String getLTRString(String content) {
        return BidiFormatter.getInstance().unicodeWrap(content, TextDirectionHeuristics.LTR);
    }

实际显示如下:
在这里插入图片描述

9、如果需要修改view的位置,请不要使用setX,setY方式。推荐采用ViewGroup.MarginLayoutParams来设置margin,或者采用layout的方式。同时需要注意该约束是相对父view而言的。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值