file-type

深入解析Android Bundle消息传递机制

4星 · 超过85%的资源 | 下载需积分: 9 | 40KB | 更新于2025-03-07 | 175 浏览量 | 3 下载量 举报 收藏
download 立即下载
Android Bundle消息机制是Android平台上用于进程间通信(IPC)的重要组件之一。Bundle(字面意思是“包裹”或“束”)是用于存储数据的容器,它可以包含各种数据类型,例如基本数据类型、对象以及其他Bundle。在Android开发中,Bundle常用于Intent、Fragment以及Activity之间传递数据。 ### Android Bundle Message 概述 Bundle在Android中扮演的角色非常关键,尤其是在Activity启动、Fragment事务以及Service与Activity通信等场景。它的设计允许开发者能够方便地存储和传递数据,使得组件之间的交互变得简洁。 ### Bundle与Intent Intent是Android中用于启动一个组件(如Activity、Service、BroadcastReceiver)的机制,而Bundle常常与Intent一起使用。通过Intent传递数据时,可以使用Bundle将需要传递的数据打包成键值对的形式,然后将这个Bundle附加到Intent上。接收方组件可以使用相应的键来检索传递的数据。 #### 使用示例: 在发送方: ```java Intent intent = new Intent(this, TargetActivity.class); Bundle bundle = new Bundle(); bundle.putString("key", "value"); intent.putExtras(bundle); startActivity(intent); ``` 在接收方: ```java Bundle bundle = getIntent().getExtras(); if (bundle != null) { String value = bundle.getString("key"); // 处理接收到的value数据 } ``` ### Bundle与Fragment Fragment是Android支持动态和灵活界面设计的组件,它经常需要在不同界面组件间共享数据。Bundle常用于Fragment的创建和恢复过程中,传递数据到Fragment或者保存Fragment的状态。 #### 使用示例: 创建Fragment时传递Bundle: ```java Fragment myFragment = new MyFragment(); Bundle args = new Bundle(); args.putString("name", "张三"); myFragment.setArguments(args); ``` 在Fragment的`onCreate`方法中获取数据: ```java @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { String name = getArguments().getString("name"); // 使用接收到的name数据 } } ``` ### Bundle与数据序列化 在Android中,所有的基本数据类型以及实现了Serializable或Parcelable接口的对象都可以通过Bundle进行传递。为了保持数据传递的高效性,推荐使用Parcelable接口,因为它比Serializable接口更加高效,专门针对Android系统进行了优化。 ### 注意事项 - Bundle在传递数据时,由于Intent有数据大小限制,因此传递的数据量不宜过大,否则可能导致应用崩溃或者异常。 - 在进行Activity与Service通信时,因为Service运行在不同的进程空间,所以可以通过Intent附加Bundle的方式来传递数据。 - Android的消息机制不仅仅限于Bundle消息,还包括Handler、Looper、Message、MessageQueue等组件构成的完整机制。通过Handler可以进行线程间通信,而Looper负责消息队列的维护。 ### 结语 以上内容展示了Android Bundle消息机制的使用方法、场景以及注意事项。通过实例演示了Bundle在不同组件间传递数据的应用方式,以及在数据序列化上的选择。这些知识点是Android应用开发中不可或缺的部分,对深入理解Android系统架构和组件通信机制非常重要。对于开发者来说,掌握这些知识点可以有效提高开发效率,编写出更加健壮、稳定的应用程序。

相关推荐

filetype

