一、效果实现
二、功能说明
需要实现一个类似微信的简单应用框架,包括以下功能:
-
界面跳转功能:设置四个主界面,分别是“聊天”、“联系人”、“发现”和“我的”。用户点击相应的图标,可以在这些界面之间自由切换,每次点击会跳转到对应的页面。
-
联系人展示与数据绑定:在“联系人”界面展示一个联系人列表(用商品图片代替联系人图标)。当用户点击某个商品图片时,能够显示对应的详细信息。
-
背景音乐功能:为“联系人”界面添加背景音乐,当用户进入“联系人”界面时自动播放音乐,退出该界面时音乐自动停止。
故需要实现的功能:
1.activity之间的跳转
2.数据的动态连接
3.背景音乐的实现
三、功能实现
1.跳转功能
1.1界面之间的跳转涉及以下xml文件 :
微信主界面设计:
在主界面的布局中,采用了 XML 文件的嵌套设计。界面整体分为三个部分:
(1)顶部区域:顶部显示“我的微信”标题部分,其布局定义在单独的 layout_top.xml
文件中
(2)中间内容区域:用于显示主功能界面的具体内容,支持动态切换。
(3)底部导航栏:底部包含四个功能图标,分别对应“聊天”、“联系人”、“发现”和“我的”模块,其布局封装在 layout_bottom.xml
文件中。
顶部区域
底部导航栏
四个跳转界面的布局
布局框架搭好后,进行跳转代码的编写
在 MainActivity_fragment
中定义了四个底部导航按钮,分别对应四个微信功能图标。通过 showFragment()
和 Fragmenthide()
方法,管理各功能页面的显示与隐藏。结合一个 if-else
判断逻辑,实现了用户点击任意图标时,能够切换到对应功能界面的效果。
package com.example.myapplication;
import static com.example.myapplication.R.id.bottom_layout1;
import static com.example.myapplication.R.id.bottom_layout2;
import static com.example.myapplication.R.id.bottom_layout3;
import static com.example.myapplication.R.id.bottom_layout4;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
public class MainActivity_fragment extends AppCompatActivity implements View.OnClickListener {
Fragment fragment1, fragment2, fragment3, fragment4;
LinearLayout layout1, layout2, layout3, layout4;
FragmentManager manager;
FragmentTransaction transaction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_fragment);
layout1 = findViewById(R.id.bottom_layout1);
layout2 = findViewById(R.id.bottom_layout2);
layout3 = findViewById(R.id.bottom_layout3);
layout4 = findViewById(R.id.bottom_layout4);
fragment1 = new BlankFragment1();
fragment2 = new BlankFragment2();
fragment3 = new BlankFragment3();
fragment4 = new BlankFragment4();
manager = getSupportFragmentManager();
transaction = manager.beginTransaction();
initial();
fragementhide();
transaction.show(fragment1);
transaction.commit();
layout1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
transaction = manager.beginTransaction();
fragementhide();
transaction.show(fragment1);
transaction.commit();
}
});
layout2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
transaction = manager.beginTransaction();
fragementhide();
transaction.show(fragment2);
transaction.commit();
}
});
layout3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
transaction = manager.beginTransaction();
fragementhide();
transaction.show(fragment3);
transaction.commit();
}
});
layout4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
transaction = manager.beginTransaction();
fragementhide();
transaction.show(fragment4);
transaction.commit();
}
});
}
public void initial() {
transaction.add(R.id.framelayout1, fragment1);
transaction.add(R.id.framelayout1, fragment2);
transaction.add(R.id.framelayout1, fragment3);
transaction.add(R.id.framelayout1, fragment4);
}
public void fragementhide() {
transaction.hide(fragment1);
transaction.hide(fragment2);
transaction.hide(fragment3);
transaction.hide(fragment4);
}
public void showfragement(Fragment fragment) {
transaction = manager.beginTransaction();
fragementhide();
transaction.show(fragment);
transaction.commit();
}
@Override
public void onClick(View view) {
if(view.getId() == bottom_layout1)
{fragementhide();showfragement(fragment1);}
else if(view.getId() == bottom_layout2)
{fragementhide();showfragement(fragment2);}
else if(view.getId() == bottom_layout3)
{fragementhide();showfragement(fragment3);}
else if(view.getId() ==bottom_layout4)
{fragementhide();showfragement(fragment4);}
}
}
1.2联系人界面的跳转
涉及intent类
intent
Intent
类是 Android 中用于在不同组件(如 Activity、Service、BroadcastReceiver)之间传递数据和触发操作的重要工具。它的主要作用包括以下几个方面
启动组件:用于启动新的 Activity 或 Service,实现页面跳转或启动后台任务。
例如,startActivity(intent) 用于打开一个新页面。
传递数据:可通过 putExtra() 方法将数据附加到 Intent 中,并在目标组件中通过 getExtra() 方法获取数据。例如,在页面间传递用户输入的数据或状态信息。
广播消息:用于向系统或应用中的其他部分发送广播消息,配合 BroadcastReceiver 使用。
隐式意图调用:通过隐式 Intent 指定动作(如查看网页、拨打电话)和数据类型,让系统匹配符合条件的组件来完成操作。
示例
启动一个新页面并传递数据
Intent intent = new Intent(CurrentActivity.this, TargetActivity.class);
intent.putExtra("key", "value"); // 附加数据
startActivity(intent); // 启动目标Activity
获取传递过来的数据:
Intent intent = getIntent();
String value = intent.getStringExtra("key");
2.连接数据
2.1创建列表
recycleview
RecyclerView
是 Android 提供的一种高级、灵活的视图组件,用于高效地显示大量数据集合(如列表或网格)。它是 ListView
和 GridView
的升级版本,具备更强的功能和更高的性能。
(1)高效复用:通过 ViewHolder
模式优化性能,适用于大数据量场景。
(2)灵活布局:支持线性、网格和瀑布流布局,可自定义显示样式。
(3)扩展性强:支持自定义动画、分隔线装饰、拖拽排序和滑动删除等功能。
(4)分页加载与滑动监听:方便实现动态数据加载。
2.2数据存储
创建list1列表进行数据存储
package com.example.myapplication;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BlankFragment2 extends Fragment {
private Context context;
private List< Map<String,Object>> list;
public RecyclerView.Adapter adapter1;
List<Map<String,Object>> list1;
private RecyclerView recyclerView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view= inflater.inflate(R.layout.activity_main3, container, false);
context=getContext();
recyclerView=view.findViewById(R.id.recyclerView);
int[] phonename={R.drawable.p1,R.drawable.p2,R.drawable.p3};
String[] price={"1000","2000","3000"};
String[] config={"sss","qqq","rrr"};
list1 =new ArrayList<Map<String,Object>>();
for(int i=0;i<phonename.length;i++)
{
Map<String,Object> map=new HashMap<>();
map.put("name",phonename[i]);
map.put("price",price[i]);
map.put("config",config[i]);
list1.add(map);
}
adapter1=new Adapter(context,list1);
recyclerView.setAdapter(adapter1);
LinearLayoutManager manager=new LinearLayoutManager(context);
recyclerView.setLayoutManager(manager);
// Inflate the layout for this fragment
return view;
}
}
2.3绑定数据
在 Adapter
中,ViewHolder
(通常简称为 holder
)的作用是用于高效绑定视图和数据。它通过缓存子视图,避免重复调用 findViewById()
,从而提高性能。
工作机制
(1)视图创建:Adapter
的 onCreateViewHolder()
方法创建 ViewHolder
对象,并将布局视图绑定到 ViewHolder
中。
(2)数据绑定:在 onBindViewHolder()
方法中,holder
被用来将数据绑定到具体的视图组件(如 TextView
、ImageView
等)。
package com.example.myapplication;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import java.util.Map;
public class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder> {
private Context context;
private View inflater;
private List< Map<String,Object>> list;
ActivityResultLauncher<Intent> launcher;
public Adapter(Context context, List<Map<String,Object>> list)
{
this.context=context;
this.list=list;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
inflater=LayoutInflater.from(context).inflate(R.layout.layout_item,viewGroup,false);
MyViewHolder holder=new MyViewHolder(inflater);
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder myViewHolder, int j) {
myViewHolder.imageView.setImageResource(Integer.parseInt(list.get(j).get("name").toString()));
//myViewHolder.textView1.setText(list.get(j).get("name").toString());
String str1=list.get(j).get("price").toString();
String str2=list.get(j).get("config").toString();
myViewHolder.textView2.setText(str1);
myViewHolder.textView3.setText(str2);
// launcher=registerForActivityResult(
// new ActivityResultContracts.StartActivityForResult(),
// new ActivityResultCallback<ActivityResult>() {
// @Override
// public void onActivityResult(ActivityResult result) {
// if(result.getResultCode()==666){
// String str1=result.getData().getStringExtra("result");
// textview.setText(str1);
// }
// }
// }
// );
myViewHolder.imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(context,MainActivity_cantact_detail.class);
intent.putExtra("price",str1);
intent.putExtra("config",str2);
context.startActivity(intent);
//launcher.launch(intent);
}
});
}
@Override
public int getItemCount() {
return list.size();
}
static class MyViewHolder extends RecyclerView.ViewHolder{
TextView textView1,textView2,textView3;
ImageView imageView;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
imageView=itemView.findViewById(R.id.rc_imageView);
//textView1 =itemView.findViewById(R.id.rc_textview1);
textView2 =itemView.findViewById(R.id.rc_textview2);
textView3 =itemView.findViewById(R.id.rc_textview3);
}
}
}
页面展示:
3.添加背景音乐
添加music.mp3(保证音乐可放,无损坏)
绑定式服务音乐播放代码
package com.example.myapplication;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
MediaPlayer mediaPlayer;
public MyService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mediaPlayer=MediaPlayer.create(this,R.raw.music);
mediaPlayer.start();
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");
Log.d("zyq","MYservice onbind...");
Mybinder binder=new Mybinder();
return binder;
}
@Override
public void onDestroy() {
Log.d("zyq","MYservice destroy....");
mediaPlayer.stop();
super.onDestroy();
}
public class Mybinder extends Binder{
public Mybinder() {
mediaPlayer=MediaPlayer.create(getApplicationContext(),R.raw.music);
mediaPlayer.start();
}
}
}
音乐服务的连接与断开
package com.example.myapplication;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity_cantact_detail extends AppCompatActivity {
TextView textView;
MyService.Mybinder mybinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_cantact_detail);
textView=findViewById(R.id.textView_detail);
String str=getIntent().getStringExtra("price")+"/"+
getIntent().getStringExtra("config");
textView.setText(str);
Intent intent=new Intent(this,MyService.class);
ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d("zyq","onserviceconnect...");
mybinder=(MyService.Mybinder) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d("zyq","onserviceconnect...");
mybinder=null;
}
};
this.bindService(intent,connection, Context.BIND_AUTO_CREATE);
// this.startService(intent);
}
}
绑定式服务启动:使用 bindService()
方法启动服务,与 Activity
组件的生命周期绑定。通过绑定后,可以在 Activity
中直接调用服务的接口来控制音乐播放和停止。绑定的服务在组件销毁时会自动停止。
四、总结
- 问题描述:
Fragment 切换过程中,由于重新加载导致屏幕闪烁,用户体验较差。 - 解决方法:
在FragmentTransaction
中,初始化时将所有 Fragment 添加到布局并隐藏,切换时仅显示目标 Fragment,避免重复创建。 - 问题描述:
数据在RecyclerView
中未正确显示,或图片资源加载错误。 - 解决方法:
检查Adapter
中数据绑定逻辑,确保数据源和视图组件一一对应;对于图片资源,使用setImageResource()
方法时需要转换为整数。 - 问题描述:
页面退出时,音乐服务仍在后台运行,导致资源浪费。 - 解决方法:
在页面销毁时(onDestroy()
方法中),确保调用unbindService()
和stopService()
停止服务;在服务类中重写onDestroy()
,确保资源彻底释放。 - 问题描述:
当页面反复切换时,音乐会多次重复播放。 - 解决方法:
在服务启动时检查MediaPlayer
的状态,确保未重复初始化;例如:if (mediaPlayer == null) { mediaPlayer = MediaPlayer.create(this, R.raw.music); }
- 问题描述:
在不同屏幕尺寸的设备上,底部导航栏或 RecyclerView 内容显示异常。 - 解决方法:
使用约束布局(ConstraintLayout)替代传统线性布局,并结合dp
和sp
单位设计界面,确保布局适配性。
本项目通过 Fragment 管理界面、RecyclerView 动态加载数据以及绑定式服务播放背景音乐,构建了一个高效、灵活的微信功能框架。