热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android开发如何写出优雅的代码

很多时候我们去面试,人家总会问一个问题,你们公司开发一个app是如何进行技术选择的,app中涉及到了哪些开发模式,谈谈你对mvc、mvp和mvvm的区别。或许在这些问题上每个人有每个人的看法,在我

很多时候我们去面试,人家总会问一个问题,你们公司开发一个app是如何进行技术选择的,app中涉及到了哪些开发模式,谈谈你对mvc、mvp和mvvm的区别。或许在这些问题上每个人有每个人的看法,在我看来把代码写清楚,用简单清晰的方式将要实现的功能写出来就可以了。

在去年的时候,我接手公司的移动开发部门,刚开始看代码的时候我是崩溃的,Android和ios的代码中,超过2000行的代码随处可见。在我以前的职业生涯中,这是不可想象的,除非你写的是系统代码,如果是业务上的代码为啥不可以抽象,为啥不可以分层呢,或者换一个写法。在那个项目中,大量的使用mvp,大量的为了抽象而抽象,就像后端的开发中先定义一个接口,然后自己去实现,对于这种写法,显得太啰嗦,会给后面的开发人员带来一定的误解。

所以在进行重构之前,我对app进行了梳理,比较老的聊天的功能我先不动,而上层的电商的实现上我做了一些基础模块的封装,而这些基础模块我考虑适量的引入开源库,而对于具体的业务模块我主要分为4层:模型层、接口层、核心层、界面层。模型层定义了所有的模型;接口层封装了服务器提供的API;核心层处理所有业务逻辑;界面层就处理界面的展示。最后按模块进行开发。
这里写图片描述
为了具体说明,下面看一个界面实例。

聊天功能实现

首先看一下最终的效果吧。
这里写图片描述
这里写图片描述
在这个界面中表情的实现是一个要点,在聊天系统中表情是特殊的文本,我们将表情发送出去另一端在接受到文本后怎么转换的呢?
例如[微笑]对应的图片为,这里使用HashMap

public class EmojiParseUtils {

private Pattern mPattern=null;
//建议使用SparseArray
public static HashMap EMOTION_MAP = new HashMap<>();

static {
EMOTION_MAP.put("[呵呵]", R.drawable.expression_01_45);
EMOTION_MAP.put("[愁]", R.drawable.expression_02_45);
EMOTION_MAP.put("[花心]", R.drawable.expression_03_45);
EMOTION_MAP.put("[思考]", R.drawable.expression_04_45);
EMOTION_MAP.put("[酷]", R.drawable.expression_05_45);
EMOTION_MAP.put("[大哭]", R.drawable.expression_06_45);
EMOTION_MAP.put("[卖萌]", R.drawable.expression_07_45);
EMOTION_MAP.put("[闭嘴]", R.drawable.expression_08_45);
EMOTION_MAP.put("[睡觉]", R.drawable.expression_09_45);
EMOTION_MAP.put("[快哭了]", R.drawable.expression_10_45);
EMOTION_MAP.put("[尴尬]", R.drawable.expression_11_45);
EMOTION_MAP.put("[怒]", R.drawable.expression_12_45);
EMOTION_MAP.put("[挤眼]", R.drawable.expression_13_45);
EMOTION_MAP.put("[呲牙大笑]", R.drawable.expression_14_45);
EMOTION_MAP.put("[惊讶]", R.drawable.expression_15_45);
EMOTION_MAP.put("[挥泪]", R.drawable.expression_16_45);
EMOTION_MAP.put("[爱你]", R.drawable.expression_17_45);
EMOTION_MAP.put("[加我]", R.drawable.expression_18_45);
EMOTION_MAP.put("[抓狂]", R.drawable.expression_19_45);
EMOTION_MAP.put("[吐]", R.drawable.expression_20_45);
EMOTION_MAP.put("[偷笑]", R.drawable.expression_21_45);
EMOTION_MAP.put("[微笑]", R.drawable.expression_22_45);
EMOTION_MAP.put("[没看见]", R.drawable.expression_23_45);
EMOTION_MAP.put("[懒得理你]", R.drawable.expression_24_45);
EMOTION_MAP.put("[馋嘴]", R.drawable.expression_25_45);
EMOTION_MAP.put("[困]", R.drawable.expression_26_45);
EMOTION_MAP.put("[惊恐]", R.drawable.expression_27_45);
EMOTION_MAP.put("[汗]", R.drawable.expression_28_45);
EMOTION_MAP.put("[哈哈]", R.drawable.expression_29_45);
EMOTION_MAP.put("[加油]", R.drawable.expression_30_45);
EMOTION_MAP.put("[奋斗]", R.drawable.expression_31_45);
EMOTION_MAP.put("[怒骂]", R.drawable.expression_32_45);
EMOTION_MAP.put("[疑问]", R.drawable.expression_33_45);
EMOTION_MAP.put("[嘘]", R.drawable.expression_34_45);
EMOTION_MAP.put("[晕]", R.drawable.expression_35_45);
EMOTION_MAP.put("[哼]", R.drawable.expression_36_45);
EMOTION_MAP.put("[哀]", R.drawable.expression_37_45);
EMOTION_MAP.put("[奥特曼]", R.drawable.expression_38_45);
EMOTION_MAP.put("[潜水]", R.drawable.expression_39_45);
EMOTION_MAP.put("[拜拜]", R.drawable.expression_40_45);
EMOTION_MAP.put("[太开心]", R.drawable.expression_41_45);
EMOTION_MAP.put("[挖鼻屎]", R.drawable.expression_42_45);
EMOTION_MAP.put("[鼓掌]", R.drawable.expression_43_45);
EMOTION_MAP.put("[生病]", R.drawable.expression_44_45);
EMOTION_MAP.put("[坏笑]", R.drawable.expression_45_45);
EMOTION_MAP.put("[左哼哼]", R.drawable.expression_46_45);
EMOTION_MAP.put("[捂脸]", R.drawable.expression_47_45);
EMOTION_MAP.put("[压历大]", R.drawable.expression_48_45);
EMOTION_MAP.put("[鄙视]", R.drawable.expression_49_45);
EMOTION_MAP.put("[委屈]", R.drawable.expression_50_45);
EMOTION_MAP.put("[钱]", R.drawable.expression_51_45);
EMOTION_MAP.put("[阴险]", R.drawable.expression_52_45);
EMOTION_MAP.put("[亲亲]", R.drawable.expression_53_45);
EMOTION_MAP.put("[黑线]", R.drawable.expression_54_45);
EMOTION_MAP.put("[可怜]", R.drawable.expression_55_45);
EMOTION_MAP.put("[菜刀]", R.drawable.expression_56_45);
EMOTION_MAP.put("[给力]", R.drawable.expression_57_45);
EMOTION_MAP.put("[啤酒]", R.drawable.expression_58_45);
EMOTION_MAP.put("[篮球]", R.drawable.expression_59_45);
EMOTION_MAP.put("[乒乓球]", R.drawable.expression_60_45);
EMOTION_MAP.put("[鸡蛋]", R.drawable.expression_61_45);
EMOTION_MAP.put("[吃饭]", R.drawable.expression_62_45);
EMOTION_MAP.put("[猪头]", R.drawable.expression_63_45);
EMOTION_MAP.put("[玫瑰花]", R.drawable.expression_64_45);
EMOTION_MAP.put("[凋谢]", R.drawable.expression_65_45);
EMOTION_MAP.put("[香吻]", R.drawable.expression_66_45);
EMOTION_MAP.put("[心]", R.drawable.expression_67_45);
EMOTION_MAP.put("[伤心]", R.drawable.expression_68_45);
EMOTION_MAP.put("[蛋糕]", R.drawable.expression_69_45);
EMOTION_MAP.put("[闪电]", R.drawable.expression_70_45);
EMOTION_MAP.put("[圣诞节]", R.drawable.expression_71_45);
EMOTION_MAP.put("[高跟鞋]", R.drawable.expression_72_45);
EMOTION_MAP.put("[足球]", R.drawable.expression_73_45);
EMOTION_MAP.put("[兔子]", R.drawable.expression_74_45);
EMOTION_MAP.put("[便便]", R.drawable.expression_75_45);
EMOTION_MAP.put("[晚安]", R.drawable.expression_76_45);
EMOTION_MAP.put("[太阳]", R.drawable.expression_77_45);
EMOTION_MAP.put("[礼物]", R.drawable.expression_78_45);
EMOTION_MAP.put("[花]", R.drawable.expression_79_45);
EMOTION_MAP.put("[good]", R.drawable.expression_80_45);
EMOTION_MAP.put("[弱]", R.drawable.expression_81_45);
EMOTION_MAP.put("[握手]", R.drawable.expression_82_45);
EMOTION_MAP.put("[耶]", R.drawable.expression_83_45);
EMOTION_MAP.put("[抱拳]", R.drawable.expression_84_45);
EMOTION_MAP.put("[来]", R.drawable.expression_85_45);
EMOTION_MAP.put("[靠]", R.drawable.expression_86_45);
EMOTION_MAP.put("[不要]", R.drawable.expression_87_45);
EMOTION_MAP.put("[OK]", R.drawable.expression_88_45);
EMOTION_MAP.put("[鞭炮]", R.drawable.expression_89_45);
EMOTION_MAP.put("[红包]", R.drawable.expression_90_45);
EMOTION_MAP.put("[发财]", R.drawable.expression_91_45);
EMOTION_MAP.put("[话筒]", R.drawable.expression_92_45);
EMOTION_MAP.put("[兵]", R.drawable.expression_93_45);
EMOTION_MAP.put("[熊猫]", R.drawable.expression_94_45);
EMOTION_MAP.put("[蜡烛]", R.drawable.expression_95_45);
EMOTION_MAP.put("[德州]", R.drawable.expression_96_45);
EMOTION_MAP.put("[恐龙]", R.drawable.expression_97_45);
EMOTION_MAP.put("[吉他]", R.drawable.expression_98_45);
EMOTION_MAP.put("[肥皂]", R.drawable.expression_99_45);
EMOTION_MAP.put("[台球]", R.drawable.expression_100_45);
EMOTION_MAP.put("[火箭]", R.drawable.expression_101_45);
EMOTION_MAP.put("[左车头]", R.drawable.expression_102_45);
EMOTION_MAP.put("[车厢]", R.drawable.expression_103_45);
EMOTION_MAP.put("[右车头]", R.drawable.expression_104_45);
EMOTION_MAP.put("[浮云]", R.drawable.expression_105_45);
EMOTION_MAP.put("[星星]", R.drawable.expression_106_45);
EMOTION_MAP.put("[钻石]", R.drawable.expression_107_45);
EMOTION_MAP.put("[钻戒]", R.drawable.expression_108_45);
EMOTION_MAP.put("[沙发]", R.drawable.expression_109_45);
EMOTION_MAP.put("[卷纸]", R.drawable.expression_110_45);
EMOTION_MAP.put("[药丸]", R.drawable.expression_111_45);
EMOTION_MAP.put("[钟]", R.drawable.expression_112_45);
EMOTION_MAP.put("[草泥马]", R.drawable.expression_113_45);
EMOTION_MAP.put("[大姨妈]", R.drawable.expression_114_45);
EMOTION_MAP.put("[鱼]", R.drawable.expression_115_45);
}

public static int getEmojiByName(String imgName) {
Integer integer = EMOTION_MAP.get(imgName);
return integer == null ? -1 : integer;
}

public static HashMap getEmojiMap() {
HashMap EmojiMap = EMOTION_MAP;
return EmojiMap;
}


}