课程设计报告:本地音乐播放器应用开发 摘要 本课程设计基于Android Studio平台,采用Java语言开发了一款功能完整的本地音乐播放器。系统采用MVC架构设计,通过四大组件协同工作,实现了音乐播放、进度控制、后台服务等核心功能。其中,Model层以硬编码方式存储三首音乐数据(空山野马、半点心、Shanghaivania),使用MediaPlayer类处理音频解码与播放控制;View层结合XML布局与自定义BaseAdapter,构建了包含唱片动画、进度条和播放控制按钮的交互界面;Controller层通过Service管理后台播放状态,利用Handler机制更新UI,并采用绑定服务机制实现组件间通信。 技术亮点: (1)后台播放服务:利用Service组件实现音乐后台持续播放,支持锁屏控制,解决了Activity生命周期对播放状态的影响。 (2)动态进度同步:通过Handler每500ms获取MediaPlayer当前位置,结合SeekBar实现进度实时显示与手动调节。 (3)界面动画效果:使用ObjectAnimator实现专辑封面360°旋转动画,旋转速度与音乐播放状态同步。 (4)模块化设计:通过Fragment实现音乐列表与收藏界面的模块化切换。 系统严格遵循Android开发规范,解决了媒体资源释放、多线程同步等技术难点。测试显示,系统在Android 8.0至13.0设备上稳定运行,内存占用峰值35MB,音频解码延迟小于0.2秒。 关键词:MusicService;ObjectAnimator;SeekBar;Fragment;后台播放;唱片动画 1. 前言 1.1 课题背景 随着智能手机的普及,音乐播放器成为用户日常娱乐的重要工具。传统系统播放器功能单一,无法满足个性化需求。本项目开发的本地音乐播放器,通过自定义界面和功能优化,提供更佳的用户体验。 1.2 研究意义及目的 研究意义: 实践Android多媒体开发核心技术(MediaPlayer、Service) 探索MVC架构在移动应用中的实现 研究用户交互设计原则与动画实现 形成可复用的多媒体应用开发方案 研究目的: 实现具备以下特性的音乐播放器: 支持三首本地音乐播放控制 动态进度显示与跳转 后台持续播放能力 专辑封面旋转动画 模块化界面切换 2. 软件开发环境 环境要素 配置说明 开发工具 Android Studio 2023.1.1 构建工具 Gradle 8.0 开发语言 Java 17 + XML 目标API级别 Android 8.0(API 26) - Android 13(API 33) 测试设备 Pixel 6 Pro (API 33) 3. 系统需求分析 3.1 可行性分析 技术可行性:MediaPlayer+Service成熟方案 社会可行性:满足基础音乐播放需求 3.2 系统需求 功能需求: 三首音乐列表展示(ListView) 播放控制(播放/暂停/继续) 进度控制(SeekBar) 唱片旋转动画 性能指标: 响应时间≤0.5s 内存占用≤50MB 支持后台播放 3.3 系统功能结构图 text 音乐播放器系统 ├── 用户界面 │ ├── 主界面 (MainActivity) │ ├── 播放界面 (MusicActivity) │ └── 收藏界面 (Fragment) ├── 核心功能 │ ├── 播放控制 (MediaPlayer) │ ├── 进度同步 (Handler) │ └── 后台服务 (MusicService) └── 数据管理 ├── 音乐数据 (硬编码) └── 播放状态 4. 系统设计 4.1 用户界面实现 4.1.1 动态布局 主界面布局 (activity_main.xml): xml <LinearLayout> <TextView android:text="我喜欢的音乐"/> <LinearLayout> <TextView android:id="@+id/menu1" android:text="歌曲"/> <TextView android:id="@+id/menu2" android:text="收藏"/> </LinearLayout> <FrameLayout android:id="@+id/content"/> </LinearLayout> 音乐列表项 (item_layout.xml): xml <RelativeLayout> <ImageView android:id="@+id/iv"/> <TextView android:id="@+id/item_name"/> </RelativeLayout> 4.1.2 交互动画 专辑封面旋转实现: java ObjectAnimator animator = ObjectAnimator.ofFloat( iv_music, "rotation", 0f, 360f ); animator.setDuration(10000); animator.setInterpolator(new LinearInterpolator()); animator.setRepeatCount(-1); 4.2 核心功能模块 4.2.1 音乐播放控制 播放指定位置音乐: java // MusicService.java public void play(int position) { Uri uri = Uri.parse("android.resource://" + getPackageName() + "/raw/music" + position); player = MediaPlayer.create(context, uri); player.start(); } 4.2.2 进度同步 定时更新进度: java timer.schedule(new TimerTask() { public void run() { Bundle bundle = new Bundle(); bundle.putInt("duration", player.getDuration()); bundle.putInt("current", player.getCurrentPosition()); handler.sendMessage(Message.obtain(handler, 0, bundle)); } }, 0, 500); 4.3 系统架构设计 4.3.1 Model层 音乐数据结构: java public class frag1 extends Fragment { public String[] name = {"空山野马", "半点心", "Shanghaivania"}; public static int[] icons = {R.drawable.music0, R.drawable.music1, R.drawable.music2}; } 播放状态管理: java public class MusicService extends Service { private MediaPlayer player; private Timer timer; } 4.3.2 View层 列表适配器实现: java class MyAdapter extends BaseAdapter { public View getView(int position, View convertView, ViewGroup parent) { View view = LayoutInflater.from(context) .inflate(R.layout.item_layout, parent, false); ((TextView)view.findViewById(R.id.item_name)).setText(songs[position]); ((ImageView)view.findViewById(R.id.iv)).setImageResource(icons[position]); return view; } } 4.3.3 Controller层 服务绑定机制: java public class MusicActivity extends AppCompatActivity { private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { musicControl = (MusicService.MusicControl) service; } }; } 5. 总结与展望 5.1 实现功能 三首本地音乐播放控制 进度精确管理(秒级) 后台持续播放 专辑封面旋转动画 模块化界面切换 5.2 技术收获 掌握MediaPlayer生命周期管理 理解Service绑定机制 熟练Handler线程通信 实践Fragment模块化开发 5.3 改进方向 功能扩展: 增加本地音乐文件扫描 实现播放列表管理 添加均衡器音效设置 体验优化: 支持歌词同步显示 添加睡眠定时功能 架构升级: 引入Room数据库 采用ViewModel管理状态 核心代码实现 主界面控制 (MainActivity.java): java public class MainActivity extends AppCompatActivity implements View.OnClickListener { protected void onCreate(Bundle savedInstanceState) { // 初始化Fragment管理器 fm = getSupportFragmentManager(); ft = fm.beginTransaction(); ft.replace(R.id.content, new frag1()); ft.commit(); } public void onClick(View v) { ft = fm.beginTransaction(); if (v.getId() == R.id.menu1) { ft.replace(R.id.content, new frag1()); // 歌曲列表 } else if (v.getId() == R.id.menu2) { ft.replace(R.id.content, new frag2()); // 收藏界面 } ft.commit(); } } 播放服务 (MusicService.java): java public class MusicService extends Service { class MusicControl extends Binder { public void play(int position) { Uri uri = Uri.parse("android.resource://" + getPackageName() + "/raw/music" + position); player = MediaPlayer.create(context, uri); player.start(); startProgressTracking(); } } private void startProgressTracking() { timer = new Timer(); timer.schedule(new TimerTask() { public void run() { Message msg = new Message(); Bundle bundle = new Bundle(); bundle.putInt("duration", player.getDuration()); bundle.putInt("current", player.getCurrentPosition()); handler.sendMessage(msg); } }, 0, 500); } } 歌曲列表 (frag1.java): java public class frag1 extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { listView.setOnItemClickListener((parent, view, position, id) -> { Intent intent = new Intent(getActivity(), MusicActivity.class); intent.putExtra("name", name[position]); intent.putExtra("position", position); startActivity(intent); }); return view; } } 结论 本系统成功实现了音乐播放器的核心功能,采用模块化设计和规范的Android组件使用方式,为后续功能扩展奠定了良好基础。项目代码结构清晰,功能完整,符合课程设计要求。package com.example.myapplication; import static com.example.myapplication.R.*; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import android.os.Bundle; import android.view.View; import android.widget.TextView; public class MainActivity extends AppCompatActivity implements View.OnClickListener { //创建需要用到的控件的变量 private TextView tv1,tv2; private FragmentManager fm; private FragmentTransaction ft; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //绑定控件 tv1=(TextView)findViewById(R.id.menu1); tv2=(TextView)findViewById(R.id.menu2); //设置监听器,固定写法 tv1.setOnClickListener(this); tv2.setOnClickListener(this); //若是继承FragmentActivity,fm=getFragmentManger(); fm=getSupportFragmentManager(); //fm可以理解为Fragment显示的管理者,ft就是它的改变者 ft=fm.beginTransaction(); //默认情况下就显示frag1 ft.replace(R.id.content,new frag1()); //提交改变的内容 ft.commit(); } @Override //控件的点击事件 public void onClick(View v) { ft = fm.beginTransaction(); int id = v.getId(); if (id == R.id.menu1) { ft.replace(R.id.content, new frag1()); } else if (id == R.id.menu2) { ft.replace(R.id.content, new frag2()); } ft.commit(); } }package com.example.myapplication; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import android.animation.ObjectAnimator; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.view.View; import android.view.animation.LinearInterpolator; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; import static java.lang.Integer.parseInt; public class Music_Activity extends AppCompatActivity implements View.OnClickListener{ //进度条 private static SeekBar sb; private static TextView tv_progress,tv_total,name_song; //动画 private ObjectAnimator animator; private MusicService.MusicControl musicControl; private String name; private Intent intent1,intent2; private MyServiceConn conn; //记录服务是否被解绑,默认没有 private boolean isUnbind =false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_music); //获取从frag1传来的信息 intent1=getIntent(); init(); } private void init(){ //进度条上小绿点的位置,也就是当前已播放时间 tv_progress=(TextView)findViewById(R.id.tv_progress); //进度条的总长度,就是总时间 tv_total=(TextView)findViewById(R.id.tv_total); //进度条的控件 sb=(SeekBar)findViewById(R.id.sb); //歌曲名显示的控件 name_song=(TextView)findViewById(R.id.song_name); //绑定控件的同时设置点击事件监听器 findViewById(R.id.btn_play).setOnClickListener(this); findViewById(R.id.btn_pause).setOnClickListener(this); findViewById(R.id.btn_continue_play).setOnClickListener(this); findViewById(R.id.btn_exit).setOnClickListener(this); name=intent1.getStringExtra("name"); name_song.setText(name); //创建一个意图对象,是从当前的Activity跳转到Service intent2=new Intent(this,MusicService.class); conn=new MyServiceConn();//创建服务连接对象 bindService(intent2,conn,BIND_AUTO_CREATE);//绑定服务 //为滑动条添加事件监听,每个控件不同果然点击事件方法名都不同 sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { //这一行注解是保证API在KITKAT以上的模拟器才能顺利运行,也就是19以上 @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { //进当滑动条到末端时,结束动画 if (progress==seekBar.getMax()){ animator.pause();//停止播放动画 } } @Override //滑动条开始滑动时调用 public void onStartTrackingTouch(SeekBar seekBar) { } @Override //滑动条停止滑动时调用 public void onStopTrackingTouch(SeekBar seekBar) { //根据拖动的进度改变音乐播放进度 int progress=seekBar.getProgress();//获取seekBar的进度 musicControl.seekTo(progress);//改变播放进度 } }); //声明并绑定音乐播放器的iv_music控件 ImageView iv_music=(ImageView)findViewById(R.id.iv_music); String position= intent1.getStringExtra("position"); //praseInt()就是将字符串变成整数类型 int i=parseInt(position); iv_music.setImageResource(frag1.icons[i]); //rotation和0f,360.0f就设置了动画是从0°旋转到360° animator=ObjectAnimator.ofFloat(iv_music,"rotation",0f,360.0f); animator.setDuration(10000);//动画旋转一周的时间为10秒 animator.setInterpolator(new LinearInterpolator());//匀速 animator.setRepeatCount(-1);//-1表示设置动画无限循环 } //handler机制,可以理解为线程间的通信,我获取到一个信息,然后把这个信息告诉你,就这么简单 public static Handler handler=new Handler(){//创建消息处理器对象 //在主线程中处理从子线程发送过来的消息 @Override public void handleMessage(Message msg){ Bundle bundle=msg.getData();//获取从子线程发送过来的音乐播放进度 //获取当前进度currentPosition和总时长duration int duration=bundle.getInt("duration"); int currentPosition=bundle.getInt("currentPosition"); //对进度条进行设置 sb.setMax(duration); sb.setProgress(currentPosition); //歌曲是多少分钟多少秒钟 int minute=duration/1000/60; int second=duration/1000%60; String strMinute=null; String strSecond=null; if(minute<10){//如果歌曲的时间中的分钟小于10 strMinute="0"+minute;//在分钟的前面加一个0 }else{ strMinute=minute+""; } if (second<10){//如果歌曲中的秒钟小于10 strSecond="0"+second;//在秒钟前面加一个0 }else{ strSecond=second+""; } //这里就显示了歌曲总时长 tv_total.setText(strMinute+":"+strSecond); //歌曲当前播放时长 minute=currentPosition/1000/60; second=currentPosition/1000%60; if(minute<10){//如果歌曲的时间中的分钟小于10 strMinute="0"+minute;//在分钟的前面加一个0 }else{ strMinute=minute+" "; } if (second<10){//如果歌曲中的秒钟小于10 strSecond="0"+second;//在秒钟前面加一个0 }else{ strSecond=second+" "; } //显示当前歌曲已经播放的时间 tv_progress.setText(strMinute+":"+strSecond); } }; //用于实现连接服务,比较模板化,不需要详细知道内容 class MyServiceConn implements ServiceConnection{ @Override public void onServiceConnected(ComponentName name, IBinder service){ musicControl=(MusicService.MusicControl) service; } @Override public void onServiceDisconnected(ComponentName name){ } } //判断服务是否被解绑 private void unbind(boolean isUnbind){ //如果解绑了 if(!isUnbind){ musicControl.pausePlay();//音乐暂停播放 unbindService(conn);//解绑服务 } } @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Override public void onClick(View v) { int id = v.getId(); if (id == R.id.btn_play) { //播放按钮点击事件 String position = intent1.getStringExtra("position"); int i = parseInt(position); musicControl.play(i); animator.start(); } else if (id == R.id.btn_pause) { //暂停按钮点击事件 musicControl.pausePlay(); animator.pause(); } else if (id == R.id.btn_continue_play) { //继续播放按钮点击事件 musicControl.continuePlay(); animator.start(); } else if (id == R.id.btn_exit) { //退出按钮点击事件 unbind(isUnbind); isUnbind = true; finish(); } } @Override protected void onDestroy(){ super.onDestroy(); unbind(isUnbind);//解绑服务 } }package com.example.myapplication; import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Message; import java.util.Timer; import java.util.TimerTask; //这是一个Service服务类 public class MusicService extends Service { //声明一个MediaPlayer引用 private MediaPlayer player; //声明一个计时器引用 private Timer timer; //构造函数 public MusicService() {} @Override public IBinder onBind(Intent intent){ return new MusicControl(); } @Override public void onCreate(){ super.onCreate(); //创建音乐播放器对象 player=new MediaPlayer(); } //添加计时器用于设置音乐播放器中的播放进度条 public void addTimer(){ //如果timer不存在,也就是没有引用实例 if(timer==null){ //创建计时器对象 timer=new Timer(); TimerTask task=new TimerTask() { @Override public void run() { if (player==null) return; int duration=player.getDuration();//获取歌曲总时长 int currentPosition=player.getCurrentPosition();//获取播放进度 Message msg= Music_Activity.handler.obtainMessage();//创建消息对象 //将音乐的总时长和播放进度封装至bundle中 Bundle bundle=new Bundle(); bundle.putInt("duration",duration); bundle.putInt("currentPosition",currentPosition); //再将bundle封装到msg消息对象中 msg.setData(bundle); //最后将消息发送到主线程的消息队列 Music_Activity.handler.sendMessage(msg); } }; //开始计时任务后的5毫秒,第一次执行task任务,以后每500毫秒(0.5s)执行一次 timer.schedule(task,5,500); } } //Binder是一种跨进程的通信方式 class MusicControl extends Binder{ public void play(int i){//String path Uri uri=Uri.parse("android.resource://"+getPackageName()+"/raw/"+"music"+i); try{ //重置音乐播放器 player.reset(); //加载多媒体文件 player=MediaPlayer.create(getApplicationContext(),uri); player.start();//播放音乐 addTimer();//添加计时器 }catch(Exception e){ e.printStackTrace(); } } //下面的暂停继续和退出方法全部调用的是MediaPlayer自带的方法 public void pausePlay(){ player.pause();//暂停播放音乐 } public void continuePlay(){ player.start();//继续播放音乐 } public void seekTo(int progress){ player.seekTo(progress);//设置音乐的播放位置 } } //销毁多媒体播放器 @Override public void onDestroy(){ super.onDestroy(); if(player==null) return; if(player.isPlaying()) player.stop();//停止播放音乐 player.release();//释放占用的资源 player=null;//将player置为空 } }package com.example.myapplication; import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import androidx.fragment.app.Fragment; public class frag1 extends Fragment { private View view; //创建歌曲的String数组和歌手图片的int数组 public String[] name={"空山野马","半点心","Shanghaivania"}; public static int[] icons={R.drawable.music0,R.drawable.music1,R.drawable.music2}; @Override public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ //绑定布局,只不过这里是用inflate()方法 view=inflater.inflate(R.layout.music_list,null); //创建listView列表并且绑定控件 ListView listView=view.findViewById(R.id.lv); //实例化一个适配器 MyBaseAdapter adapter=new MyBaseAdapter(); //列表设置适配器 listView.setAdapter(adapter); //列表元素的点击监听器 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //创建Intent对象,参数就是从frag1跳转到MusicActivity Intent intent=new Intent(frag1.this.getContext(), Music_Activity.class); //将歌曲名和歌曲的下标存入Intent对象 intent.putExtra("name",name[position]); intent.putExtra("position",String.valueOf(position)); //开始跳转 startActivity(intent); } }); return view; } //这里是创建一个自定义适配器,可以作为模板 class MyBaseAdapter extends BaseAdapter{ @Override public int getCount(){return name.length;} @Override public Object getItem(int i){return name[i];} @Override public long getItemId(int i){return i;} @Override public View getView(int i ,View convertView, ViewGroup parent) { //绑定好VIew,然后绑定控件 View view=View.inflate(frag1.this.getContext(),R.layout.item_layout,null); TextView tv_name=view.findViewById(R.id.item_name); ImageView iv=view.findViewById(R.id.iv); //设置控件显示的内容,就是获取的歌曲名和歌手图片 tv_name.setText(name[i]); iv.setImageResource(icons[i]); return view; } } }package com.example.myapplication; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.fragment.app.Fragment; public class frag2 extends Fragment { //创建一个View private View zj; //显示布局 public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { zj = inflater.inflate(R.layout.frag2_layout, null); return zj; } }<?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:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/music_bg" tools:context=".MainActivity" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:text="我喜欢的音乐" android:textSize="35dp"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/menu1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:text="歌曲" android:textSize="25dp"/> <TextView android:id="@+id/menu2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:text="收藏" android:textSize="25dp"/> </LinearLayout> <FrameLayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="9"> </FrameLayout> </LinearLayout><?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:background="@drawable/music_bg" tools:context=".Music_Activity" android:gravity="center" android:orientation="vertical"> <ImageView android:id="@+id/iv_music" android:layout_width="240dp" android:layout_height="240dp" android:layout_gravity="center_horizontal" android:layout_margin="15dp" android:src="@drawable/music0"/> <TextView android:id="@+id/song_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="歌曲名" android:textSize="20sp"/> <SeekBar android:id="@+id/sb" android:layout_width="match_parent" android:layout_height="wrap_content" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="8dp" android:paddingRight="8dp"> <TextView android:id="@+id/tv_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="00:00"/> <TextView android:id="@+id/tv_total" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:text="00:00"/> </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/btn_play" android:layout_width="0dp" android:layout_height="40dp" android:layout_margin="8dp" android:layout_weight="1" android:background="@drawable/btn_bg_selector" android:text="播放音乐"/> <Button android:id="@+id/btn_pause" android:layout_width="0dp" android:layout_height="40dp" android:layout_margin="8dp" android:layout_weight="1" android:background="@drawable/btn_bg_selector" android:text="暂停播放"/> <Button android:id="@+id/btn_continue_play" android:layout_width="0dp" android:layout_height="40dp" android:layout_margin="8dp" android:layout_weight="1" android:background="@drawable/btn_bg_selector" android:text="继续播放"/> <Button android:id="@+id/btn_exit" android:layout_width="0dp" android:layout_height="40dp" android:layout_margin="8dp" android:layout_weight="1" android:background="@drawable/btn_bg_selector" android:text="退出"/> </LinearLayout> </LinearLayout><?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"> <ImageView android:id="@+id/zj" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/bg2"/> </LinearLayout><?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp"> <ImageView android:id="@+id/iv" android:layout_width="40dp" android:layout_height="40dp" android:layout_centerVertical="true"/> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_toRightOf="@+id/iv" android:layout_centerVertical="true"> <TextView android:id="@+id/item_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="歌曲" android:textSize="15sp" android:textColor="#000000"/> </RelativeLayout> </RelativeLayout> <?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"> <ListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>再根据我给的代码在重写一份并且进行修改,每个部分的标题要进行改变

