一、实验目的
掌握各类代码的分类管理知识,掌握移动电商项目主页架构搭建, 掌握Fragment的使用。掌握菜单页内容切换以及菜单样式切换的开发技术。
二、实验设备及器件
Android Studio
三、实验内容
1.创建一个Android应用,设置项目名MobileShop,包名为com.huatec.edu.mobileshop,创建activity包存放Activity,创建fragment存放Fragment。
2.创建drawable-xhdpi目录,解压图片资源包xhdpi.rar里的所有文件到该目录下。
3.在fragment下创建HomeFragment,CategoryFragment,CartFragment,PersonFragment分别对应首页,分类,购物车,我的(个人中心)页面。
4、在fragment下创建NavigationFragment作为主页最外层内容,上半部分切换各页面Fragment下半部分是导航栏,点击切换Fragment。
5、修改MainActivity内容,将NavigationFragment添加到窗口显示。
最终效果:
代码结构:
资源结构:
四、实验步骤
1、创建一个项目,项目名称:MobileShop 项目包名:com.huatec.edu.mobileshop
然后加入项目需要的各种包:
2、创建drawable-xhdpi目录,解压图片资源包xhdpi.rar里的所有文件到该目录下。
3、在NavigationFragment添加一个导航栏,添加四个按钮,在点击不同按钮时切换到对应的页面,所以我们先创建四个Fragment。
在layout里创建四个布局文件:fragment_home.xml、fragment_category.xml、fragment_cart.xml、fragment_person.xml。在com.huatec.edu.mobileshop.fragment包下创建4个Fragment类:HomeFragment,CategoryFragment,CartFragment,PersonFragment。代码如下:
fragment_home.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--首页测试页面-->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="首页"
android:textSize="20sp"/>
</LinearLayout>
HomeFragment:
package com.example.administrator.myapplication.fragment;
import android.os.Bundle;
import android.app.Fragment;
import android.support.v4.view.LayoutInflaterCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.Layout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.administrator.myapplication.R;
/**
* Created by Administrator on 2020/5/24 0024.
*/
//fragment在教程P.139
//ViewGroup在教程39
public class HomeFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState){
//用于创建和返回跟fragment关联的View对象
View view = inflater.inflate(R.layout.fragment_home,container,false);
// LayoutInflater inflater:作用类似于findViewById(),
// findViewById()用来寻找xml布局下的具体的控件(Button、TextView等),
// LayoutInflater inflater()用来找res/layout/下的xml布局文件
// ViewGroup container:表示容器,View放在里面
// Bundle savedInstanceState:保存当前的状态,在活动的生命周期中,
// 只要离开了可见阶段,活动很可能就会被进程终止,这种机制能保存当时的状态
return view;
}
}
fragment_category.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--分类测试页面-->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="分类"
android:textSize="20sp"/>
</LinearLayout>
CategoryFragment:
package com.example.administrator.myapplication.fragment;
import android.os.Bundle;
import android.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.administrator.myapplication.R;
/**
* 分类fragment
*/
public class CategoryFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState){
View view = inflater.inflate(R.layout.fragment_category,container,false);
return view;
}
}
fragment_cart.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--购物车测试页面 -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="购物车"
android:textSize="20sp"/>
</RelativeLayout>
CartFragment:
package com.example.administrator.myapplication.fragment;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.administrator.myapplication.R;
/**
* 购物车fragment
*/
public class CartFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle saveInstenceState){
View view = inflater.inflate(R.layout.fragment_cart,null);
//inflate(int resource,ViewGroup root,boolean attachToRoot)
//resource:布局资源id
//root:resource生成view对象要填入的父布局。
// 为null,则返回的view就是布局资源;否则,需要参照第三个参数
//attachToRoot:是否将resource生成view对象填入root,以root作为最终返回view的根布局。
// false,root不为null,则提供root的LayoutParams约束resource生成的view;
// true,root不为null,以root作为最终返回view的根布局
//inflate(int resource,ViewGroup root)
//resource:布局资源
//root:resource生成view对象要填入的父布局。
// 为null,则返回的view就是布局资源的根布局;
// 否则,返回的view以传入的root为根布局(相当于上面的那个第三个参数为true)
return view;
}
}
fragment_person.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--个人中心测试-->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="个人中心"
android:textSize="20sp"/>
</RelativeLayout>
PersonnalFragment:
package com.example.administrator.myapplication.fragment;
import android.os.Bundle;
import android.os.ParcelUuid;
import android.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.administrator.myapplication.R;
/**
* 个人中心fragment
*/
public class PersonFragment extends Fragment {
public View onCreateView(final LayoutInflater inflater, ViewGroup container,
Bundle saveInstanceState){
View view = inflater.inflate(R.layout.fragment_person,container,false);
return view;
}
}
创建完四个Fragment之后把他们添加到NavigationFragment的上半部,下半部分添加一个导航栏,以下是NavigationFragment对应的代码:
fragment_navigation.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--不显示Fragment的内容窗口-->
<FrameLayout
android:id="@+id/fl_main_navigation"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"></FrameLayout>
<!--底部菜单栏-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:layout_weight="0">
<!--首页菜单-->
<ImageButton
android:id="@+id/ib_home"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/black"
android:src="@drawable/tab_item_home_normal"/>
<!--分类菜单-->
<ImageButton
android:id="@+id/ib_category"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/black"
android:src="@drawable/tab_item_category_normal"/>
<!--购物车菜单-->
<ImageButton
android:id="@+id/ib_cart"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/black"
android:src="@drawable/tab_item_cart_normal"/>
<!--个人中心菜单-->
<ImageButton
android:id="@+id/ib_personal"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/black"
android:src="@drawable/tab_item_personal_normal"/>
</LinearLayout>
</LinearLayout>
NavigationFragment类代码:
package com.example.administrator.myapplication.fragment;
import android.app.Fragment;
//import:导入,引入
//导入的包必须一样的,新建了5个fragment,5个导入的包都要用这个
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import com.example.administrator.myapplication.R;
/**
* 导航fragment,包括内容区和下面的菜单栏
*/
public class NavigationFragment extends Fragment implements View.OnClickListener{
// 底部上的四个图片按钮
private ImageButton mIbHome;
private ImageButton mIbCategory;
private ImageButton mIbCart;
private ImageButton mIbPersonal;
// 定义几个控件的名字,私有的
// 窗口显示的四个Fragment
private HomeFragment homeFragment;
private CategoryFragment categoryFragment;
private CartFragment cartFragment;
private PersonFragment personFragment;
public NavigationFragment() {
}
public View onCreateView(LayoutInflater inflater,ViewGroup container,
Bundle saveInstanceState){
View view = inflater.inflate(R.layout.fragment_navigation,container,false);
// inflate()的作用就是将一个用xml定义的布局文件查找出来,
// 注意与findViewById()的区别,inflate是加载一个布局文件,
// 而findViewById则是从布局文件中查找一个控件。
//第三个参数为true那么返回可能就不是view。当为fale的时候返回就是View。
//inflate(int resource,ViewGroup root,boolean attachToRoot)
//resource:布局资源id
//root:resource生成view对象要填入的父布局。
// 为null,则返回的view就是布局资源;否则,需要参照第三个参数
//attachToRoot:是否将resource生成view对象填入root,以root作为最终返回view的根布局。
// false,root不为null,则提供root的LayoutParams约束(限制)resource生成的view;
// true,root不为null,以root作为最终返回view的根布局
//inflate(int resource,ViewGroup root)
//resource:布局资源
//root:resource生成view对象要填入的父布局。
// 为null,则返回的view就是布局资源的根布局;
// 否则,返回的view以传入的root为根布局(相当于上面的那个第三个参数为true)
initView(view);//天字第一号进程
//初始化就是把变量赋为默认值,把控件设为默认状态,把没准备的准备好。
// 把该给我的分给我。布局文件和一些资源需要在代码里面知初始化声明,
// 才能使用某些方法进行相关操作。简单点儿说,就道是你想加载页面,
// 必须先要告诉我页面里面都有什么,然后你才能调用界面里控件的方法和属性,
// 你不属初始化不告诉我,就找不到相关资源,就无法使用相关的方法。
//初始化所有控件,第一种是全局的,比如你oncreate里初始化了,onresume里也可以用;
//第二copy种是局部变量,
// 比如你在Oncreate里初始化了,
// 只能在Oncreate里用,到onresume里就不能使用了。
return view;
} //这个意思就是返回要浏览的视图(也就是我们的页面文件)他的名字是与你的控制器名字一样的view
//这里就是初始化完以后就返回视图 前面View view中的view;这个view是已经加了f_n的界面
//
//初始化fragment里所有的控件
//
protected void initView(View view){
//获取菜单图片按钮实例对象
mIbHome = view.findViewById(R.id.ib_home);
//findViewById的返回值必须(默认)为View类型,(xx)findViewById(R.XX.XX)前面xx是强制转换类型;
//view.findViewById(R.id.ib_home);view就是前面指定的View类有一个具体的方法就是调用 findViewById,并且传入一个资源 id,
// findViewById 方法会找到与传入的 id 相对应的 View,
// Activity 在 XML 的视图层次结构中搜索这个视图,再在 onCreate 方法中处理它,
// 这个 activity 的 onCreate 方法建立了一个视图层次结构。然后 findViewById 方法遍历它,找到那个视图层次结构中的某个 View,这个方法的返回值是 View 类型的对象。
mIbCategory = view.findViewById(R.id.ib_category);
mIbCart = view.findViewById(R.id.ib_cart);
mIbPersonal = view.findViewById(R.id.ib_personal);
//图片按钮设置监听器,因为本类已实现View.onClickListener,
// 所以可以使用本类做按钮监听,就是点击按钮才会运行的
mIbHome.setOnClickListener(this);
mIbCategory.setOnClickListener(this);
mIbCart.setOnClickListener(this);
mIbPersonal.setOnClickListener(this);
//初始化所有控件后,默认选中并显示内容
select(mIbHome);
}
private void select(View v) {
//重置UI,将按钮菜单全部恢复为未选中的状态
/* Android 获取设置好的image.setImageResource(R.drawable.xxx)资源
第一步设置资源
image.setImageResource(R.drawable.xxx);
image.setTag(R.drawable.xxx);
第二步获取资源
int res = (int) image.getTag();
第三步做一些判断
if(res==R.drawable.xxa){
……..
}else if(res==R.drawable.xxb){
…….
}*/
mIbHome.setImageResource(R.drawable.tab_item_home_normal);
mIbCategory.setImageResource(R.drawable.tab_item_category_normal);
mIbCart.setImageResource(R.drawable.tab_item_cart_normal);
mIbPersonal.setImageResource(R.drawable.tab_item_personal_normal);
//启动fragment事务管理fragment
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// FragmentTransaction 的创建:
// FragmentTransaction是用来换页面的,
// ed:想从微信的消息页面换到通讯录的页面就要用这个才行。
//隐藏所有Fragment
if (homeFragment != null) {
fragmentTransaction.hide(homeFragment);
//hide(Fragement fragment)
// hide(Fragment fragment): hide 隐藏容器中的 Fragment
// 隐藏已经存在的fragment,
// 只适用那些视图已经被添加到容器中的Fragment,
// hide 和 show 是配对的,当要显示隐藏的 Fragment A 时,就 show(A);
// 而对于其他 Fragment,则先 hide 起来,等之后要显示时再 show
// fragmentTransaction.hide(homeFragment);
// 事物管理,隐藏(显示),(叫xx);
// 如果可找到homeFragment,不为空,
// 打开的是这个页面,就把它藏起来。
// ed:微信消息页面换到通讯录页面要把消息页面隐藏起来。
}
if (cartFragment != null) {
fragmentTransaction.hide(cartFragment);
//不为空代表已经有实例了 不需要new
}
if (categoryFragment != null) {
fragmentTransaction.hide(categoryFragment);
}
if (personFragment != null) {
fragmentTransaction.hide(personFragment);
}
// 根据选中的菜单按钮的id来执行不同的操作
switch (v.getId()){
// 循环语句switch,获取其资源id,通过资源id,可以判断用户点击了哪个按钮了
switch语句格式
// switch(表达式){
// case 值1:
// 语句体1;
// break;}
case R.id.ib_home://点击了首页菜单
mIbHome.setImageResource(R.drawable.tab_item_home_focus);
//设置菜单图片为这张
if (homeFragment == null){
//如果fragment未初始化,则初始化在加入显示
//一个按钮已经点过一次了,按钮已经暗了
// 还想再点一次,就要初始化回到没点击之前,之后就可再点了。
//不然会出现点了没有用,跟没点一样的情况(不显示),它已经被按下去了,
//不恢复一下,就没法再点了
homeFragment = new HomeFragment();
fragmentTransaction.add(R.id.fl_main_navigation,homeFragment);
//addToBackStack()是把被替换的Fragment放入回退栈中
}
else{
//如果fragment已经初始化,则直接显示,
fragmentTransaction.show(homeFragment);
}
break;
case R.id.ib_category:
mIbCategory.setImageResource(R.drawable.tab_item_category_focus);
if (categoryFragment == null){
//为空代表没有实例 需要new一个
categoryFragment = new CategoryFragment();
fragmentTransaction.add(R.id.fl_main_navigation,categoryFragment);
}
else{
fragmentTransaction.show(categoryFragment);
//show,hide,remove 格式都是一样的
}
break;
case R.id.ib_cart:
mIbCart.setImageResource(R.drawable.tab_item_cart_focus);
if (cartFragment == null){
cartFragment = new CartFragment();
fragmentTransaction.add(R.id.fl_main_navigation,cartFragment);
}
else{
fragmentTransaction.show(cartFragment);
}
break;
case R.id.ib_personal:
mIbPersonal.setImageResource(R.drawable.tab_item_personal_focus);
if (personFragment == null){
personFragment = new PersonFragment();
fragmentTransaction.add(R.id.fl_main_navigation,personFragment);
}
else{
fragmentTransaction.show(personFragment);
//show(Fragement fragment)
}
break;
}
fragmentTransaction.commit();
}
@Override
public void onClick(View v) {
select(v);
}
}
4、修改MainActivity内容,将NavigationFragment添加到窗口显示。
activity_main.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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.administrator.myapplication.acticity.MainActivity">
<FrameLayout
android:id="@+id/fl_main"
android:layout_width="match_parent"
android:layout_height="match_parent"></FrameLayout>
</LinearLayout>
MainActivity代码:
package com.example.administrator.myapplication.acticity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.example.administrator.myapplication.R;
import com.example.administrator.myapplication.fragment.NavigationFragment;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//调用父类的oncreate方法,完成系统工作,父类指AppCompatActivity(源码里)
setContentView(R.layout.activity_main);
//一旦调用 setContentView,activity就会找到 XML 布局文件,并且读取它的每行代码,
//我们没有创建新的 TextView对象,我们只是在视图树中找到了现有的 TextView,
// 为了与这个视图树中的 View 进行交互,我们应该创建变量,用来引用这些具体的 View,
initView();
}
// 初始化控件
protected void initView(){
//开始fragment事务
//FragmentTransaction 的创建
FragmentManager fragmentManager = getFragmentManager();
//getFragmentManager();来获得一个对象赋给fragmentManager;找实例的
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// //返回(开始一个事务)事务对象
// FragmentTransaction transaction = getFragmentManager().beginTransaction();
//将fragment添加到activity_main.xml的fl_main控件上
fragmentTransaction.replace(R.id.fl_main, new NavigationFragment());
//先将之前存在于容器的 Fragment 全部移除(销毁),
// 然后添加要显示的 Fragment(会重新执行一遍它的生命流程)
//显示NavigationFragment,还没有,先new一个 后replace(显示)它
//xx方法里的 , 功能 , (在哪实现,实现的是什么)
fragmentTransaction.commit();
//提交事务
//FragmentTransaction现在提供了四种不同的方法来commit一个
// transaction:commit()、
// commitAllowingStateLoss()、
// commitNow()、立即执行并且只会执行你当前要提交的transaction。
// commitNowAllowingStateLoss()。
// 1.如果你操作很多transactions, 并且不需要同步, 或者你需要把transactions加在back stack里, 那就使用commit().
// 2.如果你需要同步的操作, 并且你不需要加到back stack里, 使用commitNow().
// 3.当报错“IllegalStateException:Can not perform this action after onSaveInstanceState”时,使用commitAllowingStateLoss()。
// 4.如果你希望在某一个指定的点, 确保所有的transactions都被执行, 那么使用executePendingTransactions().
}
}
结果:
篮奏云apk地址:
https://rod.lanzous.com/b0dk28ida
密码:6k50