
AndroID 动态改变布局
最近项目需求,动态的改变布局,为了增加客户体验,尤其是在输入框出现小键盘的时候,为了避免小键盘遮挡APP内容就需要动态改变布局:
先看下实现效果图:
其实是一个软件的登录界面,初始是第一个图的样子,当软键盘d出后变为第二个图的样子,因为登录界面有用户名、密码、登录按钮,不这样的话软键盘d出后会遮住登录按钮(其实之前的实现放到了ScrollVIEw里面,监听软键盘d出后滚动到底部,软键盘隐藏后滚动到顶部,也是可以的)。
最简单的方法就是多加几个冗余的VIEw,根据软键盘的状态隐藏不需要的VIEw,显示需要的VIEw,但这样感觉太挫了,然后就想起了前两年研究的relativeLayout布局,relativeLayout中子控件的布局都是相对位置,只需要在软键盘d出隐藏时改变应用的位置规则就行了。
先来看一下布局文件
<relativeLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" xmlns:tools="http://schemas.androID.com/tools" androID:ID="@+ID/root" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" androID:padding="20dp" tools:context="${packagename}.${activityClass}" > <relativeLayout androID:ID="@+ID/container" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:layout_alignParenttop="true" > <ImageVIEw androID:ID="@+ID/logo" androID:layout_wIDth="150dp" androID:layout_height="150dp" androID:layout_centerHorizontal="true" androID:scaleType="centerCrop" androID:src="@drawable/ic_launcher" tools:ignore="ContentDescription" /> <TextVIEw androID:ID="@+ID/label" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:layout_below="@ID/logo" androID:layout_centerHorizontal="true" androID:layout_marginleft="10dp" androID:layout_margintop="10dp" androID:text="@string/hello_world" androID:textSize="20sp" /> </relativeLayout> <EditText androID:ID="@+ID/input" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:layout_below="@ID/container" androID:layout_margin="16dp" androID:hint="input sth." tools:ignore="HardcodedText" /></relativeLayout>软键盘的d出隐藏用OnGlobalLayoutListener监听实现,对Activity应用androID:windowsoftinputMode="stateHIDden|adjustResize",这样开始时软键盘不显示,当软键盘d出时布局被Resize。
接下来是代码,所有的代码都在这里了
public class MainActivity extends Activity { private VIEw root; // 最外层布局 private VIEw logo; // logo图标 private VIEw label; // logo附近的文字 private int rootBottom = Integer.MIN_VALUE; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); root = findVIEwByID(R.ID.root); logo = findVIEwByID(R.ID.logo); label = findVIEwByID(R.ID.label); root.getVIEwTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @OverrIDe public voID onGlobalLayout() { Rect r = new Rect(); root.getGlobalVisibleRect(r); // 进入Activity时会布局,第一次调用onGlobalLayout,先记录开始软键盘没有d出时底部的位置 if (rootBottom == Integer.MIN_VALUE) { rootBottom = r.bottom; return; } // adjustResize,软键盘d出后高度会变小 if (r.bottom < rootBottom) { relativeLayout.LayoutParams lp = (LayoutParams) logo.getLayoutParams(); // 如果logo不是水平居中,说明是因为接下来的改变logo大小位置导致的再次布局,忽略掉,否则无限循环 if (lp.getRules()[relativeLayout.CENTER_HORIZONTAL] != 0) { // logo显示到左上角 lp.addRule(relativeLayout.CENTER_HORIZONTAL,0); // 取消水平居中 lp.addRule(relativeLayout.AliGN_PARENT_left); // 左对齐 // 缩小logo为1/2 int height = logo.getHeight(); // getMeasuredHeight() int wIDth = logo.getWIDth(); lp.wIDth = wIDth / 2; lp.height = height / 2; logo.setLayoutParams(lp); // logo下的文字 relativeLayout.LayoutParams labelParams = (LayoutParams) label.getLayoutParams(); labelParams.addRule(relativeLayout.CENTER_HORIZONTAL,0); // 取消水平居中 labelParams.addRule(relativeLayout.BELOW,0); // 取消显示到logo的下方 labelParams.addRule(relativeLayout.RIGHT_OF,R.ID.logo); // 显示到logo的右方 labelParams.addRule(relativeLayout.CENTER_VERTICAL); // 垂直居中 label.setLayoutParams(labelParams); } } else { // 软键盘收起或初始化时 relativeLayout.LayoutParams lp = (LayoutParams) logo.getLayoutParams(); // 如果没有水平居中,说明是软键盘收起,否则是开始时的初始化或者因为此处if条件里的语句修改控件导致的再次布局,忽略掉,否则无限循环 if (lp.getRules()[relativeLayout.CENTER_HORIZONTAL] == 0) { // 居中logo lp.addRule(relativeLayout.CENTER_HORIZONTAL); lp.addRule(relativeLayout.AliGN_PARENT_left,0); // 还原logo为原来大小 int height = logo.getHeight(); int wIDth = logo.getWIDth(); lp.wIDth = wIDth * 2; lp.height = height * 2; logo.setLayoutParams(lp); // logo下的文字 relativeLayout.LayoutParams labelParams = (LayoutParams) label.getLayoutParams(); labelParams.addRule(relativeLayout.CENTER_HORIZONTAL); // 设置水平居中 labelParams.addRule(relativeLayout.BELOW,R.ID.logo); // 设置显示到logo下面 labelParams.addRule(relativeLayout.RIGHT_OF,0); // 取消显示到logo右面 labelParams.addRule(relativeLayout.CENTER_VERTICAL,0); // 取消垂直居中 label.setLayoutParams(labelParams); } } } }); }}当Activity启动时也会进行Layout,此时用rootBottom记录了初始时最外层布局底部的位置,此后当软键盘d出时,布局被压缩,再次获取同一个VIEw底部的位置,如果比rootBottom小说明软键盘d出了,如果大于或等于rootBottom说明软键盘隐藏了。
所有的代码都在上面,也有详细注释,有两点需要注意一下:
1.Activity启动时会进行Layout,此时会调用onGlobalLayout,而且一般会调用两次,这样第二次时会进入else语句,要注意过滤
2.软键盘d出或隐藏时进入onGlobalLayout,此时根据需要缩放logo的大小,并改变logo和Label的位置,这些 *** 作会引起再次onGlobalLayout,需要将之后的onGlobalLayout过滤掉,不然就无限循环了。
可以看到上面代码中的过滤条件,以else语句中的为例,Activity启动时会进入else,此时logo是水平居中状态,会跳过else里面的if语句,这样就处理掉了第一种情况。
当因为软键盘收起进入else时,logo已经因为if语句块变为了显示在左上角,所以会进入else中的if语句,重新改变logo为水平居中,由于修改了logo的大小和位置,会导致再次进入onGlobalLayout,仍是进入else,但此时已经设置logo为水平居中了,不会再次进入else中的if语句,这样通过一个条件判断就处理了上面提到的两点注意事项。
关于addRule
relativeLayout中每一个子控件所应用的规则都是通过数组保存的,如下所示:
public static final int TRUE = -1;public voID addRule(int verb) { mRules[verb] = TRUE; mInitialRules[verb] = TRUE; mRulesChanged = true;}public voID addRule(int verb,int anchor) { mRules[verb] = anchor; mInitialRules[verb] = anchor; mRulesChanged = true;}以某一规则的索引为下标,值就是规则对应的anchor,如果是相对于另一个子控件,值就是另一个子控件的ID,如果是相对于父控件,值就是`TRUE`,即-1,如果没有应用某一规则值就是0,可以看到,removeRule就是把相应位置的值改为了0:
public voID removeRule(int verb) { mRules[verb] = 0; mInitialRules[verb] = 0; mRulesChanged = true; }removeRule是API 17才加的方法,为了在API 17前也能使用,可以使用它的等价方法,像上面的例子中的一样,使用addRule(verb,0)。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
总结以上是内存溢出为你收集整理的Android 动态改变布局实例详解全部内容,希望文章能够帮你解决Android 动态改变布局实例详解所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)