背景
很乐意分享,也很感谢同学们阅读。
有个项目需要设计页面展示给甲方,用的毫无疑问是 Android
系统。这里还是采用单Activity+多Fragment
的架构来实现,架构确定好后,页面设计与功能开发都会快很多。
下面分享一下实现单Activity+多Fragment
架构的过程。
参考
我这里也参考了许多大神的技术文章,自己也从中巩固学习到了很多东西,这里强烈建议同学们都去仔细的研究细读一遍。
Android 一个Activity 里面放置多个 Fragment 实现点击切换的Tab 页面效果
打造安卓App丝滑的操作体验--Fragment深入使用和封装之道
项目 package 结构图示
只是单纯搭建架构就只需要上面红色圈选出来的5个类:BaseActivity.java
、MainActivity.java
、BaseFragment.java
、ControlFragment.java
和DisplayFragment.java
,其余的类是我展示页面数据的时候另外添加上去,最开始就只用到了上面的5个类。
基类代码
基类有两个,Activity的基类BaseActivity.java
和Fragment的基类BaseFragment.java
。
架构中的一个Activity要继承基类,架构中的多个fragment也都继承基类。
BaseFragment.java
代码如下:
package com.mushiny.www.hlitpro.activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import com.mushiny.www.hlitpro.fragment.DisplayFragment;
import com.mushiny.www.hlitpro.util.LogUtil;
import java.util.List;
/**
* activity 基类
*/
public class BaseActivity extends FragmentActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
/**
* 根据 tag 获取 fragment
* 注:别在 activity 的 onCreate 方法中使用该方法,因为此时 fragment 还未完全创建好,会返回为 null
* @param fragmentTag
* @return
*/
public Fragment findFragmentByTag(String fragmentTag){
return getSupportFragmentManager().findFragmentByTag(fragmentTag);
}
/**
* 加载根 fragment
*/
public void loadRootFragment(int containerId, @NonNull Fragment rootFragment, String tag){
getSupportFragmentManager().beginTransaction().add(containerId, rootFragment, tag).commitAllowingStateLoss();
}
/**
* 获取当前 activity 栈内的 fragment 个数
* 注:别在 activity 的 onCreate 方法中使用该方法
* @return
*/
public int getFragmentCount(){
return getSupportFragmentManager().getFragments().size();
}
// 隐藏 activity 中的 FragmentManager 栈内的所有已经添加了的 fragment
public void hideAllFragment(FragmentTransaction transaction){
List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments != null && fragments.size() > 0){
for (Fragment fragment : fragments){
// 加上不为空判断,防止出现异常(hide方法的参数不能为空,否则会报错)
if (fragment != null){
transaction.hide(fragment);
}
}
}
}
/**
* 获取加入了返回栈的 fragment 个数
* @return
*/
public int getBackStackEntryCount(){
return getSupportFragmentManager().getBackStackEntryCount();
}
}
BaseActivity.java
类中写了一些公共方法供继承了该类的子类(这里也就是MainActivity.java)调用。每个公共方法我都详细的写了注释,这里不再详细说明。
BaseFragment.java
代码如下:
package com.mushiny.www.hlitpro.fragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
/**
* fragment 的基类
*/
public class BaseFragment extends Fragment {
private static final String STATE_SAVE_IS_HIDDEN = "STATE_SAVE_IS_HIDDEN";
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// fragment 重影解决方案:自己保存Fragment的Hidden状态
if (savedInstanceState != null) {
boolean isSupportHidden = savedInstanceState.getBoolean(STATE_SAVE_IS_HIDDEN);
FragmentTransaction ft = getFragmentManager().beginTransaction();
if (isSupportHidden) {
ft.hide(this);
} else {
ft.show(this);
}
ft.commit();
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(STATE_SAVE_IS_HIDDEN, isHidden());
}
}
在BaseFragment.java
这个类中我这里只做了一个防止重影的解决方案,并没有做另外的操作,因为我后面如果有需要会再往这个基类中添加,所以目前这样就足以搭建框架了。
关键的 MainActivity.java
代码如下:
package com.mushiny.www.hlitpro.activity;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.mushiny.www.hlitpro.R;
import com.mushiny.www.hlitpro.fragment.ControlFragment;
import com.mushiny.www.hlitpro.fragment.DisplayFragment;
import com.mushiny.www.hlitpro.util.LogUtil;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* fragment 的容器
*/
public class MainActivity extends BaseActivity {
@BindView(R.id.tv_agv_display)TextView tv_agv_display;
@BindView(R.id.tv_agv_control)TextView tv_agv_control;
private Fragment fragment;
private DisplayFragment displayFragment;
private ControlFragment controlFragment;
private Fragment mCurrentFragment = null;// 记录保存当前显示的 fragment
// fragment的tag变量
private String tag_displayF;
private String tag_controlF;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);// 控件绑定
initData();
if (displayFragment == null){
displayFragment = DisplayFragment.newInstance();
// 加载根 fragment,第一次进入应用显示的界面
loadRootFragment(R.id.framelayout_container, displayFragment, tag_displayF);
mCurrentFragment = displayFragment;
}
}
/**
* 初始化
*/
private void initData() {
// tag变量初始化
tag_displayF = DisplayFragment.class.getSimpleName();
tag_controlF = ControlFragment.class.getSimpleName();
}
/**
* 控件的点击事件
*/
@OnClick({R.id.tv_agv_display,
R.id.tv_agv_control})
public void doClick(View view){
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
switch (view.getId()){
case R.id.tv_agv_control:// 控制
setCheckedColor(tv_agv_control);
setUnCheckedColor(tv_agv_display);
show(ControlFragment.newInstance(), tag_controlF, transaction);
break;
case R.id.tv_agv_display:// 显示
setCheckedColor(tv_agv_display);
setUnCheckedColor(tv_agv_control);
show(DisplayFragment.newInstance(), tag_displayF, transaction);
break;
}
}
/**
* 显示选中的页面
* @param fragment
* @param tag
* @param transaction
*/
private void show(Fragment fragment, String tag, FragmentTransaction transaction) {
if (findFragmentByTag(tag) == null){
hideAllFragment(transaction);
// activity 添加还没有添加的 fragment
transaction.add(R.id.framelayout_container, fragment, tag).commitAllowingStateLoss();
mCurrentFragment = fragment;
return;
}else {
this.fragment = findFragmentByTag(tag);
}
// 隐藏所有fragment
hideAllFragment(transaction);
// 显示
transaction.show(this.fragment).commitAllowingStateLoss();
mCurrentFragment = this.fragment;
}
/**
* 设置 tab 选中时控件的字体颜色
* @param textView
*/
private void setCheckedColor(TextView textView) {
textView.setTextColor(getResources().getColor(R.color.color_tab_selected));
textView.setBackgroundColor(getResources().getColor(R.color.color_lighter_gray));
}
/**
* 设置 tab 未选中时控件的字体颜色
* @param textView
*/
private void setUnCheckedColor(TextView textView) {
textView.setTextColor(getResources().getColor(R.color.color_black));
textView.setBackgroundColor(getResources().getColor(R.color.color_white));
}
}
MainActivity对应的布局文件代码和展示如下所示:
<?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:orientation="vertical"
android:weightSum="1"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.MainActivity">
<FrameLayout
android:id="@+id/framelayout_container"
android:layout_weight="0.9"
android:layout_width="match_parent"
android:layout_height="0dp">
</FrameLayout>
<View
android:id="@+id/view_tab_line"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@android:color/darker_gray" />
<LinearLayout
android:id="@+id/linear_tab"
android:weightSum="1"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_weight="0.1"
android:layout_height="0dp">
<TextView
android:id="@+id/tv_agv_display"
android:text="@string/str_agv_display"
android:textSize="18sp"
android:gravity="center"
android:textColor="@color/color_tab_selected"
android:layout_weight="0.5"
android:layout_width="0dp"
android:background="@color/color_lighter_gray"
android:layout_height="match_parent" />
<View
android:background="@android:color/darker_gray"
android:layout_width="1px"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/tv_agv_control"
android:textSize="18sp"
android:text="@string/str_agv_control"
android:gravity="center"
android:layout_weight="0.5"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
</LinearLayout>
让我们回到MainActivity.java
这个类来,大家仔细看看其中的代码就知道,其实关键操作就两个步骤,我下面用截图的方式给出,如下所示:
上面两个截图中给出了搭建框架的两个关键步骤说明。
对于ControlFragment.java
和DisplayFragment.java
两个类来说,通过android studio(我当前的开发工具)来简单创建即可。这里就不贴出这两个类的代码了,大家自行进行创建,如果有疑问的话可以看下面给大家提供的项目源码。
做完上面这步后其实也就完成了框架的搭建。
应用体验
demo源码
说明
这篇文章更多的是以实践层面的表达来让同学们快速的搭建起一个框架,与研究搭建框架过程中的细节和技术点相比较会更加的不枯燥。会让你搭建起来后并看到效果有较大的成就感(相信大部分同学都是这样的吧)。但是我建议,仅仅是个人建议,希望同学们看看上面我给出的几篇大神写的文章,然后配合自己的理解去弄清楚fragment
的一些本质的东西。也只有这样,你在遇到关于fragment
的问题时,才能更好的找出问题的根源,更好的解决问题。
A little bit of progress every day!Come on!