【移动开发】Android中的底部菜单框架(Fragment)

简介:

今天,我将总结一下Android应用中大家经常见到的底部导航栏的几种实现!


一。TabHost + RadioGroup实现方式

在我们平时开发过程中使用的TabHost是在上方,这里我们简单修改了一下<TabHost>的布局,让叶片放到了底部。

main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?xml version= "1.0"  encoding= "utf-8" ?>
<TabHost xmlns:android= "http://schemas.android.com/apk/res/android"
     android:id= "@android:id/tabhost"
     android:layout_width= "fill_parent"
     android:layout_height= "fill_parent"
     android:orientation= "horizontal"  >
     <LinearLayout
         android:layout_width= "fill_parent"
         android:layout_height= "fill_parent"
         android:orientation= "vertical"  >
         <TabWidget
             android:id= "@android:id/tabs"
             android:layout_width= "fill_parent"
             android:layout_height= "wrap_content"
             android:visibility= "gone"  />
         <FrameLayout
             android:id= "@android:id/tabcontent"
             android:layout_width= "fill_parent"
             android:layout_height= "fill_parent"
             android:layout_weight= "1.0"  />
         <RadioGroup
             android:id= "@+id/radioGroup"
             android:layout_width= "fill_parent"
             android:layout_height= "wrap_content"
             android:layout_gravity= "bottom"
             android:background= "@drawable/bar"
             android:gravity= "center_vertical"
             android:orientation= "horizontal"  >
             <RadioButton
                 android:id= "@+id/rd_home"
                 style= "@style/main_btn_style"
                 android:layout_width= "wrap_content"
                 android:layout_height= "wrap_content"
                 android:drawableTop= "@drawable/home_icon"
                 android:text= "主页"  />
             <RadioButton
                 android:id= "@+id/rd_wt"
                 style= "@style/main_btn_style"
                 android:layout_width= "wrap_content"
                 android:layout_height= "wrap_content"
                 android:drawableTop= "@drawable/wb_icon_write_n"
                 android:text= "发表"  />
             <RadioButton
                 android:id= "@+id/rd_msg"
                 style= "@style/main_btn_style"
                 android:layout_width= "wrap_content"
                 android:layout_height= "wrap_content"
                 android:drawableTop= "@drawable/msg_icon"
                 android:text= "信息"  />
             <RadioButton
                 android:id= "@+id/rd_more"
                 style= "@style/main_btn_style"
                 android:layout_width= "wrap_content"
                 android:layout_height= "wrap_content"
                 android:drawableTop= "@drawable/more_icon"
                 android:text= "更多"  />
         </RadioGroup>
     </LinearLayout>
</TabHost>

styles.xml

1
2
3
4
5
6
7
8
9
<style name= "main_btn_style" >
        <item name= "android:button" >@ null </item>
        <item name= "android:textSize" >10dp</item>
        <item name= "android:textColor" >#ffffff</item>
        <item name= "android:gravity" >center_horizontal</item>
        <item name= "android:drawablePadding" >4dp</item>
        <item name= "android:layout_weight" > 1.0 </item>
        <item name= "android:background" >@drawable/main_btn_bg_d</item>
    </style>

main_btn_bg_d.xml

1
2
3
4
5
<?xml version= "1.0"  encoding= "utf-8" ?>
<selector xmlns:android= "http://schemas.android.com/apk/res/android" >
     <item android:drawable= "@drawable/main_btn_bg"  android:state_enabled= "true"  android:state_pressed= "true" ></item>
     <item android:drawable= "@drawable/main_btn_bg"  android:state_checked= "true"  android:state_enabled= "true" ></item>
</selector>

这里需要注意的是:

1.main.xml中:TabWidget的id必须是@android:id/tabs,FrameLayout的id必须是 @android:id/tabcontent。

2.把TabWidget的Visibility设置成了gone!也就是默认难看的风格不见了2011-3-1_2.jpg取而代之的是5个带风格的单选按钮.


MainActivity类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package  com.zhf.android_tabhost;
import  android.app.TabActivity;
import  android.content.Intent;
import  android.os.Bundle;
import  android.widget.RadioGroup;
import  android.widget.RadioGroup.OnCheckedChangeListener;
import  android.widget.TabHost;
import  android.widget.TabHost.TabSpec;
public  class  MainActivity  extends  TabActivity {
     private  TabHost tabHost;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
     @Override
     public  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.main);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
         tabHost =  this .getTabHost();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
         TabSpec homeSpec = tabHost.newTabSpec( "home" ).setIndicator( "home" ).setContent( new  Intent( this ,HomeActivity. class ));
         TabSpec writeSpec = tabHost.newTabSpec( "write" ).setIndicator( "write" ).setContent( new  Intent( this ,WriteActivity. class ));
         TabSpec msgSpec = tabHost.newTabSpec( "msg" ).setIndicator( "msg" ).setContent( new  Intent( this ,MsgActivity. class ));
         TabSpec moreSpec = tabHost.newTabSpec( "more" ).setIndicator( "more" ).setContent( new  Intent( this ,MoreActivity. class ));
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
         tabHost.addTab(homeSpec);
         tabHost.addTab(writeSpec);
         tabHost.addTab(msgSpec);
         tabHost.addTab(moreSpec);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
         RadioGroup rg = (RadioGroup)  this .findViewById(R.id.radioGroup);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
         rg.setOnCheckedChangeListener( new  OnCheckedChangeListener() {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
             @Override
             public  void  onCheckedChanged(RadioGroup group,  int  checkedId) {
                 switch  (checkedId) {
                 case  R.id.rd_home:
                     tabHost.setCurrentTabByTag( "home" );
                     break ;
                 case  R.id.rd_wt:
                     tabHost.setCurrentTabByTag( "write" );
                     break ;
                 case  R.id.rd_msg:
                     tabHost.setCurrentTabByTag( "msg" );
                     break ;
                 case  R.id.rd_more:
                     tabHost.setCurrentTabByTag( "more" );
                     break ;
                 default :
                     break ;
                 }
             }
         });
     }
}