filetype

为什么日志不显示 package com.example.planting import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import com.example.planting.ui.theme.PlantingTheme class MainActivity : ComponentActivity() { private lateinit var mqttManager: MQTTManager //private lateinit var receiver: BroadcastReceiver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d("MainActivity", "Debug log message"); Log.e("MainActivity", "Error log message"); Log.i("MainActivity", "Info log message"); Log.w("MainActivity", "Warning log message"); Log.v("MainActivity", "Verbose log message"); enableEdgeToEdge() setContent { PlantingTheme { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> Greeting( name = "Android", modifier = Modifier.padding(innerPadding) ) } } } // setContentView(R.layout.activity_main) Log.d("MainActivity", "我不跟SD谈恋爱") // 初始化MQTT(注意上下文传递方式) //mqttManager = MQTTManager(this).apply { // connect() //} } } @Composable fun Greeting(name: String, modifier: Modifier = Modifier) { Text( text = "Hello $name!", modifier = modifier ) } @Preview(showBackground = true) @Composable fun GreetingPreview() { PlantingTheme { Greeting("Android") } }

filetype

1. 服务器端 - ChatServer.java import java.io.*; import java.net.*; import java.util.*; public class ChatServer { private static final int PORT = 8080; private static Set<PrintWriter> clientWriters = new HashSet<>(); public static void main(String[] args) { System.out.println("聊天服务器启动中..."); try (ServerSocket serverSocket = new ServerSocket(PORT)) { System.out.println("服务器已启动,监听端口: " + PORT); while (true) { new ClientHandler(serverSocket.accept()).start(); } } catch (IOException e) { System.out.println("服务器异常: " + e.getMessage()); } } private static class ClientHandler extends Thread { private Socket socket; private PrintWriter out; private BufferedReader in; private String nickname; // 存储实际昵称 public ClientHandler(Socket socket) { this.socket = socket; } public void run() { try { in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new PrintWriter(socket.getOutputStream(), true); // 读取客户端发送的第一条消息作为昵称 nickname = in.readLine(); if (nickname == null || nickname.trim().isEmpty()) { nickname = "匿名用户"; } synchronized (clientWriters) { clientWriters.add(out); } // 使用实际昵称广播加入消息 System.out.println(nickname + " 加入了聊天室"); broadcastSystemMessage(nickname + " 加入了聊天室"); String message; while ((message = in.readLine()) != null) { System.out.println("收到消息: " + message); broadcastMessage(message, out); } } catch (IOException e) { System.out.println("客户端断开: " + e.getMessage()); } finally { try { socket.close(); } catch (IOException e) { // 忽略 } synchronized (clientWriters) { clientWriters.remove(out); } // 使用实际昵称广播退出消息 if (nickname != null) { System.out.println(nickname + " 离开了聊天室"); broadcastSystemMessage(nickname + " 离开了聊天室"); } } } private void broadcastMessage(String message, PrintWriter senderWriter) { synchronized (clientWriters) { for (PrintWriter writer : clientWriters) { if (writer != senderWriter) { writer.println(message); } } } } private void broadcastSystemMessage(String message) { long timestamp = System.currentTimeMillis(); String systemMessage = "系统|" + message + "|" + timestamp; synchronized (clientWriters) { for (PrintWriter writer : clientWriters) { writer.println(systemMessage); } } } } } 2.Android客户端AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapplication"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="骆树涛群聊" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.AppCompat.Light.NoActionBar"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ChatActivity" /> <activity android:name=".SettingsActivity" /> </application> </manifest> arrays.xml代码 <?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="color_entries"> <item>黑色</item> <item>红色</item> <item>黄色</item> <item>蓝色</item> <item>绿色</item> </string-array> <string-array name="color_values"> <item>#FF000000</item> <item>#FFFF0000</item> <item>#FFFFFF00</item> <item>#FF0000FF</item> <item>#FF00FF00</item> </string-array> <string-array name="bg_entries"> <item>科技</item> <item>书籍</item> <item>简约</item> </string-array> <string-array name="bg_values"> <item>bg_books</item> <item>bg_nature</item> <item>bg_simple</item> </string-array> </resources> Srimg.xml代码 <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <EditTextPreference android:key="font_size" android:title="字号设置" android:summary="输入文字大小(单位:sp)" android:dialogTitle="设置字号" android:defaultValue="15" android:inputType="number"/> <ListPreference android:key="text_color" android:title="文字颜色" android:summary="选择文字显示颜色" android:dialogTitle="选择颜色" android:defaultValue="#FF000000" android:entries="@array/color_entries" android:entryValues="@array/color_values"/> <ListPreference android:key="bg" android:title="聊天背景" android:summary="选择聊天背景图片" android:dialogTitle="选择背景" android:entries="@array/bg_entries" android:entryValues="@array/bg_values" android:defaultValue="bg_tech"/> </PreferenceScreen> ChatMessage.java package com.example.myapplication; public class ChatMessage { private String nickname; private String message; private long timestamp; private boolean isSelf; public ChatMessage(String nickname, String message, long timestamp, boolean isSelf) { this.nickname = nickname; this.message = message; this.timestamp = timestamp; this.isSelf = isSelf; } public String getNickname() { return nickname; } public String getMessage() { return message; } public long getTimestamp() { return timestamp; } public boolean isSelf() { return isSelf; } } ChatAdapter.java package com.example.myapplication; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Color; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.RecyclerView; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Locale; public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.MessageViewHolder> { private List<ChatMessage> messages; private Context context; public ChatAdapter(List<ChatMessage> messages, Context context) { this.messages = messages; this.context = context; } @Override public int getItemViewType(int position) { return messages.get(position).isSelf() ? 1 : 0; } @NonNull @Override public MessageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View view; if (viewType == 1) { view = inflater.inflate(R.layout.message_item_self, parent, false); } else { view = inflater.inflate(R.layout.message_item, parent, false); } return new MessageViewHolder(view); } @Override public void onBindViewHolder(@NonNull MessageViewHolder holder, int position) { ChatMessage message = messages.get(position); holder.messageText.setText(message.getMessage()); holder.nicknameText.setText(message.getNickname()); // 格式化时间 SimpleDateFormat sdf = new SimpleDateFormat("HH:mm", Locale.getDefault()); holder.timeText.setText(sdf.format(new Date(message.getTimestamp()))); // 应用设置 - 特别处理自己消息的颜色 applySettings(holder, message.isSelf()); } private void applySettings(MessageViewHolder holder, boolean isSelf) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); // 设置字号 float fontSize = Float.parseFloat(prefs.getString("font_size", "15")); holder.messageText.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize); holder.nicknameText.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize - 2); holder.timeText.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize - 4); // 设置文字颜色 - 自己消息使用特殊颜色 String colorValue; if (isSelf) { // 自己消息使用固定颜色,确保可读性 colorValue = "#FF000000"; // 黑色 } else { colorValue = prefs.getString("text_color", "#FF000000"); } holder.messageText.setTextColor(Color.parseColor(colorValue)); holder.nicknameText.setTextColor(Color.parseColor(colorValue)); holder.timeText.setTextColor(Color.parseColor(colorValue)); } @Override public int getItemCount() { return messages.size(); } static class MessageViewHolder extends RecyclerView.ViewHolder { TextView nicknameText, messageText, timeText; public MessageViewHolder(@NonNull View itemView) { super(itemView); nicknameText = itemView.findViewById(R.id.nickname); messageText = itemView.findViewById(R.id.message); timeText = itemView.findViewById(R.id.time); } } } MainActivity.java package com.example.myapplication; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.widget.Button; import android.widget.EditText; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { private static final int SETTINGS_REQUEST = 1; private EditText nicknameEditText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); nicknameEditText = findViewById(R.id.nickname_edit_text); Button enterChatButton = findViewById(R.id.enter_chat_button); Button settingsButton = findViewById(R.id.settings_button); // 从SharedPreferences加载保存的昵称 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); String savedNickname = prefs.getString("nickname", ""); nicknameEditText.setText(savedNickname); enterChatButton.setOnClickListener(v -> { // 保存昵称到SharedPreferences String nickname = nicknameEditText.getText().toString().trim(); if (nickname.isEmpty()) { nickname = "匿名用户"; } SharedPreferences.Editor editor = prefs.edit(); editor.putString("nickname", nickname); editor.apply(); // 启动聊天室 Intent intent = new Intent(MainActivity.this, ChatActivity.class); startActivity(intent); }); settingsButton.setOnClickListener(v -> startActivityForResult( new Intent(MainActivity.this, SettingsActivity.class), SETTINGS_REQUEST)); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == SETTINGS_REQUEST && resultCode == SettingsActivity.RESULT_SETTINGS_CHANGED) { // 设置已更改,更新主界面背景 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); String bgKey = prefs.getString("bg", "bg_tech"); int bgResId = getResources().getIdentifier(bgKey, "drawable", getPackageName()); if (bgResId != 0) { findViewById(android.R.id.content).setBackgroundResource(bgResId); } } } } SettingsActivity.java package com.example.myapplication; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; public class SettingsActivity extends AppCompatActivity { public static final int RESULT_SETTINGS_CHANGED = 1001; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); getSupportFragmentManager() .beginTransaction() .replace(R.id.settings_container, new SettingsFragment()) .commit(); } } SettingsFragment.java  package com.example.myapplication; import android.content.SharedPreferences; import android.os.Bundle; import androidx.preference.PreferenceFragmentCompat; public class SettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { setPreferencesFromResource(R.xml.sring, rootKey); } @Override public void onResume() { super.onResume(); getPreferenceManager().getSharedPreferences() .registerOnSharedPreferenceChangeListener(this); } @Override public void onPause() { super.onPause(); getPreferenceManager().getSharedPreferences() .unregisterOnSharedPreferenceChangeListener(this); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { // 设置变更时通知ChatActivity刷新 if (getActivity() != null) { getActivity().setResult(SettingsActivity.RESULT_SETTINGS_CHANGED); } } } ChatActivity.java  package com.example.myapplication; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import java.io.*; import java.net.Socket; import java.util.ArrayList; import java.util.List; public class ChatActivity extends AppCompatActivity { private static final String SERVER_IP = "192.168.199.1"; // 模拟器访问本机 private static final int SERVER_PORT = 8080; private RecyclerView chatRecyclerView; private EditText messageEditText; private Button sendButton; private ChatAdapter adapter; private List<ChatMessage> messages = new ArrayList<>(); private PrintWriter out; private BufferedReader in; private Socket socket; private String nickname; private SharedPreferences prefs; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_chat); // 从SharedPreferences获取昵称 prefs = PreferenceManager.getDefaultSharedPreferences(this); nickname = prefs.getString("nickname", "匿名用户"); setupUI(); loadChatHistory(); connectToServer(); } @Override protected void onResume() { super.onResume(); // 重新应用设置(从设置页面返回时) applyBackgroundSettings(); if (adapter != null) { adapter.notifyDataSetChanged(); // 刷新消息显示 } } private void setupUI() { chatRecyclerView = findViewById(R.id.chat_recycler_view); messageEditText = findViewById(R.id.message_edit_text); sendButton = findViewById(R.id.send_button); adapter = new ChatAdapter(messages, this); chatRecyclerView.setAdapter(adapter); chatRecyclerView.setLayoutManager(new LinearLayoutManager(this)); applyBackgroundSettings(); // 初始应用背景设置 sendButton.setOnClickListener(v -> sendMessage()); } private void applyBackgroundSettings() { String bgKey = prefs.getString("bg", "bg_tech"); int bgResId = getResources().getIdentifier(bgKey, "drawable", getPackageName()); if (bgResId != 0) { chatRecyclerView.setBackgroundResource(bgResId); } } private void loadChatHistory() { File file = new File(getExternalFilesDir(null), "luoshutao.txt"); if (file.exists()) { try (BufferedReader reader = new BufferedReader(new FileReader(file))) { String line; while ((line = reader.readLine()) != null) { String[] parts = line.split("\\|"); if (parts.length == 4) { boolean isSelf = Boolean.parseBoolean(parts[3]); messages.add(new ChatMessage(parts[0], parts[1], Long.parseLong(parts[2]), isSelf)); } } adapter.notifyDataSetChanged(); chatRecyclerView.scrollToPosition(messages.size() - 1); } catch (IOException e) { e.printStackTrace(); } } } private void saveChatMessage(ChatMessage message) { File file = new File(getExternalFilesDir(null), "luoshutao.txt"); try (FileWriter writer = new FileWriter(file, true)) { writer.write(String.format("%s|%s|%d|%b\n", message.getNickname(), message.getMessage(), message.getTimestamp(), message.isSelf())); } catch (IOException e) { e.printStackTrace(); } } private void connectToServer() { new Thread(() -> { try { socket = new Socket(SERVER_IP, SERVER_PORT); out = new PrintWriter(socket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out.println(nickname); // 监听服务器消息 String serverMessage; while ((serverMessage = in.readLine()) != null) { if (isSystemJoinLeaveMessage(serverMessage)) { continue; // 跳过不显示 } // 解析服务器消息:格式为 "昵称|消息|时间戳" String[] parts = serverMessage.split("\\|", 3); if (parts.length == 3) { final ChatMessage message = new ChatMessage( parts[0], parts[1], Long.parseLong(parts[2]), false ); runOnUiThread(() -> { messages.add(message); adapter.notifyItemInserted(messages.size() - 1); chatRecyclerView.scrollToPosition(messages.size() - 1); saveChatMessage(message); }); } } } catch (IOException e) { runOnUiThread(() -> Toast.makeText(ChatActivity.this, "服务器连接失败: " + e.getMessage(), Toast.LENGTH_LONG).show()); } }).start(); } private boolean isSystemJoinLeaveMessage(String message) { return message.contains("加入了聊天室") || message.contains("离开了聊天室"); } private void sendMessage() { String messageText = messageEditText.getText().toString().trim(); if (!messageText.isEmpty()) { long timestamp = System.currentTimeMillis(); final ChatMessage message = new ChatMessage(nickname, messageText, timestamp, true); // 添加到UI runOnUiThread(() -> { messages.add(message); adapter.notifyItemInserted(messages.size() - 1); chatRecyclerView.scrollToPosition(messages.size() - 1); saveChatMessage(message); }); // 发送到服务器 new Thread(() -> { if (out != null) { out.println(nickname + "|" + messageText + "|" + timestamp); } }).start(); messageEditText.setText(""); } } @Override protected void onDestroy() { super.onDestroy(); try { if (socket != null) socket.close(); if (out != null) out.close(); if (in != null) in.close(); } catch (IOException e) { e.printStackTrace(); } } } activity_main.xml <?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:gravity="center" android:padding="16dp" android:background="@drawable/bg_tech"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="骆树涛聊天室" android:textSize="40sp" android:layout_gravity="center" android:layout_marginBottom="16dp" android:textStyle="bold" android:textColor="#2196F3"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="16dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="昵称:" android:textSize="25sp" android:layout_marginEnd="8dp"/> <EditText android:id="@+id/nickname_edit_text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="请输入昵称" android:textSize="25sp" android:maxLines="1"/> </LinearLayout> <Button android:id="@+id/enter_chat_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="进入聊天室" android:layout_marginBottom="16dp"/> <Button android:id="@+id/settings_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="设置"/> </LinearLayout> activity_chat.xml <?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"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/chat_recycler_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:scrollbars="vertical"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="8dp"> <EditText android:id="@+id/message_edit_text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="输入消息" android:inputType="textMultiLine" android:maxLines="3"/> <Button android:id="@+id/send_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="发送"/> </LinearLayout> </LinearLayout> activity_settings.xml  <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/settings_container" android:layout_width="match_parent" android:layout_height="match_parent" /> message_item.xml <?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="wrap_content" android:orientation="vertical" android:padding="8dp" android:layout_marginTop="4dp" android:layout_marginEnd="60dp"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical"> <TextView android:id="@+id/nickname" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp"/> <TextView android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="10sp" android:layout_marginStart="8dp"/> </LinearLayout> <TextView android:id="@+id/message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/bubble_other" android:padding="8dp"/> </LinearLayout> message_item_self.xml  <?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="wrap_content" android:orientation="vertical" android:padding="8dp" android:layout_marginTop="4dp" android:layout_marginStart="60dp" android:gravity="end"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical"> <TextView android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="10sp" android:layout_marginEnd="8dp"/> <TextView android:id="@+id/nickname" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp"/> </LinearLayout> <TextView android:id="@+id/message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/bg_books" android:padding="8dp"/> </LinearLayout>

fancong2010
  • 粉丝: 0
上传资源 快速赚钱