问题描述
问题1 如何实现一个Activity多个Fragment
问题2 底部导航栏fragment切换时会反复加载网络数据,最终造成网络阻塞甚至崩溃
底部导航栏切换时如果使用remove和add方法,这样每次切换都会导致创建多个Fragment实例,所以造成网络阻塞甚至程序崩溃。这里采用hide和show方法来切换fragment
问题3 去掉BottomNavigationMenuView的默认动画效果
为了方便布局采用了BottomNavigationMenuView进行底部导航栏布局,但当底部按钮数量较多时,会有个自带动画效果,这显然不是我们想要的,可以通过反射去掉这个效果。
代码实现
编写Fragment,这里创了5个简易的Fragment,代码都是类似的
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import example.com.myapplication.R;
public class FiveFragment extends Fragment {
public FiveFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_five, container, false);
}
}
对Fragment布局,这里只是简单地加了个背景
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/md_light_green_A700"/>
</LinearLayout>
添加design包,这里版本号根据实际情况自行更改
compile 'com.android.support:design:25.3.1'
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"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"/>
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottomNav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="?android:attr/windowBackground"
app:itemBackground="@null"
app:itemIconTint="@drawable/bottom_navigation_selector"
app:itemTextColor="@drawable/bottom_navigation_selector"
app:menu="@menu/tab" />
</LinearLayout>
添加tab.xml 用于按钮文字及图片配置
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/navigation_home"
android:icon="@drawable/home"
android:title="首页" />
<item
android:id="@+id/navigation_two"
android:icon="@drawable/home"
android:title="分类"
/>
<item
android:id="@+id/navigation_three"
android:icon="@drawable/home"
android:title="圈子"
/>
<item
android:id="@+id/navigation_four"
android:icon="@drawable/home"
android:title="发现" />
<item
android:id="@+id/navigation_five"
android:icon="@drawable/home"
android:title="记录" />
</menu>
bottom_navigation_selector.xml用于选中和未选中的颜色的配置
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/md_red_400" android:state_checked="true" />
<item android:color="@color/md_grey_600" android:state_checked="false" />
</selector>
最后,MainActivity 用isFragment指向当前正在使用的Fragment,同时使用hide和show方法,防止切换时重新创建实例。在去掉底部导航栏动画效果上,采用了反射的方法。
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.internal.BottomNavigationItemView;
import android.support.design.internal.BottomNavigationMenuView;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MenuItem;
import java.lang.reflect.Field;
import example.com.myapplication.Fragment.FiveFragment;
import example.com.myapplication.Fragment.FourFragment;
import example.com.myapplication.Fragment.HomeFragment;
import example.com.myapplication.Fragment.SecondFragment;
import example.com.myapplication.Fragment.ThirdFragment;
import static example.com.myapplication.R.id.bottomNav;
public class MainActivity extends AppCompatActivity {
private BottomNavigationView bottomNavigationView;
//定义Fragment
private HomeFragment homeFragment;
private SecondFragment secondFragment;
private ThirdFragment thirdFragment;
private FourFragment fourFragment;
private FiveFragment fiveFragment;
//记录当前正在使用的fragment
private Fragment isFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化Fragment及底部导航栏
initFragment(savedInstanceState);
bottomNavigationView = (BottomNavigationView) findViewById(bottomNav);
//关闭底部导航栏默认动画效果并添加监听器
disableShiftMode(bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
}
public void initFragment(Bundle savedInstanceState) {
//判断activity是否重建,如果不是,则不需要重新建立fragment.
if (savedInstanceState == null) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (homeFragment == null) {
homeFragment = new HomeFragment();
}
isFragment = homeFragment;
ft.replace(R.id.container, homeFragment).commit();
}
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
if (homeFragment == null) {
homeFragment = new HomeFragment();
}
switchContent(isFragment, homeFragment);
return true;
case R.id.navigation_two:
if (secondFragment == null) {
secondFragment = new SecondFragment();
}
switchContent(isFragment, secondFragment);
return true;
case R.id.navigation_three:
if (thirdFragment == null) {
thirdFragment = new ThirdFragment();
}
switchContent(isFragment, thirdFragment);
return true;
case R.id.navigation_four:
if (fourFragment == null) {
fourFragment = new FourFragment();
}
switchContent(isFragment, fourFragment);
return true;
case R.id.navigation_five:
if (fiveFragment == null) {
fiveFragment = new FiveFragment();
}
switchContent(isFragment, fiveFragment);
return true;
}
return false;
}
};
public void switchContent(Fragment from, Fragment to) {
if (isFragment != to) {
isFragment = to;
FragmentManager fm = getSupportFragmentManager();
//添加渐隐渐现的动画
FragmentTransaction ft = fm.beginTransaction();
if (!to.isAdded()) { // 先判断是否被add过
ft.hide(from).add(R.id.container, to).commit(); // 隐藏当前的fragment,add下一个到Activity中
} else {
ft.hide(from).show(to).commit(); // 隐藏当前的fragment,显示下一个
}
}
}
//利用反射关闭底部导航栏默认动画效果,使多个按钮平分界面
public void disableShiftMode(BottomNavigationView view) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
try {
Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
shiftingMode.setAccessible(true);
shiftingMode.setBoolean(menuView, false);
shiftingMode.setAccessible(false);
for (int i = 0; i < menuView.getChildCount(); i++) {
BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i);
//noinspection RestrictedApi
item.setShiftingMode(false);
// set once again checked value, so view will be updated
//noinspection RestrictedApi
item.setChecked(item.getItemData().isChecked());
}
} catch (NoSuchFieldException e) {
Log.e("BNVHelper", "Unable to get shift mode field", e);
} catch (IllegalAccessException e) {
Log.e("BNVHelper", "Unable to change value of shift mode", e);
}
}
}
在本例中,用到了BottomNavigationMenuView作为底部导航栏的布局,实际上不用也可以的,只需要将它替换为线性布局并适当修改代码即可。
整个框架搭建好后,就可以在Fragment中加入网络线程了。