读书程序gitee----2023/10/15https://gitee.com/zhangqiaoer/read-app.git
目录
一、目标
设计一个读书app,实现切换3-4个tab切换功能
在任一tab中显示列表效果
二、技术准备
- 熟悉基本LinearLayout、掌握TextView、ImageView、FrameLayout等控件的使用
- 掌握控件常用属性 layout_width、gravity、weight等的修改和调整
- 利用RecycleView实现列表功能
- 理解布局文件和java文件间的联系
三、布局设计
3.1 总体布局
总体效果:
- 读书共包含4个tab页,即阅读,书签,书友,我的,页面分为上中下三个部分
- 头部是界面标题部分,中部是界面展示部分,底部是按钮控制部分
- 我们需要头部显示的xml文件(top.xml),底部按钮控制的xml文件(bottom.xml),点击的四个button要对应四个中部展示界面的xml文件(read.xml,book.xml,friend.xml,my.xml)
3.2 顶部top.xml
新建xml文件
在LinearLayout下拖入一个TextView控件,LinearLayout设置为horizontal
TextView的gravity、textSize、textColor、background等属性进行调整
代码如下
<?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/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black"
android:gravity="center"
android:text="读书"
android:textColor="@color/white"
android:textSize="40sp" />
</LinearLayout>
3.3 底部bottom.xml
在horizontal的LinearLayout下拖入四个vertical的LinearLayout并且及时修改id,方便后续使用
在每个vertical的LinearLayout下又拖入一个imageView和一个textView,即表示每个按钮的图标和名称
在drawable文件夹下加入导航栏图标图片文件
将imageView换成所加入的对应图片
调整每个textView的text为底部导航栏按钮名称,并对对textView的textColor、textSize等属性进行调整
其中一个按钮部分的代码为
<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/imageView"
android:layout_width="match_parent"
android:layout_height="80dp"
android:src="@drawable/read" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="30dp"
android:gravity="center"
android:text="阅读"
android:textColor="@color/white"
android:textSize="20sp" />
</LinearLayout>
其余三个对应更改
3.4 四个tab页面
新建四个xml文件对应四个tab页面,调整每个xml文件下的控件属性
下图是read.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" >
<TextView
android:id="@+id/textView8"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="这是阅读界面"
android:textColor="@color/black"
android:textSize="50sp" />
</LinearLayout>
其余三个页面对应修改
3.5主布局页面activity_main.xml
用"include" 将刚才写的top和button两个xml放到这个activity_main.xml中来
中间用FrameLayout标签作为内容区,并将其id修改为content。
效果如下
代码如下
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
layout="@layout/top"
android:layout_width="match_parent"
android:layout_height="wrap_content"></include>
<FrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
</FrameLayout>
<include
layout="@layout/bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></include>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
四、切换效果实现
4.1 总体思路
- 用户在点击导航栏其中一个图标时,页面切换到相应tab(主题),所以需要为导航栏图标设置监听,当发生点击事件,则需切换页面。
- 需要将bottom.xml里设计的导航栏的图标按钮与4个fragment(控制主题文件)进行绑定,在MainActivity与bottom里导航栏的每个linearLayout(vertical)绑定。
4.2 四个fragment
新建四个空白的fragment文件
每个fragment.java只留下一个onCreateview()函数,控制一个tab。代码如下
package com.example.read;
import android.annotation.SuppressLint;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.read, container, false);
}
}
4个fragment只需要修改inflater的参数中的页面id,即不同fragment对应的布局的资源id(4个tab文件对应名称)。inflate方法的主要作用就是将xml转换成一个View对象,用于动态的创建布局。
4.3 主函数MainActivity.java
创建四个fragment对象实例化为四个fragment文件
创建四个LinearLayout对象实例化为导航栏四个按钮,并且设置监听
FragmentManager接口的实现类,管理四个主题页面
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Fragment fragment1,fragment2,fragment3,fragment4;
FragmentManager fm;
LinearLayout linearLayout1,linearLayout2,linearLayout3,linearLayout4;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragment1 =new Fragment1();
fragment2 =new Fragment2();
fragment3 =new Fragment3();
fragment4 =new Fragment4();
linearLayout1=findViewById(R.id.LinearLayout1);
linearLayout2=findViewById(R.id.LinearLayout2);
linearLayout3=findViewById(R.id.LinearLayout3);
linearLayout4=findViewById(R.id.LinearLayout4);
fm=getSupportFragmentManager();
initial();
fragmenthide();
fragmentshow(fragment1);
linearLayout1.setOnClickListener(this);
linearLayout2.setOnClickListener(this);
linearLayout3.setOnClickListener(this);
linearLayout4.setOnClickListener(this);
}
将四个fragment初始化到一个transaction中,值得注意的是transation必须commit
private void initial(){
FragmentTransaction ft=fm.beginTransaction()
.add(R.id.content,fragment1)
.add(R.id.content,fragment2)
.add(R.id.content,fragment3)
.add(R.id.content,fragment4);//Transaction交互,需要提交,事务在类里面定义,不在外面,防止整体使用,并且提交commit需要单独分行写
ft.commit();
}
隐藏fragment (将fragment全部隐藏)
private void fragmenthide(){
FragmentTransaction ft=fm.beginTransaction()
.hide(fragment1)
.hide(fragment2)
.hide(fragment3)
.hide(fragment4);
ft.commit();
}
显示fragment
private void fragmentshow(Fragment fragment){
FragmentTransaction transaction= fm.beginTransaction()
.show(fragment);
transaction.commit();
}
点击事件:显示所点击导航栏按钮所绑定的fragment对应的tab
public void onClick(View view) {//view是全屏幕
fragmenthide();
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);}
/* //if是万能的,但是case只能处理静态编译,不能处理动态编译,能不能用取决于gradle版本
switch (view.getId()){
case R.id.LinerLayout1:fragmentshow(fragment1);
case R.id.LinerLayout2:fragmentshow(fragment1);
case R.id.LinerLayout3:fragmentshow(fragment1);
case R.id.LinerLayout4:fragmentshow(fragment1);
break;*/
}
4.4 效果展示
MainActivity总体代码
package com.example.read;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Fragment fragment1,fragment2,fragment3,fragment4;
FragmentManager fm;
LinearLayout linearLayout1,linearLayout2,linearLayout3,linearLayout4;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragment1 =new Fragment1();
fragment2 =new Fragment2();
fragment3 =new Fragment3();
fragment4 =new Fragment4();
linearLayout1=findViewById(R.id.LinearLayout1);
linearLayout2=findViewById(R.id.LinearLayout2);
linearLayout3=findViewById(R.id.LinearLayout3);
linearLayout4=findViewById(R.id.LinearLayout4);
fm=getSupportFragmentManager();
initial();
fragmenthide();
fragmentshow(fragment1);
linearLayout1.setOnClickListener(this);
linearLayout2.setOnClickListener(this);
linearLayout3.setOnClickListener(this);
linearLayout4.setOnClickListener(this);
}
private void fragmenthide(){
FragmentTransaction ft=fm.beginTransaction()
.hide(fragment1)
.hide(fragment2)
.hide(fragment3)
.hide(fragment4);
ft.commit();
}
private void initial(){
FragmentTransaction ft=fm.beginTransaction()
.add(R.id.content,fragment1)
.add(R.id.content,fragment2)
.add(R.id.content,fragment3)
.add(R.id.content,fragment4);//Transaction交互,需要提交,事务在类里面定义,不在外面,防止整体使用,并且提交commit需要单独分行写
ft.commit();
}
public void onClick(View view) {//view是全屏幕
fragmenthide();
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);}
/* //if是万能的,但是case只能处理静态编译,不能处理动态编译,能不能用取决于gradle版本
switch (view.getId()){
case R.id.LinerLayout1:fragmentshow(fragment1);
case R.id.LinerLayout2:fragmentshow(fragment1);
case R.id.LinerLayout3:fragmentshow(fragment1);
case R.id.LinerLayout4:fragmentshow(fragment1);
break;*/
}
private void fragmentshow(Fragment fragment){
FragmentTransaction transaction= fm.beginTransaction()
.show(fragment);
transaction.commit();
}
}
五、列表效果实现
5.1 总体思路
- 改写Adapter类,实现对列表item内容的适配
- 在书架栏,用recyclerview编写书单列表
5.2 改写Adapter
编写Myholder类,理解为指向行的指针;指向bookitem.xml的id为booktextView的控件
定义了嵌套类,在外面类中会直接调用里面的MYHolder类的对象,泛型传入,不是对象传值,其实例对象用来接收每个item中的内容
public class Myholder extends RecyclerView.ViewHolder{//定义这个类时,报错的现删掉,再重新补一次,补回来就不报错了
TextView textView;
public Myholder( View itemView)//嵌套类,在外面类中会直接调用里面的MYHolder类的对象,泛型传入,不是对象传值
{
public MyHolder(View itemview){
super(itemview);
textView=itemview.findViewById(R.id.booktextView);
}
继承RecyclerView.Adapter类编写自己的BookAdapter文件,充分利用提词器功能,导入三个必备函数,并且进行改写
代码如下
package com.example.read;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class BookAdapter extends RecyclerView.Adapter<BookAdapter.MyHolder> {
Context context1;
List<String> list1;
public BookAdapter(Context context,List list)//构造函数
{
context1=context;
list1=list;
}
@NonNull
@Override
public MyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {//确定每个item的具体视图
View view= LayoutInflater.from(context1).inflate(R.layout.bookitem,parent,false);
// Inflater inflater=new Inflater(); 这个压缩是像文件压缩一样的真压缩
MyHolder holder=new MyHolder(view);//将列表传进来了
return holder;
}
@Override
public void onBindViewHolder(@NonNull MyHolder holder, int position) {//每个item的内容
holder.textView.setText(list1.get(position));
}
@Override
public int getItemCount() {
return list1.size();
}
//Holder理解为指向行的指针;需要做个行的xml
public class MyHolder extends RecyclerView.ViewHolder{
TextView textView;
public MyHolder(View itemview){
super(itemview);
textView=itemview.findViewById(R.id.booktextView);
}
}
}
5.3修改对应fragment文件
将书城对应的fragment2改写
实例化List类对象存储书名信息。初始化渲染视图,绑定recycleView对应的控件
代码如下
RecyclerView recyclerView;
List list;
BookAdapter bookAdapter ;
Context context;
View view=inflater.inflate(R.layout.activity_book,container,false);
recyclerView = view.findViewById(R.id.bookRecyclerview);//实例化
list=new ArrayList();
list.add("平凡的世界");
list.add("国境以南,太阳以西");
list.add("撒哈拉的故事");
list.add("围城");
list.add("深山夏牧场");
list.add("减压脑科学");
list.add("月亮与六便士");
利用所给内容实例化适配器bookAdapter
从而适配列表控件recyclerView
LinearLayoutManager接口的实现类,管理列表内容
最后返回渲染视图
context = view.getContext();
bookAdapter=new BookAdapter(context,list);//将数据传入适配器
recyclerView.setAdapter(bookAdapter);//绑定适配器
LinearLayoutManager manager = new LinearLayoutManager(context);
manager.setOrientation(RecyclerView.VERTICAL);//设置item显示方法,竖直方向
recyclerView.setLayoutManager(manager);
return view;
整体代码如下
5.4 效果展示
六、总结
- 在本次项目里,我学习到了 安卓开发里基本的UI设计,能够对LinearLayout布局下的textView、imageView控件下的属性进行调整,从而进行简单的页面设计。
- 对FrameLayout布局的学习使我更加理解了xml文件之间在设计上的联系。一个复杂的UI界面都是通过一个个简单的XMl文件拼凑起来。以及主布局页面文件作为联系xml文件的纽带。
- 通过编写主题控制文件(fragment.java)理解了java文件与xml文件的联系,以及学习了inflate渲染方法等实现页面绑定。
- 编写主函数文件实现页面切换效果,更加理解到了as开发中的编程思路。初始化视图,一个个点击动作背后是通过对点击动作调用监听方法从而反馈到对页面进行显示、影藏的方法上。
- 通过对RecyclerView实现列表,学习了RecyclerView中重要的Adapter类的作用。RecyclerView的每个item也要通过xml文件进行设计,adpater是i数据传入并且装入每一个item的桥梁
通过本次项目的学习,觉得打开了新世界的大门,原来每天使用的app就是如此一步步开发出来的。很新奇很吸引人,想学习越来越多的安卓开发技能!!!!!
仓库地址: