Wakim's泛泛之谈


  • 首页

  • 分类

  • 归档

自定义View的一种使用场景

发表于 2019-04-09 | 分类于 Android

在我的SideProject中有这样一个场景,使用RecyclerView和CardView的组合实现一个展示列表,其中CardView有subItem,每个CardView的subItem个数可能不一样,subItem包括name和weight两个value,分别靠左、靠右对齐。效果如下:

1

用TextView结合String换行无法实现weight value靠右对齐。用CardView嵌套ListView又显得太重,还有性能问题。分析下来,更合理的做法是自定义一个View实现。

Android的自定义View控件可以分为三种:组合式、继承式、自绘式,这个场景适合用自绘式实现。分享下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

public class OptionsTextView extends View {

private TextPaint mContentPaint;

private List<RouletteModel.Item> mOptionList;

private float mLineSpace;

private int mSingleTextHeight;

private Rect mBounds;

private float mTextSize;

// spacing between name and weight
private float mSpacing;

public OptionsTextView(Context context) {
this(context,null);
}

public OptionsTextView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}

public OptionsTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

// 也可以自定义属性,在xml中传入
mLineSpace = getResources().getDimension(R.dimen.roulette_spacing_smaller);
mSpacing = getResources().getDimension(R.dimen.roulette_spacing_normal);
mTextSize = getResources().getDimension(R.dimen.roulette_font_normal);

mBounds = new Rect();

mContentPaint = new TextPaint();
mContentPaint.setTextSize(mTextSize);
mContentPaint.setAntiAlias(true);
mContentPaint.setColor(context.getResources().getColor(R.color.roulette_black));
mContentPaint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));

// 计算文字的高度
Paint.FontMetricsInt fm = mContentPaint.getFontMetricsInt();
mSingleTextHeight = Math.abs(fm.top - fm.bottom);
}

/**
* 这个自定义View作为RecyclerView的item,当复用时由于dataList的数据改变,高度可能有变化,
* 所以需要调用requestLayout,而不是invalidate,requestLayout会请求布局,invalidate只会重绘
*
* @return
*/
public void addOptionList(List<RouletteModel.Item> dataList) {
mOptionList = dataList;

requestLayout();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), (int)getViewHeight());
}

/**
* 根据OptionList计算View的高度
*
* @return
*/
private float getViewHeight() {
return mSingleTextHeight * mOptionList.size() + mLineSpace * (mOptionList.size() - 1) + getPaddingBottom() + getPaddingTop();
}

@Override
protected void onDraw(Canvas canvas) {
if (mOptionList == null) {
return;
}

for (int i = 0; i < mOptionList.size(); i++) {
// 先画右边的权重文字区域
String weight = String.valueOf(mOptionList.get(i).weight);
mContentPaint.setTextSize(mTextSize);
mContentPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(weight, getMeasuredWidth() - getPaddingRight(), getPaddingTop() + mSingleTextHeight * (i + 1) + mLineSpace * i, mContentPaint);

// 再画左边的选项文字区域
String name = mOptionList.get(i).name;
float totalWidth = mContentPaint.measureText(mOptionList.get(i).name + weight) + mSpacing;
// 如果超出总长度超出绘制区域宽度
if (totalWidth >= getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) {
// 算出选项文字区域可用的宽度,注意为了美观两者之间有个间距
mContentPaint.getTextBounds(weight, 0, weight.length(), mBounds);
float desiredWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - mBounds.width() - mSpacing;

mContentPaint.getTextBounds(name, 0, name.length(), mBounds);
float desiredTextSize = mTextSize * desiredWidth / mBounds.width();
mContentPaint.setTextSize(desiredTextSize);
}

mContentPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(name, getPaddingLeft(), getPaddingTop() + mSingleTextHeight * (i + 1) + mLineSpace * i, mContentPaint);
}
}
}

MVVM模式及响应式编程实践-搜索功能

发表于 2019-04-08 | 分类于 Android

前言