注:

1.由于TabWidget被隐藏,所以相关的事件也会无效,这里取巧用RadioGroup与RadioButton的特性来处理切换,然后监听事件调用setCurrentTabByTag来切换Activity。

2.注意即使TabWidget被隐藏,也要为其设置indicator,否则会保持。

效果图:

174435324.png

点击底部就可以实现切换不同的Activity的操作了,这里需要注意的一点是,切换底部菜单时不会重新调用onCreate()方法的!!)



二.底部回调接口实现(使用Fragment)--- 重要!


这种方式使用了最新的Fragment,采用了底部导航栏回调接口的方法,来切换底部菜单,并且每次切换回重新调用onCreate()方法!!


main.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?xml version= "1.0"  encoding= "utf-8" ?>
<LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android"
     xmlns:tools= "http://schemas.android.com/tools"
     android:layout_width= "fill_parent"
     android:layout_height= "fill_parent"
     android:orientation= "vertical"
     tools:context= ".MainActivity"  >
     <LinearLayout
         android:layout_width= "fill_parent"
         android:layout_height= "0dp"
         android:layout_weight= "10"
         android:orientation= "vertical"  >
         <RelativeLayout
             android:id= "@+id/main_title_RelativeLayout"
             android:layout_width= "fill_parent"
             android:layout_height= "50dp"
             android:background= "@drawable/fragment_bottom_normal"
             android:orientation= "horizontal"  >
             <TextView
                 android:id= "@+id/main_title_TextView"
                 android:layout_width= "wrap_content"
                 android:layout_height= "wrap_content"
                 android:layout_centerInParent= "true"
                 android:text= "主页"
                 android:textColor= "@android:color/white"
                 android:textSize= "24sp"  />
         </RelativeLayout>
         <FrameLayout
             android:id= "@+id/main_detail_FrameLayout"
             android:layout_width= "fill_parent"
             android:layout_height= "fill_parent"
             android:background= "#ffffff"  >
         </FrameLayout>
     </LinearLayout>
     <fragment
         android:id= "@+id/bottom_fragment"
         android:name= "com.zhf.frameworkdemo02.fragments.BottomFragment"
         android:layout_width= "fill_parent"
         android:layout_height= "0dp"
         android:layout_weight= "1"  />
</LinearLayout>


这里由于我们底部菜单我们采用了fragment,所以布局里面要留出位置!


BottomFragment类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package  com.zhf.frameworkdemo02.fragments;
import  com.zhf.frameworkdemo02.R;
import  android.app.Activity;
import  android.os.Bundle;
import  android.support.v4.app.Fragment;
import  android.view.LayoutInflater;
import  android.view.View;
import  android.view.ViewGroup;
import  android.widget.RadioGroup;
import  android.widget.RadioGroup.OnCheckedChangeListener;
/**
  * 页面底部导航栏
  *
  * @author ZHF
  *
  */
public  class  BottomFragment  extends  Fragment {
     //默认回调接口实现类的对象
     private  Callbacks callbacks = defaultCallbacks;
     /** Fragment和Activity建立关联的时候调用 **/
     @Override
     public  void  onAttach(Activity activity) {
         super .onAttach(activity);
         //当前类是否实现了底部导航栏点击事件回调接口
         if (!(activity  instanceof  Callbacks)) {
             throw  new  IllegalStateException( "Activity must implements fragment's callbacks !" );
         }
         callbacks = (Callbacks) activity;
     }
     /** 为Fragment加载布局时调用 **/
     @Override
     public  View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         RadioGroup radioGroup = (RadioGroup) inflater.inflate(R.layout.fragment_bottom, container,  false );
         //绑定监听器
         radioGroup.setOnCheckedChangeListener(changeListener);
         return  radioGroup;
     }
                                                                                                                                                                                                                                                                                                                   
     /**RadioGroup监听器**/
     private  OnCheckedChangeListener changeListener =  new  OnCheckedChangeListener() {
         @Override
         public  void  onCheckedChanged(RadioGroup group,  int  checkedId) {
             System.out.println(checkedId);
             callbacks.onItemSelected(checkedId);  //调用接口中方法
         }
     };
     public  interface  Callbacks{
         /**导航栏回调接口**/
         public  void  onItemSelected( int  item);
     }
     /**默认回调实现类的对象**/
     private  static  Callbacks defaultCallbacks =  new  Callbacks() {
         @Override
         public  void  onItemSelected( int  item) {
         //什么都不干
         }
     };
                                                                                                                                                                                                                                                                                                                   
     /**Fragment和Activity解除关联的时候调用**/
     @Override
     public  void  onDetach() {
         super .onDetach();
        callbacks = defaultCallbacks;
     }
}

底部菜单布局fragment_bottom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?xml version= "1.0"  encoding= "utf-8" ?>
<RadioGroup xmlns:android= "http://schemas.android.com/apk/res/android"
     android:layout_width= "fill_parent"
     android:layout_height= "fill_parent"
     android:orientation= "horizontal"  >
     <RadioButton
         android:id= "@+id/fragment_bottom_home"
         android:layout_width= "fill_parent"
         android:layout_height= "fill_parent"
         android:layout_weight= "1"
         android:background= "@drawable/fragment_bottom_selector"
         android:button= "@null"
         android:drawableTop= "@drawable/main_readiobutton_home"
         android:gravity= "center"
         android:text= "@string/home"
         android:textColor= "@color/white"
         android:textSize= "12sp"  />
     <View
         android:layout_width= "1dp"
         android:layout_height= "fill_parent"
         android:background= "@color/white"  />
     <RadioButton
         android:id= "@+id/fragment_bottom_order"
         android:layout_width= "fill_parent"
         android:layout_height= "fill_parent"
         android:layout_weight= "1"
         android:background= "@drawable/fragment_bottom_selector"
         android:button= "@null"
         android:drawableTop= "@drawable/main_readiobutton_order"
         android:gravity= "center"
         android:text= "@string/order"
         android:textColor= "@color/white"
         android:textSize= "12sp"  />
     <View
         android:layout_width= "1dp"
         android:layout_height= "fill_parent"
         android:background= "@color/white"
         />
     <RadioButton
         android:id= "@+id/fragment_bottom_notice"
         android:layout_width= "fill_parent"
         android:layout_height= "fill_parent"
         android:layout_weight= "1"
         android:background= "@drawable/fragment_bottom_selector"
         android:button= "@null"
         android:drawableTop= "@drawable/main_readiobutton_notice"
         android:gravity= "center"
         android:text= "@string/notice"
         android:textColor= "@color/white"
         android:textSize= "12sp"  />
     <View
         android:layout_width= "1dp"
         android:layout_height= "fill_parent"
         android:background= "@color/white"  />
     <RadioButton
         android:id= "@+id/fragment_bottom_more"
         android:layout_width= "fill_parent"
         android:layout_height= "fill_parent"
         android:layout_weight= "1"
         android:background= "@drawable/fragment_bottom_selector"
         android:button= "@null"
         android:drawablePadding= "3dip"
         android:drawableTop= "@drawable/main_readiobutton_more"
         android:gravity= "center"
         android:text= "@string/more"
         android:textColor= "@color/white"
         android:textSize= "12sp"  />
</RadioGroup>


这里我们定义了一个框架类:GeneralFragment(所有的fragment界面都需继承它)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package  com.zhf.frameworkdemo02.fragments;
import  java.io.Serializable;
import  com.zhf.frameworkdemo02.R;
import  com.zhf.frameworkdemo02.view.OrderView;
import  com.zhf.frameworkdemo02.view.HomeView;
import  com.zhf.frameworkdemo02.view.MoreView;
import  com.zhf.frameworkdemo02.view.NoticeView;
import  android.os.Bundle;
import  android.support.v4.app.Fragment;
import  android.view.LayoutInflater;
import  android.view.View;
import  android.view.ViewGroup;
import  android.widget.TextView;
/**
  * 框架类,抽象公共方法
  * @author ZHF
  *
  */
public  class  GeneralFragment  extends  Fragment  implements  Serializable{
     /**
      *
      */
     private  static  final  long  serialVersionUID = 1L;
                                                                                                                                                                                                                                                               
     private  int  item;  //用于区分底部菜单项
     protected  static  View main_title_RelativeLayout;  //标题栏
     protected  final  static  String key =  "Bundle" ;    //跳转值传递key的名称
                                                                                                                                                                                                                                                               
     @Override
     public  void  onCreate(Bundle savedInstanceState) {
         // TODO Auto-generated method stub
         super .onCreate(savedInstanceState);
         if (getArguments() !=  null ) {   //根据接收子类传来的arguments判断item的哪一项
             if (getArguments().containsKey(MainFragment.Item)) {
                 item = getArguments().getInt(MainFragment.Item);
             }
         }
     }
                                                                                                                                                                                                                                                               
     /**为Fragment加载布局时调用 **/
     @Override
     public  View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         View view = inflater.inflate(R.layout.fragment_general, container,  false );
         GeneralFragment fragment =  null ;
         switch (item) {
         case  R.id.fragment_bottom_home:   //初始化主页
             fragment =  new  HomeView();
             break ;
         case  R.id.fragment_bottom_order:
             fragment =  new  OrderView();   //初始化订单
              break ;
         case  R.id.fragment_bottom_notice:
             fragment =  new  NoticeView();    //初始化公告
             break ;
         case  R.id.fragment_bottom_more:
             fragment =  new  MoreView();   //初始化更多
             break ;
         default :
             break ;
         }
         if (fragment !=  null ) {
             //更换mainView中间的内容(home,msg,at,more)
             getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.general_fragment, fragment).commit();
         }
         main_title_RelativeLayout =  ((View) container.getParent()).findViewById(R.id.main_title_RelativeLayout);
         //将生成的view返回
         return  view;
     }
                                                                                                                                                                                                                                                               
     /**设置标题**/
     protected  void  setTitle(Object title) {
         if (main_title_RelativeLayout !=  null ) {
             //标题栏中的文字
             TextView mTvTitle = (TextView) main_title_RelativeLayout.findViewById(R.id.main_title_TextView);
             if (mTvTitle !=  null ) {
                 if (title  instanceof  Integer) {   //整型
                     mTvTitle.setText((Integer)title);
                 else  //字符类型
                     mTvTitle.setText((CharSequence)title);
                 }
             }
         }
     }
                                                                                                                                                                                                                                                               
     /**页面跳转值传递**/
     protected  void  setBundle(Object... objects) {
         Bundle arguments =  new  Bundle();
         arguments.putSerializable(key, objects);
         GeneralFragment generalFragment =  new  GeneralFragment();
         generalFragment.setArguments(arguments);
     }
                                                                                                                                                                                                                                                               
     /**获取所传递的值**/
     protected  Object[] getBundle() {
         if (getArguments() !=  null ) {
             System.out.println( "getBundle" );
             if (getArguments().containsKey(key)) {
                 Object[] object = (Object[]) getArguments().getSerializable(key);
                 return  object;
             }
         }
         return  null ;
     }
                                                                                                                                                                                                                                                               
     /**无参页面跳转**/
     protected  void  toIntent(GeneralFragment generalFragment) {
         if (generalFragment !=  null ) {
             getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.general_fragment, generalFragment).commit();
         }
     }
}


