目录
fragment1.java,fragment2.java,fragment3.java,fragment4.java(对应fragment布局文件):
作业目标
- 设计一个app的门户框架,需要实现3-4个tab切换效果。实现一个类似微信的布局,包括聊天、通讯录、发现和设置4个页面。本功能要求需要的技术为:activity、xml、fragment。
- 在任一tab页中实现列表效果,我选择在tab1(聊天页面)实现一个左边是图标、右边是文本的列表。本功能的实现需要使用recyclerview。
技术说明
环境准备
在MyApplication2项目中完成项目基础搭建,安装了SDK和AVD以供实验顺利运行。
主要技术
- Activity组件:为应用程序提供生命周期管理,允许应用程序在不同状态下执行操作;展示应用程序的用户界面,使用户通过这些界面和应用程序进行交互;启动其他的组件,使得不同的组件直接可以进行通信和协作。
- Fragment:包含在Activity中,更好地适应不同屏幕尺寸和设备类型,提供更一致的用户体验。
- Wiget控件:主要使用了TextView控件(用于显示字符)、ImageView控件(用于显示图像)。
图片准备
将该项目需要的图标图片上传到目录MyApplication2\app\src\main\res\drawable下,便于调用。需要注意的是图片的文件名中不能使用大写字母,否则会在构建或运行时遇到问题。
关键代码解析
-
top.xml(头部文件):
在layout中创建头部文件,添加一个TextView,输入文本“微信”,文本颜色修改为白色,LinearLayout背景颜色修改为黑色,文本大小调整为50sp并居中。
具体代码如下:
<?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:background="@color/black">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="微信"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="50sp" />
</LinearLayout>
效果如下:
-
bottom.xml(底部文件):
首先调整文件布局:在已有的LinearLayout(horizontal)下添加LinearLayout(vertical),在LinearLayout(vertical)下添加一个imageView和一个textView,将LinearLayout(vertical)复制粘贴3次,生成4个水平的LinearLayout。
然后,我在imageView中使用了之前上传到drawable中的图片,textView中分别填入“聊天”、“通讯录”、“发现”和设置,修改各种参数使4个LinearLayout及其内部的图片和文本能够排列整齐。
最后为4个LinearLayout及其图片与文本设置各自的id,便于之后的调用。
具体代码如下:
最外层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="wrap_content"
android:orientation="horizontal">
</LinearLayout>
内层LinearLayout(聊天页面的LinearLayout1为例):一开始使用了AS自带的样例图片,运行时会失败和报错,因此要使用自己上传的图片。
<LinearLayout
android:id="@+id/LinearLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<ImageView
android:id="@+id/imageView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/jjh" />
<TextView
android:id="@+id/textView11"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="聊天"
android:textSize="30sp" />
</LinearLayout>
效果如下:
-
activity_main.xml(基本布局文件):
首先调整文件布局:将LinearLayout布局设置为vertical,然后用include把top.xml和buttom.xml添加到activity_main1.xml中,再添加FrameLayout,使页面中间能够显示需要的文本。
然后修改各控件的参数:添加FrameLayout的id为content,layout_width设置match_parent、layout_height设置为match_parent、layout_weight设置为1,从而让FrameLayout布满父布局的所有可用空间。
具体代码如下:
<?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:orientation="vertical"
tools:context=".MainActivity1">
<include layout="@layout/top" />
<FrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</FrameLayout>
<include
layout="@layout/bottom" />
</LinearLayout>
效果如下:
fragment.xml(中间布局文件):
为了类微信布局的设计,我们需要创建4个不同的fragment.xml文件,分别表示出用户当前所处的页面。
以下是我创建的4个文件:fragment1.xml展示聊天页面,fragment2.xml展示通讯录页面,fragment3.xml展示发现页面,fragment4.xml展示设置页面。
分别添加textView并且修改各种参数,另外在fragment1.xml中添加一个recyclerView,因为我需要在聊天页面中实现列表效果。
具体代码如下:
fragment1.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">
<TextView
android:id="@+id/textView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="聊天界面"
android:textColor="#009688"
android:textSize="60sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="15" />
</LinearLayout>
效果如下:
fragment2.xml(其他3个除了id和文本内容以外没有区别):
<?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">
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:gravity="center"
android:text="通讯录界面"
android:textSize="60sp" />
</LinearLayout>
效果如下:
item.xml(列表项布局):
为想要输出的列表进行布局的设计,我选择左边显示图标,右边显示文本的布局,因此LinearLayout布局方式为horizontal,然后存入一个imageView和一个textView,并且调整各种参数。
预览时我发现我准备导入的图片有点大,所以把imageView的layout_width和layout_height调整为100dp。
具体代码如下:
<?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">
<ImageView
android:id="@+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_weight="1" />
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="20"
android:gravity="center"
android:text="TextView"
android:textSize="40sp" />
</LinearLayout>
效果如下:
MainActivity.java:
MainActivity类继承Activity类,并且添加implements View.OnClickListener。
- 首先创建Fragment类的实例对象,定义FragmentManager对Fragment进行控制,以及用于监听的变量:
// Fragment类的实例对象
Fragment fragment1,fragment2,fragment3,fragment4;
// FragmentManager
FragmentManager fm;
// 设置交互控件的变量
LinearLayout linearLayout1,linearLayout2,linearLayout3,linearLayout4;
- onCreate()函数(初始化activity):从activity_main1.xml文件中,添加需要找到的LinearLayout(bottom.xml中的4个linearlayout控件),然后初始化fragment实例对象并且调用fragment页面的展示函数fragmentshow(),最后用setOnClickListener()对添加的4个linearlayout作全局的点击监听。
// Activity的初始化
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main1);
linearLayout1=findViewById(R.id.LinearLayout1);
linearLayout2=findViewById(R.id.LinearLayout2);
linearLayout3=findViewById(R.id.LinearLayout3);
linearLayout4=findViewById(R.id.LinearLayout4);
fm=getSupportFragmentManager();
fragment1=new Fragment1();
fragment2=new Fragment2();
fragment3=new Fragment3();
fragment4=new Fragment4();
initial();
fragmentshow(fragment1);
// 在当前类有全局的点击监听
linearLayout1.setOnClickListener(this);
linearLayout2.setOnClickListener(this);
linearLayout3.setOnClickListener(this);
linearLayout4.setOnClickListener(this);
}
- fragmenthide()函数:因为4个页面需要分别显示,所以需要隐藏fragment中定义的content。在这里使用FragmentTransaction来管理fragment的隐藏。
// 隐藏不需要显示的content
private void fragmenthide() {
FragmentTransaction ft=fm.beginTransaction()
.hide(fragment1)
.hide(fragment2)
.hide(fragment3)
.hide(fragment4);
ft.commit();
}
- fragmentshow()函数:使用FragmentTransaction来管理每个fragment的显示,函数的意义是先调用前面设计好的fragmenthide()函数,将4个fragment都隐藏起来,然后再显示我们希望显示的fragment。
// 展示所需要的content
private void fragmentshow(Fragment fragment) {
// 先隐藏所有的content,再显示出需要的content
fragmenthide();
FragmentTransaction ft=fm.beginTransaction()
.show(fragment);
ft.commit();
}
- initial()函数:一个初始化函数,把4个fragment放到ft中,实现页面的切换。
// 定义初始化函数
private void initial() {
// 把fragment加入content
FragmentTransaction ft =fm.beginTransaction()
.add(R.id.content,fragment1)
.add(R.id.content,fragment2)
.add(R.id.content,fragment3)
.add(R.id.content,fragment4);
// 事务的完整性以提交commit为准
ft.commit();
}
- onClick()函数:用于监听被点击的图标,并根据此显示对应页面的内容。一开始我使用的是switch case语句进行分类,但是在我的AS上会一直报错“constant expression required”,即使定义静态常量l1=R.id.LinearLayout也无法解决,因此换成if语句进行分类讨论。
@Override
public void onClick(View view) {
// 获取控件变量的id,然后决定点击监听
if (view.getId()==R.id.LinearLayout1){
fragmentshow(fragment1);
}
if (view.getId()==R.id.LinearLayout2){
fragmentshow(fragment2);
}
if (view.getId()==R.id.LinearLayout3){
fragmentshow(fragment3);
}
if (view.getId()==R.id.LinearLayout4){
fragmentshow(fragment4);
}
}
fragment1.java,fragment2.java,fragment3.java,fragment4.java(对应fragment布局文件):
首先继承fragment类。
- fragment1.java:
创建全局变量recyclerview和ma
RecyclerView recyclerView;
Myadapter2 ma;
onCreateView():创建视图,与fragment1.xml关联,然后通过findViewById获取recyclerView的视图,通过setLayoutManager设置recyclerView的布局管理器。然后将列表需要的图片及其文本添加进Album类和list,最后调用适配器Myadapter2关联recyclerView,使列表内容能在fragment1中显示。
View view=inflater.inflate(R.layout.fragment1, container, false);
recyclerView=view.findViewById(R.id.recyclerView1);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
// 把图片及其名称存入类Album
List<Album> list = new ArrayList();
Album thestorybegins = new Album(getRandomLengthName("The Story Begins"), R.drawable.tsb);
list.add(thestorybegins);
Album pagetwo = new Album(getRandomLengthName("Page Two"), R.drawable.cheerup);
list.add(pagetwo);
Album lane1 = new Album(getRandomLengthName("Twicecoaster:Lane1"), R.drawable.tt);
list.add(lane1);
Album lane2 = new Album(getRandomLengthName("Twicecoaster:Lane2"), R.drawable.knock);
list.add(lane2);
Album signal = new Album(getRandomLengthName("Signal"), R.drawable.sig);
list.add(signal);
Album twicetagram = new Album(getRandomLengthName("Twicetagram"), R.drawable.likey);
list.add(twicetagram);
Album merryhappy = new Album(getRandomLengthName("Merry&Happy"), R.drawable.mh);
list.add(merryhappy);
Album whatislove = new Album(getRandomLengthName("What Is Love"), R.drawable.wil);
list.add(whatislove);
Album summernights = new Album(getRandomLengthName("Summer Nights"), R.drawable.sn);
list.add(summernights);
Album yesoryes = new Album(getRandomLengthName("YES or YES"), R.drawable.yoy);
list.add(yesoryes);
Album fancyyou = new Album(getRandomLengthName("FANCY YOU"), R.drawable.fancy);
list.add(fancyyou);
Album feelspecial = new Album(getRandomLengthName("Feel Special"), R.drawable.fs);
list.add(feelspecial);
Album moremore = new Album(getRandomLengthName("MORE&MORE"), R.drawable.mm);
list.add(moremore);
Album eyeswideopen = new Album(getRandomLengthName("Eyes Wide Open"), R.drawable.ewo);
list.add(eyeswideopen);
Album tasteoflove = new Album(getRandomLengthName("Taste Of Love"), R.drawable.tol);
list.add(tasteoflove);
Album formulaoflove = new Album(getRandomLengthName("Formula Of Love:O+T=<3"), R.drawable.fol);
list.add(formulaoflove);
Album between12 = new Album(getRandomLengthName("BETWEEN 1&2"), R.drawable.ttt);
list.add(between12);
Album readytobe = new Album(getRandomLengthName("READY TO BE"), R.drawable.rtb);
list.add(readytobe);
// 适配器
ma=new Myadapter2(list);
recyclerView.setAdapter(ma);
return view;
getRandomLengthName():将需要的文本转换为字符串,存入Album类和list中。如果不使用getRandomLengthName()函数,将会存在关于字符类型的报错。
// 将专辑名转换为string
private String getRandomLengthName(String name){
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 1; i++) {
builder.append(name);
}
return builder.toString();
}
- fragment2.java(fragment3和fragment4与fragment2相同):
onCreateView():创建并返回了与Fragment2.xml关联的布局视图,将其添加到container中,false参数表示不要立即将布局添加到container中,而是允许后续的逻辑来处理它。
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState){
return inflater.inflate(R.layout.fragment2, container, false);
}
Album.java:
存放列表的每张图片和每个文件,使列表能够通过适配器存入fragment1。
package com.example.myapplication;
// 存放专辑的名称和图片
public class Album {
private String name;
private int imageid;
public Album(String name,int imageid){
this.name=name;
this.imageid=imageid;
}
public String getName() {
return name;
}
public int getImageid() {
return imageid;
}
}
Myadapter2.java:
- 首先定义一个Album类型的list1,然后通过Myadapter2函数传入list的值:
private List<Album> list1;
// 传入值
public Myadapter2(List list) {
list1=list;
}
- onCreateViewHolder():创建viewHolder,使用from函数生成静态压缩器,然后把数据填入viewHolder并返回。
@NonNull
@Override
public Viewholder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 压缩器(from生成静态压缩器)
// 闪退时可以挂布尔值false运行
View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.item1,parent,false);
Viewholder viewholder=new Viewholder(view);
return viewholder;
}
- onBindViewHolder():得到list1中存放的数据,存入每个子项holder中。
@Override
public void onBindViewHolder(@NonNull Viewholder holder, int position) {
Album album=list1.get(position);
// 获取专辑的名称和封面图片
holder.textView.setText(album.getName());
holder.imageView.setImageResource(album.getImageid());
}
- getItemCount():返回list1的长度(行数)。
@Override
public int getItemCount() {
return list1.size();
}
- 内嵌类holder:相当于一个行指针。
// 内嵌类 holder类似一个行指针
static class Viewholder extends RecyclerView.ViewHolder{
View view;
// 存放专辑名称
TextView textView;
// 存放专辑封面
ImageView imageView;
public Viewholder(@NonNull View itemView) {
super(itemView);
view=itemView;
// item1.xml中的控件对象
textView=itemView.findViewById(R.id.textView4);
imageView=itemView.findViewById(R.id.imageView);
}
}
运行效果
动态效果:
实验心得
通过本次实验,我初次接触到了AS项目的开发,在无数次遇到错漏和解决问题的过程中,终于完成了这个类微信的门户页面设计项目。这使我对Android的各种基本功能和运行模式、以及各个文件和控件直接的关联与逻辑有了清晰的理解,也记住了activity和fragment的生命周期和运行时会产生的各种状态。
源码
存储在gitee:test1: 作业1:app门户页面设计与开发