一、前言
随着混合开发、android快应用热更新、以及微信的小程序开发的发展,虽然苹果官方极力限制iOS开发中使用混合开发和热更新,但是隐隐感觉iOS原生开发的重要性在下降,像诺基亚一样短时间内被新技术迅速击垮也不是不可能的。对比iOS先简单介绍一下android中的一些关键词
- Activity类似Controller
- Fragment类似UIView
- Dao类似Model
暂时可以这么认为,但是要知道差别还是挺大的,例如Fragment比iOS中UIView强大的多,有自己的生命周期方法,说其相当于Controller也不过分,但是现在我们不深究,为了降低入门的学习成本。Android中有MVC,但是好像比较落后了,MVP使用比较多,这里也暂时不谈论,我们还是参照iOS使用MVC,Activity相当于C,Fragment相当于V,Dao相当于M,先入门写出来一个完整app,其余的以后优化。Android有一些特点和iOS是有区别的,例如Android不一定有返回按钮因为Android手机有实体返回按键,例如Android的标题可能不在头部中间而是紧挨着返回键,为了降低学习成本这里我们都按照iOS风格来开发。
二、整体思路
在iOS中Controller有一个父类,称之为BaseController,同样android中也有这个父类,称之为BaseActivity,那么在这个父类中要实现那些功能呢?分为三大部分:
- 头部的NavigationBar要可以实现自身的显示和隐藏,创建和隐藏左侧返回按钮,创建和隐藏右侧按钮,添加中间部分的标题,将背景延伸到屏幕顶部Statusbar实现NavigationBar和StatusBar融为一体的效果,现在很多手机都是不规则的屏幕,如果不融为一体那么头部会出现很宽的状态栏,不协调。
- 中间的内容显示区域最重要的是要能实现隐藏底部的Tabbar和头部的NavigationBar时内容要能跟着向上和向下延伸。
- 底部的Tabbar能创建多个按钮以及实现点击不同按钮切换到不同的功能模块,可以实现显示角标的功能,配合推送使用
我的整体实现是新建BaseActivity类布局文件使用RelativeLayout,Android中有很多中布局为什么我选择使用RelativeLayout呢??,很简单,因为其他的我不会啊,Android中有至少6中布局,想要不实际开发功能而掌握这些布局是很难的,所以我使用我比较熟悉的RelativeLayout,没准随着我的深入理解我会改用其他的layout,那是后话。
三、屏幕头部NavigationBar和StatusBar的实现
上面我们确定了整体布局使用RelativeLayout下面我们往这个父类中添加头部NavigationBar和StatusBar,要实现NavigationBar和StatusBar融为一体效果那么背景使用同一个view
- 头部使用一个透明的view填充StatusBar
- 中间view作为NavigationBar来使用,可以添加左右按钮和中间标题
- 底部还可以插入一个宽度为1的View作为NavigationBar底部分割线
代码中看到总体高度设置为0,这是因为Android手机种类繁多,StatusBar高度不确定,所以在布局文件中不能确定StatusBar的具体高度,需要在java类中来更新高度值,背景view的高度为获取的StatusBar高度加上NavigationBar的高度。
//整个navigationBar和statusBar的背景视图,实现navigationBar和statusbar连为一体的效果
navBackViewLayout = (RelativeLayout)findViewById(R.id.navigationBarBackView);
//填充statusbar背景的view 设置为透明色
statusBarBackView = (View)findViewById(R.id.navigationBarTopClearView);
//navigationBar的背景布局
navLayout = (RelativeLayout)findViewById(R.id.navigationBar);
//navigationBar中间的标题
titleTextView = (TextView)findViewById(R.id.nav_text_title); //标题
//navigationbar左侧按钮 (返回按钮,可设置图片和文字)
leftBtn = (Button)findViewById(R.id.button_backward);//返回按钮
//navigationBar右侧按钮
rightBtn = (Button)findViewById(R.id.button_forward);//右侧按钮
//导航栏分割线
navSepLine = (View)findViewById(R.id.nav_bottom_line);
@Override
public void onClick( View v ) {
if (v.getId()==R.id.button_backward){//返回按钮
leftBtnDidClicked(v);
}else if (v.getId()==R.id.button_forward){//右侧按钮
rightBtnDidClicked(v);
}
}
/**
* 点击左侧按钮的响应方法,在子类中做具体操作
* @param view
*/
public void leftBtnDidClicked(View view){
}
/**
* 点击右侧按钮的响应方法 在子类中做具体操作
* @param view
*/
public void rightBtnDidClicked(View view){
}
/**
* 显示navigationbar上的title信息
* @param title
*/
public void createTitle(String title){
this.titleTextView.setText(title);
}
/**
* 显示返回按钮
*/
public void createBackBtn(){
leftBtn.setVisibility(View.VISIBLE);
}
/**
* 设置显示/隐藏navigationbar
* @param isShow true显示 false隐藏
*/
public void isShowNavigationBar( boolean isShow ){
if (navBackViewLayout!=null){
if (isShow){ //显示navigationbar
navBackViewLayout.setVisibility(View.VISIBLE);
}else {//隐藏navigationbar
navBackViewLayout.setVisibility(View.GONE);
}
}
}
根据实际设备动态修改statusbar背景填充区域高度,使得navigationbar能正确显示
/**
* 根据实际情况适配navigationBar
*/
private void initStatusBar(){
//让布局扩展到statusbar后面
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
Class c = null;
int statusBarHeight = 0;
try {
c = Class.forName("com.android.internal.R$dimen");
Object obj = c.newInstance();
Field field = c.getField("status_bar_height");
int x = Integer.parseInt(field.get(obj).toString());
statusBarHeight = this.getResources().getDimensionPixelSize(x);
Log.d("状态栏绝对高度为(单位px)", String.valueOf(statusBarHeight));
} catch (Exception e) {
e.printStackTrace();
statusBarHeight = 20;
}
//设置状态栏背景填充高度
ViewGroup.LayoutParams layoutParams = statusBarBackView.getLayoutParams();
layoutParams.height = statusBarHeight;
statusBarBackView.setLayoutParams(layoutParams);
ViewGroup.LayoutParams navLayoutPara = navBackViewLayout.getLayoutParams();
navLayoutPara.height = navLayout.getLayoutParams().height + statusBarHeight + navSepLine.getLayoutParams().height;
navBackViewLayout.setLayoutParams(navLayoutPara);
}
四、Tabbar的实现
和NavigationBar类似,作为BaseActivity的一部分放到整个页面的底部,高度可以直接确定,逻辑简单了不少
在java类中使用配合下面的内容一块看,因为BottomBar和中间显示区域有联动
五、内容区域
中间内容显示区域使用Fragment来实现,要在java代码中动态添加Fragment,那么布局文件应该使用FrameLayout
在BaseActivity类中使用
/**
* 给中间内容显示区域赋值
* @param fragment
*/
public void setContentFragment( Fragment fragment ){
if (fragment != null){
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container,fragment)
.commit();
}
}
首页要实现点击tabbar按钮切换功能模块,新建一个Fragment添加一个左右滚动控件CustomerViewPager,继承自ViewPager类,ViewPager无法实现关闭左右滑动切换功能模块的功能,这里使用子类,具体代码见demo
在Fragment的java类中使用
@Override
public View onCreateView( LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState ) {
contentView = inflater.inflate(R.layout.fragment_tabbar, container, false);
//viewPager
viewPager = (CustomerViewPager)contentView.findViewById(R.id.viewPager);
bottomBarTab1 = new BottomBarTab(getContext(),R.mipmap.customer_unselected,R.mipmap.customer,"宝贝");
bottomBarTab2 = new BottomBarTab(getContext(),R.mipmap.message,R.mipmap.message_selected,"消息");
bottomBarTab3 = new BottomBarTab(getContext(),R.mipmap.mine,R.mipmap.mine_selected,"我的");
CustomerFragment customerFragment = new CustomerFragment();
MessageFragment messageFragment = new MessageFragment();
MineFragment mineFragment = new MineFragment();
List mListFragmentEntity = new ArrayList();
mListFragmentEntity.add(getFragmentEntity(customerFragment,"CustomerFragment"));
mListFragmentEntity.add(getFragmentEntity(messageFragment,"MessageFragment"));
mListFragmentEntity.add(getFragmentEntity(mineFragment,"MineFragment"));
viewPager.setAdapter(new MyFragmentAdapter((TabbarActivity)getContext(),mListFragmentEntity));
viewPager.setCurrentItem(0);
viewPager.setCanScroll(false); //不要左右滚动
viewPager.setOffscreenPageLimit(3);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled( int i, float v, int i1 ) {
}
@Override
public void onPageSelected( int i ) {
for (int j = 0; j < ((TabbarActivity) getContext()).getBottomBar().getBottomBarTabs().size(); j++){
((TabbarActivity) getContext()).getBottomBar().getBottomBarTabs().get(j).setSelected(false);
}
((TabbarActivity) getContext()).getBottomBar().getBottomBarTabs().get(i).setSelected(true);
}
@Override
public void onPageScrollStateChanged( int i ) {
}
});
((TabbarActivity) getContext()).getBottomBar().addItem(bottomBarTab1).addItem(bottomBarTab2).addItem(bottomBarTab3);
((TabbarActivity) getContext()).getBottomBar().setOnTabSelectedListener(new BottomBar.OnTabSelectedListener() {
/**
* 点击tab时调用(点击的和之前选中的不是同一个时执行)
* @param position 当前选中的索引
* @param prePosition 被取消的索引
*/
@Override
public void onTabSelected( int position, int prePosition ) {
Log.d("onTabSelected","position="+position+"prePosition="+prePosition);
viewPager.setCurrentItem(position);
switch (position){
case 0:
((TabbarActivity) getContext()).createTitle("客户");
((TabbarActivity) getContext()).isShowNavigationBar(true);
break;
case 1:
((TabbarActivity) getContext()).createTitle("消息");
((TabbarActivity) getContext()).isShowNavigationBar(true);
break;
case 2:
((TabbarActivity) getContext()).createTitle("我的");
((TabbarActivity) getContext()).isShowNavigationBar(true);
break;
}
}
/**
* tab取消选中(点击的和之前选中的不是同一个时执行)
* @param position 取消选中tab的索引值
*/
@Override
public void onTabUnselected( int position ) {
Log.d("onTabUnselected","position="+position);
}
/**
* 两次点击同一个tab时调用
* @param position 点击tab的索引
*/
@Override
public void onTabReselected( int position ) {
Log.d("onTabReselected","position="+position);
}
});
//设置默认选中的值
((TabbarActivity) getContext()).getBottomBar().getBottomBarTabs().get(0).setSelected(true);
// Inflate the layout for this fragment
return contentView;
}
到这里BaseActivity新建完成,需要注意的是
android:layout_alignWithParentIfMissing="true"
当所以来的控件为空时那么以父试图为准,设置其所依赖的视图
android:visibility="gone"
可以实现隐藏和显示NavigationBar和Tabbar功能
demo地址
https://github.com/jzglovewjr/crmandroidj