程序入口MainFragment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package  com.zhf.frameworkdemo02.fragments;
import  com.zhf.frameworkdemo02.R;
import  android.os.Bundle;
import  android.support.v4.app.FragmentActivity;
import  android.support.v4.app.FragmentManager;
/**
  *MainView
  * @author ZHF
  *
  */
public  class  MainFragment  extends  FragmentActivity  implements  BottomFragment.Callbacks {
                                                                                                                                                                                                                                  
     public  final  static  String  Item =  "item" ;
                                                                                                                                                                                                                                  
     @Override
     protected  void  onCreate(Bundle savedInstanceState) {
         // TODO Auto-generated method stub
         super .onCreate(savedInstanceState);
         setContentView(R.layout.main);
         //初始化默认调用接口中item选中方法
         onItemSelected(R.id.fragment_bottom_home);
     }
     @Override
     public  void  onItemSelected( int  item) {
         Bundle arguments =  new  Bundle();
         arguments.putInt(Item, item);  //将选中的底部radio的Id放进去
         GeneralFragment generalFragment =  new  GeneralFragment();
         generalFragment.setArguments(arguments);  //设置参数值
         //这里根据item的ID进行界面跳转
         FragmentManager fm = getSupportFragmentManager();
         fm.beginTransaction().replace(R.id.main_detail_FrameLayout, generalFragment).commit();
     }
}


说明:这里我们的每个界面都将采用Fragment,故每个界面需重写onCreateView()


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package  com.zhf.frameworkdemo02.view;
import  android.os.Bundle;
import  android.view.LayoutInflater;
import  android.view.View;
import  android.view.ViewGroup;
import  com.zhf.frameworkdemo02.R;
import  com.zhf.frameworkdemo02.fragments.GeneralFragment;
/**
  * 主页面
  * @author ZHF
  *
  */
public  class  HomeView  extends  GeneralFragment{
     @Override
     public  void  onCreate(Bundle savedInstanceState) {
         // TODO Auto-generated method stub
         super .onCreate(savedInstanceState);
         super .setTitle( "主页" );
     }
                                                                                                                                               
     @Override
     public  View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         return  inflater.inflate(R.layout.home, container,  false );
     }
}

(其他三个略)

最终效果图:

225533378.png


ok!大功告成!相当实用的!有兴趣的可以学习一下!


源码下载:http://down.51cto.com/data/1009354





今天,我将总结一下Android应用中大家经常见到的底部导航栏的几种实现!


一。TabHost + RadioGroup实现方式

在我们平时开发过程中使用的TabHost是在上方,这里我们简单修改了一下<TabHost>的布局,让叶片放到了底部。

main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?xml version= "1.0"  encoding= "utf-8" ?>
<TabHost xmlns:android= "http://schemas.android.com/apk/res/android"
     android:id= "@android:id/tabhost"
     android:layout_width= "fill_parent"
     android:layout_height= "fill_parent"
     android:orientation= "horizontal"  >
     <LinearLayout
         android:layout_width= "fill_parent"
         android:layout_height= "fill_parent"
         android:orientation= "vertical"  >
         <TabWidget
             android:id= "@android:id/tabs"
             android:layout_width= "fill_parent"
             android:layout_height= "wrap_content"
             android:visibility= "gone"  />
         <FrameLayout
             android:id= "@android:id/tabcontent"
             android:layout_width= "fill_parent"
             android:layout_height= "fill_parent"
             android:layout_weight= "1.0"  />
         <RadioGroup
             android:id= "@+id/radioGroup"
             android:layout_width= "fill_parent"
             android:layout_height= "wrap_content"
             android:layout_gravity= "bottom"
             android:background= "@drawable/bar"
             android:gravity= "center_vertical"
             android:orientation= "horizontal"  >
             <RadioButton
                 android:id= "@+id/rd_home"
                 style= "@style/main_btn_style"
                 android:layout_width= "wrap_content"
                 android:layout_height= "wrap_content"
                 android:drawableTop= "@drawable/home_icon"
                 android:text= "主页"  />
             <RadioButton
                 android:id= "@+id/rd_wt"
                 style= "@style/main_btn_style"
                 android:layout_width= "wrap_content"
                 android:layout_height= "wrap_content"
                 android:drawableTop= "@drawable/wb_icon_write_n"
                 android:text= "发表"  />
             <RadioButton
                 android:id= "@+id/rd_msg"
                 style= "@style/main_btn_style"
                 android:layout_width= "wrap_content"
                 android:layout_height= "wrap_content"
                 android:drawableTop= "@drawable/msg_icon"
                 android:text= "信息"  />
             <RadioButton
                 android:id= "@+id/rd_more"
                 style= "@style/main_btn_style"
                 android:layout_width= "wrap_content"
                 android:layout_height= "wrap_content"
                 android:drawableTop= "@drawable/more_icon"
                 android:text= "更多"  />
         </RadioGroup>
     </LinearLayout>
