目录
- 从现实需求来看场景
- Fragment的嵌套
- fg_content.xml
- pet_the_cat.xml
- fg_content layout所属的Fragment Java端代码
从现实需求来看场景
我们经常会在现实需求中碰到这样的场景,如下图所示。
一个手机APP的首页,一般在布局时会有:
- 永久固定区;
- 一级联动区;
- 二级(多级)联动区;
如果是一级联动区域,它跟随着固定区域的功能按钮点击而不断变化这个还好理解一些。烦是烦在二级(甚至多级联动)如我图中所标出的深蓝色部分,深蓝色部分有一排功能按钮,然后当用户在点击这些按钮时下部的Fragment也在跟随着顶部的功能按钮(页签)的点而变化着不同的内容。
而。。。更复杂的是每一个页签的点击时,在本Fragment里还有二级按钮(圆型),而在点击这些二级按钮时,本身内部还有一堆孙子Fragment的内容也在发生着变化。
我们来看一个实际生产界面的例子你们就可以感受到上述我描述的场景了。
这边顶部的按钮为一级Fragment,它跟随着下部的按钮联动。
而这边用红色方框框起来的区域就是我说的,它需要被嵌在一级Fragment里的。
比如说点击“排行榜”下部显示的内容和点击“推荐”是完全不一样的。
现在再来看一块子区域:下部的“撸宠吸宠”,这部分也竟然被业务提出了要可以切换比如说有一个按钮叫“换一批”的需求。因此,这边又是一个个的“三行四列”,“单行三列”,“2行单列”的孙子fragment。
Fragment的嵌套
首先我们说嵌套Fragment调用这种用法是合理的,也是存在的。在现实中应用到的场景实在是太多太多。问题是很多初学者在使用时,就直接在Fragment嵌套时当Fragment应用在Activity里一样那样用了。甚至运行时由于Log输出过多疏忽了一些报错,结果呢在有些开发机上运行的好好的而实际在一些真机上或者是换了一台开发机如:windows换mac或者是mac换windows后,报了一堆本来不会报的错而根本无从入手。
这就是Fragment没有完全学好其原理,特别是其生命周期。所以这边我们给出正确的Fragment嵌套的用法。
就拿上例来说,我们外层有一个fg_content.xml,在fg_content.xml里我们嵌了多个孙子Fragment。此处:
- pet_the_cat(撸猫)
- must_read_list(必读)
- recommend_item_list(推荐)
以及后面我们还会增加一些孙子Fragment,它们都从属于fg_content.xml。
来看下面代码。
fg_content.xml
<layout 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"> | |
<!--绑定数据--> | |
<data> | |
<variable | |
name="viewModel" | |
type="com.mkyuan.aset.mall.android.home.HomeContentViewModel" /> | |
</data> | |
<LinearLayout | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:background="@color/bg_white" | |
android:orientation="vertical" | |
android:clipChildren="false" | |
android:showDividers="end"> | |
<com.google.android.material.tabs.TabLayout | |
android:id="@+id/tl" | |
android:layout_width="match_parent" | |
android:layout_height="dp" | |
android:background="@color/white" | |
app:tabIndicatorColor="@color/tab_indicator_color" | |
app:tabMaxWidth="dp" | |
app:tabMinWidth="dp" | |
app:tabMode="scrollable" | |
app:tabSelectedTextColor="@color/black" | |
app:tabTextAppearance="@style/MyCustomTabText" | |
app:tabTextColor="@color/black" /> | |
<com.youth.banner.Banner | |
android:id="@+id/ad_banner" | |
android:layout_width="match_parent" | |
android:layout_height="dp" | |
android:gravity="center" | |
app:banner_loop_time="" | |
app:banner_radius="dp" /> | |
<FrameLayout | |
android:id="@+id/fg_recommend" | |
android:layout_width="match_parent" | |
android:layout_height="dp" | |
android:layout_gravity="center_horizontal" | |
android:layout_marginTop="dp" /> | |
<ImageView | |
android:layout_width="match_parent" | |
android:layout_height="dp" | |
android:background="#ECECEC" /> | |
<LinearLayout | |
android:layout_width="match_parent" | |
android:layout_height="dp" | |
android:background="@drawable/layout_gray_background" | |
android:orientation="horizontal"> | |
<ImageView | |
android:layout_width="dp" | |
android:layout_height="dp" | |
android:layout_gravity="top" | |
android:scaleType="fitStart" | |
android:src="@mipmap/newest_"></ImageView> | |
<TextView | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:layout_gravity="top" | |
android:layout_marginLeft="dp" | |
android:includeFontPadding="false" | |
android:text="同人活动最新点亮词条:[魔道祖师]" | |
android:textColor="@color/black" /> | |
</LinearLayout> | |
<androidx.viewpager.widget.ViewPager | |
android:id="@+id/vpMustRead" | |
android:layout_marginLeft="dp" | |
android:layout_marginRight="dp" | |
android:layout_marginTop="dp" | |
android:layout_marginBottom="dp" | |
android:layout_width="dp" | |
android:layout_height="dp" | |
android:layout_gravity="top" | |
android:clipChildren="false"> | |
</androidx.viewpager.widget.ViewPager> | |
<FrameLayout | |
android:id="@+id/fg_nearbystore" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:layout_gravity="center_horizontal" | |
android:layout_marginTop="dp" /> | |
<FrameLayout | |
android:id="@+id/fg_petthecat" | |
android:layout_width="match_parent" | |
android:layout_height="dp" | |
android:layout_gravity="center_horizontal" | |
android:layout_marginTop="dp" /> | |
</LinearLayout> | |
</layout> |
注意此处的:<FrameLayout android:id="@+id/fg_petthecat"这边就是用来显示孙子Fragment-pet_the_cat.xml的。
于是我们来看pet_the_cat.xml。
pet_the_cat.xml
它是用一个Fragment来加载的。
先来看它的前端layout。
pet_the_cat.xml的layout
<layout 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"> | |
<LinearLayout | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:orientation="horizontal"> | |
<TextView | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:includeFontPadding="false" | |
android:padding="dp" | |
android:text="撸宠吸宠" | |
android:textColor="@color/black" | |
android:textSize="dp" | |
android:textStyle="bold" /> | |
<GridView | |
android:id="@+id/grid_petthecat" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:gravity="center" | |
android:horizontalSpacing="px" | |
android:numColumns="" | |
android:stretchMode="columnWidth" | |
android:verticalSpacing="px" /> | |
</LinearLayout> | |
</layout> |
而这个pet_the_cat里要用到Adapter即撸宠吸宠的“具体内容了”。我们下面顺便再来看pet_cat_detail.xml
pet_cat_detail.xml
<layout 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"> | |
<data> | |
<variable | |
name="petCatBean" | |
type="com.mkyuan.aset.mall.android.home.petthecat.PetTheCatBean"/> | |
</data> | |
<LinearLayout | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:orientation="vertical"> | |
<ImageView | |
android:id="@+id/ivPetCatImg" | |
android:layout_width="dp" | |
android:layout_height="dp" | |
android:layout_gravity="center" | |
android:scaleType="fitStart" | |
app:petImgUrl="@{petCatBean.pegImg}" /> | |
<TextView | |
android:id="@+id/tvPetCatDescr" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_gravity="center" | |
android:gravity="top" | |
android:includeFontPadding="false" | |
android:padding="dp" | |
android:text="@{petCatBean.descrText}" | |
android:textColor="@color/black" | |
android:textSize="dp" | |
android:textStyle="bold" /> | |
<TextView | |
android:id="@+id/tvPetCatPrice" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_gravity="center" | |
android:gravity="top" | |
android:includeFontPadding="false" | |
android:padding="dp" | |
android:text="@{petCatBean.price}" | |
android:textColor="@color/custom_red" | |
android:textSize="dp" | |
android:textStyle="bold" /> | |
</LinearLayout> | |
</layout> |
知道了样式文件后关键我们来看在fg_content里如何套入这个pet_the_cat的layout的。
pet_the_cat的后端代码-FragmentPetTheCat.java
package com.mkyuan.aset.mall.android.home.petthecat; | |
import android.content.Context; | |
import android.os.Bundle; | |
import android.util.Log; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import android.widget.GridView; | |
import androidx.databinding.DataBindingUtil; | |
import androidx.fragment.app.Fragment; | |
import com.mkyuan.aset.mall.android.BR; | |
import com.mkyuan.aset.mall.android.R; | |
import com.mkyuan.aset.mall.android.databinding.PetTheCatBinding; | |
import com.mkyuan.aset.mall.android.home.DatabindingGridAdapter; | |
import com.mkyuan.aset.mall.android.util.AsetMallConstants; | |
import java.util.ArrayList; | |
import java.util.List; | |
public class FragmentPetTheCat extends Fragment { | |
protected static final String TAG = "AsetMall"; | |
private Context ctx; | |
//private Banner adBanner; | |
private GridView petCatGridView; | |
private PetTheCatBinding dataBinding; | |
public View onCreateView(LayoutInflater inflater, ViewGroup container, | |
Bundle savedInstanceState) { | |
ctx = this.getActivity(); | |
dataBinding = DataBindingUtil.inflate(inflater, R.layout.pet_the_cat, container, | |
false); | |
petCatGridView = dataBinding.gridPetthecat; | |
Log.i(TAG, ">>>>>FragmentPetTheCat->get dataBinding"); | |
initPetTheCatDataList(); | |
return dataBinding.getRoot(); | |
} | |
private void initPetTheCatDataList() { | |
List<PetTheCatBean> list = new ArrayList<PetTheCatBean>(); | |
list.add(new PetTheCatBean(AsetMallConstants.CDN_URL + "/img/petthecat/pet_the_cat_.jpg", | |
"羊陀上门撸你", "")); | |
list.add(new PetTheCatBean(AsetMallConstants.CDN_URL + "/img/petthecat/pet_the_cat_.jpg", | |
"吸松鼠要么?", "")); | |
list.add(new PetTheCatBean(AsetMallConstants.CDN_URL + "/img/petthecat/pet_the_cat_.png", | |
"寄养傻狗天", "500")); | |
DatabindingGridAdapter<PetTheCatBean> adapter = | |
new DatabindingGridAdapter<PetTheCatBean>(ctx, | |
R.layout.pet_cat_detail, list, | |
BR.petCatBean); | |
petCatGridView.setAdapter(adapter); | |
} | |
} |
我们这边可以看到,这个Fragment通过我们在之前MVVM与ListView课程内使用到的那个“万能DataBinding Adapter“了。它把pet_cat_detail加载到了一个1行3列的GridView里进行显示。
现在我们就来看在fg_content里,怎么显示这个子Fragment。
fg_content layout所属的Fragment Java端代码
在fg_content里加载子Fragment-FragmentPetTheCat需要注意的地方
fg_content的后端Java代码为FragmentContent.
private FgContentBinding dataBinding; | |
private FragmentManager fManager; | |
private Fragment fragmentRecommend; | |
private Fragment fragmentNearbyStore; | |
private Fragment fragmentPetTheCat; | |
dataBinding = DataBindingUtil.inflate(inflater, R.layout.fg_content, container, false); | |
fManager = getChildFragmentManager(); | |
if (fragmentPetTheCat == null) { | |
fragmentPetTheCat = new FragmentPetTheCat(); | |
fTransaction.add(R.id.fg_petthecat, fragmentPetTheCat); | |
} | |
fTransaction.show(fragmentPetTheCat); | |
fTransaction.commit(); |
这边我们可以看到有这么一行,特别关键,要敲黑板了:
fManager = getChildFragmentManager();而不是我们在Fragment使用那一课里使用的:fManager = getFragmentManager();。
这是因为我们要嵌套子Fragment,因此对于子Fragment来说它的parent可不一样,很多人正是在这边烦了错。
接着我们回到fg_content的孙子Fragment-pet_the_cat.xml的后端代码-FragmentPetTheCat.java 。
从FragmentPetTheCata.java里加载完数据后如何返回到FragmentContent
在FragmentPetTheCat.java的public View onCreateView最后return你必须使用以下写法:
return dataBinding.getRoot();//这边要用dataBind.getRoot()返回的view再返回activity,因为此处非MainActivity
即:子Fragment向父Fragment返回view时你已经不能再用
Android入门第47天-Fragment的基本使用中以下这样的写法了,是错误的:
return view;
正确的写法为:
return dataBinding.getRoot();