想必大家都会用到微信吧,不知道大家有没有注意到微信的登录界面,但我们在点击输入框的时候,界面上的部分UI布局会因为软键盘的弹出而向上移动,效果如下:(今天使用小萝贝控机的,所以可能会看着效果有点卡卡的)
为什么要说这个呢,对,就是因为需求就是如此,我们的需求是,在键盘弹起的时候,指定的区域最下方必须离软键盘的最上方有多少个像素的间距,其实细细想想,跟微信登录得这个效果差不多的。
起初,也是找了下度娘,好多帖子都说是在manifest配置文件中配置属性便可解决,属性如下:
android:windowSoftInputMode="adjustResize"
windowSoftInputMode全部的属性如下:
“stateUnspecified” 软键盘的状态(是否它是隐藏或可见)没有被指定。系统将选择一个合适的状态或依赖于主题的设置。这个是为了软件盘行为默认的设置。
“stateUnchanged” 软键盘被保持无论它上次是什么状态,是否可见或隐藏,当主窗口出现在前面时。
“stateHidden” 当用户选择该Activity时,软键盘被隐藏——也就是,当用户确定导航到该Activity时,而不是返回到它由于离开另一个Activity。
“stateAlwaysHidden” 软键盘总是被隐藏的,当该Activity主窗口获取焦点时。
“stateVisible” 软键盘是可见的,当那个是正常合适的时(当用户导航到Activity主窗口时)。
“stateAlwaysVisible” 当用户选择这个Activity时,软键盘是可见的——也就是,也就是,当用户确定导航到该Activity时,而不是返回到它由于离开另一个Activity。
“adjustUnspecified” 它不被指定是否该Activity主窗口调整大小以便留出软键盘的空间,或是否窗口上的内容得到屏幕上当前的焦点是可见的。系统将自动选择这些模式中一种主要依赖于是否窗口的内容有任何布局视图能够滚动他们的内容。如果有这样的一个视图,这个窗口将调整大小,这样的假设可以使滚动窗口的内容在一个较小的区域中可见的。这个是主窗口默认的行为设置。
“adjustResize” 该Activity主窗口总是被调整屏幕的大小以便留出软键盘的空间。
“adjustPan” 该Activity主窗口并不调整屏幕的大小以便留出软键盘的空间。相反,当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分。这个通常是不期望比调整大小,因为用户可能关闭软键盘以便获得与被覆盖内容的交互操作
但其实发现,使用adjustResize属性之后,与预期想要达到的效果还是有蛮大差距的,下面我来分享下我的做法:
先上效果图:
首先,adjustResize的属性还是要设置的,其次,我个人认为,adjustResize的属性,配上相对布局,才会显得更加完美,设置adjustResize属性的时候,当软键盘弹出,系统会根据剩下的空余界面来进行重新布局。所以,在这里,布局的最外层使用相对布局。
布局如下(布局中引入了一些自定义的空间,这里不做强调):
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/top_corner_white_16px"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:justh="http://schemas.android.com/apk/res-auto"
>
<ImageView
android:id="@+id/login_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_close_balck"
android:padding="20dp"/>
<TextView
android:layout_width="80dp"
android:layout_height="32dp"
android:background="@drawable/round_tasknew"
android:layout_alignParentRight="true"
android:layout_marginTop="16dp"
android:text="快速注册"
android:gravity="center"
android:textColor="#1A1A1A"
android:textSize="14sp"
android:layout_marginRight="15dp"/>
<LinearLayout
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="20dp"
android:layout_marginLeft="40dp"
android:layout_marginRight="40dp"
android:layout_marginTop="80dp"
android:layout_centerHorizontal="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登录"
android:textSize="24sp"
android:textColor="#1A1A1A"
android:textStyle="bold"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="祝你身体健康,生活愉快"
android:paddingTop="12dp"
android:textSize="15sp"
android:textColor="#1A1A1A"/>
<com.example.dell.softkeyboarddemo.CustomInputView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
justh:input_type="1"
justh:desc_left_drawable="@drawable/login_tel"
android:layout_marginTop="32dp"
justh:hint_text="手机号码"
/>
<com.example.dell.softkeyboarddemo.CustomInputView
android:layout_below="@id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
justh:input_type="3" justh:desc_left_drawable="@drawable/login_password" justh:desc_right_drawable_selected="@drawable/show_password" justh:desc_right_drawable_unselected="@drawable/notshow_password"
justh:hint_text="密码"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@drawable/selector_login_button"
android:textColor="@color/white"
android:gravity="center"
android:textSize="16sp"
android:text="登\t录"
android:layout_marginTop="28dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="忘记密码"
android:textSize="14dp"
android:textColor="#666666"
android:layout_gravity="center_horizontal"
android:paddingTop="20dp"/>
</LinearLayout>
<RelativeLayout
android:id="@+id/linearLayout8"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="20dp"
android:background="@color/white"
android:layout_below="@id/scrollView"
>
<LinearLayout
android:id="@+id/other_login"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_gravity="bottom"
>
<ImageView
android:layout_width="45dp"
android:layout_height="45dp"
android:src="@drawable/qq_login"/>
<ImageView
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginLeft="40dp"
android:layout_marginRight="40dp"
android:src="@drawable/weibo_login"/>
<ImageView
android:layout_width="45dp"
android:layout_height="45dp"
android:src="@drawable/wechat_login"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="其他登录方式"
android:textColor="#1A1A1A"
android:layout_centerHorizontal="true"
android:layout_marginBottom="28dp"
android:layout_above="@id/other_login"/>
</RelativeLayout>
</RelativeLayout>
布局写好了 ,下面,我们要做的,就在在键盘弹起的时候,给我们需要滚动的布局,重新设置其布局的rule,使其能在键盘上方距键盘多少个像素的位置,代码如下:
public class LoginActivity extends AppCompatActivity {
private LinearLayout mScrollView;
int height;
private EditText mInput;
private int mMarginTop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//设置沉浸式状态栏
setStatusBarColor(this, Color.parseColor("#6B000000"));
mInput = ((CustomInputView)findViewById(R.id.name)).getEditText();
mScrollView = (LinearLayout) findViewById(R.id.scrollView);
mInput.getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener);
}
//监听键盘弹起和收起的状态
ViewTreeObserver.OnGlobalLayoutListener mOnGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//获取当前界面可视部分
LoginActivity.this.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
//获取屏幕的高度
int screenHeight = LoginActivity.this.getWindow().getDecorView().getRootView().getHeight();
//此处就是用来获取键盘的高度的, 在键盘没有弹出的时候 此高度为0 键盘弹出的时候为一个正数
int heightDifference = screenHeight - r.bottom;
Log.e("justh", "keyBoardSize: " + heightDifference);
//移动屏幕部分布局
if(height != heightDifference) {
height = heightDifference;
if(height>0){
//键盘弹起状态
moveLayout();
}else {
//键盘收起状态
restoreLayout();
}
}
}
};
private void moveLayout(){
//获取到LayoutParams
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mScrollView.getLayoutParams();
//设置其属性为在父容器的地步
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,RelativeLayout.TRUE);
//记录最初状态的时候,该布局的margin值,一边键盘收起的时候,做好还原
mMarginTop = params.topMargin;
//将当前的topMargin设置为0
params.topMargin = 0;
mScrollView.setLayoutParams(params);
//请求重新布局
mScrollView.requestLayout();
}
private void restoreLayout(){
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mScrollView.getLayoutParams();
//由于removeRule对低版本存在兼容性的问题,所以这里可以采取addRule(设置的属性,0)来做处理
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,0);
//还原到最初的margin值
params.topMargin = mMarginTop;
mScrollView.setLayoutParams(params);
mScrollView.requestLayout();
}
@Override
protected void onDestroy() {
super.onDestroy();
//在该activity销毁的时候,取消监听
mInput.getViewTreeObserver().removeOnGlobalLayoutListener(mOnGlobalLayoutListener);
}
//设置沉浸是状态栏,这里为了方便,直接拷贝过来了,
// 有问题的可以看我的另外一篇博客:http://blog.csdn.net/JUXINHAU/article/details/78009699
public static void setStatusBarColor(Activity activity, int color){
//1:SDK>=5.0
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
activity.getWindow().setStatusBarColor(color);
}
//2:SDK>=4.4
else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
//1:先将其设置成全屏模式 但是电量 信号条还得存在
//该设置会导致不显电量等图标
//activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//2给跟布局添加一个view
View view = new View(activity);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,getStatusBarHeight(activity));
view.setLayoutParams(params);
view.setBackgroundColor(color);
ViewGroup mDecor = (ViewGroup) activity.getWindow().getDecorView();//FrameLayout
mDecor.addView(view);
//3:获取setContentView中的更布局 给其设置statusBar的padding值 或者设置其属性fitSystemWindows
ViewGroup content = (ViewGroup) activity.findViewById(android.R.id.content);
//content.getChildAt(0).setPadding(0,getStatusBarHeight(activity),0,0);
ViewGroup rootView = (ViewGroup) content.getChildAt(0);//我们写的布局的根布局
rootView.setFitsSystemWindows(true);
}
}
//获取状态栏高度
public static int getStatusBarHeight(Activity activity) {
Resources resources = activity.getResources();
int statusBarHeightId = resources.getIdentifier("status_bar_height","dimen","android");
int statusBarHeight = resources.getDimensionPixelOffset(statusBarHeightId);
Log.i("justh","statusBarHeight ----->"+statusBarHeight);
return statusBarHeight;
}
}
还是老规矩,注释都写在代码里了,方便阅读。
到这里,需要的效果便实现了。
但是不知道大家有没有注意到一个问题,当我们需要滚动到键盘顶部的控件的高度大于剩余屏幕的距离的时候,我们做的这些处理其实是达不到语气效果的,如下,再上一个效果:
在图中,我们可以看到,需要滚动的布局过长,已经超出了我们屏幕的剩下高度(这种需求我想大家都会碰到,毕竟现在小屏手机还是有的,哈哈哈)
下面,我来说说我的一个处理方法,如果大家有更好的方法,可以推荐给我下,谢谢!
首先,我在需要随着滚动的布局外面添加了一个ScrollView,让其可以开始滚动起来,因为如果不滚动的话,布局顶端在到达屏幕最上端的时候,便不会再往上去滚动了,其次,见ScrollView设置布局规则:
代码如下:
布局文件:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:justh="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/top_corner_white_16px"
>
<TextView
android:layout_width="56dp"
android:layout_height="32dp"
android:background="@drawable/round_tasknew"
android:layout_alignParentRight="true"
android:layout_marginTop="10dp"
android:text="跳过"
android:gravity="center"
android:textColor="#666666"
android:textSize="14sp"
android:layout_marginRight="15dp"/>
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="80dp">
<LinearLayout
android:id="@+id/ll_scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="40dp"
android:layout_marginRight="40dp"
android:layout_centerHorizontal="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="完善资料"
android:textSize="24sp"
android:textColor="#1A1A1A"
android:textStyle="bold"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="请根据实际情况完善资料"
android:paddingTop="12dp"
android:textSize="15sp"
android:textColor="#1A1A1A"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="28dp">
<RelativeLayout
android:id="@+id/radio_man"
android:layout_width="48dp"
android:layout_height="48dp">
<com.example.dell.softkeyboarddemo.CommonShapeTextView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:tag="男"
android:text="男"
android:textColor="@color/white"
android:textSize="16sp"
justh:corners_radius="23dp"
justh:solid_color="#cccccc"
justh:solid_selected_color="@color/top_bar_color"/>
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:src="@drawable/selector_complete_sex"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/radio_woman"
android:layout_width="88dp"
android:layout_height="48dp">
<com.example.dell.softkeyboarddemo.CommonShapeTextView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="40dp"
android:gravity="center"
android:tag="女"
android:text="女"
android:textColor="@color/white"
android:textSize="16sp"
justh:corners_radius="23dp"
justh:solid_color="#cccccc"
justh:solid_selected_color="@color/top_bar_color"/>
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:src="@drawable/selector_complete_sex"/>
</RelativeLayout>
</LinearLayout>
<com.example.dell.softkeyboarddemo.CustomInputView
android:id="@+id/real_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
justh:input_type="1"
android:layout_marginTop="24dp"
justh:hint_text="真实姓名"
justh:show_left_tag = "false"
/>
<com.example.dell.softkeyboarddemo.CustomInputView
android:id="@+id/birthday_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
justh:input_type="1"
justh:hint_text="出生日期"
justh:show_left_tag = "false"
/>
<com.example.dell.softkeyboarddemo.CustomInputView
android:id="@+id/telephone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
justh:input_type="1"
justh:hint_text="手机号码"
justh:show_left_tag = "false"
/>
<com.example.dell.softkeyboarddemo.CustomInputView
android:layout_width="match_parent"
android:layout_height="wrap_content"
justh:input_type="2"
justh:hint_text="验证码"
justh:show_left_tag = "false"/>
</LinearLayout>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_below="@id/scrollView"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="* 请填写您的手机号,以便获得个性化服务"
android:singleLine="true"
android:textSize="14sp"
android:textColor="#aaaaaa"
android:paddingTop="16dp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@drawable/selector_login_button"
android:textColor="@color/white"
android:gravity="center"
android:textSize="16sp"
android:layout_marginLeft="40dp"
android:layout_marginRight="40dp"
android:text="完\t成"
android:layout_marginTop="28dp"/>
</LinearLayout>
</RelativeLayout>
activity.java代码:
public class CompleteMessageActivity extends AppCompatActivity implements View.OnClickListener{
private ScrollView mScrollView;
int height;
private EditText mInput;
private int mMarginTop;
private RelativeLayout mRadioMan;
private RelativeLayout mRadioWomen;
private LinearLayout mLlScrollview;
private int mScrollViewHeight;
private boolean first = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_complete_message);
setStatusBarColor(this, Color.parseColor("#6B000000"));
mInput = ((CustomInputView)findViewById(R.id.real_name)).getEditText();
mScrollView = (ScrollView) findViewById(R.id.scrollView);
mRadioMan = (RelativeLayout) findViewById(R.id.radio_man);
mRadioWomen = (RelativeLayout) findViewById(R.id.radio_woman);
mLlScrollview = (LinearLayout) findViewById(R.id.ll_scrollview);
mRadioMan.setOnClickListener(this);
mRadioWomen.setOnClickListener(this);
mInput.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Log.e("justh","onGlobalLayout");
Rect r = new Rect();
//获取当前界面可视部分
CompleteMessageActivity.this.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
//获取屏幕的高度
int screenHeight = CompleteMessageActivity.this.getWindow().getDecorView().getRootView().getHeight();
//此处就是用来获取键盘的高度的, 在键盘没有弹出的时候 此高度为0 键盘弹出的时候为一个正数
int heightDifference = screenHeight - r.bottom;
Log.e("justh", "keyBoardSize: " + heightDifference);
//移动屏幕部分布局
if(height != heightDifference) {
height = heightDifference;
if(height>0){
moveLayout(r.bottom);
}else {
restoreLayout();
}
}
}
});
}
private void moveLayout(int height){
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mScrollView.getLayoutParams();
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,RelativeLayout.TRUE);
mMarginTop = params.topMargin;
mScrollViewHeight = mLlScrollview.getHeight();
params.topMargin = 0;
if(mScrollViewHeight >= 0){
params.height = height;
}
mScrollView.setLayoutParams(params);
mScrollView.requestLayout();
mScrollView.scrollTo(0,height);
}
private void restoreLayout(){
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mScrollView.getLayoutParams();
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,0);
params.topMargin = mMarginTop;
Log.i("justh","mScrollViewHeight="+mScrollViewHeight);
if(mScrollViewHeight >= 0){
params.height = mScrollViewHeight;
}
mScrollView.setLayoutParams(params);
mScrollView.requestLayout();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
public static void setStatusBarColor(Activity activity, int color){
//1:SDK>=5.0
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
activity.getWindow().setStatusBarColor(color);
}
//2:SDK>=4.4
else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
//1:先将其设置成全屏模式 但是电量 信号条还得存在
//该设置会导致不显电量等图标
//activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//2给跟布局添加一个view
View view = new View(activity);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,getStatusBarHeight(activity));
view.setLayoutParams(params);
view.setBackgroundColor(color);
ViewGroup mDecor = (ViewGroup) activity.getWindow().getDecorView();//FrameLayout
mDecor.addView(view);
//3:获取setContentView中的更布局 给其设置statusBar的padding值 或者设置其属性fitSystemWindows
ViewGroup content = (ViewGroup) activity.findViewById(android.R.id.content);
//content.getChildAt(0).setPadding(0,getStatusBarHeight(activity),0,0);
ViewGroup rootView = (ViewGroup) content.getChildAt(0);//我们写的布局的根布局
rootView.setFitsSystemWindows(true);
}
}
public static int getStatusBarHeight(Activity activity) {
Resources resources = activity.getResources();
int statusBarHeightId = resources.getIdentifier("status_bar_height","dimen","android");
int statusBarHeight = resources.getDimensionPixelOffset(statusBarHeightId);
Log.i("justh","statusBarHeight ----->"+statusBarHeight);
return statusBarHeight;
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.radio_man:
mRadioMan.setSelected(true);
mRadioWomen.setSelected(false);
break;
case R.id.radio_woman:
mRadioMan.setSelected(false);
mRadioWomen.setSelected(true);
break;
}
}
}
注意:当碰到带有导航栏的手机时,还需要处理关于导航栏这一部分的高度给我们带来的影响。
注释其实跟第一个差不多,这里就不写了,偷个懒,嘿嘿嘿!