</TabHost>

styles.xml

1
2
3
4
5
6
7
8
9
<style name= "main_btn_style" >
        <item name= "android:button" >@ null </item>
        <item name= "android:textSize" >10dp</item>
        <item name= "android:textColor" >#ffffff</item>
        <item name= "android:gravity" >center_horizontal</item>
        <item name= "android:drawablePadding" >4dp</item>
        <item name= "android:layout_weight" > 1.0 </item>
        <item name= "android:background" >@drawable/main_btn_bg_d</item>
    </style>

main_btn_bg_d.xml

1
2
3
4
5
<?xml version= "1.0"  encoding= "utf-8" ?>
<selector xmlns:android= "http://schemas.android.com/apk/res/android" >
     <item android:drawable= "@drawable/main_btn_bg"  android:state_enabled= "true"  android:state_pressed= "true" ></item>
     <item android:drawable= "@drawable/main_btn_bg"  android:state_checked= "true"  android:state_enabled= "true" ></item>
</selector>

这里需要注意的是:

1.main.xml中:TabWidget的id必须是@android:id/tabs,FrameLayout的id必须是 @android:id/tabcontent。

2.把TabWidget的Visibility设置成了gone!也就是默认难看的风格不见了2011-3-1_2.jpg取而代之的是5个带风格的单选按钮.


MainActivity类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package  com.zhf.android_tabhost;
import  android.app.TabActivity;
import  android.content.Intent;
import  android.os.Bundle;
import  android.widget.RadioGroup;
import  android.widget.RadioGroup.OnCheckedChangeListener;
import  android.widget.TabHost;
import  android.widget.TabHost.TabSpec;
public  class  MainActivity  extends  TabActivity {
     private  TabHost tabHost;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
     @Override
     public  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.main);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
         tabHost =  this .getTabHost();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
         TabSpec homeSpec = tabHost.newTabSpec( "home" ).setIndicator( "home" ).setContent( new  Intent( this ,HomeActivity. class ));
         TabSpec writeSpec = tabHost.newTabSpec( "write" ).setIndicator( "write" ).setContent( new  Intent( this ,WriteActivity. class ));
         TabSpec msgSpec = tabHost.newTabSpec( "msg" ).setIndicator( "msg" ).setContent( new  Intent( this ,MsgActivity. class ));
         TabSpec moreSpec = tabHost.newTabSpec( "more" ).setIndicator( "more" ).setContent( new  Intent( this ,MoreActivity. class ));
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
         tabHost.addTab(homeSpec);
         tabHost.addTab(writeSpec);
         tabHost.addTab(msgSpec);
         tabHost.addTab(moreSpec);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
         RadioGroup rg = (RadioGroup)  this .findViewById(R.id.radioGroup);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
         rg.setOnCheckedChangeListener( new  OnCheckedChangeListener() {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
             @Override
             public  void  onCheckedChanged(RadioGroup group,  int  checkedId) {
                 switch  (checkedId) {
                 case  R.id.rd_home:
                     tabHost.setCurrentTabByTag( "home" );
                     break ;
                 case  R.id.rd_wt:
                     tabHost.setCurrentTabByTag( "write" );
                     break ;
                 case  R.id.rd_msg:
                     tabHost.setCurrentTabByTag( "msg" );
                     break ;
                 case  R.id.rd_more:
                     tabHost.setCurrentTabByTag( "more" );
                     break ;
                 default :
                     break ;
                 }
             }
         });
     }
}

注:

1.由于TabWidget被隐藏,所以相关的事件也会无效,这里取巧用RadioGroup与RadioButton的特性来处理切换,然后监听事件调用setCurrentTabByTag来切换Activity。

2.注意即使TabWidget被隐藏,也要为其设置indicator,否则会保持。

效果图:

174435324.png

点击底部就可以实现切换不同的Activity的操作了,这里需要注意的一点是,切换底部菜单时不会重新调用onCreate()方法的!!)



二.底部回调接口实现(使用Fragment)--- 重要!


这种方式使用了最新的Fragment,采用了底部导航栏回调接口的方法,来切换底部菜单,并且每次切换回重新调用onCreate()方法!!


main.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?xml version= "1.0"  encoding= "utf-8" ?>
<LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android"
     xmlns:tools= "http://schemas.android.com/tools"
     android:layout_width= "fill_parent"
     android:layout_height= "fill_parent"
     android:orientation= "vertical"
     tools:context= ".MainActivity"  >
     <LinearLayout
         android:layout_width= "fill_parent"
         android:layout_height= "0dp"
         android:layout_weight= "10"
         android:orientation= "vertical"  >
         <RelativeLayout
             android:id= "@+id/main_title_RelativeLayout"
             android:layout_width= "fill_parent"
             android:layout_height= "50dp"
             android:background= "@drawable/fragment_bottom_normal"
             android:orientation= "horizontal"  >
             <TextView
                 android:id= "@+id/main_title_TextView"
                 android:layout_width= "wrap_content"
                 android:layout_height= "wrap_content"
                 android:layout_centerInParent= "true"
                 android:text= "主页"
                 android:textColor= "@android:color/white"
                 android:textSize= "24sp"  />
         </RelativeLayout>
         <FrameLayout
             android:id= "@+id/main_detail_FrameLayout"
             android:layout_width= "fill_parent"
             android:layout_height= "fill_parent"
             android:background= "#ffffff"  >
         </FrameLayout>
     </LinearLayout>
     <fragment
         android:id= "@+id/bottom_fragment"
         android:name= "com.zhf.frameworkdemo02.fragments.BottomFragment"
         android:layout_width= "fill_parent"
         android:layout_height= "0dp"
         android:layout_weight= "1"  />