软件开发过程中为了提高代码的可维护性、可扩展性、减少依赖耦合,往往会遵循某种编程模式进行代码的编写。目前Android应用开发中较流行的编程模式有MVC、MVP、MVVM三种。

  • MVC模式会使得Activity/Fragment臃肿庞大,并且Controller和View耦合较深。
  • MVP模式虽然引入Presenter解耦了视图操作和业务逻辑,但是Presenter会持有View层的抽象接口,接口粒度不好把握,而且需要手动调用接口才能实现UI响应,还要关心View层对象是否被销毁。
  • MVVM模式引入了ViewModel的概念用来处理对应的View层的业务逻辑,并借助DataBinding技术实现视图和数据的双向绑定,这也正好解决了MVP模式的弊端。
    业务不复杂的时候MVC和MVP模式都可以满足。天猫进口App搜索功能的结果展示页包含排序、结果筛选、结果集,并且有较为复杂的用户交互过程,能否做好层次划分和模块组件化,直接影响到以后的扩展和维护成本。基于此,我采用了MVVM模式来实现。

设计

一个拥有搜索功能的APP在产品设计上一般包括以下三个方面:搜索入口、启动页、结果页。假设APP的搜索入口在首页的ActionBar位置,进入首页的时候需要从服务端请求底纹信息展示在搜索入口,底纹可以是运营干预词也可以是个性化推荐词。点击搜索入口即进入启动页,把底纹带入启动页。启动页可以接收用户的输入,还包含输入联想词列表、历史记录和热搜词推荐。结果页由排序栏、外透筛选栏、内置筛选、结果集列表组成。为了做到模态切换,分别用两个fragment来承载启动页和结果页,下面介绍下启动页和结果页的MVVM结构是如何设计的。

  • 启动页的MVVM结构:

1

Launcher VM包含了三个Child VM:Histroy VM、Popular VM、Fuzzy VM,这三个VM对应的View都允许用户操作跳往搜索结果页。为了做到VM对View层的无感知,各个Child VM负责操作Observer对象,LauncherFragment观察这个对象的属性变化并作出跳转的响应,这需要Launcher VM维护这个可观察的对象,并把它传给三个Child VM。

  • 结果页的MVVM结构:

2

Result VM主要包含:SearchBox VM、ResultList VM、SortTab VM、PreFilter VM、Filter VM,这五个Child VM,其中SortTab VM、PreFilter VM、Filter VM会持有Result VM的Request Observer。在与服务端定义搜索接口时,约定了服务端对排序类型和筛选项增加selected字段记录当前结果集的query选择了哪些条件,这样客户端就不用做本地状态保存,只要根据selected字段渲染组件的选中和非选中状态。在MVVM模式中,要把页面的各个模块组件化,每个组件有对应的VM,当用户有操作行为发生的时候,这些Child VM可以更改自己负责的请求参数,Request Observer观察到数据的变化会发起新的搜索请求,然后根据服务端返回的数据通知各个Child VM更新对应的View。

另外,两个页面都创建了不是用来做UI展示的Fragment持有页面的顶级VM,使得它不随着View组件生命周期的销毁而销毁。通过这样的模式设计,Fragment里的代码量较少,把业务逻辑规划到各个Child VM分而治之,并且这些Child VM是可进行单元测试的,也使得将来的功能扩展更加方便。

参考

DataBinding

Android消息推送的思考

发表于 2019-04-08 | 分类于 Android

消息推送是移动APP运营中的关键环节,然而对于面向国内的Android开发者,建设这个基础能力一直是个痛点加难点。虽然Google有推出GCM服务,但是由于Google服务在国内不稳定,加上国内各大厂商定制了ROM,阉割了Google服务,所以GCM在国内基本作废。各大厂商纷纷推出自家推送服务,小米、华为、Oppo、Vivo、魅族等都有,各种第三方推送服务也应运而生。各大厂商自家的推送服务进程在自己定制过的Android系统中,享有较高的优先级,因此稳定性不在话下。到了别的牌子的手机上就要想办法让推送服务的后台进程保活了。各种三方推送服务想尽一切办法让推送服务进程在后台包活,由此衍生了很多“黑科技”,遭殃的却是Android用户,造成了手机资源占用高,手机卡、费电等问题。

