目录
一、主要内容
二、核心代码
三、效果展示
在上次的基础上: 安卓 类微信开发(二)_qingsongxyz的博客-CSDN博客
一、主要内容
完成好友聊天界面的开发和activity之间的数据传递
二、核心代码
编写ChatActivity.xml好友聊天界面UI:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:id="@+id/activity_chat_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#DCDFE6"
android:orientation="vertical"
tools:context=".ChatActivity">
<!--聊天界面顶部-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<ImageView
android:id="@+id/activity_chat_imageView_back"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="5dp"
android:layout_weight="1"
android:scaleType="center"
app:srcCompat="@drawable/back" />
<TextView
android:id="@+id/activity_chat_textView1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="8"
android:gravity="center"
android:text="TextView"
android:textSize="30sp" />
<ImageView
android:id="@+id/activity_chat_imageView_settings"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginRight="1dp"
android:layout_weight="1"
android:scaleType="center"
app:srcCompat="@drawable/settings" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginBottom="20dp"
android:background="@color/black" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/activity_chat_recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="10" />
<!--聊天界面底部-->
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginTop="30dp"
android:layout_marginBottom="15dp"
android:background="@color/black" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="bottom"
android:orientation="horizontal"
android:layout_gravity="center_vertical">
<ImageView
android:id="@+id/activity_chat_imageView_voice"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:scaleType="fitCenter"
app:srcCompat="@drawable/voice" />
<EditText
android:id="@+id/activity_chat_editText"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_weight="6"
android:background="@drawable/shape_wihte"
android:ems="19"
android:inputType="textMultiLine"
android:padding="1dp"
android:textSize="12sp"
android:layout_marginBottom="4dp"/>
<ImageView
android:id="@+id/activity_chat_imageView_face"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:scaleType="fitCenter"
app:srcCompat="@drawable/face" />
<ImageView
android:id="@+id/activity_chat_imageView_more"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
app:srcCompat="@drawable/more" />
<Button
android:id="@+id/activity_chat_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="#fff"
android:background="#27C600"
android:text="发送"
android:visibility="gone"
android:layout_marginLeft="2dp"/>
</LinearLayout>
</LinearLayout>
在网上公共库找到封装过的TextView样式进行导入(导入外部库):
修改如下文件settings.gradle
修改如下文件build.gradle(Module):
下面开始使用外部库
编写好友发送的聊天消息样式文件layout_message_left_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/layout_message_left_item_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Time"
android:textSize="18sp"
android:gravity="center"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/layout_message_left_item_imageView1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:srcCompat="@drawable/avatars1"
android:scaleType="center" />
<com.cxd.chatview.moudle.ChatView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="4"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_marginRight="30dp"
xmlns:chat="http://schemas.android.com/apk/res-auto"
chat:arrow_direction="left"
chat:is_arrow_center="true"
chat:arrow_up_distance = "10dp"
chat:arrow_width = "15dp"
chat:arrow_height = "15dp"
chat:stroke_width = "1px"
chat:stroke_color = "#FFFFFF"
chat:fill_color = "#FFFFFF"
chat:press_stroke_color = "#FFFFFF"
chat:press_fill_color = "#FFFFFF"
chat:conner_radius = "12dp">
<TextView
android:id="@+id/layout_message_left_item_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="4"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="TextView"
android:textSize="25sp" />
</com.cxd.chatview.moudle.ChatView>
</LinearLayout>
</LinearLayout>
编写自己发送的聊天消息样式文件layout_message_right_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/layout_message_right_item_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Time"
android:textSize="18sp"
android:gravity="center"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.cxd.chatview.moudle.ChatView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="4"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_marginLeft="30dp"
xmlns:chat="http://schemas.android.com/apk/res-auto"
chat:arrow_direction="right"
chat:is_arrow_center="true"
chat:arrow_up_distance = "10dp"
chat:arrow_width = "15dp"
chat:arrow_height = "15dp"
chat:stroke_width = "1px"
chat:stroke_color = "#FFFFFF"
chat:fill_color = "#7FE865"
chat:press_stroke_color = "#FFFFFF"
chat:press_fill_color = "#FFFFFF"
chat:conner_radius = "12dp">
<TextView
android:id="@+id/layout_message_right_item_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="25sp" />
</com.cxd.chatview.moudle.ChatView>
<ImageView
android:id="@+id/layout_message_right_item_imageView1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:srcCompat="@drawable/avatars1"
android:scaleType="center" />
</LinearLayout>
</LinearLayout>
这里创建一个工具类DrawableUtil 进行图片转换为字节数组的操作(后面需要使用):
public class DrawableUtil {
public static Bitmap drawableToBitmap(Drawable drawable) {
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
// System.out.println("Drawable转Bitmap");
Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
Bitmap bitmap = Bitmap.createBitmap(w,h,config);
//注意,下面三行代码要用到,否在在View或者surfaceview里的canvas.drawBitmap会看不到图
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
drawable.draw(canvas);
return bitmap;
}
public static byte[] bitmapToBytes(Bitmap bm){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
}
}
然后编写activity_chat.xml中RecyclerView对应的MessageAdapter类:
注意: 使用ViewType属性动态控制MessageAdapter加载哪一个样式文件,用于区分好友发送的消息和自己发送的消息。
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MessageViewHolder> {
private final static int I = 0;
private final static int OTHERS = 1;
private Context context;
private List<Map<String, Object>> data;
public MessageAdapter(Context context, List<Map<String, Object>> data) {
this.context = context;
this.data = data;
}
@Override
public int getItemViewType(int position) {
if(data.get(position).get("type").toString().equals("I"))
{
return I;
}
else{
return OTHERS;
}
}
@NonNull
@Override
public MessageAdapter.MessageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if(viewType == OTHERS) {
return new MessageViewHolder(LayoutInflater.from(context).inflate(R.layout.layout_message_left_item, parent, false));
}
else
{
return new MessageViewHolder(LayoutInflater.from(context).inflate(R.layout.layout_message_right_item, parent, false));
}
}
@Override
public void onBindViewHolder(@NonNull MessageAdapter.MessageViewHolder holder, int position) {
if(data.get(position).get("type").toString().equals("I"))
{
holder.imageView_right.setImageResource(R.drawable.car);
holder.right_textView1.setText(data.get(position).get("time").toString());
holder.right_textView2.setText(data.get(position).get("message").toString());
}
else
{
byte[] bytes = (byte[]) data.get(position).get("avatars");
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
holder.imageView_left.setImageBitmap(bitmap);
holder.left_textView1.setText(data.get(position).get("time").toString());
holder.left_textView2.setText(data.get(position).get("message").toString());
}
}
@Override
public int getItemCount() {
return data.size();
}
public class MessageViewHolder extends RecyclerView.ViewHolder {
TextView left_textView1, left_textView2, right_textView1, right_textView2;
ImageView imageView_left, imageView_right;
public MessageViewHolder(@NonNull View itemView) {
super(itemView);
left_textView1 = itemView.findViewById(R.id.layout_message_left_item_time);
left_textView2 = itemView.findViewById(R.id.layout_message_left_item_content);
imageView_left = itemView.findViewById(R.id.layout_message_left_item_imageView1);
right_textView1 = itemView.findViewById(R.id.layout_message_right_item_time);
right_textView2 = itemView.findViewById(R.id.layout_message_right_item_content);
imageView_right = itemView.findViewById(R.id.layout_message_right_item_imageView1);
}
}
}
修改ChatAdapter类(添加点击RecyclerView中的item的相关代码):
public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder> {
private Context context;
private List<Map<String, Object>> data;
private OnItemClickListener onItemClickListener;
public ChatAdapter(Context context, List<Map<String, Object>> data) {
this.context = context;
this.data = data;
}
/**
* 定义 RecyclerView 选项单击事件的回调接口
*/
public interface OnItemClickListener{
void onItemClick(View view, Map<String, Object> data);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
@NonNull
@Override
public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ChatViewHolder(LayoutInflater.from(context).inflate(R.layout.layout_chat_item, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ChatViewHolder holder, int position) {
holder.textView1.setText(data.get(position).get("name").toString());
holder.textView2.setText(data.get(position).get("message").toString());
holder.textView3.setText(data.get(position).get("time").toString());
holder.imageView.setImageResource(Integer.parseInt(data.get(position).get("avatars").toString()));
}
@Override
public int getItemCount() {
return data.size();
}
public class ChatViewHolder extends RecyclerView.ViewHolder {
TextView textView1, textView2, textView3;
ImageView imageView;
public ChatViewHolder(@NonNull View itemView) {
super(itemView);
textView1 = itemView.findViewById(R.id.layout_chat_item_textView1);
textView2 = itemView.findViewById(R.id.layout_chat_item_textView2);
textView3 = itemView.findViewById(R.id.layout_chat_item_textView3);
imageView = itemView.findViewById(R.id.layout_chat_item_imageView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//此处回传点击监听事件
if(onItemClickListener!=null){
onItemClickListener.onItemClick(v, data.get(getLayoutPosition()));
}
}
});
}
}
}
修改ChatFragment类,调用chatAdapter类的点击事件,完成Activity之间的跳转和信息传递:
public class ChatFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener{
private View view;
private RecyclerView fragment_chat_recyclerView;
private SwipeRefreshLayout swipeRefreshLayout;
private ChatAdapter chatAdapter;
private Context context;
private List<Map<String, Object>> chatData = new ArrayList<>();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_chat, container, false);
fragment_chat_recyclerView = view.findViewById(R.id.fragment_chat_recyclerView);
swipeRefreshLayout = view.findViewById(R.id.fragment_chat_swipeRefreshLayout);
context = getContext();
initData();
configSwipeRefreshLayout();
chatAdapter = new ChatAdapter(context, chatData);
chatAdapter.setOnItemClickListener(new ChatAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, Map<String, Object> data) {
ImageView avatars = view.findViewById(R.id.layout_chat_item_imageView);
TextView name = view.findViewById(R.id.layout_chat_item_textView1);
TextView message = view.findViewById(R.id.layout_chat_item_textView2);
TextView time = view.findViewById(R.id.layout_chat_item_textView3);
//跳转
Intent intent = new Intent(context, ChatActivity.class);
Bundle bundle = new Bundle();
//将图片转换成字节数组传递
Bitmap bitmap = DrawableUtil.drawableToBitmap(avatars.getDrawable());
byte[] bytes = DrawableUtil.bitmapToBytes(bitmap);
bundle.putByteArray("avatars", bytes);
bundle.putString("name", name.getText().toString());
bundle.putString("message", message.getText().toString());
bundle.putString("time", time.getText().toString());
if(message.getText().toString().contains("我:"))
{
bundle.putString("type", "I");
}
else
{
bundle.putString("type", "others");
}
bundle.putInt("requestCode", 1);
intent.putExtras(bundle);
startActivityForResult(intent, 1);
}
});
LinearLayoutManager manager = new LinearLayoutManager(context);
manager.setOrientation(RecyclerView.VERTICAL);
fragment_chat_recyclerView.setLayoutManager(manager);
fragment_chat_recyclerView.setAdapter(chatAdapter);
return view;
}
private void configSwipeRefreshLayout() {
swipeRefreshLayout.setSize(CircularProgressDrawable.LARGE);
swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
android.R.color.system_accent1_200, android.R.color.system_neutral2_300);
swipeRefreshLayout.setOnRefreshListener(this);
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 1 && resultCode == 1)
{
Bundle bundle = data.getExtras();
String name = bundle.getString("name");
String message = bundle.getString("message");
String time = bundle.getString("time");
//更新聊天记录
for (int i = 0; i < chatData.size(); i++) {
if(chatData.get(i).get("name").toString().equals(name))
{
chatData.get(i).put("message", "我:" + message);
chatData.get(i).put("time", time);
chatAdapter.notifyItemChanged(i);
break;
}
}
}
}
private void initData() {
String[] name = {"倚楼听风雨", "璎婲", "の大脸猫", "静秋┐", ">.<",
"旧街凉风", "初見", "雨嘉ψ", "鸢尾*", "凉栀"};
String[] message = {"在吗?", "哈哈哈", "...", "呜呜呜", "呵呵",
"吃饭了吗?", "~~~", "今天有空吗?", "出去玩呀", "爱你哟"};
String[] time = {"12:10", "昨天", "3月15日", "3月15日", "3月15日",
"9:00", "5:00", "4月17日", "18:00", "9月1日"};
Integer[] avatars = {R.drawable.avatars1, R.drawable.avatars2, R.drawable.avatars3,
R.drawable.avatars4, R.drawable.avatars5, R.drawable.avatars6,
R.drawable.avatars7, R.drawable.avatars8, R.drawable.avatars9,
R.drawable.avatars10,};
for (int i = 0; i < name.length; i++) {
HashMap<String, Object> d = new HashMap<>();
d.put("name", name[i]);
d.put("message", message[i]);
d.put("time", time[i]);
d.put("avatars", avatars[i]);
chatData.add(d);
}
}
@Override
public void onRefresh() {
//延时2s
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(false);
}
}, 3000);
}
}
编写ChatActivity类,接受信息进行处理:
public class ChatActivity extends AppCompatActivity {
private List<Map<String, Object>> data = new ArrayList<>();
private RecyclerView recyclerView;
private TextView textView1;
private ImageView back, more;
private Button button1;
private EditText editText;
private LinearLayout linearLayout;
private Boolean isSend = false;
private Map<String, Object> result = new HashMap<>();
private Intent intent;
private String name;
private MessageAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
linearLayout = findViewById(R.id.activity_chat_layout);
recyclerView = findViewById(R.id.activity_chat_recyclerView);
textView1 = findViewById(R.id.activity_chat_textView1);
back = findViewById(R.id.activity_chat_imageView_back);
more = findViewById(R.id.activity_chat_imageView_more);
button1 = findViewById(R.id.activity_chat_button);
editText = findViewById(R.id.activity_chat_editText);
intent = getIntent();
Bundle bundle = intent.getExtras();
byte[] avatars = bundle.getByteArray("avatars");
name = bundle.getString("name");
String message = bundle.getString("message");
String time = bundle.getString("time");
String type = bundle.getString("type");
int requestCode = bundle.getInt("requestCode");
if(requestCode == 1)
{
textView1.setText(name);
Map<String, Object> map = new HashMap<>();
map.put("avatars", avatars);
map.put("message", message);
map.put("time", time);
map.put("type", type);
data.add(map);
adapter = new MessageAdapter(this, data);
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(RecyclerView.VERTICAL);
recyclerView.setLayoutManager(manager);
recyclerView.setAdapter(adapter);
}
setbackListener();
setEditTextListener();
setSendMsgListener();
}
/**
* 监听输入框是否失去焦点进行按钮的切换
*/
private void setEditTextListener() {
editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
if(hasFocus)
{
more.setVisibility(View.GONE);
button1.setVisibility(View.VISIBLE);
}else
{
more.setVisibility(View.VISIBLE);
button1.setVisibility(View.GONE);
}
}
});
}
private void setSendMsgListener() {
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!editText.getText().toString().trim().equals(""))
{
isSend = true;
result.put("message", editText.getText().toString());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM月dd日 HH:mm");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
result.put("time", simpleDateFormat.format(new Date()));
result.put("type", "I");
data.add(result);
//清楚输入的聊天信息并失去焦点
editText.setText("");
editText.clearFocus();
adapter.notifyItemInserted(data.size() - 1);
}
}
});
}
/**
* 返回主菜单
*/
private void setbackListener() {
back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//如果发送了新的消息,返回主界面时要更新信息
int resultCode = 0;
Bundle bundle = new Bundle();
if(isSend)
{
resultCode = 1;
bundle.putString("name", name);
bundle.putString("message", result.get("message").toString());
bundle.putString("time", result.get("time").toString());
bundle.putInt("resultCode", 1);
}
else
{
bundle.putInt("resultCode", 0);
}
intent.putExtras(bundle);
setResult(resultCode, intent);
finish();
}
});
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// 获得当前得到焦点的View,一般情况下就是EditText(特殊情况就是轨迹求或者实体案件会移动焦点)
View v = getCurrentFocus();
if (isShouldHideInput(v, ev)) {
//关闭键盘,并让输入框失去焦点
hideSoftInput(v.getWindowToken());
}
}
return super.dispatchTouchEvent(ev);
}
/**
* 根据EditText所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘,因为当用户点击EditText时没必要隐藏
*
* @param v
* @param event
* @return
*/
private boolean isShouldHideInput(View v, MotionEvent event) {
if (v != null && (v instanceof EditText)) {
int[] l = { 0, 0 };
v.getLocationInWindow(l);
int left = l[0], top = l[1], bottom = top + v.getHeight(), right = left + v.getWidth();
if (event.getX() > left && event.getX() < right && event.getY() > top && event.getY() < bottom) {
// 点击EditText的事件,忽略它。
return false;
} else {
return true;
}
}
// 如果焦点不是EditText则忽略,这个发生在视图刚绘制完,第一个焦点不在EditView上,和用户用轨迹球选择其他的焦点
return false;
}
/**
* 隐藏键盘
*
* @param token
*/
private void hideSoftInput(IBinder token) {
if (token != null) {
InputMethodManager im = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
im.hideSoftInputFromWindow(token, InputMethodManager.HIDE_NOT_ALWAYS);
}
}
}
细节处理:
1.好友聊天界面弹出键盘会影响页面布局:
修改AndroidManifest.xml,在相应的Activity添加一下两行解决:
android:screenOrientation="portrait" android:windowSoftInputMode="adjustPan"
2.设置button颜色不生效:
修改一下两个文件即可:
将 parent="Theme.MaterialComponents.DayNight.DarkActionBar"改Theme.MaterialComponents.DayNight.NoActionBar.Bridge,在设置按钮颜色即可
三、效果展示
仓库地址:青松xyz/WechatForm