</LinearLayout>


这里由于我们底部菜单我们采用了fragment,所以布局里面要留出位置!


BottomFragment类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package  com.zhf.frameworkdemo02.fragments;
import  com.zhf.frameworkdemo02.R;
import  android.app.Activity;
import  android.os.Bundle;
import  android.support.v4.app.Fragment;
import  android.view.LayoutInflater;
import  android.view.View;
import  android.view.ViewGroup;
import  android.widget.RadioGroup;
import  android.widget.RadioGroup.OnCheckedChangeListener;
/**
  * 页面底部导航栏
  *
  * @author ZHF
  *
  */
public  class  BottomFragment  extends  Fragment {
     //默认回调接口实现类的对象
     private  Callbacks callbacks = defaultCallbacks;
     /** Fragment和Activity建立关联的时候调用 **/
     @Override
     public  void  onAttach(Activity activity) {
         super .onAttach(activity);
         //当前类是否实现了底部导航栏点击事件回调接口
         if (!(activity  instanceof  Callbacks)) {
             throw  new  IllegalStateException( "Activity must implements fragment's callbacks !" );
         }
         callbacks = (Callbacks) activity;
     }
     /** 为Fragment加载布局时调用 **/
     @Override
     public  View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         RadioGroup radioGroup = (RadioGroup) inflater.inflate(R.layout.fragment_bottom, container,  false );
         //绑定监听器
         radioGroup.setOnCheckedChangeListener(changeListener);
         return  radioGroup;
     }
                                                                                                                                                                                                                                                                                                                   
     /**RadioGroup监听器**/
     private  OnCheckedChangeListener changeListener =  new  OnCheckedChangeListener() {
         @Override
         public  void  onCheckedChanged(RadioGroup group,  int  checkedId) {
             System.out.println(checkedId);
             callbacks.onItemSelected(checkedId);  //调用接口中方法
         }
     };
     public  interface  Callbacks{
         /**导航栏回调接口**/
         public  void  onItemSelected( int  item);
     }
     /**默认回调实现类的对象**/
     private  static  Callbacks defaultCallbacks =  new  Callbacks() {
         @Override
         public  void  onItemSelected( int  item) {
         //什么都不干
         }
     };
                                                                                                                                                                                                                                                                                                                   
     /**Fragment和Activity解除关联的时候调用**/
     @Override
     public  void  onDetach() {
         super .onDetach();
        callbacks = defaultCallbacks;
     }
}

底部菜单布局fragment_bottom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?xml version= "1.0"  encoding= "utf-8" ?>
<RadioGroup xmlns:android= "http://schemas.android.com/apk/res/android"
     android:layout_width= "fill_parent"
     android:layout_height= "fill_parent"
     android:orientation= "horizontal"  >
     <RadioButton
         android:id= "@+id/fragment_bottom_home"
         android:layout_width= "fill_parent"
         android:layout_height= "fill_parent"
         android:layout_weight= "1"
         android:background= "@drawable/fragment_bottom_selector"
         android:button= "@null"
         android:drawableTop= "@drawable/main_readiobutton_home"
         android:gravity= "center"
         android:text= "@string/home"
         android:textColor= "@color/white"
         android:textSize= "12sp"  />
     <View
         android:layout_width= "1dp"
         android:layout_height= "fill_parent"
         android:background= "@color/white"  />
     <RadioButton
         android:id= "@+id/fragment_bottom_order"
         android:layout_width= "fill_parent"
         android:layout_height= "fill_parent"
         android:layout_weight= "1"
         android:background= "@drawable/fragment_bottom_selector"
         android:button= "@null"
         android:drawableTop= "@drawable/main_readiobutton_order"
         android:gravity= "center"
         android:text= "@string/order"
         android:textColor= "@color/white"
         android:textSize= "12sp"  />
     <View
         android:layout_width= "1dp"
         android:layout_height= "fill_parent"
         android:background= "@color/white"
         />
     <RadioButton
         android:id= "@+id/fragment_bottom_notice"
         android:layout_width= "fill_parent"
         android:layout_height= "fill_parent"
         android:layout_weight= "1"
         android:background= "@drawable/fragment_bottom_selector"
         android:button= "@null"
         android:drawableTop= "@drawable/main_readiobutton_notice"
         android:gravity= "center"
         android:text= "@string/notice"
         android:textColor= "@color/white"
         android:textSize= "12sp"  />
     <View
         android:layout_width= "1dp"
         android:layout_height= "fill_parent"
         android:background= "@color/white"  />
     <RadioButton
         android:id= "@+id/fragment_bottom_more"
         android:layout_width= "fill_parent"
         android:layout_height= "fill_parent"
         android:layout_weight= "1"
         android:background= "@drawable/fragment_bottom_selector"
         android:button= "@null"
         android:drawablePadding= "3dip"
         android:drawableTop= "@drawable/main_readiobutton_more"
         android:gravity= "center"
         android:text= "@string/more"
         android:textColor= "@color/white"
         android:textSize= "12sp"  />
</RadioGroup>