当接受到表情时候我们只需要通过文字匹配就行:

public SpannableString getEmotionContent(final Context context, final TextView tv, String source) {
SpannableString spannableString = new SpannableString(source);
Resources res = context.getResources();
String regexEmotion = "\\[([\u4e00-\u9fa5\\w])+\\]";
Pattern patternEmotion = Pattern.compile(regexEmotion);
Matcher matcherEmotion = patternEmotion.matcher(spannableString);
while (matcherEmotion.find()) {
String key = matcherEmotion.group();
int start = matcherEmotion.start();
Integer imgRes = EmojiParseUtils.getEmojiByName(key);
if (imgRes != null) {
// 压缩表情图片
int size = (int) tv.getTextSize() * 13 / 10;
Bitmap bitmap = BitmapFactory.decodeResource(res, imgRes);
Bitmap scaleBitmap = Bitmap.createScaledBitmap(bitmap, size, size, true);

ImageSpan span = new ImageSpan(context, scaleBitmap);
spannableString.setSpan(span, start, start + key.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return spannableString;
}

接下来我们需要对表情进行简单的封装,这个界面主要实现的是表情的滑动和指示器。

public class EmojiFragment extends Fragment {

@BindView(R.id.viewPager)
ViewPager viewPager;
@BindView(R.id.indicatorView)
IndicatorView indicatorView;

private View view = null;
private EmojiPagerAdapter pagerAdapter=null;
private List eViews = new ArrayList<>();
private List eNames = new ArrayList<>();
private int screenWidth,spacing,itemWidth,gridViewH;
private int prePagerPosition=0;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.gragment_chat_emoji, container, false);
ButterKnife.bind(this, view);
init();
return view;
}

private void init() {
initParam();
initEmojiView();
initIndicator(screenWidth, gridViewH, eViews);
}

private void initParam() {
screenWidth = UIUtils.getScreenWidth(getActivity());
spacing = UIUtils.dp2px(getActivity(),10);
itemWidth = (screenWidth - spacing * 8) / 7;
gridViewH = itemWidth * 3 + spacing * 3;
}

//绘制一个3行7列的View,每20个表情作为一组,同时添加到ViewPager对应的view集合中
private void initEmojiView() {
for (String emojiName : EmojiParseUtils.getEmojiMap().keySet()) {
eNames.add(emojiName);
if (eNames.size() == 20) {
GridView gv = initEmotionGridView(eNames, screenWidth, spacing, itemWidth, gridViewH);
eViews.add(gv);
eNames = new ArrayList<>();
}
}

// 判断最后是否有不足20个表情,中间补空
if (eNames.size() > 0&&eNames.size()<20) {
for (int i=0;i<24-eNames.size();i++){
eNames.add("");
}
GridView grid = initEmotionGridView(eNames, screenWidth, spacing, itemWidth, gridViewH);
eViews.add(grid);
}

pagerAdapter = new EmojiPagerAdapter(eViews);
viewPager.setAdapter(pagerAdapter);
}

//表情指示器
private void initIndicator(int screenWidth, int gvHeight, List eViews) {
indicatorView.initIndicator(eViews.size());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(screenWidth, gvHeight);
viewPager.setLayoutParams(params);

viewPager.addOnPageChangeListener(new SimpleViewPagerListener(){
@Override
public void onPageSelected(int position) {
indicatorView.indicatorView(prePagerPosition,position);
prePagerPosition=position;
}
});
}

private GridView initEmotionGridView(List emotionNames, int gvWidth, int padding, int itemWidth, int gvHeight) {
GridView gridView = new GridView(getActivity());
gridView.setNumColumns(7);
gridView.setPadding(padding, padding, padding, padding);
gridView.setHorizontalSpacing(padding);
gridView.setVerticalSpacing(padding/2);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(gvWidth, gvHeight);
gridView.setLayoutParams(params);
// 给GridView设置表情图片
EmojiGridAdapter adapter = new EmojiGridAdapter(getActivity(), emotionNames, itemWidth);
gridView.setAdapter(adapter);

gridView.setOnItemClickListener(EmojiUtils.getInstance().getOnItemClickListener(getActivity()));
return gridView;
}

}

然后是一个指示器,根据表情的大小和每页显示的个数计算出指示器的位置。

public class IndicatorView extends LinearLayout {

private Context mCOntext=null;
private ArrayList dotViews =new ArrayList<>();
private int size = 6;
private int marginSize=15;
private int pointSize ;
private int marginLeft;
private LayoutParams layoutParams=null;

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

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

public IndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mCOntext=context;
init();
}

private void init() {
pointSize= UIUtils.dp2px(mContext,size);
marginLeft=UIUtils.dp2px(mContext,marginSize);
}

public void initIndicator(int count){
removeAllViews();
for (int i = 0 ; i View view = new View(mContext);
layoutParams = new LayoutParams(pointSize,pointSize);
if(i!=0)
layoutParams.leftMargin = marginLeft;
view.setLayoutParams(layoutParams);
if (i == 0){
view.setBackgroundResource(R.drawable.point_white);
}else{
view.setBackgroundResource(R.drawable.point_gray);
}
dotViews.add(view);
addView(view);
}
}

public void indicatorView(int start,int next){
if(start <0 || next <0 || next == start){
start = next = 0;
}
final View ViewStrat = dotViews.get(start);
final View ViewNext = dotViews.get(next);
ViewNext.setBackgroundResource(R.drawable.point_white);
ViewStrat.setBackgroundResource(R.drawable.point_gray);
}

}

附:相关源码


推荐阅读
  • HTML学习02 图像标签的使用和属性
    本文介绍了HTML中图像标签的使用和属性,包括定义图像、定义图像地图、使用源属性和替换文本属性。同时提供了相关实例和注意事项,帮助读者更好地理解和应用图像标签。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 前端性能优化无损压缩webp格式的图片
    一、什么是webpWebP格式,谷歌开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG的23,并能节省大量的服务器宽带资源和数据空 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文介绍了MVP架构模式及其在国庆技术博客中的应用。MVP架构模式是一种演变自MVC架构的新模式,其中View和Model之间的通信通过Presenter进行。相比MVC架构,MVP架构将交互逻辑放在Presenter内部,而View直接从Model中读取数据而不是通过Controller。本文还探讨了MVP架构在国庆技术博客中的具体应用。 ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • Windows7企业版怎样存储安全新功能详解
    本文介绍了电脑公司发布的GHOST WIN7 SP1 X64 通用特别版 V2019.12,软件大小为5.71 GB,支持简体中文,属于国产软件,免费使用。文章还提到了用户评分和软件分类为Win7系统,运行环境为Windows。同时,文章还介绍了平台检测结果,无插件,通过了360、腾讯、金山和瑞星的检测。此外,文章还提到了本地下载文件大小为5.71 GB,需要先下载高速下载器才能进行高速下载。最后,文章详细解释了Windows7企业版的存储安全新功能。 ... [详细]
  • 本文详细介绍了Android中的坐标系以及与View相关的方法。首先介绍了Android坐标系和视图坐标系的概念,并通过图示进行了解释。接着提到了View的大小可以超过手机屏幕,并且只有在手机屏幕内才能看到。最后,作者表示将在后续文章中继续探讨与View相关的内容。 ... [详细]
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • Android源码中的Builder模式及其作用
    本文主要解释了什么是Builder模式以及其作用,并结合Android源码来分析Builder模式的实现。Builder模式是将产品的设计、表示和构建进行分离,通过引入建造者角色,简化了构建复杂产品的流程,并且使得产品的构建可以灵活适应变化。使用Builder模式可以解决开发者需要关注产品表示和构建步骤的问题,并且当构建流程发生变化时,无需修改代码即可适配新的构建流程。 ... [详细]
  • 前端开发工程师必读书籍有哪些值得推荐?我们直接进入代码复杂版式设置,如下所示,先写些标签,源码在这个链接里面:https://codepen.io/Shadid ... [详细]
  • 涉及的知识点-ViewGroup的测量与布局-View的测量与布局-滑动冲突的处理-VelocityTracker滑动速率跟踪-Scroller实现弹性滑动-屏幕宽高的获取等实现步 ... [详细]
author-avatar
陈上意535
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有