对于Android推送的方案,我的观点是宁愿降低消息到达率,也不耍流氓。在不用尽各种办法来保活后台进程的基础上最大限度的提高消息到达率。根据市场占有率分析,华为、小米、Oppo、Vivo占据大头,而这几家都有自己系统级的推送服务,所以接入他们的服务即可。这对海外用户,采用GCM的方案。站在运营人员的角度,希望抹平平台的差异,配置一份消息,就能够触到iOS和Android用户。考虑下来,需要自己起一个后端服务平台对接这些推送渠道(包括Apple的APNS)。这些渠道外的手机用户在静默状态近乎放弃,只能等到应用被打开之后,与服务器建立了长连接再推送。推送场景分为用户相关和用户无关。用户无关的全量推送可以按照服务器登记的所有deviceId进行下发。用户相关的推送则要根据服务器登记的最新的deviceId进行下发。假设用户1在A、B两台设备进行了登录,最近的一次登录是B设备,那么对于用户1来说用户相关的推送只要针对B设备进行下发。下发之前还要检查当前用户是否在线,当前在线则走应用内的长连接,不在线则判断是否拥有自主推送渠道,若有则发给对应的渠道下发消息,若无则存储这条消息等到下次用户在线走应用内的长连接下发。这种做法既能保证应用不占用过多的系统资源,还能保证推送服务的稳定性。不足之处是,没有自主推送渠道也没有GCM服务的手机在离线状态下,推送无法送达,不过这部分的市场占有率相对较低。

一个好消息是,工信部在2017年联合国内各大Android厂商和互联网企业成立了安卓统一推送联盟,想要解决这种乱象。时间已经到了2019年,期待尽快有个结果。让Android开发者把心力专注在开发产品本身,这才是提高用户粘性的根本。

CrossFit健身记

发表于 2019-02-28 | 分类于 运动

CrossFit是什么

CrossFit是近些年来发展最为迅猛的健身方式之一。燃,热血,拼到精疲力竭,这些强烈的氛围,让你欲罢不能。团队式训练,加上躁动的音乐,一组一组的动作限时循环,练完之后累趴在地上,这是其他健身房没有的,也是CF的魅力之一。CF馆的器械跟其他健身房有些差别,没有太多花样的各种铁具,没有那么多跑步机。也因此,CF馆不需要那么大的场地,不需要那么多的私教。CF馆必需的器械就几种,综合训练架、划船机、壶铃、哑铃、杠铃、跳绳、跳箱、拉力带、吊环、脚踏风车。这么看来,CF馆成本不大、但是卡费却不低。CF馆一般由教练开十人以内的团课。

术语介绍

Warm Up:热身
WOD:每天的训练计划(Workout Of the Day)
PR:个人单个项目的最好记录(Personal Record)
Rep:训练动作的个数(Repetation)
Snatch:抓举,一般指完整的抓举,需要全蹲去接杠,即臀部低于膝盖的位置接杠
Power Snatch:高位抓举,接杠位置在臀部高于膝盖
Muscle snatch:力量抓举或称为直腿抓举
Hollow:刚体保持,是一种体操的基础训练动作,将身体平躺,同时肩背和双脚离开地面,保持腹部、大腿、脚尖绷紧,同时双手靠着双耳,眼睛目视脚尖
Arch:俗称超人,与Hollow相反,将身体趴在地上,保持胸部和双脚离开地面,收紧臀部和后背,眼睛目视地面

Body 45

这是课程类型之一,偏向无氧训练。WOD有四个动作:Good Morging、俯卧支撑、跳箱、背部划船。每个动作做40秒休息20秒,共做8轮。

热身动作:双手背后握紧,做Good Morning鞠躬姿势,10次/轮,共3轮。可以加个拉力带套在肩膀,脚踩住。注意背不能弯,屁股向后撅,双脚与髋同宽。

Good Morging主要拉伸大腿后侧肌肉。双手握住哑铃,先站直再下蹲,膝盖微屈,背部不要弯弓,屁股向后,哑铃要过膝。到位之后再双脚蹬直站立,屁股要夹紧,双脚站距与髋同宽。

