这篇就够了

2017安卓主流框架搭建?看这篇就够了(上)

2017-09-21  本文已影响0人  伦子汪不造轮子

构建顺序:

1.常用基础工具类(包括File,Bitmap,字符串处理,图片加载等,UI辅助等等)
2.基类BaseActivity,BaseFragment的构建(titlebar,statebar,loadingview,defalutview四大模块的封装)
3.屏幕适配(图片,长度)
4.mvp模式(使用MVPPluge插件,自动生成MVP的类文件以及该插件的改装)
5.网络框架的接入(RXJava2+Retrofit2.0)


6.gradle多环境配置(测试,预发布,正式)
7.CI自动化打包上传(jenkins+git+码云or蒲公英)

*其中1,2,3跟业务逻辑关系不大,我抽取出来作为一个lib,下载过去直接应用就行。

本次系列文章,本人一改之前懒得传代码的尿性,完整的demo项目地址将会在系列结束后贴上(目前还在整理中),恩,拿去就用!你值得clone


一.常用工具类清单:

Base64Util:图片文件与Base64互转。
BaseBitmapUtil:处理图片的压缩,缩放,裁剪,旋转,并且含有代码创建一些Drawable XML的方法
BaseConstant:定义一下常量
BaseFileUtil:文件的常用操作方法
BasePackageUtil:获取包信息,检查包名应用是否安装等pack相关方法
BaseStringUtil:常规例如:手机号,邮箱等的正则检验,全半角转换,等字符串处理方法
DateUtil:给我时间戳~给你各种日期,时间
KeyboardHelper:专业处理软键盘遮挡,软键盘隐藏,显示等
NetworkUtil:获取网络状态等方法
PreferUtil:专业处理shareperder数据
UiUtil:.提供常见的获取屏幕宽高、获取各种资源等方法,px dp转换,提供延时处理的Handle
这部分没啥好说,都是一些常用的方法,开发必备,当然也不那么完善,根据需求,后期再添加


二.(重点来了)基类BaseActivity,BaseFragment的构建:

一个功能完善,封装优雅的基类无疑可以很大程度上减少重复代码,使得开发时可以专注于业务逻辑,而不是在什么导航栏啊,缺省页啊,加载框啊之类的东西上反复花时间!BaseActivity里面封装了titlebar,statebar,loadingview,defalutview四大模块,开发时界面Activity的相关的UI直接几行代码配置即可,十分方面。
且BaseFragment里面会获取父容器activity,然后直接复用BaseActivity的各种方法。既然是重点,那么下面就来详细讲讲四大模块的封装。

TitleBar

导航栏,toolbar有自身的一些缺陷,还是感觉不够灵活,所以自己封装一个公用的TitleBar是很有必要的。
封装过程:
1.画一个导航栏布局layout,明确导航栏的基本组成,这里我是直接封装了三个Textview,由于Textview有一个drawableX属性,这就使得每个Textview既能做纯文本,又能图文混合, 左右中,三个Textview,基本够用了。

2.定义一个BaseTitleBar接口,里面定义好常用方法:

 /*设置整体背景色*/
BaseTitleBar setBgColor(int color);

/*标题栏相关*/
BaseTitleBar setTitle(@StringRes int StringResId);

BaseTitleBar setTitle(String text);

BaseTitleBar setTitleIcon(@DrawableRes int drawableId);

BaseTitleBar setTitleTextColor(int color);


/*右侧文本或按钮相关*/
BaseTitleBar setRightText(String text);

BaseTitleBar setRightText(@StringRes int stringResId);

BaseTitleBar setRightIcon(@DrawableRes int drawableId);

BaseTitleBar showRightTextView();

BaseTitleBar hideRightTextView();

BaseTitleBar setRightTextColor(int color);

BaseTitleBar setRightTextClickListener(View.OnClickListener listener);


/*左侧文本或按钮相关*/
BaseTitleBar setLeftText(String text);

