{
"activeColor" : "#ff678f" ,
"inActiveColor" : "#666666" ,
"selectTab" : 0 ,
"tabs" : [
{
"size" : 24 ,
"enable" : true ,
"index" : 0 ,
"pageUrl" : "main/tabs/home" ,
"title" : "首頁"
} ,
{
"size" : 24 ,
"enable" : true ,
"index" : 1 ,
"pageUrl" : "main/tabs/sofa" ,
"title" : "沙發"
} ,
{
"size" : 40 ,
"enable" : true ,
"index" : 2 ,
"tintColor" : "#ff678f" ,
"pageUrl" : "main/tabs/publish" ,
"title" : ""
} ,
{
"size" : 24 ,
"enable" : true ,
"index" : 3 ,
"pageUrl" : "main/tabs/find" ,
"title" : "發現"
} ,
{
"size" : 24 ,
"enable" : true ,
"index" : 4 ,
"pageUrl" : "main/tabs/my" ,
"title" : "我的"
}
]
}
package cn. example. jetpackdemo. view;
import android. annotation. SuppressLint;
import android. content. Context;
import android. content. res. ColorStateList;
import android. graphics. Color;
import android. text. TextUtils;
import android. util. AttributeSet;
import android. util. DisplayMetrics;
import android. view. MenuItem;
import androidx. annotation. NonNull;
import androidx. annotation. Nullable;
import com. google. android. material. bottomnavigation. BottomNavigationItemView;
import com. google. android. material. bottomnavigation. BottomNavigationMenuView;
import com. google. android. material. bottomnavigation. BottomNavigationView;
import com. google. android. material. bottomnavigation. LabelVisibilityMode;
import java. util. List;
import cn. example. jetpackdemo. R;
import cn. example. jetpackdemo. ui. model. BottomBar;
import cn. example. jetpackdemo. ui. model. Destination;
import cn. example. jetpackdemo. utils. AppConfig;
public class HAppBottomBar extends BottomNavigationView {
private static int [ ] sIcons = new int [ ] { R. drawable. icon_tab_home, R. drawable. icon_tab_sofa,
R. drawable. icon_tab_publish, R. drawable. icon_tab_find, R. drawable. icon_tab_mine} ;
private BottomBar config;
public HAppBottomBar ( @NonNull Context context) {
this ( context, null) ;
}
public HAppBottomBar ( @NonNull Context context, @Nullable AttributeSet attrs) {
this ( context, attrs, 0 ) ;
}
@SuppressLint ( "RestrictedApi" )
public HAppBottomBar ( @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super ( context, attrs, defStyleAttr) ;
config = AppConfig. getBottomBarConfig ( ) ;
int [ ] [ ] state = new int [ 2 ] [ ] ;
state[ 0 ] = new int [ ] { android. R. attr. state_selected} ;
state[ 1 ] = new int [ ] { } ;
int [ ] colors = new int [ ] { Color. parseColor ( config. getActiveColor ( ) ) , Color. parseColor ( config. getInActiveColor ( ) ) } ;
ColorStateList stateList = new ColorStateList ( state, colors) ;
setItemTextColor ( stateList) ;
setItemIconTintList ( stateList) ;
setLabelVisibilityMode ( LabelVisibilityMode. LABEL_VISIBILITY_LABELED) ;
List< BottomBar. TabsBean> tabs = config. getTabs ( ) ;
for ( BottomBar. TabsBean tab : tabs) {
if ( ! tab. isEnable ( ) ) {
continue ;
}
int itemId = getItemId ( tab. getPageUrl ( ) ) ;
if ( itemId < 0 ) {
continue ;
}
MenuItem menuItem = getMenu ( ) . add ( 0 , itemId, tab. getIndex ( ) , tab. getTitle ( ) ) ;
menuItem. setIcon ( sIcons[ tab. getIndex ( ) ] ) ;
}
int index = 0 ;
for ( BottomBar. TabsBean tab : config. getTabs ( ) ) {
if ( ! tab. isEnable ( ) ) {
continue ;
}
int itemId = getItemId ( tab. getPageUrl ( ) ) ;
if ( itemId < 0 ) {
continue ;
}
int iconSize = dp2Px ( tab. getSize ( ) ) ;
BottomNavigationMenuView menuView = ( BottomNavigationMenuView) getChildAt ( 0 ) ;
BottomNavigationItemView itemView = ( BottomNavigationItemView) menuView. getChildAt ( index) ;
itemView. setIconSize ( iconSize) ;
if ( TextUtils. isEmpty ( tab. getTitle ( ) ) ) {
int tintColor = TextUtils. isEmpty ( tab. getTintColor ( ) ) ? Color. parseColor ( "#ff678f" ) : Color. parseColor ( tab. getTintColor ( ) ) ;
itemView. setIconTintList ( ColorStateList. valueOf ( tintColor) ) ;
itemView. setShifting ( false ) ;
}
index++ ;
}
if ( config. getSelectTab ( ) != 0 ) {
BottomBar. TabsBean selectTab = config. getTabs ( ) . get ( config. getSelectTab ( ) ) ;
if ( selectTab. isEnable ( ) ) {
int itemId = getItemId ( selectTab. getPageUrl ( ) ) ;
post ( ( ) - > setSelectedItemId ( itemId) ) ;
}
}
}
private int dp2Px ( int dpValue) {
DisplayMetrics metrics = getContext ( ) . getResources ( ) . getDisplayMetrics ( ) ;
return ( int ) ( metrics. density * dpValue + 0.5f ) ;
}
private int getItemId ( String pageUrl) {
Destination destination = AppConfig. getDestConfig ( ) . get ( pageUrl) ;
if ( destination == null) {
return - 1 ;
}
return destination. getId ( ) ;
}
}
package cn. example. jetpackdemo. utils;
import android. content. res. AssetManager;
import android. util. Log;
import com. alibaba. fastjson. JSON;
import com. alibaba. fastjson. TypeReference;
import java. io. BufferedReader;
import java. io. IOException;
import java. io. InputStream;
import java. io. InputStreamReader;
import java. util. HashMap;
import cn. example. jetpackdemo. ui. model. BottomBar;
import cn. example. jetpackdemo. ui. model. Destination;
import cn. yumakeji. lib_common. global. AppGlobals;
public class AppConfig {
private static HashMap< String, Destination> sDestConfig;
private static BottomBar sBottomBar;
public static HashMap< String, Destination> getDestConfig ( ) {
if ( sDestConfig == null) {
String content = parseFile ( "destination.json" ) ;
sDestConfig = JSON. parseObject ( content, new TypeReference < HashMap< String, Destination> > ( ) {
} ) ;
}
return sDestConfig;
}
public static BottomBar getBottomBarConfig ( ) {
if ( sBottomBar == null) {
String content = parseFile ( "main_tabs_config.json" ) ;
sBottomBar = JSON. parseObject ( content, BottomBar. class ) ;
}
return sBottomBar;
}
private static String parseFile ( String fileName) {
AssetManager assets = AppGlobals. getApplication ( ) . getAssets ( ) ;
InputStream is = null;
BufferedReader br = null;
StringBuilder builder = new StringBuilder ( ) ;
try {
is = assets. open ( fileName) ;
br = new BufferedReader ( new InputStreamReader ( is) ) ;
String line = null;
while ( ( line = br. readLine ( ) ) != null) {
builder. append ( line) ;
}
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
try {
if ( is != null) {
is. close ( ) ;
}
if ( br != null) {
br. close ( ) ;
}
} catch ( Exception e) {
}
}
return builder. toString ( ) ;
}
}
創建Fragment切換幫助類NavGraphBuilder
package cn. example. jetpackdemo. utils;
import android. content. ComponentName;
import androidx. fragment. app. FragmentActivity;
import androidx. navigation. ActivityNavigator;
import androidx. navigation. NavController;
import androidx. navigation. NavGraph;
import androidx. navigation. NavGraphNavigator;
import androidx. navigation. NavigatorProvider;
import androidx. navigation. fragment. FragmentNavigator;
import java. util. HashMap;
import java. util. Iterator;
import cn. example. jetpackdemo. navigator. FixFragmentNavigator;
import cn. example. jetpackdemo. ui. model. Destination;
import cn. yumakeji. lib_common. global. AppGlobals;
public class NavGraphBuilder {
public static void build ( NavController controller) {
NavigatorProvider provider = controller. getNavigatorProvider ( ) ;
FragmentNavigator fragmentNavigator = provider. getNavigator ( FragmentNavigator. class ) ;
ActivityNavigator activityNavigator = provider. getNavigator ( ActivityNavigator. class ) ;
NavGraph navGraph = new NavGraph ( new NavGraphNavigator ( provider) ) ;
HashMap< String, Destination> destConfig = AppConfig. getDestConfig ( ) ;
for ( Destination node : destConfig. values ( ) ) {
if ( node. isIsFragment ( ) ) {
FragmentNavigator. Destination destination = fragmentNavigator. createDestination ( ) ;
destination. setId ( node. getId ( ) ) ;
destination. setClassName ( node. getClazName ( ) ) ;
destination. addDeepLink ( node. getPageUrl ( ) ) ;
navGraph. addDestination ( destination) ;
} else {
ActivityNavigator. Destination destination = activityNavigator. createDestination ( ) ;
destination. setId ( node. getId ( ) ) ;
destination. setComponentName ( new ComponentName ( AppGlobals. getApplication ( ) . getPackageName ( ) , node. getClazName ( ) ) ) ;
destination. addDeepLink ( node. getPageUrl ( ) ) ;
navGraph. addDestination ( destination) ;
}
if ( node. isAsStarter ( ) ) {
navGraph. setStartDestination ( node. getId ( ) ) ;
}
}
controller. setGraph ( navGraph) ;
}
public static void build ( FragmentActivity activity, NavController controller, int containerId) {
NavigatorProvider provider = controller. getNavigatorProvider ( ) ;
NavGraph navGraph = new NavGraph ( new NavGraphNavigator ( provider) ) ;
FixFragmentNavigator fragmentNavigator = new FixFragmentNavigator ( activity, activity. getSupportFragmentManager ( ) , containerId) ;
provider. addNavigator ( fragmentNavigator) ;
ActivityNavigator activityNavigator = provider. getNavigator ( ActivityNavigator. class ) ;
HashMap< String, Destination> destConfig = AppConfig. getDestConfig ( ) ;
Iterator< Destination> iterator = destConfig. values ( ) . iterator ( ) ;
while ( iterator. hasNext ( ) ) {
Destination node = iterator. next ( ) ;
if ( node. isIsFragment ( ) ) {
FragmentNavigator. Destination destination = fragmentNavigator. createDestination ( ) ;
destination. setId ( node. getId ( ) ) ;
destination. setClassName ( node. getClazName ( ) ) ;
destination. addDeepLink ( node. getPageUrl ( ) ) ;
navGraph. addDestination ( destination) ;
} else {
ActivityNavigator. Destination destination = activityNavigator. createDestination ( ) ;
destination. setId ( node. getId ( ) ) ;
destination. setComponentName ( new ComponentName ( AppGlobals. getApplication ( ) . getPackageName ( ) , node. getClazName ( ) ) ) ;
destination. addDeepLink ( node. getPageUrl ( ) ) ;
navGraph. addDestination ( destination) ;
}
if ( node. isAsStarter ( ) ) {
navGraph. setStartDestination ( node. getId ( ) ) ;
}
}
controller. setGraph ( navGraph) ;
}
}
定製的Fragment導航器,替換ft.replace(mContainerId, frag);爲 hide()/show()
package cn. example. jetpackdemo. navigator;
import android. content. Context;
import android. os. Bundle;
import android. util. Log;
import android. view. View;
import androidx. annotation. IdRes;
import androidx. annotation. NonNull;
import androidx. annotation. Nullable;
import androidx. fragment. app. Fragment;
import androidx. fragment. app. FragmentManager;
import androidx. fragment. app. FragmentTransaction;
import androidx. navigation. NavDestination;
import androidx. navigation. NavOptions;
import androidx. navigation. Navigator;
import androidx. navigation. fragment. FragmentNavigator;
import java. lang. reflect. Field;
import java. util. ArrayDeque;
import java. util. Map;
@Navigator . Name ( "fixfragment" )
public class FixFragmentNavigator extends FragmentNavigator {
private static final String TAG = "FixFragmentNavigator" ;
private Context mContext;
private FragmentManager mManager;
private int mContainerId;
public FixFragmentNavigator ( @NonNull Context context, @NonNull FragmentManager manager, int containerId) {
super ( context, manager, containerId) ;
mContext = context;
mManager = manager;
mContainerId = containerId;
}
@Nullable
@Override
public NavDestination navigate ( @NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator. Extras navigatorExtras) {
if ( mManager. isStateSaved ( ) ) {
Log. i ( TAG, "Ignoring navigate() call: FragmentManager has already"
+ " saved its state" ) ;
return null;
}
String className = destination. getClassName ( ) ;
if ( className. charAt ( 0 ) == '.' ) {
className = mContext. getPackageName ( ) + className;
}
final FragmentTransaction ft = mManager. beginTransaction ( ) ;
int enterAnim = navOptions != null ? navOptions. getEnterAnim ( ) : - 1 ;
int exitAnim = navOptions != null ? navOptions. getExitAnim ( ) : - 1 ;
int popEnterAnim = navOptions != null ? navOptions. getPopEnterAnim ( ) : - 1 ;
int popExitAnim = navOptions != null ? navOptions. getPopExitAnim ( ) : - 1 ;
if ( enterAnim != - 1 || exitAnim != - 1 || popEnterAnim != - 1 || popExitAnim != - 1 ) {
enterAnim = enterAnim != - 1 ? enterAnim : 0 ;
exitAnim = exitAnim != - 1 ? exitAnim : 0 ;
popEnterAnim = popEnterAnim != - 1 ? popEnterAnim : 0 ;
popExitAnim = popExitAnim != - 1 ? popExitAnim : 0 ;
ft. setCustomAnimations ( enterAnim, exitAnim, popEnterAnim, popExitAnim) ;
}
Fragment fragment = mManager. getPrimaryNavigationFragment ( ) ;
if ( fragment != null) {
ft. hide ( fragment) ;
}
Fragment frag = null;
String tag = String. valueOf ( destination. getId ( ) ) ;
frag = mManager. findFragmentByTag ( tag) ;
if ( frag != null) {
ft. show ( frag) ;
} else {
frag = instantiateFragment ( mContext, mManager, className, args) ;
frag. setArguments ( args) ;
ft. add ( mContainerId, frag, tag) ;
}
ft. setPrimaryNavigationFragment ( frag) ;
final @IdRes int destId = destination. getId ( ) ;
ArrayDeque< Integer> mBackStack = null;
try {
Field field = FragmentNavigator. class . getDeclaredField ( "mBackStack" ) ;
field. setAccessible ( true ) ;
mBackStack = ( ArrayDeque< Integer> ) field. get ( this ) ;
} catch ( NoSuchFieldException e) {
e. printStackTrace ( ) ;
} catch ( IllegalAccessException e) {
e. printStackTrace ( ) ;
}
final boolean initialNavigation = mBackStack. isEmpty ( ) ;
final boolean isSingleTopReplacement = navOptions != null && ! initialNavigation
&& navOptions. shouldLaunchSingleTop ( )
&& mBackStack. peekLast ( ) == destId;
boolean isAdded;
if ( initialNavigation) {
isAdded = true ;
} else if ( isSingleTopReplacement) {
if ( mBackStack. size ( ) > 1 ) {
mManager. popBackStack (
generateBackStackName ( mBackStack. size ( ) , mBackStack. peekLast ( ) ) ,
FragmentManager. POP_BACK_STACK_INCLUSIVE) ;
ft. addToBackStack ( generateBackStackName ( mBackStack. size ( ) , destId) ) ;
}
isAdded = false ;
} else {
ft. addToBackStack ( generateBackStackName ( mBackStack. size ( ) + 1 , destId) ) ;
isAdded = true ;
}
if ( navigatorExtras instanceof Extras ) {
Extras extras = ( Extras) navigatorExtras;
for ( Map. Entry< View, String> sharedElement : extras. getSharedElements ( ) . entrySet ( ) ) {
ft. addSharedElement ( sharedElement. getKey ( ) , sharedElement. getValue ( ) ) ;
}
}
ft. setReorderingAllowed ( true ) ;
ft. commit ( ) ;
if ( isAdded) {
mBackStack. add ( destId) ;
return destination;
} else {
return null;
}
}
private String generateBackStackName ( int backStackindex, int destid) {
return backStackindex + "-" + destid;
}
}
<?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"
android: id= " @+id/container"
android: layout_width= " match_parent"
android: layout_height= " match_parent"
android: paddingTop= " ?attr/actionBarSize" >
< cn.example.jetpackdemo.view.HAppBottomBar
android: id= " @+id/nav_view"
android: layout_width= " 0dp"
android: layout_height= " wrap_content"
android: layout_marginStart= " 0dp"
android: layout_marginEnd= " 0dp"
android: background= " ?android:attr/windowBackground"
app: itemTextAppearanceActive= " @style/active"
app: itemTextAppearanceInactive= " @style/inActive"
app: layout_constraintBottom_toBottomOf= " parent"
app: layout_constraintLeft_toLeftOf= " parent"
app: layout_constraintRight_toRightOf= " parent" />
< fragment
android: id= " @+id/nav_host_fragment"
android: name= " androidx.navigation.fragment.NavHostFragment"
android: layout_width= " match_parent"
android: layout_height= " match_parent"
app: defaultNavHost= " true"
app: layout_constraintBottom_toTopOf= " @id/nav_view"
app: layout_constraintLeft_toLeftOf= " parent"
app: layout_constraintRight_toRightOf= " parent"
app: layout_constraintTop_toTopOf= " parent" />
</ androidx.constraintlayout.widget.ConstraintLayout>
public class MainActivity extends AppCompatActivity implements BottomNavigationView. OnNavigationItemSelectedListener {
private NavController navController;
@Override
protected void onCreate ( Bundle savedInstanceState) {
super . onCreate ( savedInstanceState) ;
setContentView ( R. layout. activity_main) ;
BottomNavigationView navView = findViewById ( R. id. nav_view) ;
Fragment fragment = getSupportFragmentManager ( ) . findFragmentById ( R. id. nav_host_fragment) ;
navController = NavHostFragment. findNavController ( fragment) ;
NavGraphBuilder. build ( this , navController, fragment. getId ( ) ) ;
navView. setOnNavigationItemSelectedListener ( this ) ;
}
@Override
public void onAttachedToWindow ( ) {
super . onAttachedToWindow ( ) ;
}
@Override
public boolean onNavigationItemSelected ( @NonNull MenuItem item) {
HashMap< String, Destination> destConfig = AppConfig. getDestConfig ( ) ;
Iterator< Map. Entry< String, Destination> > iterator = destConfig. entrySet ( ) . iterator ( ) ;
while ( iterator. hasNext ( ) ) {
Map. Entry< String, Destination> entry = iterator. next ( ) ;
Destination value = entry. getValue ( ) ;
if ( value != null && value. isNeedLogin ( ) && value. getId ( ) == item. getItemId ( ) ) {
Toast. makeText ( AppGlobals. getApplication ( ) , "當前頁面需要登錄" , Toast. LENGTH_LONG) . show ( ) ;
return false ;
}
}
navController. navigate ( item. getItemId ( ) ) ;
return ! TextUtils. isEmpty ( item. getTitle ( ) ) ;
}
}
@FragmentDestination ( pageUrl = "main/tabs/home" , asStarter = true )
public class HomeFragment extends Fragment { }
@FragmentDestination ( pageUrl = "main/tabs/my" , needLogin = true , asStarter = false )
public class MyFragment extends Fragment { }
@ActivityDestination ( pageUrl = "main/tabs/publish" , asStarter = false )
public class PublishActivity extends AppCompatActivity { }
package cn. yumakeji. lib_common. global;
import android. app. Application;
import java. lang. reflect. InvocationTargetException;
public class AppGlobals {
private static Application sApplication;
public static Application getApplication ( ) {
if ( sApplication == null) {
try {
sApplication = ( Application) Class. forName ( "android.app.ActivityThread" )
. getMethod ( "currentApplication" )
. invoke ( null, ( Object[ ] ) null) ;
} catch ( IllegalAccessException e) {
e. printStackTrace ( ) ;
} catch ( InvocationTargetException e) {
e. printStackTrace ( ) ;
} catch ( NoSuchMethodException e) {
e. printStackTrace ( ) ;
} catch ( ClassNotFoundException e) {
e. printStackTrace ( ) ;
}
}
return sApplication;
}
}
api 'androidx.appcompat:appcompat:1.1.0'
api 'com.google.android.material:material:1.1.0'
api 'androidx.constraintlayout:constraintlayout:1.1.3'
api 'androidx.navigation:navigation-fragment:2.2.2'
api 'androidx.navigation:navigation-ui:2.2.2'
api 'androidx.lifecycle:lifecycle-extensions:2.2.0'
api 'androidx.legacy:legacy-support-v4:1.0.0'
api project ( path: ':lib_navannotation' )
annotationProcessor project ( path: ':lib_navcompiler' )
implementation 'com.alibaba:fastjson:1.2.68'
api project ( path: ':lib_common' )