年级的工程设计实践课要组队开发一个项目,我们组决定开发一款空闲自习室人数查询app,可以让学生更快的找到合适的自习室学习。
这个项目分为服务器端与客户端,我和另一个小老弟负责客户端的开发,我主要负责登录进入系统之后的界面布局和逻辑开发。
首先是基础界面设计,采用google的Material Design设计风格以获得较好的显示效果。
一、Toolbar
- 打开 res/values/styles.xml 将AppTheme属性的parent改为
Theme.AppCompat.Light.NoActionBar
- 在 activity_main.xml 中引入Toolbar
<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
- 修改 MainActivity 中的代码:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initActivity(); private void initActivity() { // 使用Toolbar Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); } }
二、侧边栏
-
打开 app/build.gradle 文件, 添加两行依赖:
implementation 'com.android.support:design:28.0.0' implementation 'de.hdodenhof:circleimageview:3.0.0'
其中前一行是谷歌提供的design库, 我们要使用其中的NavigationView控件来实现滑动菜单, 第二行是一个开源项目CircleImageView, 它可以用来轻松的切割圆形图片, 项目地址 CircleImageView
-
准备menu和headerLayout来显示侧边栏中的菜单项和头部布局
- 在 menu 文件夹中新建 nav_menu.xml :
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <group android:checkableBehavior="none"> <item android:id="@+id/nav_menu1" android:icon="@drawable/nav_menu1" android:title="菜单1"/> <item android:id="@+id/nav_menu2" android:icon="@drawable/nav_menu2" android:title="菜单2"/> </group> </menu>
- 在 layout 文件夹中新建 nav_header.xml :
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="180dp" android:padding="10dp" android:background="?attr/colorPrimary"> <de.hdodenhof.circleimageview.CircleImageView android:id="@+id/icon_image" android:layout_width="70dp" android:layout_height="70dp" android:src="@drawable/nav_icon" android:layout_centerInParent="true"/> <TextView android:id="@+id/school" android:layout_alignParentBottom="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="郑州大学" android:layout_centerHorizontal="true" android:textColor="#fff" android:textSize="14sp"/> <TextView android:id="@+id/username" android:layout_above="@id/school" android:text="ZZU" android:layout_centerHorizontal="true" android:textColor="#fff" android:textSize="14sp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
- 在 menu 文件夹中新建 nav_menu.xml :
-
在 activity_main.xml 中使用NavigationView:
<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.Toolbar android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:popupTheme="@style/ThemeOverlay.AppCompat.Light"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="hello world" /> </LinearLayout> <android.support.design.widget.NavigationView android:id="@+id/nav_view" android:layout_gravity="start" android:layout_width="match_parent" android:layout_height="match_parent" app:menu="@menu/nav_menu" app:headerLayout="@layout/nav_header"> </android.support.design.widget.NavigationView> </android.support.v4.widget.DrawerLayout>
-
在 MainActivity 中处理菜单项点击事件:
public class MainActivity extends AppCompatActivity { private DrawerLayout drawerLayout; private NavigationView navigationView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initActivity(); // 侧边栏菜单项的点击事件 navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { switch (menuItem.getItemId()){ case R.id.nav_menu1: drawerLayout.closeDrawers(); break; case R.id.nav_menu2: drawerLayout.closeDrawers(); break; } return true; } }); } private void initActivity() { drawerLayout = findViewById(R.id.drawer_layout); navigationView = findViewById(R.id.nav_view); // 使用Toolbar Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); } }
-
使用 ActionBarDrawerToggle 控制侧边栏弹出:
// 使用toggle控制侧边栏弹出: ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this,drawerLayout,toolbar,R.string.app_name,R.string.app_name); toggle.syncState(); drawerLayout.addDrawerListener(toggle);
-
默认情况下侧边栏的头部布局是无法设置点击事件的,如果需要设置点击事件,例如点击头部布局进入个人信息界面,首先要取消 app:headerLayout ,然后在代码中设置头部布局,在对其中的控件添加点击事件
View headerView = navigationView.inflateHeaderView(R.layout.nav_header); nav_header = headerView.findViewById(R.id.nav_header); nav_header.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, PersonalActivity.class); startActivity(intent); } });
三、状态栏透明
不重复造轮子, 直接使用某大牛写的状态栏工具类实现透明效果:
- 在 app/build.gradle 中引入:
implementation 'com.jaeger.statusbarutil:library:1.4.0'
- 由于使用了侧边栏布局, 需要在 activity_main.xml 中为 DrawerLayout 设置
android:fitsSystemWindows="true"
- 在 MainActivity 中设置状态栏, 这个步骤一定要放在控件绑定完之后:
StatusBarUtil.setTransparentForDrawerLayout(this, drawerLayout);
- 状态栏工具类的中文主页
- 使用中发现了一个bug, 有侧边栏的主活动设置透明后跳转到别的活动后再设置状态栏透明会变成白色,
android:fitsSystemWindows="true"
也失效了, 好像是一个flag被占用了, 目前还不能理解, 解决办法是在 setContentView 之前加上一段代码getWindow().requestFeature(Window.FEATURE_NO_TITLE); Window window = getWindow(); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT);
四、底部导航栏
使用 BottomNavigationView + FrameLayout 实现底部导航栏效果
- 准备导航栏图标
- 在 menu 文件夹中新建 bottom_nav.xml :
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/navigation_home" android:icon="@drawable/ic_home_black_24dp" android:title="导航1" /> <item android:id="@+id/navigation_dashboard" android:icon="@drawable/ic_dashboard_black_24dp" android:title="导航2" /> <item android:id="@+id/navigation_notifications" android:icon="@drawable/ic_notifications_black_24dp" android:title="导航3" /> </menu>
- 修改 activity_main.xml , 引入BottomNavigationView和三个FrameLayout, 默认显示第一个 :
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <View android:layout_width="match_parent" android:layout_height="18dp" android:background="?attr/colorPrimary" /> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:popupTheme="@style/ThemeOverlay.AppCompat.Light"/> <FrameLayout android:id="@+id/frame1" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="导航1" /> </FrameLayout> <FrameLayout android:id="@+id/frame2" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:visibility="gone" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="导航2" /> </FrameLayout> <FrameLayout android:id="@+id/frame3" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:visibility="gone" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="导航3" /> </FrameLayout> <android.support.design.widget.BottomNavigationView android:id="@+id/bottom_nav" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/windowBackground" app:menu="@menu/bottom_nav"/> </LinearLayout>
- 在 MainActivity 中控制三个FrameLayout的显示:
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.navigation_home: // 显示第一页 frameLayout1.setVisibility(View.VISIBLE); frameLayout2.setVisibility(View.GONE); frameLayout3.setVisibility(View.GONE); return true; case R.id.navigation_dashboard: // 显示第二页 frameLayout2.setVisibility(View.VISIBLE); frameLayout1.setVisibility(View.GONE); frameLayout3.setVisibility(View.GONE); return true; case R.id.navigation_notifications: // 显示第三页 frameLayout1.setVisibility(View.GONE); frameLayout2.setVisibility(View.GONE); frameLayout3.setVisibility(View.VISIBLE); return true; } return false; } }; // 监听底部导航栏变化 BottomNavigationView bottom_nav = findViewById(R.id.bottom_nav); bottom_nav.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);