BaseTitleBar setLeftText(@StringRes int stringResId);

BaseTitleBar setLeftIcon(@DrawableRes int drawableId);

BaseTitleBar showLeftTextView();

BaseTitleBar hideLeftTextVeiw();

BaseTitleBar setLeftTextColor(int color);

BaseTitleBar setLeftTextClickListener(View.OnClickListener listener);

int getId();

OK,机智如你,一看这些方法名字就懂了吧。

3.创建TitleBar,继承FrameLayout,实现BaseTitleBar,具体的代码就不贴了,之后自己看。

StateBar

关于状态栏,网上有太多教程,太多方法,太多框架了,这里针对SDK>Build.VERSION_CODES.KITKAT,统一隐藏状态栏,然后建个StateBar去覆盖,StateBar的颜色自定义,还可以隐藏,效果感觉还不错,就不去使用框架了,如果您对状态栏要求比较高,一定要多种效果,一定要适配侧滑等等 那你可以略过StateBar,自己去封装下就好。
封装过程:
1.定义接口BaseStateBar,明确需要提供的方法

void hide();

void show();

void setBackgroundColor(int color);

void setBackgroundDrawable(@DrawableRes int resId);

View getView();

int getId();

boolean isEnabled();

2.创建StateBar,实现BaseStateBar接口

至于具体在BaseActivity中如何去初始化StateBar,可以详见BaseActivity代码

Loadingview

终于到了Loadingview,想想还有点小激动!因为这次,再也不用gif,不用帧动画,不同一张破图旋啊转,用上了大名鼎鼎的lottie,然如您还没有听说过或者使用过lottie(好low啊,掩面偷笑中..)可以看看这篇简单的介绍,内有大量免费炫酷示例,down一下就进自己的app了,,这X装的豪不费功夫有木有?
http://www.jianshu.com/p/15c18049f642

LoadingView的封装相对简单,画个xml,在baseactivity中提供两个方法show,hide 你懂的,重点就是引入了lottie,炫酷不止一点点~打了好多字,这里放一段demo里用lottie实现的启动动画来缓解一下木有图的尴尬吧!

SM-G9350_20170920211104.gif

缺省页Defaultview

总结起来,app中的缺省页其实无外乎以下几种:
1.无网络缺省页面
2.网络请求错误缺省页
3.空数据缺省页
另外除了缺省页有时候只是toast一下,并不需要缺省页,具体如何,得看业务,得听产品的!哈哈
这里针对最为复杂的情况做一下封装,其他简单情况自然好处理
复杂情况:

进入页面一瞬掉咔嚓断网,显示带有““”刷新看看”按钮的无网络缺省页
点击刷新看看请求网络后服务错抛出错误,显示网络错误缺省页,并且带有按钮“再试试"
点击再试试,请求正常了,可是没有业务数据,显示空数据缺省页,带有按钮“XXXX”
点击按钮XXXX,响应别的业务逻辑

思路有两种:
1.多个defaultview实例,分别控制各个view的层级,显示状态
2.一个实例,根据需要动态变换view中的文本,按钮,图片
对比一下就明白,思路1处理起来会比较麻烦,万一有更复杂的情况需要继续添加不同的defaultview
所以这次的框架中采用思路2,一个defaultview,各种变换!

封装过程和loading大同小异,只不过提供的方法会多一些,三个核心view
图片Image,文本Textview,按钮Textview

BaseActivty中需要封装的四个模块已经分析完,那么他们如何封装进BaseActivity里呢?
大家都知道,ViewGroup有个Addview方法可以添加子类,那么在BaseActivity中设置一个根RelativeLayout,初始化添加StateBar和TitleBar后,将子界面的contentview添加到TitleBar下方:

  //mContainer为根RelativeLayout
  mContainer.addView(view, getLayoutParams());
  private RelativeLayout.LayoutParams getLayoutParams() {
    RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    if (titleBar != null) {
        lp.addRule(RelativeLayout.BELOW, titleBar.getId());
    } else if (stateBar.isEnabled()) {
        lp.addRule(RelativeLayout.BELOW, stateBar.getId());
    }
    return lp;
}

而LoadingView和DefaultVie在需要的时候直接addView进根RelativeLayout,不用addrule。
到此为止,BaseActivity的封装基本完成。至于BaseFragment中就更简单了,除了DefultView之外,其他的地方直接:

         public void XXXXX() {
          BaseActivity baseActivity = getBaseActivity();
          if (baseActivity != null) {
               baseActivity.XXXXX();
       }
  }

意思就是任何BaseActivity里面封装过调度UI的方法在BaseFragment里直接通过获取父容器Activity后复用一下。 可以是对于DefaultVie却不能这么用,这是为神马呢?哈哈,留个课后习题,欢迎留言里回答~


三.屏幕适配

网上关于anroid适配的文章太多太多,这里就不去复制了,直接说简单处理方法

适配图片:使用mipmap系列文件夹放图片,mipmap系统会在缩放上提供一定的性能优化,

让UI切一套720P的图(或者用ios 750的,如果UI太懒,你又搞不定他),放入mipmap-xhdpi文件夹 这一套其实就够了,不同分辨率手机上系统会自动去缩放,如果担心高分辨率图片变的模糊可以再适配一套xxx的,反正我的S7edge上基本看不出差别。

长度适配:
多dimens.png

如图,简单解释一下,w300dp表示手机 分辨率和手机屏幕密度经过计算后得出该手机宽度300dp
框架中设置了300-420范围的宽度文件夹,绝对涵盖了96%+主流的手机宽度。

像素px =dp* (屏幕密度/160)

一个720px的手机,如果屏幕密度是320,那么他的宽度用dp表示就是360dp,会使用w360里面的dimen,而点开W360里面的deimens看一下

W360.png

我们设置的dp1正好也是1dp,那么如果UI按照720P给你标注,你直接按标注的px除以2用dp就好。
你可能会问,如果我的手机不是W360的呢? 例如去年的机皇S7edge:

S7edge.png

按照公式像素px =dp* (屏幕密度/160)
算出 S7的屏幕宽度是1440/(534/160)= w431.46

系统会根据手机的宽度去选择接近的尺寸文件夹(听说是向下取,431的手机还是会用w420,不会用w440?未实测哦),如果UI按照720P给你标注一个头像Image的宽度是100px, 你还是除以2,用50dp

   <ImageView         
       android:layout_width="@dimen/dp50"
        android:layout_height="@dimen/dp50"
      />

注意,这时候是W420文件夹里的dp50哦,同样 打开看看

w420.png

可以看到,同样取dp50,这时候设置的是58.3, 然后你再算算
58.3/50 是不是 约= 420/360
至于明明是w431,可是取的是w420,或者一个w359的设备向下取到w340怎么办?

joke.png

其实不用太纠结,正常设备宽度350+,359和340差了屏幕宽度的1/17,也就是说如果50dp最多相差3dp,基本可以忽略。如果你不能接受这个说法,那么我会继续说服你,359-340也是极端情况了,哪里去找正好359的设备呢? 如果你还不服,那你去建立一个w350,甚至w355的吧,误差可以控制在1dp。
如果你真的打算这么做的话,那你一定是处女座!处女座!!

truth.png

处女座追求完美也没有错,至于w350,甚至w430+的dimens怎么产生的 其实很简单,网上有脚本,找来跑一下,或者直接一个for循环啊,比如wXXX的

  for(int i=1:i<500;i++){
    float value=XXX/360*i;
    Logger.d("<dimen name="dp"+i+">"+value+"dp</dimen>")

}

好了,篇幅原因,上篇到此为止,可能文中有些不准确或者错误的地方欢迎指出,大家一起进步!下个月25号见。

上一篇下一篇

猜你喜欢

热点阅读