这里我们定义了一个框架类:GeneralFragment(所有的fragment界面都需继承它)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package  com.zhf.frameworkdemo02.fragments;
import  java.io.Serializable;
import  com.zhf.frameworkdemo02.R;
import  com.zhf.frameworkdemo02.view.OrderView;
import  com.zhf.frameworkdemo02.view.HomeView;
import  com.zhf.frameworkdemo02.view.MoreView;
import  com.zhf.frameworkdemo02.view.NoticeView;
import  android.os.Bundle;
import  android.support.v4.app.Fragment;
import  android.view.LayoutInflater;
import  android.view.View;
import  android.view.ViewGroup;
import  android.widget.TextView;
/**
  * 框架类,抽象公共方法
  * @author ZHF
  *
  */
public  class  GeneralFragment  extends  Fragment  implements  Serializable{
     /**
      *
      */
     private  static  final  long  serialVersionUID = 1L;
                                                                                                                                                                                                                                                               
     private  int  item;  //用于区分底部菜单项
     protected  static  View main_title_RelativeLayout;  //标题栏
     protected  final  static  String key =  "Bundle" ;    //跳转值传递key的名称
                                                                                                                                                                                                                                                               
     @Override
     public  void  onCreate(Bundle savedInstanceState) {
         // TODO Auto-generated method stub
         super .onCreate(savedInstanceState);
         if (getArguments() !=  null ) {   //根据接收子类传来的arguments判断item的哪一项
             if (getArguments().containsKey(MainFragment.Item)) {
                 item = getArguments().getInt(MainFragment.Item);
             }
         }
     }
                                                                                                                                                                                                                                                               
     /**为Fragment加载布局时调用 **/
     @Override
     public  View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         View view = inflater.inflate(R.layout.fragment_general, container,  false );
         GeneralFragment fragment =  null ;
         switch (item) {
         case  R.id.fragment_bottom_home:   //初始化主页
             fragment =  new  HomeView();
             break ;
         case  R.id.fragment_bottom_order:
             fragment =  new  OrderView();   //初始化订单
              break ;
         case  R.id.fragment_bottom_notice:
             fragment =  new  NoticeView();    //初始化公告
             break ;
         case  R.id.fragment_bottom_more:
             fragment =  new  MoreView();   //初始化更多
             break ;
         default :
             break ;
         }
         if (fragment !=  null ) {
             //更换mainView中间的内容(home,msg,at,more)
             getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.general_fragment, fragment).commit();
         }
         main_title_RelativeLayout =  ((View) container.getParent()).findViewById(R.id.main_title_RelativeLayout);
         //将生成的view返回
         return  view;
     }
                                                                                                                                                                                                                                                               
     /**设置标题**/
     protected  void  setTitle(Object title) {
         if (main_title_RelativeLayout !=  null ) {
             //标题栏中的文字
             TextView mTvTitle = (TextView) main_title_RelativeLayout.findViewById(R.id.main_title_TextView);
             if (mTvTitle !=  null ) {
                 if (title  instanceof  Integer) {   //整型
                     mTvTitle.setText((Integer)title);
                 else  //字符类型
                     mTvTitle.setText((CharSequence)title);
                 }
             }
         }
     }
                                                                                                                                                                                                                                                               
     /**页面跳转值传递**/
     protected  void  setBundle(Object... objects) {
         Bundle arguments =  new  Bundle();
         arguments.putSerializable(key, objects);
         GeneralFragment generalFragment =  new  GeneralFragment();
         generalFragment.setArguments(arguments);
     }
                                                                                                                                                                                                                                                               
     /**获取所传递的值**/
     protected  Object[] getBundle() {
         if (getArguments() !=  null ) {
             System.out.println( "getBundle" );
             if (getArguments().containsKey(key)) {
                 Object[] object = (Object[]) getArguments().getSerializable(key);
                 return  object;
             }
         }
         return  null ;
     }
                                                                                                                                                                                                                                                               
     /**无参页面跳转**/
     protected  void  toIntent(GeneralFragment generalFragment) {
         if (generalFragment !=  null ) {
             getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.general_fragment, generalFragment).commit();
         }
     }
}


程序入口MainFragment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package  com.zhf.frameworkdemo02.fragments;
import  com.zhf.frameworkdemo02.R;
import  android.os.Bundle;
import  android.support.v4.app.FragmentActivity;
import  android.support.v4.app.FragmentManager;
/**
  *MainView
  * @author ZHF
  *
  */
public  class  MainFragment  extends  FragmentActivity  implements  BottomFragment.Callbacks {
                                                                                                                                                                                                                                  
     public  final  static  String  Item =  "item" ;
                                                                                                                                                                                                                                  
     @Override
     protected  void  onCreate(Bundle savedInstanceState) {
         // TODO Auto-generated method stub
         super .onCreate(savedInstanceState);
         setContentView(R.layout.main);
         //初始化默认调用接口中item选中方法
         onItemSelected(R.id.fragment_bottom_home);
     }
     @Override
     public  void  onItemSelected( int  item) {
         Bundle arguments =  new  Bundle();
         arguments.putInt(Item, item);  //将选中的底部radio的Id放进去
         GeneralFragment generalFragment =  new  GeneralFragment();
         generalFragment.setArguments(arguments);  //设置参数值
         //这里根据item的ID进行界面跳转
         FragmentManager fm = getSupportFragmentManager();
         fm.beginTransaction().replace(R.id.main_detail_FrameLayout, generalFragment).commit();
     }
}


