一、前言
随着混合开发、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,那是后话。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
tools:context=".base.activity.BaseActivity"
android:id="@+id/main_root_layout"
>
</RelativeLayout>
三、屏幕头部NavigationBar和StatusBar的实现
上面我们确定了整体布局使用RelativeLayout下面我们往这个父类中添加头部NavigationBar和StatusBar,要实现NavigationBar和StatusBar融为一体效果那么背景使用同一个view
- 头部使用一个透明的view填充StatusBar
- 中间view作为NavigationBar来使用,可以添加左右按钮和中间标题
- 底部还可以插入一个宽度为1的View作为NavigationBar底部分割线
<?xml version="1.0" encoding="utf-8"?>
<!--
具体layout_height在java类中获取statusbar高度之后进行设置
layout_height的值为navigationbar告诉加上statusBar高度
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navigationBarBackView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@mipmap/nav_back"
>
<!--填充状态栏背景部分 默认和navigationbar连为一体 所以设置为透明色-->
<View
android:id="@+id/navigationBarTopClearView"
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="#00000000"
/>
<RelativeLayout
android:id="@+id/navigationBar"
android:layout_width="match_parent"
android:layout_height="58dp"
android:layout_below="@+id/navigationBarTopClearView"
android:layout_above="@+id/nav_bottom_line"
android:background="#00000000"
>
<!--标题-->
<TextView
android:id="@+id/nav_text_title"
android:text="标题"
android:textSize="18dp"
android:textColor="#fff"
android:gravity="center"
android:singleLine="true"
android:ellipsize="marquee"
android:layout_width="100dp"
android:layout_centerInParent="true"
android:layout_height="wrap_content" />
<!--返回按钮-->
<Button
android:id="@+id/button_backward"
android:layout_width="70dp"
android:layout_height="match_parent"
android:drawableLeft="@mipmap/tm_nav_back"
android:drawablePadding="6dp"
android:layout_marginLeft="15dp"
android:background="#00000000"
android:ellipsize="end"
android:gravity="center"
android:onClick="onClick"
android:paddingLeft="5dp"
android:singleLine="true"
android:text=""
android:textColor="#ffffff"
android:textSize="15dp"
android:visibility="invisible" />
<!--右侧按钮-->
<Button
android:id="@+id/button_forward"
android:layout_width="70dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:drawablePadding="6dp"
android:background="#00000000"
android:ellipsize="end"
android:gravity="center"
android:onClick="onClick"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="搜索"
android:textColor="#ffffffff"
android:textSize="15dp"
android:visibility="invisible" />
</RelativeLayout>
<!--导航栏底部分割线-->
<View
android:id="@+id/nav_bottom_line"
android:layout_height="1px"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:background="#C7C7C7"/>
</RelativeLayout>
代码中看到总体高度设置为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的一部分放到整个页面的底部,高度可以直接确定,逻辑简单了不少
<!--tabbar布局-->
<RelativeLayout
android:id="@+id/bottomBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
>
<!--tabbar上边的分割线-->
<View
android:id="@+id/bottomTopLineView"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#C7C7C7"
/>
<com.example.jizhigang.crm_android_j.base.widge.BottomBar
android:id="@+id/bottomBar"
android:layout_below="@+id/bottomTopLineView"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#ffffffff"
android:layout_gravity="bottom"
/>
</RelativeLayout>
在java类中使用配合下面👇的内容一块看,因为BottomBar和中间显示区域有联动
五、内容区域
中间内容显示区域使用Fragment来实现,要在java代码中动态添加Fragment,那么布局文件应该使用FrameLayout
<!--android:layout_alignParentBottom="true"-->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/fragment_container"
android:layout_below="@+id/navigationBarBackView"
android:layout_above="@+id/bottomBarLayout"
android:layout_alignWithParentIfMissing="true"
android:background="#EFF3F6"
/>
在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
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".base.fragment.TabbarFragment">
<com.example.jizhigang.crm_android_j.base.widge.CustomerViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</com.example.jizhigang.crm_android_j.base.widge.CustomerViewPager>
</FrameLayout>
在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<FragmentEntity> mListFragmentEntity = new ArrayList<FragmentEntity>();
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功能
网友评论