俯卧支撑,平行放两个哑铃在地上,双手撑住,手腕不弯曲,呈一条直线向上,双脚并拢。先撑一次再双脚打开,再两边手各一次提拉哑铃,分别靠两边的大臂和背部发力向后拉,过程中要保持核心收紧不塌腰。

跳箱的高度和速度决定难度。跳上去之后要挺直腰收紧屁股,再下来,如此反复。

背部划船,姿势同Good Morning,发力窍门同俯卧支撑的后拉。

职场上的大小考

发表于 2019-02-26 | 分类于 职场

前言

职场上每当年底的时候,公司会对员工进行全年的绩效考核,这种考核会决定员工当年的奖金和来年的升职加薪。有些公司还有半年考核,甚至是季度考核。绩效考核的时候一般有直属老板、老板的老板、HR、或者其他横向的老板参与。直属老板对员工的工作内容和表现是比较清楚的,但是其他人就不一定了。

考核主要看你Owner的项目结果好不好。你作为技术人员是通过什么技术手段,如何帮助项目取得了好的结果,要指出技术的难点和复杂性在哪里。而且好的结果还要考虑如何量化,如何做数据指标的对比,你做这项工作之前的数据和你接手之后的数据的直观比对,以此来打动评委。

如何在短时间内直观、有重点地向他们展示这一年的工作成果是很需要技巧的。“临时抱佛脚”在临近考核的时候匆匆忙忙做出不直观的PPT,或者Presentation的能力欠佳,又或者对解决的问题看得不够全面透彻、缺少独立的思考和见解,都会在考核过程中受到很多挑战。

所以在平时工作中就要做好总结、积累和练习。公司一般会要求员工写周报、双周报,甚至有的会要求写日报。日常工作中要重视这些总结,否则到了年底容易陷入不知如何做好这场“职场考试”的窘境中。下面我想从自己已经经历的三份工作中做一下这方面的总结,期待以后能够做得更好。

2012.08~2014.04

第一份工作是在厦门歌乐电子企业有限公司,日立集团旗下的一家子公司。在日常工作中引入了“PDCA管理循环”,要求员工写日报总结一天的工作,以邮件的形式发给主管以及组内成员。PDCA循环,即计划(Plan)、执行(Do)、检查(Check)、调整(Action)。这个原则要求对手头的各项工作先做出计划、设定目标,然后实施计划、记录具体步骤过程,之后要检查进度和效果,有哪些阻碍和困难,最后把完成的总结出来,没完成的放到下一个循环中去。我当时的做法是在Excel中制作PDCA的表格填写日报,年终总结的时候方便回顾。一般P和A部分每天的变化比较小,而D和C可写的内容比较多。

这份工作总共经历过两次绩效考核,考核的等级分为A、B、C、D。2012年8月到2013年1月是第一年,刚毕业工作中表现平平,拿到了C,那一年的绩效考核是我职场生涯的第一次考试。2013年1月至2014年1月是第二年,这一年做了一个比较成功的项目,但是年底Review的时候自我感觉并不好,当众演讲的时候很紧张,PPT也做得不够直观,这一年拿到了B。等到2014年3月提出辞职的时候,HR找我聊说到因为去年拿了B,所以今年在加薪方面也会比大多数人多,但是我那时候已经下定决心要转型做移动互联网,所以就没有留下了。

总结这份工作,那个时候对这种工作管理的方式理解的并不深,很多地方没有做到位,特别是在PPT制作方面和Presentation方面的能力也都因为锻炼得少而做得不够好。

2014.04~2016.09

第二份工作来到一家移动互联网创业公司,厦门幻世网络科技有限公司。因为是初创公司,所以这里有很多管理还不完善。也因为是创业公司,工作中引入了一些比较先进的协作方式。比如每天站立会沟通重要事项,采用敏捷开发方法小步快速迭代版本,把任务写在便签钉在墙上认领。日报也是要发的,但是没有严格的格式要求。年底没有绩效考核,只是普发十三薪(老板会根据员工的表现、贡献给予一定的期权)。在这里职场考核这方面的锻炼减少了,也就没有更多心得了。然而从2014年4月到2016年9月,在这里工作了将近两年半的时间,以至于刚去下一家公司的时候,对绩效考核的门道几乎是又从零开始摸索的。

值得一提的是,这份工作我专注在Android开发,特别是NDK开发,和iOS的哥们儿一起完成了一个跨平台移动开发框架,支撑了公司的移动端业务。

2016.09~至今

第三份工作到了阿里,这里无论是对周报的严格要求程度,还是对绩效考核的重视程度,都可以说是近乎“苛刻”。这是好事,对员工的个人成长相当有帮助,也有利于这么一个庞大组织的管理。

首先说周报,一线的员工要把周报发给整个技术部门的邮件大组,如果是项目PM的角色,还要抄送业务方。Team Leader除了自己的个人工作汇报,还要收集团队里成员的周报汇总发给老板,老板要汇总Team Leader的周报发给老板的老板。所以为了老板们能有足够的时间写周报,一线的同学往往每周四晚上就要把周报发出来了。一线同学要写的是做项目的过程中踩的坑、某个技术点的解决方案、甚至可以是代码的分享,还可以写个人的学习、收获了什么新知识。要及时思考,有感就发。如果是项目PM,还要写项目背景、项目目标、关键时间节点(提测时间、上线时间、接口方的时间点、里程碑时间点等等)、当前进度(正常还是延期、延期的原因、带来的风险)、技术难点如何克服、技术重点总结、跨部门沟通事项、项目数据(业务数据、性能数据)、分析数据波动的原因(为什么涨、为什么跌)、项目思考(为什么要做这个项目、要解决现在存在的什么问题、做了之后真的能解决吗、有没有自己的见解)。

再说绩效考核,初期要先做好规划,今年要支撑哪些业务、要做什么技术提升。一般绩效分为以下几个方面,质量效率、业务支撑、团队协作、个人成长,所占的比例有所侧重。年底的时候再对这些目标进行自评。但是往往会发现,在年初要做好这样的规划是很难的,因为变化永远在意料之外。绩效考核的一个重要环节就是Review,要准备PPT把这一年的工作成果写清楚。这时候就能体会周报的重要性了。如果没有每周的总结积累,想在年底回顾这三百多天的工作内容和成果,以及重新思考一遍技术方案,那就是临时抱佛脚,不会做得很好的。PPT里要体现一年做的重点项目,业务方的诉求是什么,前期有没有数据可以推导出做这件事的必要性,技术上用什么方案实现了需求,带来的结果数据如何,为什么做这些能带来数据的利好,如果结果不好又是因为什么,个人从中有没有得到什么经验,成长在哪里(业务理解能力、技术沉淀)。当然还要做下一年的规划,当前还存在什么问题,用自己的观点规划产品,抓住重点规划技术。另外一个重要的点是,PPT要做得简洁直观,做完要留有时间试讲,自己掐时间模拟现场讲出来,克服紧张的心理。每一页和每一页之间要有逻辑地衔接。我有两次讲完都被主管提出讲得让听众昏昏欲睡。意思是讲的不够有激情、不够有吸引力、没有跟台下有良好的互动。考核的最后一个环节,往往是会有人提出一些比较有挑战性的问题,这时候要不畏“强权”,勇于探讨,把自己独立的思考讲出来,让提问者知道自己不是一个被动的执行者。

除了在平时工作中向自己Owner的项目拿结果,如果能帮助团队招聘到新人,在老板那里也是很加分的。因为老板的老板往往会给他设定这方面的kpi。老板除了要招新人还要保障现有成员不要流失,否则老板在被考核管理能力这一项时会站不住脚。

最后

总结职场上的考核要注意的点有如下几个:

  • 平时要保质保量的完成交到手里的每个任务,争取拿到超出预期的结果。
  • 对工作要有系统性的思考,并且能逻辑清晰的表达。
  • 不会写文章的工程师不是好的架构师。
  • 做不好PPT的工程师不是好的架构师。

Wakim Sun

努力 奋斗

5 日志
3 分类
© 2019 Wakim Sun
用 Hexo 搭建
|
主题 — NexT.Mist v5.1.4