概述
本次快速开发Android应用系列,是基于课工场的公开课高效Android工程师6周培养计划,记录微服私访APP的整个开发过程以及当中碰到的问题,供日后学习参考。
上一篇我们主要实现基于okhttp解析服务端数据,并且以json格式返回给客户端,从而完成用户登录远程验证的功能。
还没看过前一篇文章的朋友可以先去参考快速开发android应用3-基于okhttp解析服务数据
这是本系列的第四篇,主要是实现APP的主页界面的框架,使用viewpager+fragment来展现主页内容,使用BottomNavigationBar来完成页面的切换。
效果图:
主页实现
构建主页布局
activity_main.xml
文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.torch.chainmanage.activity.MainActivity">
<include
android:id="@+id/layout_header"
layout="@layout/header_title_bar" />
<include
android:id="@+id/layout_footer"
layout="@layout/footer_bottom_bar" />
<android.support.v4.view.ViewPager
android:id="@+id/view_pager_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/layout_footer"
android:layout_below="@id/layout_header" />
</RelativeLayout>
header_title_bar.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="@dimen/title_bar_h"
android:background="@color/colorAccent"
android:gravity="center_vertical"
>
<TextView
android:id="@+id/title_bar_back"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:drawableLeft="@drawable/title_bar_back"
android:gravity="center"
android:paddingLeft="@dimen/title_bar_back_left"
android:paddingRight="@dimen/title_bar_add_padding"
android:paddingBottom="@dimen/title_bar_back_bottom"
android:paddingTop="@dimen/title_bar_back_bottom"/>
<TextView
android:id="@+id/title_bar_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:gravity="center"
android:text="name"
android:singleLine="true"
android:textColor="@android:color/white"
android:textSize="@dimen/title_bar_text_size" />
<ImageView
android:id="@+id/title_bar_more"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@drawable/title_bar_add"
android:paddingTop="@dimen/title_bar_add_bottom"
android:paddingBottom="@dimen/title_bar_add_bottom"
android:paddingLeft="@dimen/title_bar_add_padding"
android:paddingRight="@dimen/title_bar_add_right"
android:visibility="gone"
/>
<ImageView
android:id="@+id/title_bar_save"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@drawable/title_bar_save"
android:paddingTop="@dimen/title_bar_save_bootom"
android:paddingBottom="@dimen/title_bar_save_bootom"
android:paddingLeft="@dimen/title_bar_save_right"
android:paddingRight="@dimen/title_bar_save_right"
android:visibility="gone"
/>
<ImageView
android:id="@+id/title_bar_change"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_toLeftOf="@+id/title_bar_more"
android:layout_centerVertical="true"
android:src="@drawable/title_bar_histroy"
android:paddingTop="@dimen/title_bar_add_padding"
android:paddingBottom="@dimen/title_bar_add_padding"
android:paddingLeft="@dimen/title_bar_histroy_right"
android:paddingRight="@dimen/title_bar_histroy_right"
android:visibility="gone"
/>
</RelativeLayout>
footer_bottom_bar.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="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_alignParentBottom="true">
<com.ashokvarma.bottomnavigation.BottomNavigationBar
android:id="@+id/footer_bottom_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:bnbBackgroundColor="@color/color_bottom_layout_bg"
app:bnbMode="mode_fixed"
app:bnbInactiveColor="@color/bottom_text_color"
app:bnbActiveColor="@color/colorAccent">
</com.ashokvarma.bottomnavigation.BottomNavigationBar>
</RelativeLayout>
主页布局主要包括三个部分。
- 自定义标题栏:包括标题以及增加、刷新等按钮,统一使用一个
LinearLayout
实现 - 主页内容:使用一个
viewpager
,每个页面用一个fragment
来展现,之后要修改界面只需修改fragment
的布局即可。 - 底部导航栏:使用
BottomNavigationBar
控件,控件中包含5个BottomNavigationItem
,用来控制5个不同页面的切换。
页面frament实现
本次5个页面fragment
的实现比较简单(后续会扩展),只是获取MainActivity
传过来的fragment name
,并显示在中间。
定义一个fragment
使用的布局,以HomeFragment
为例,其余类似。
<?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="match_parent"
android:background="@color/white"
android:orientation="vertical">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textColor="@color/colorPrimary"
android:textSize="22sp" />
</RelativeLayout>
新建fragment
对象时,将需要传递的参数通过fragment.setArguments()
方法传递进来。
//将name传递给fragment
public static HomeFragment newInstance(String name) {
Bundle bundle = new Bundle();
bundle.putString(BUNDLE_NAME, name);
HomeFragment fragment = new HomeFragment();
fragment.setArguments(bundle);
return fragment;
}
然后在调用fragment onCreate()
方法时,解析出name,设置到textview
当中。
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivity = getActivity();
Bundle bundle = getArguments();
if (bundle != null) {
mFragmentName = bundle.getString(BUNDLE_NAME);
}
Log.d(TAG, "[onCreate] - " + mFragmentName);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "onCreateView - " + mFragmentName);
view = inflater.inflate(R.layout.fragment_home, container, false);
TextView content = (TextView) view.findViewById(R.id.tv);
content.setText(mFragmentName);
content.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(getActivity(), LoginActivity.class));
}
});
return view;
}
BottomNavigationBar实现
BottomNavigationBar就是一个Material风格的底部导航栏,用于实现多个底部tag的切换效果,具体实现步骤如下。
第一步,在布局文件中配置
<com.ashokvarma.bottomnavigation.BottomNavigationBar
android:id="@+id/footer_bottom_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:bnbBackgroundColor="@color/color_bottom_layout_bg"
app:bnbMode="mode_fixed"
app:bnbInactiveColor="@color/bottom_text_color"
app:bnbActiveColor="@color/colorAccent">
</com.ashokvarma.bottomnavigation.BottomNavigationBar>
其中:
- app:bnbMode=”mode_fixed”为显示文字,使用静态效果
- app:bnbInactiveColor=”@color/bottom_text_color”为设置未选中文字颜色
- app:bnbActiveColor=”@color/colorAccent”为设置选中文字颜色
第二步,初始化BottomNavigationBar的图片和文字
private final String[] mTitles = new String[]{"首页", "巡店", "拜访", "培训", "个人中心"}; //标题列表
/** 底部图标资源-未选中状态 **/
private final int[] mIconNormalRes = new int[]{R.drawable.menu_home_normal, R.drawable.menu_shop_normal,
R.drawable.menu_visit_normal, R.drawable.menu_train_normal, R.drawable.menu_me_normal};
/** 底部图标资源-选中状态 **/
private final int[] mIconPressedRes = new int[]{R.drawable.menu_home_press, R.drawable.menu_shop_pressed,
R.drawable.menu_visit_pressed, R.drawable.menu_train_pressed, R.drawable.menu_me_pressed};
private List<BaseFragment> mFragments = new ArrayList<>(); //页面fragment列表
private List<BottomNavigationItem> mNavigationItems = new ArrayList<>(); //底部item列表
//初始化bottombar相关数据
for (int i = 0; i < mTitles.length; i++) {
BottomNavigationItem item = new BottomNavigationItem(mIconPressedRes[i], mTitles[i]);
Drawable drawable = getResources().getDrawable(mIconNormalRes[i]);
item.setInactiveIcon(drawable); //定制item未选中图片
mNavigationItems.add(item);
}
第三步,初始化BottomNavigationBar view,展现底部导航效果
mBottomNavigationBar = (BottomNavigationBar)findViewById(R.id.footer_bottom_bar);
for (BottomNavigationItem item : mNavigationItems) {
mBottomNavigationBar.addItem(item);
}
mBottomNavigationBar.initialise();
mBottomNavigationBar.setFirstSelectedPosition(0);
setTitleName(mTitles[0]);
第四步,经过前三步,已经实现了展现的效果,这一步主要是监听item项的切换,完成标题栏和页面fragment的刷新。
mBottomNavigationBar.setTabSelectedListener(new BottomNavigationBar.SimpleOnTabSelectedListener() {
@Override
public void onTabSelected(int position) {
//未选中->选中
mViewPager.setCurrentItem(position);
refreshHeaderView(position);
}
});
viewpager实现
前面已经实现了页面布局及fragment的创建,接下来就要要将fragment加到viewpager中。
第一步,初始化viewpager所需的fragment实例
private List<BaseFragment> mFragments = new ArrayList<>(); //页面fragment列表
BaseFragment home = HomeFragment.newInstance("首页界面");
mFragments.add(home);
BaseFragment shop = ShopFragment.newInstance("巡店界面");
mFragments.add(shop);
BaseFragment visit = VisitFragment.newInstance("拜访界面");
mFragments.add(visit);
BaseFragment train = TrainFragment.newInstance("培训界面");
mFragments.add(train);
BaseFragment me = MeFragment.newInstance("个人中心");
mFragments.add(me);
第二步,新建一个FragmentPagerAdapter对象,设置viewpager.setAdapter()方法初始化adapter对象
private FragmentPagerAdapter mFragmentPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments.size();
}
};
mViewPager = (ViewPager)findViewById(R.id.view_pager_content);
mViewPager.setAdapter(mFragmentPagerAdapter);
mViewPager.setCurrentItem(0);
第三步,通过viewpager.addOnPageChangeListener()方法监听页面切换,更新标题及底部item
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
Log.d(TAG, "onPageSelected - " + position);
mBottomNavigationBar.selectTab(position);
refreshHeaderView(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
/**
* 更新当前页面的header view
* @param position
*/
private void refreshHeaderView(int position) {
setTitleName(mTitles[position]);
switch (position) {
case 1:
mTitleAddImage.setVisibility(View.VISIBLE);
mTitleRefreshImage.setVisibility(View.VISIBLE);
break;
case 2:
mTitleAddImage.setVisibility(View.VISIBLE);
mTitleRefreshImage.setVisibility(View.INVISIBLE);
break;
default:
mTitleAddImage.setVisibility(View.INVISIBLE);
mTitleRefreshImage.setVisibility(View.INVISIBLE);
break;
}
}
附录
快速开发android应用
相关的代码都会更新在我的github上,大家可以通过star来跟进项目代码的变动
https://github.com/youyutorch/RapidDevAndroid
参考资料: