写在最后
在技术领域内,没有任何一门课程可以让你学完后一劳永逸,再好的课程也只能是“师傅领进门,修行靠个人”。“学无止境”这句话,在任何技术领域,都不只是良好的习惯,更是程序员和工程师们不被时代淘汰、获得更好机会和发展的必要前提。
如果你觉得自己学习效率低,缺乏正确的指导,可以一起学习交流!
加入我们吧!群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
}
一旦有了这个基础适配器,实际业务的适配器即可由此派生而来,真正需要开发者编写的代码一下精简了不少。下面便是个循环视图的网格适配器,它实现了类似淘宝主页的网格频道栏目,具体的Kotlin代码如下所示:
//把公共属性和公共方法剥离到基类RecyclerBaseAdapter,
//此处仅需实现getItemCount、onCreateViewHolder、onBindViewHolder三个方法,以及视图持有者的类定义
class RecyclerGridAdapter(context: Context, private val infos: MutableList) : RecyclerBaseAdapter<RecyclerView.ViewHolder>(context) {
override fun getItemCount(): Int = infos.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view: View = inflater.inflate(R.layout.item_recycler_grid, parent, false)
return ItemHolder(view)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val vh = holder as ItemHolder
vh.iv_pic.setImageResource(infos[position].pic_id)
vh.tv_title.text = infos[position].title
}
inner class ItemHolder(view: View) : RecyclerView.ViewHolder(view) {
var ll_item = view.findViewById(R.id.ll_item) as LinearLayout
var iv_pic = view.findViewById(R.id.iv_pic) as ImageView
var tv_title = view.findViewById(R.id.tv_title) as TextView
}
}
然而基类不过是雕虫小技,Java也照样能够运用,所以这根本不入Kotlin的法眼,要想超越Java,还得拥有独门秘笈才行。注意到适配器代码仍然通过findViewById方法获得控件对象,可是号称在Anko库的支持之下,Kotlin早就无需该方法就能直接访问控件对象了呀,为啥这里依旧靠老牛拉破车呢?其中的缘由是Anko库仅仅实现了Activity活动页面的控件自动获取,并未实现适配器内部的自动获取。不过Kotlin早就料到了这一手,为此专门提供了一个插件名叫LayoutContainer,只要开发者让自定义的ViewHolder继承该接口,即可在视图持有者内部无需获取就能使用控件对象了。这下不管是在Activity代码,还是在适配器代码中,均可将控件名称拿来直接调用了。这么神奇的魔法,快来看看Kotlin的适配器代码是如何书写的:
//利用Kotlin的插件LayoutContainer,在适配器中直接使用控件对象,而无需对其进行显式声明
class RecyclerStaggeredAdapter(context: Context, private val infos: MutableList) : RecyclerBaseAdapter<RecyclerView.ViewHolder>(context) {
override fun getItemCount(): Int = infos.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view: View = inflater.inflate(R.layout.item_recycler_staggered, parent, false)
return ItemHolder(view)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as ItemHolder).bind(infos[position])
}
//注意这里要去掉inner,否则运行报错“java.lang.NoSuchMethodError: No virtual method _$_findCachedViewById”
class ItemHolder(override val containerView: View?) : RecyclerView.ViewHolder(containerView), LayoutContainer {
fun bind(item: RecyclerInfo) {
iv_pic.setImageResource(item.pic_id)
tv_title.text = item.title
}
}
}
还需要在模块的build.gradle增加:
androidExtensions {
experimental = true
}
上面采用了新的适配器插件,似乎已经大功告成,可是依然要书写单独的适配器代码,仔细研究发现这个RecyclerStaggeredAdapter还有三个要素是随着具体业务而变化的,包括:
1、列表项的布局文件资源编码,如R.layout.item\_recycler\_staggered;
2、列表项信息的数据结构名称,如RecyclerInfo;
3、对各种控件对象的设置操作,如ItemHolder类的bind方法;
除了以上三个要素,RecyclerStaggeredAdapter内部的其余代码都是允许复用的,因此,接下来的工作就是想办法把这三个要素抽象为公共类的某种变量。对于第一个的布局编码,可以考虑将其作为一个整型的输入参数;对于第二个的数据结构,可以考虑定义一个模板类,在外部调用时再指定具体的数据类;对于第三个的bind方法,若是Java编码早已束手无策,现用Kotlin编码正好将该方法作为一个函数参数传入。依照三个要素的三种处理对策,进而提炼出来了循环适配器的通用类RecyclerCommonAdapter,详细的Kotlin代码示例如下:
//循环视图通用适配器
//将具体业务中会变化的三类要素抽取出来,作为外部传进来的变量。这三类要素包括:
//布局文件对应的资源编号、列表项的数据结构、各个控件对象的初始化操作
class RecyclerCommonAdapter(context: Context, private val layoutId: Int, private val items: List, val init: (View, T) -> Unit): RecyclerBaseAdapter<RecyclerCommonAdapter.ItemHolder>(context) {
override fun getItemCount(): Int = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view: View = inflater.inflate(layoutId, parent, false)
return ItemHolder<T>(view, init)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val vh: ItemHolder<T> = holder as ItemHolder<T>
vh.bind(items.get(position))
}
//注意init是个函数形式的输入参数
class ItemHolder<in T>(val view: View, val init: (View, T) -> Unit) : RecyclerView.ViewHolder(view) {
fun bind(item: T) {
init(view, item)
}
}
}
有了这个通用适配器,外部使用适配器只需像函数调用那样传入这三种变量就好了,具体调用的Kotlin代码如下所示:
//第二种方式:使用把三类可变要素抽象出来的通用适配器
val adapter = RecyclerCommonAdapter(this, R.layout.item_recycler_staggered, RecyclerInfo.defaultStag,
{view, item ->
val iv_pic = view.findViewById(R.id.iv_pic) as ImageView
val tv_title = view.findViewById(R.id.tv_title) as TextView
iv_pic.setImageResource(item.pic_id)
tv_title.text = item.title
})
rv_staggered.adapter = adapter
最终出炉的适配器仅有十行代码不到,其中的关键技术——函数参数真是不鸣则已、一鸣惊人。至此本节的适配器实现过程终于落下帷幕,一路上可谓是过五关斩六将,硬生生把数十行的Java代码压缩到不到十行的Kotlin代码,经过不断迭代优化方取得如此彪炳战绩。尤其是最后的两种实现方式,分别运用了Kotlin的多项综合技术,才能集Kotlin精妙语法之大成。
[]( )7.2使用材质设计
-----------------------------------------------------------------------
MaterialDesign库提供了协调布局CoordinatorLayout,AppBarLayout,CollapsingToolbarLayout
### []( )协调布局CoordinatorLayout
继承自ViewGroup
对齐方式:layout\_gravity,
子视图位置:app:layout\_anchor?app:layout\_anchorGravity
行为:app:layout\_behavior
FloatingActionButton 悬浮按钮
悬浮按钮会悬浮在其他视图之上
隐藏和显示悬浮按钮时会播放切换动画,hide和show方法
悬浮按钮默认会随着便签条Snackbar的出现或消失动态调整位置
###工具栏Toolbar
Android 5.0之后使用Toolbar代替ActionBar
不过为了兼容老版本,ActionBar仍然保留,可是ActionBar和Toolbar都占着顶部导航栏的位置,所以想引入Toolbar就得关闭ActionBar,具体步骤如下:
1.定义一个style