解決RecyclerView實現聊天介面,但點選下面的EditText後彈出的輸入法會遮蓋RecyclerView內容的方法
學習Android也將近4個年頭了,一直想寫點自己所學的內容來幫助後來人,為網際網路奉獻自己的一份力量,也算自己的積累。但是之前由於自己的惰性一直沒有下筆,那就從今天這一篇開始吧!
我們做Android開發經常會有做即時聊天的需求,產品經理不管做啥APP,都要塞個IM,那就算是扯上社交了。我今天要講的就是如何實現Android聊天介面。可能聽到這個,在看文章的你就準備關閉網頁瞭然後開噴了,但請別急,我寫這篇文章自然有寫這篇文章的道理。
1、首先我們建立專案,寫好佈局。佈局比較簡單,上面是個RecyclerView,用來顯示聊天的對話。下面一個水平佈局的LinearLayout,裡面一個EditText作為文字編輯框,一個Button用來發送。這樣佈局就算寫好了。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView_chat" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:overScrollMode="never" android:scrollbars="vertical"/> <LinearLayout android:layout_width="match_parent" android:layout_height="55dp" android:orientation="horizontal"> <EditText android:id="@+id/editText_message" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:hint="輸入訊息內容" android:padding="10dp" /> <Button android:id="@+id/btn_send" android:layout_width="wrap_content" android:layout_height="match_parent" android:text="傳送" /> </LinearLayout> </LinearLayout>
2、我們在MainActivity裡面初始化一下佈局,同樣也非常的簡單。
package com.liuxiaoyi.chatdemo; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.widget.Button; import android.widget.EditText; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private RecyclerView recyclerView_chat; private EditText editText_message; private Button btn_send; private ChatAdapter chatAdapter; private ArrayList<Chat> chatArrayList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView_chat = (RecyclerView) findViewById(R.id.recyclerView_chat); editText_message = (EditText) findViewById(R.id.editText_message); btn_send = (Button) findViewById(R.id.btn_send); chatArrayList.add(new Chat("你好啊。",ChatAdapter.TYPE_SEND)); chatArrayList.add(new Chat("你叫什麼名字啊?",ChatAdapter.TYPE_SEND)); chatArrayList.add(new Chat("你好,我叫小麗",ChatAdapter.TYPE_RECEIVE)); chatArrayList.add(new Chat("你是哪裡人啊?",ChatAdapter.TYPE_SEND)); chatArrayList.add(new Chat("我是湖南長沙人",ChatAdapter.TYPE_RECEIVE)); chatArrayList.add(new Chat("好巧啊,我也是長沙的",ChatAdapter.TYPE_SEND)); chatArrayList.add(new Chat("咱們真有緣分誒",ChatAdapter.TYPE_SEND)); chatArrayList.add(new Chat("我也覺得呢",ChatAdapter.TYPE_RECEIVE)); chatAdapter = new ChatAdapter(MainActivity.this,chatArrayList); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(MainActivity.this); recyclerView_chat.setLayoutManager(linearLayoutManager); recyclerView_chat.setAdapter(chatAdapter); } }
3、構造一下聊天實體
package com.liuxiaoyi.chatdemo;
public class Chat {
private String chatContent;
private int type;
public Chat(String chatContent, int type) {
this.chatContent = chatContent;
this.type = type;
}
public String getChatContent() {
return chatContent;
}
public void setChatContent(String chatContent) {
this.chatContent = chatContent;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
4、最後寫好介面卡
package com.liuxiaoyi.chatdemo;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
public class ChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
public static final int TYPE_SEND = 0;
public static final int TYPE_RECEIVE = 1;
private Context context;
private ArrayList<Chat> chatArrayList = new ArrayList<>();
public ChatAdapter(Context context,ArrayList<Chat> chatArrayList){
this.context = context;
this.chatArrayList = chatArrayList;
}
@Override
public int getItemViewType(int position) {
if(chatArrayList.get(position).getType() == TYPE_SEND){
return TYPE_SEND;
}else if(chatArrayList.get(position).getType() == TYPE_RECEIVE){
return TYPE_RECEIVE;
}else {
return super.getItemViewType(position);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if(viewType==TYPE_SEND){
view = LayoutInflater.from(context).inflate(R.layout.item_send,parent,false);
return new SendViewHolder(view);
}else {
view = LayoutInflater.from(context).inflate(R.layout.item_receive,parent,false);
return new ReceiveViewHolder(view);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(holder instanceof SendViewHolder){
SendViewHolder viewHolder = (SendViewHolder) holder;
viewHolder.textView_send.setText(chatArrayList.get(position).getChatContent());
}else if(holder instanceof ReceiveViewHolder){
ReceiveViewHolder viewHolder = (ReceiveViewHolder) holder;
viewHolder.textView_receive.setText(chatArrayList.get(position).getChatContent());
}
}
@Override
public int getItemCount() {
return chatArrayList.size();
}
private class SendViewHolder extends RecyclerView.ViewHolder{
private TextView textView_send;
public SendViewHolder(View itemView) {
super(itemView);
textView_send = (TextView) itemView.findViewById(R.id.textView_send);
}
}
private class ReceiveViewHolder extends RecyclerView.ViewHolder{
private TextView textView_receive;
public ReceiveViewHolder(View itemView) {
super(itemView);
textView_receive = (TextView) itemView.findViewById(R.id.textView_receive);
}
}
}
這樣也就算完工了。我們看一下效果。
感覺好像不錯,是這麼個意思。這時候我們心裡就在暗自慶幸,聊天介面也不過如此嘛,做出微信這樣的APP也太簡單了,老子天下程式碼水平天下第一。
但是稍微點選一下,我們就會發現問題。我們看圖
當我們點選EditText的時候,鍵盤彈了出來。但是他好像和微信QQ又那麼一點點不一樣,微信QQ的聊天框彈出之後,鍵盤是不會遮住聊天內容的。
這怎麼能難倒我們呢?網上搜一下解決方案不就可以了麼?於是我們開始上網搜。
網上給出的方法總得來說有兩種。
第一種:首先在清單檔案中的Activity節點增加android:windowSoftInputMode="adjustResize"這麼一句話,然後
LinearLayoutManager manager = new LinearLayoutManager(context);
manager.setStackFromEnd(true);
recyclerView.setLayoutManager(manager);
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.liuxiaoyi.chatdemo" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity"
android:windowSoftInputMode="stateHidden|adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity修改為package com.liuxiaoyi.chatdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.Button;
import android.widget.EditText;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView_chat;
private EditText editText_message;
private Button btn_send;
private ChatAdapter chatAdapter;
private ArrayList<Chat> chatArrayList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView_chat = (RecyclerView) findViewById(R.id.recyclerView_chat);
editText_message = (EditText) findViewById(R.id.editText_message);
btn_send = (Button) findViewById(R.id.btn_send);
chatArrayList.add(new Chat("你好啊。",ChatAdapter.TYPE_SEND));
chatArrayList.add(new Chat("你叫什麼名字啊?",ChatAdapter.TYPE_SEND));
chatArrayList.add(new Chat("你好,我叫小麗",ChatAdapter.TYPE_RECEIVE));
chatArrayList.add(new Chat("你是哪裡人啊?",ChatAdapter.TYPE_SEND));
chatArrayList.add(new Chat("我是湖南長沙人",ChatAdapter.TYPE_RECEIVE));
chatArrayList.add(new Chat("好巧啊,我也是長沙的",ChatAdapter.TYPE_SEND));
chatArrayList.add(new Chat("咱們真有緣分誒",ChatAdapter.TYPE_SEND));
chatArrayList.add(new Chat("我也覺得呢",ChatAdapter.TYPE_RECEIVE));
chatAdapter = new ChatAdapter(MainActivity.this,chatArrayList);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(MainActivity.this);
linearLayoutManager.setStackFromEnd(true);
recyclerView_chat.setLayoutManager(linearLayoutManager);
recyclerView_chat.setLayoutManager(linearLayoutManager);
recyclerView_chat.setAdapter(chatAdapter);
}
}
執行起來。大神誠不欺我啊,果然可以滾動了。聊天介面也不過如此嘛,做出微信這樣的APP也太簡單了,老子天下程式碼水平天下第一。
又稍微點選一下,又發現了問題
開始的介面是這樣子的
點選聊天框後變成了這樣
發現問題沒有?微信QQ點選之後是滾動到底部,而不是隻滾動到這一頁的底部的哪一個item啊,這不是坑人嘛。唉,大神居然也會欺騙我,世態炎涼啊。
第一種方法不行 我們看第二種方法:
1、設定RecyclerView.scrollToPosition(list.size()
- 1);
2、設定mLayoutManager.setStackFromEnd(true);
3、RecyclerView用ScrollView包裹起來,然後獲取螢幕的高度和鍵盤的高度,計算出ScrollView需要滑動到的位置。
大神你不是騙我吧,前面我還能理解,用ScrollView包裹RecyclerView????(此處黑人臉問號),這幹嘛還用RecyclerView,直接ScrollView裡面add子view不就好了麼。
這種方案我就不試了,如果在看文章的你有興趣,可以嘗試一下。
好了,文章寫到這裡就結束了,拜拜。
當然沒有啦,我還沒有提出解決方案。接下來看我的。(此處柯南背景音樂),新几子哇,一次莫黑一多次。
首先我們仔細看一下微信和QQ的實現方案,它的方案是不管我當前在RecyclerView 的哪個位置,只要點選輸入框,就會跳回最後一條訊息的位置。那我們只需要在我們點選輸入框的時候滾到最後一個位置就可以了。思路就是這樣,非常的簡單。
接下來我們就上手實現
佈局檔案修改如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView_chat"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:overScrollMode="never"
android:scrollbars="vertical"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:orientation="horizontal">
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<EditText
android:id="@+id/editText_message"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="輸入訊息內容"
android:padding="10dp" />
<LinearLayout
android:id="@+id/layout_edit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
</FrameLayout>
<Button
android:id="@+id/btn_send"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="傳送" />
</LinearLayout>
</LinearLayout>
仔細看,我在EditText上增加了一個佈局,用他來控制EditText的點選事件(當點選的時候,手動請求焦點)。
看MainActivity裡面的操作
package com.liuxiaoyi.chatdemo;
import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView_chat;
private EditText editText_message;
private Button btn_send;
private LinearLayout layout_edit;
private ChatAdapter chatAdapter;
private ArrayList<Chat> chatArrayList = new ArrayList<>();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0:
recyclerView_chat.scrollToPosition(chatArrayList.size()-1);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView_chat = (RecyclerView) findViewById(R.id.recyclerView_chat);
editText_message = (EditText) findViewById(R.id.editText_message);
btn_send = (Button) findViewById(R.id.btn_send);
layout_edit = (LinearLayout) findViewById(R.id.layout_edit);
chatArrayList.add(new Chat("你好啊。",ChatAdapter.TYPE_SEND));
chatArrayList.add(new Chat("你叫什麼名字啊?",ChatAdapter.TYPE_SEND));
chatArrayList.add(new Chat("你好,我叫小麗",ChatAdapter.TYPE_RECEIVE));
chatArrayList.add(new Chat("你是哪裡人啊?",ChatAdapter.TYPE_SEND));
chatArrayList.add(new Chat("我是湖南長沙人",ChatAdapter.TYPE_RECEIVE));
chatArrayList.add(new Chat("好巧啊,我也是長沙的",ChatAdapter.TYPE_SEND));
chatArrayList.add(new Chat("咱們真有緣分誒",ChatAdapter.TYPE_SEND));
chatArrayList.add(new Chat("我也覺得呢",ChatAdapter.TYPE_RECEIVE));
chatAdapter = new ChatAdapter(MainActivity.this,chatArrayList);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(MainActivity.this);
recyclerView_chat.setLayoutManager(linearLayoutManager);
recyclerView_chat.setAdapter(chatAdapter);
layout_edit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
editText_message.requestFocus();
showSoftInput(MainActivity.this, editText_message);
handler.sendEmptyMessageDelayed(0,250);
}
});
recyclerView_chat.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
hideSoftInput(MainActivity.this, editText_message);
return false;
}
});
}
public static void showSoftInput(Context context, View view) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.showSoftInput(view, InputMethodManager.SHOW_FORCED);
}
public static void hideSoftInput(Context context, View view) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
我在這裡獲得佈局檔案中新增加的那個View,然後讓他來給EditText請求焦點,同時用Handler的sendEmptyMessageDelayed(0,250)方法讓recyclerView等一會兒再滑動(此時彈出框已被彈出)。當RecyclerView有Touch事件的時候隱藏掉輸入法,這樣就可以了。
這裡行文就真正結束了,有問題可以在底下留言。謝謝大家。