说明:这里我们的每个界面都将采用Fragment,故每个界面需重写onCreateView()


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package  com.zhf.frameworkdemo02.view;
import  android.os.Bundle;
import  android.view.LayoutInflater;
import  android.view.View;
import  android.view.ViewGroup;
import  com.zhf.frameworkdemo02.R;
import  com.zhf.frameworkdemo02.fragments.GeneralFragment;
/**
  * 主页面
  * @author ZHF
  *
  */
public  class  HomeView  extends  GeneralFragment{
     @Override
     public  void  onCreate(Bundle savedInstanceState) {
         // TODO Auto-generated method stub
         super .onCreate(savedInstanceState);
         super .setTitle( "主页" );
     }
                                                                                                                                               
     @Override
     public  View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         return  inflater.inflate(R.layout.home, container,  false );
     }
}

(其他三个略)

最终效果图:

225533378.png


ok!大功告成!相当实用的!有兴趣的可以学习一下!


源码下载:http://down.51cto.com/data/1009354



     本文转自zhf651555765 51CTO博客,原文链接:http://blog.51cto.com/smallwoniu/1324123,如需转载请自行联系原作者


相关文章
|
1月前
|
Java Linux Android开发
移动应用开发与操作系统的交互:深入理解Android和iOS
在数字时代,移动应用成为我们日常生活的一部分。本文将深入探讨移动应用开发的核心概念、移动操作系统的工作原理以及它们如何相互作用。我们将通过实际代码示例,展示如何在Android和iOS平台上创建一个简单的“Hello World”应用,并解释其背后的技术原理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和知识。
|
5天前
|
算法 JavaScript Android开发
|
14天前
|
Java 程序员 API
Android|集成 slf4j + logback 作为日志框架
做个简单改造,统一 Android APP 和 Java 后端项目打印日志的体验。
60 1
|
18天前
|
缓存 前端开发 Android开发
Android实战之如何截取Activity或者Fragment的内容?
本文首发于公众号“AntDream”,介绍了如何在Android中截取Activity或Fragment的屏幕内容并保存为图片。包括截取整个Activity、特定控件或区域的方法,以及处理包含RecyclerView的复杂情况。
16 3
|
4天前
|
开发框架 前端开发 JavaScript
移动应用开发的未来:跨平台框架的崛起
【10月更文挑战第32天】随着智能手机用户数量的激增,移动应用开发已成为软件开发中增长最快的领域之一。传统的移动应用开发模式要求开发者为不同的操作系统分别编写和维护代码,这不仅耗时耗力,也增加了成本。近年来,跨平台开发框架的出现极大地改变了这一局面,它们允许开发者使用单一的代码库来部署应用到多个平台。本文将探讨跨平台开发框架的优势、面临的挑战以及未来发展趋势,旨在为读者提供一个关于移动应用开发新趋势的全面视角。
|
2月前
|
存储 Java 开发工具
移动应用开发之旅:探索Android操作系统的无限可能
【8月更文挑战第56天】随着智能手机的普及,移动应用已成为我们日常生活中不可或缺的一部分。本文将引导读者了解移动应用开发的基本概念,重点探讨Android操作系统的开发环境搭建、界面设计、功能实现以及与后端服务的交互。通过简单的代码示例和清晰的步骤说明,即便是初学者也能快速入门,开启自己的移动应用开发之旅。
|
2月前
|
设计模式 前端开发 JavaScript
探索移动应用开发:从Android到iOS的跨平台之旅
【9月更文挑战第21天】在这篇文章中,我们将一同揭开移动应用开发的神秘面纱,从Android和iOS这两个主流平台出发,探讨如何利用现代技术栈实现跨平台开发。文章将通过具体的代码示例,带领读者理解不同平台间的差异与联系,以及如何运用React Native框架简化开发流程,实现一次编写,多平台运行的目标。无论你是刚入门的新手还是希望拓展技能的老手,这篇文章都将为你提供宝贵的知识和启示。
67 3
|
2月前
|
前端开发 JavaScript C#
移动应用开发中的跨平台框架解析
【9月更文挑战第5天】在移动应用开发领域,跨平台框架因其“一次编写,处处运行”的便利性而受到开发者的青睐。本文将深入探讨几种流行的跨平台框架,包括React Native、Flutter和Xamarin,并比较它们的优势与局限。我们将通过代码示例揭示这些框架如何简化移动应用的开发过程,同时保持高性能和良好的用户体验。无论你是新手还是有经验的开发者,这篇文章都将成为你了解和选择跨平台框架的宝贵资源。
63 19
|
2月前
|
前端开发 Java 数据库
💡Android开发者必看!掌握这5大框架,轻松打造爆款应用不是梦!🏆
在Android开发领域,框架犹如指路明灯,助力开发者加速应用开发并提升品质。本文将介绍五大必备框架:Retrofit简化网络请求,Room优化数据库访问,MVVM架构提高代码可维护性,Dagger 2管理依赖注入,Jetpack Compose革新UI开发。掌握这些框架,助你在竞争激烈的市场中脱颖而出,打造爆款应用。
316 3
|
2月前
|
编译器 Android开发 开发者
带你了解Android Jetpack库中的依赖注入框架:Hilt
本文介绍了Hilt,这是Google为Android开发的依赖注入框架,基于Dagger构建,旨在简化依赖注入过程。Hilt通过自动化的组件和注解减少了DI的样板代码,提高了应用的可测试性和可维护性。文章详细讲解了Hilt的主要概念、基本用法及原理,帮助开发者更好地理解和应用Hilt。
75 8
下一篇
无影云桌面