Android 架构模式之 MVP
目录
- 架构设计的目的
- 对 MVP 的理解
- 代码
- Model
- View
- Presenter
- Android 中 MVP 的问题
- 试吃个小李子
- Model
- View
- Presenter
大家好!
作为 Android 程序猿,你有研究过 MVP 架构吗?在开始接触 Android 那一刻起,我们就开始接触 MVC 架构,可谓是用的不亦乐乎。可为什么又出现了 MVP 呢?都说它比 MVC 好,到底又好在哪里呢?
架构设计的目的
通过设计使程序模块化,模块内 高内聚、模块间 低耦合,提高开发效率,便于复用及后续维护。
对 MVP 的理解
上图是 MVP 的架构图,我们都知道,MVP架构中 M 代表 Model(模型)、V 代表 View(视图)、P 代表 Presenter(主持人/控制器)。它们的职责分别是:
- View 负责接收用户的输入事件,然后将事件传递给 Presenter;
- Presenter 收到事件后,会进行业务分发,通知 Model 获取数据;
- Model 区分数据来源,进而通过不同渠道获取数据,拿到数据后返回给 Presenter;
- Presenter 进行后续处理,或者通知 View 更新 UI。
相比 MVC 架构,MVP 架构看上去就清晰了很多:事件由 View 流向 Presenter 流向 Model,然后再由 Model 流回 Presenter 流回 View。在 MVP 架构中,Activity 就全心的处理着和 View 相关的事情,Model 负责向下分发数据请求,替 Presenter 分担了很大一部分负担,这里特意新增了一个 CacheRepository 来体现提取 Model 层的用意,这样就可以在 Model 层进行不同渠道的分发,既体现了单一职责原则,又很好的提高了代码的可读性。所以看上去是多了一层 Model 层,可实际上作用还是很大的。另外,由于 MVP 是基于接口编程,所以会多了很多接口文件,来定义业务约束,虽然看上去需要新增很多文件/函数,或者理解为额外工作量,但其实这也很好地锻炼架构思维和抽象能力,因为你可以完全放下业务实现来搭建整体框架。
代码
Model
IModel.java
public interface IModel {}
BaseModel.java
public abstract class BaseModel implements IModel {}
View
public interface IView {void showErr(String errMsg);
}
BaseActivity.java
public abstract class BaseActivity<P extends IPresenter> extends AppCompatActivityimplements IView {protected P mPresenter;public BaseActivity() {this.mPresenter = createPresenter();mPresenter.attachView(this);}public abstract P createPresenter();@Overrideprotected void onDestroy() {super.onDestroy();// Activity 销毁时,需要调用 detachView,防止内存泄漏if (mPresenter != null) {mPresenter.detachView();mPresenter.onDestroy();mPresenter = null;}}@Overridepublic void showErr(String errMsg) {Toast.makeText(this, errMsg, Toast.LENGTH_SHORT);}
}
Presenter
IPresenter.java
public interface IPresenter<V extends IView> {void attachView(V view);void detachView();void onDestroy();
}
BasePresenter.java
public abstract class BasePresenter<V extends IView, M extends IModel> implements IPresenter<V> {protected WeakReference<V> mView;protected M mModel;public BasePresenter() {this.mModel = createModel();}protected abstract M createModel();@Overridepublic void attachView(V view) {mView = new WeakReference<>(view);}@Overridepublic void detachView() {if (mView.get() != null) {mView.clear();}}@Overridepublic void onDestroy() {if (mModel != null) {mModel = null;}}
}
上述代码中可以看到,Presenter 中持有 View 引用,想象一种情况,Activity 发起一个网络请求/耗时操作,Presenter 收到需求后就分发去处理需求并等待结果了,但是还没等处理结束,Activity 就执行关闭操作了,此时 Presenter 还持有着 Activity 的强引用,导致 Activity 无法被及时回收掉,这便导致了大名鼎鼎的内存泄漏了;上述代码中通过 WeakReference 持有 View 引用,这样可以有效解决内存泄漏问题,并且在涉及到 Model/View/Presenter 的引用调用的地方,都进行了非空判断,需要规避空指针的风险出现。
Android 中 MVP 的问题
不幸的是,MVP 中 Presenter 的职责就是 MVC 中 Controller 负责的内容,只不过在 Android 中 Controller 在全职作 控制器 的同时,还需要兼职一部分 View 的职责,而 Presenter 就只是全职作 控制器。所以 Presenter 同样存在 Controller 存在的问题,随着业务的增多,Presenter 会变得越拉越 臃肿/复杂,以及 很糟糕的代码可读性。
另外,由于 MVP 模式依赖于接口,所以在新增一个业务需求时,会 爆炸式增长文件和函数,这个也很让人头疼。
其次,由于 View 会只有 Presenter 引用,Presenter 持有 View 和 Model 的引用,如果处理不当,也会存在 空指针 的风险。
最后,Presenter 会持有 View 的引用,这样就埋下了 内存泄漏 的种子,如果处理不好,问题还是蛮大的。所以就有了后来的MVVM。
试吃个小李子
点击按钮,请求 wanandroid 网站的 banner 接口数据,请求成功后更新到UI上显示接口数据
MVP 架构的 Demo 是从 MVC 架构那套代码变更过来的,添加了很多文件,主要部分 涉及上图中展开的这几个文件,仔细看上图蓝框中的内容会发现,新增一个业务 Activity,共需要新增 6 个文件,其中 3 个是接口约束类、3 个是具体实现类,这也体现了上面说的文件/函数暴增的问题。
Model
请求接口
缓存数据
IMainModel.java
public interface IMainModel extends IModel {/*** 请求 banner 数据** @param callback*/void getNetworkBanner(ResponseCallback<List<Banner>> callback);/*** 读取 banner 本地数据** @return*/List<Banner> getLocalBanner();/*** 持久化存储 banner 数据** @param banners*/void saveBanner(List<Banner> banners);/*** 清空本地数据*/void clearLocalData();
}
MainModel.java
public class MainModel extends BaseModel implements IMainModel {@Overridepublic void getNetworkBanner(ResponseCallback<List<Banner>> callback) {// 收到需求,请求接口数据NetworkRepository.getInstance().requestBanners(callback);}@Overridepublic List<Banner> getLocalBanner() {// 收到需求,读取本地数据return CacheRepository.getInstance().getBanners();}@Overridepublic void saveBanner(List<Banner> banners) {// 收到需求,持久化存储 banner 数据CacheRepository.getInstance().saveBanners(banners);}@Overridepublic void clearLocalData() {// 收到需求,清空本地缓存数据CacheRepository.getInstance().clearLocalData();}
}
View
Button1,点击请求接口数据
Button2,获取本读缓存数据
Button3,清空本地缓存数据
TextView,用于回显数据
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"tools:context=".main.MainActivity"><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="getNetworkInfo"android:text="@string/get_network_info" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="getLocalInfo"android:text="@string/get_local_info" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="clearLocalInfo"android:text="@string/clear_local_info" /><TextViewandroid:id="@+id/tv_banner_info"android:layout_width="wrap_content"android:layout_height="wrap_content" /></LinearLayout>
IMainView.java
public interface IMainView extends IView {/*** 更新 banner 数据** @param banners*/void updateBanner(List<Banner> banners, String from);
}
MainActivity.java
public class MainActivity extends BaseActivity<IMainPresenter> implements IMainView {private TextView mBannerInfoTv;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mBannerInfoTv = (TextView) findViewById(R.id.tv_banner_info);}@Overridepublic IMainPresenter createPresenter() {return new MainPresenter();}/*** 按钮点击事件** @param view*/public void getNetworkInfo(View view) {// 收到点击事件,交给 presenter 进行业务处理if (mPresenter != null) {mPresenter.getNetworkBanner();}}/*** 按钮点击事件** @param view*/public void getLocalInfo(View view) {// 收到点击事件,交给 presenter 进行业务处理if (mPresenter != null) {mPresenter.getLocalBanner();}}/*** 按钮点击事件** @param view*/public void clearLocalInfo(View view) {// 收到点击事件,交给 presenter 进行业务处理if (mPresenter != null) {mPresenter.clearLocalData();}}@Overridepublic void updateBanner(List<Banner> banners, String from) {// 收到更新 ui 事件,更新 uishowBannerInfo(banners, from);}/*** 更新UI** @param banners*/private void showBannerInfo(List<Banner> banners, String from) {StringBuilder sb = new StringBuilder();if (banners.size() > 0) {sb.append("wanandroid 官网\nhttps://www.wanandroid.com\n\n").append("data from ").append(from).append(":\n");for (Banner item : banners) {Log.e("banner", item.toString());sb.append(item.getTitle()).append('\n');}}mBannerInfoTv.setText(sb.toString());}
}
Presenter
业务处理
IMainPresenter.java
public interface IMainPresenter<V extends IView> extends IPresenter<V> {/*** 获取 banner 网络数据*/void getNetworkBanner();/*** 获取 banner 本地数据*/void getLocalBanner();/*** 存储 banner 数据** @param banners*/void saveBanner(List<Banner> banners);/*** 清空本地数据*/void clearLocalData();
}
MainPresenter.java
public class MainPresenter extends BasePresenter<IMainView, IMainModel>implements IMainPresenter<IMainView> {@Overrideprotected IMainModel createModel() {return new MainModel();}@Overridepublic void getNetworkBanner() {if (mModel == null) {return;}// 收到新需求,分发给 model 处理mModel.getNetworkBanner(new ResponseCallback<List<Banner>>() {@Overridepublic void onSuccess(List<Banner> banners) {// 数据缓存saveBanner(banners);// 通知更新UInotifyUpdateBanner(banners, CommonConstant.FROM_NETWORK);}@Overridepublic void onFail(String msg) {IMainView view = mView.get();if (view != null) {view.showErr(msg);}}});}@Overridepublic void getLocalBanner() {if (mModel == null) {return;}// 收到新需求,分发给 model 处理List<Banner> banners = mModel.getLocalBanner();// 通知更新UInotifyUpdateBanner(banners, CommonConstant.FROM_LOCAL);}@Overridepublic void saveBanner(List<Banner> banners) {// 收到新需求,分发给 model 处理if (mModel != null) {mModel.saveBanner(banners);}}@Overridepublic void clearLocalData() {// 收到新需求,分发给 model 处理if (mModel != null) {mModel.clearLocalData();}}/*** 通知更新UI** @param banners*/private void notifyUpdateBanner(List<Banner> banners, String from) {// 获取到数据,通知更新 uiif (mView != null) {IMainView view = mView.get();if (view != null) {view.updateBanner(banners, from);}}}
}
附上源码链接
致谢:
感谢 wanandroid 提供的开放API
参考:
一个小例子彻底搞懂 MVP
写在最后:
很荣幸成为一名 Android 程序猿,虽然不是一名合格的猿。一路走来磕磕绊绊,借此感谢帮助过我的人,感谢指点、感恩遇见!
相关文章:
![](https://i-blog.csdnimg.cn/direct/c52e606abe214ab383dd2ad376852a75.png)
Android 架构模式之 MVP
目录 架构设计的目的对 MVP 的理解代码ModelViewPresenter Android 中 MVP 的问题试吃个小李子ModelViewPresenter 大家好! 作为 Android 程序猿,你有研究过 MVP 架构吗?在开始接触 Android 那一刻起,我们就开始接触 MVC 架构&am…...
![](https://i-blog.csdnimg.cn/direct/fe4ec4dfd4d14a298a6198585949a89b.png)
Ciallo~(∠・ω・ )⌒☆第二十二篇 入门request请求库使用
请求库是用于发送HTTP请求的工具。常见的请求库有requests,它是一个功能强大且易于使用的HTTP库。 使用requests库发送GET请求: import requests url "https://httpbin.org/get"# 携带get请求参数 params {"pn": 10,"size&q…...
![](https://i-blog.csdnimg.cn/direct/5bdef1dcd40f43a1806d8e4c87ec7947.png)
设计模式-创建型模式-原型模式
1.原型模式定义 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象; 1.1 原型模式优缺点 优点 当创建一个新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有的实例…...
![](https://i-blog.csdnimg.cn/direct/78afbaed18124cdfa866558009b0a46a.png#pic_center)
遗传算法与深度学习实战(7)——使用遗传算法解决N皇后问题
遗传算法与深度学习实战(7)——使用遗传算法解决N皇后问题 0. 前言1. N 皇后问题2. 解的表示3. 遗传算法解决 N 皇后问题小结系列链接 0. 前言 进化算法 (Evolutionary Algorithm, EA) 和遗传算法 (Genetic Algorithms, GA) 已成功解决了许多复杂的设计…...
![](https://i-blog.csdnimg.cn/direct/e3c5309675614fbf9c317630ba51b823.png)
R语言:如何安装包“linkET”
自己在R语言中安装包“linkET”时报错不存在叫‘linket’这个名字的程辑包 尝试了install.packages("linkET")和BiocManager::install("linkET")两种安装办法都不行 >install.packages("linkET") WARNING: Rtools is required to build R pa…...
![](https://i-blog.csdnimg.cn/direct/46a032f16c654b10871593f5f7152989.png)
JSON, YAML, XML, CSV交互可视化
1、jsoncrack https://jsoncrack.com/editor...
![](https://www.ngui.cc/images/no-images.jpg)
Android UI:PopupWindow:源码分析:设置WindowManager.LayoutParams中的各种参数
文章目录 设置flags是否包含某些flag设置gravity设置type设置softInputMode设置windowAnimations设置width/height设置token 在WindowManager.addView之前设置在WindowManager.addView之后,可通过i熬夜难过update方法设置设置format设置flags是否包含某些flag 1666 …...
![](https://www.ngui.cc/images/no-images.jpg)
MySQL:从入门到放弃
基础查询 MySQL:基础查询 Mybatis:基础巩固-DDL 项目实战 MySQL:按照日期分组查询 查询开始时间与结束时间在指定的日期范围之内,并且结束时间可以为NULL的数据...
![](https://i-blog.csdnimg.cn/direct/3819bd3681484d76bd5725c1ac194fa5.png)
C++OpenGL三维显示镜面反射光线漫反射实例
程序示例精选 COpenGL三维显示镜面反射光线漫反射实例 如需安装运行环境或远程调试,见文章底部个人QQ名片,由专业技术人员远程协助! 前言 这篇博客针对《COpenGL三维显示镜面反射光线漫反射实例》编写代码,代码整洁,…...
![](https://www.ngui.cc/images/no-images.jpg)
【前端面试】从npm 升级到 pnpm的总结
pnpm优势 pnpm 和 npm 在性能上存在一些明显的差异,这也是一些开发者选择从 npm 切换到 pnpm 的原因。以下是一些关键的差异和原因: 1. 速度: pnpm 比 npm 快了近 2 倍,它通过优化的依赖管理,显著提高了安装速度 。 2. 磁盘空间效率: pnpm 使用基于内容寻址的文件系…...
![](https://www.ngui.cc/images/no-images.jpg)
同步外网YUM源-3
在企业实际应用场景中,仅仅靠光盘里面的RPM软件包是不能满足需要,我们可以把外网的YUM源中的所有软件包同步至本地,可以完善本地YUM源的软件包数量及完整性。 获取外网YUM源软件常见方法包括Rsync、Wget、Reposync,三种同步方法的区别Rsync方式需要外网YUM源支持RSYNC协议…...
![](https://i-blog.csdnimg.cn/direct/ed9b928019ca4146a33d73c7e7d3fb30.png)
Linux的oracle数据库导入其他用户导出的数据库文件
如果用户使用的是expdp的命令,导入就要使用impdp命令,本文以impdp为例进行介绍 1、查看当前创建的所有dmp导出目录 select * from dba_directories 2、为创建的目录赋权限 比如咱们将数据库导入到test用户, grant read,write on directo…...
![](https://img-blog.csdnimg.cn/img_convert/843c1942c793072dc1be3997f082e6e4.png)
FLUX.1 文生图模型微调指南
FLUX.1 是 Black Forest Labs 今年夏天发布的文本转图像模型系列。FLUX.1 模型为开源图像生成模型树立了新标准:它们可以生成逼真的手、清晰的文本,甚至可以生成搞笑表情包这样异常困难的任务。 现在,你可以使用 Ostris 的 Replicate 上的 A…...
![](https://www.ngui.cc/images/no-images.jpg)
JavaWeb基础:HTTP协议与Tomcat服务器
目录 1. HTTP协议简介 示例代码:创建HTTP GET请求 2. Tomcat服务器介绍 Tomcat的基本操作 示例代码:部署简单Servlet 3. 使用Servlet处理请求 示例代码:处理POST请求 在现代网络开发中,理解HTTP协议和如何使用Tomcat作为服…...
![](https://i-blog.csdnimg.cn/direct/811edff1994d49baa5462efd4e462c2a.png)
python井字棋游戏设计与实现
python实现井字棋游戏 游戏规则,有三个井字棋盘,看谁连成的直线棋盘多谁就获胜 棋盘的展现形式为 棋盘号ABC和位置数字1-9 输入A1 代表在A棋盘1号位数下棋 效果图如下 部分源码如下: 卫星工纵浩 白龙码程序设计,点 代码获取 …...
![](https://img-blog.csdnimg.cn/img_convert/94e1e3b9b564f596c8208c7272935d3f.png)
据说是可以和 Windows 一拼的 5个 Linux 发行版
现如今有数以千计的 Linux 发行版可供您使用,然而人们却无法选择一个完美的操作系统来替代 Windows。 使用 Windows 时,傻瓜都能操作自如,同样的方法却不适用于 Linux。在这里,您必须具备操作和使用操作系统的基本知识。因此人们经…...
![](https://www.ngui.cc/images/no-images.jpg)
PHP 常用函数
1. ksort() 如果你有一个数组 array([11] > array(XX), [6] > array(YYY)),你想要返回按照key重新排序,并不改变键和值之间的关联,处理之后的结果为 array([6] > array(YY…...
![](https://img-blog.csdnimg.cn/img_convert/9630604e6cab8c96c15d8a527c5751f8.png)
如何将MySQL迁移到TiDB,完成无缝业务切换?
当 MySQL 数据库的单表数据量达到了亿级,会发生什么? 这个现象表示公司的业务上了一个台阶,随着数据量的增加,公司规模也进一步扩大了,是非常喜人的一个改变 ,然而随之而来的其他变化,就没那么…...
![](https://i-blog.csdnimg.cn/direct/55d908cbb30a42c19e267fbaa456514e.png)
【嵌入式烧录刷写文件】-2.10-为一个Intel Hex文件计算校验和Checksum
案例背景(共6页精讲): 有如下一段Intel Hex文件,为其创建Checksum校验和:CRC16,CRC32(CVN),SHA-256 Hash算法…, 将Checksum Value填充到指定地址。 :2091000058595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767…...
![](https://i-blog.csdnimg.cn/direct/d4de07551f8f4313b9403193cfbc4e3a.png)
整体思想以及取模
前言:一开始由于失误,误以为分数相加取模不能,但是其实是可以取模的 这个题目如果按照一般方法,到达每个节点再进行概率统计,但是不知道为什么只过了百分之十五的测试集 题目地址 附上没过关的代码 #include<bits…...
![](https://i-blog.csdnimg.cn/direct/8168921e39044c99aa8f07eed54f20f2.png)
RabbitMQ 消息可靠保障
RabbitMQ 消息可靠保障 消息的可靠性保证生产者重连生产者确认解决思路A-确认机制解决思路B-备份交换机 MQ 服务器宕机导致消息丢失消费端消息的可靠性保障 消费端限流给消息生成唯一id 消息的可靠性保证 实际项目中 MQ 的流程一般是:生产端把消息路由到交换机&…...
![](https://www.ngui.cc/images/no-images.jpg)
Redis 作为 PHP 的会话存储
使用 Redis 作为 PHP 的会话存储,可以实现多个服务器之间的会话共享,提高会话管理的效率,特别是在分布式系统中。这种方法将会话数据存储在 Redis 中,而不是使用默认的文件系统,从而使多个服务器可以访问相同的会话数据…...
![](https://img-blog.csdnimg.cn/img_convert/1c48ec3e052f2b548022c73e67c239ef.jpeg)
基于伏图的数字心脏模拟仿真APP应用介绍
一、背景介绍 心脏是保证人体正常运转最重要的动力,人体内的血液循环通过心血管运输到各个部位,因此,心血管系统的稳定是人体健康的关键。心血管内科领域极具专业性,其理论研究与技术发展日新月异,心血管疾病患者往往…...
![](https://i-blog.csdnimg.cn/direct/fd826eef84a44ed298ff6867f4ffc1df.png)
智云-一个抓取web流量的轻量级蜜罐docker一键启动
智云-一个抓取web流量的轻量级蜜罐docker安装教程 github地址 https://github.com/xiaoxiaoranxxx/POT-ZHIYUN docker快速启动(v1.4) git clone https://github.com/xiaoxiaoranxxx/POT-ZHIYUN.git cd POT-ZHIYUN docker-compose up -d默认映射到80和8080端口 mysql不对外开放…...
![](https://i-blog.csdnimg.cn/direct/b2be2fc44e8e4478845a0536e2487510.png)
原生HTML5、CSS、JavaScript实现简易网易云音乐播放
1.效果图 2.源码 1.index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>网易云音乐</title><link rel"stylesheet" href"../CSS/index.css"> </head>…...
![](https://img-blog.csdnimg.cn/b5ec7b667bf34a8fa717d503b1f2cff5.png)
网上商城小程序的设计
管理员账户功能包括:系统首页,个人中心,用户管理,商品信息管理,商品类型管理,活动专区管理,新品上架管理,用户评价管理,订单管理,系统管理 微信端账号功能包…...
![](https://i-blog.csdnimg.cn/direct/b349cc12e04c45d1835f9662e816cc27.png#pic_center)
微分方程(Blanchard Differential Equations 4th)中文版Section2.2
动力系统的几何分析 捕食者-猎物系统的向量场 在第2.1节中,我们展示了两个不同捕食者-猎物系统的 R ( t ) R(t) R(t) 和 F ( t ) F(t) F(t) 图形,但没有描述我们是如何生成这些图形的。我们将在第2.5节中解决这个问题,采用欧拉方法推广到…...
![](https://www.ngui.cc/images/no-images.jpg)
Swift 环境搭建
Swift 环境搭建 Swift 是由苹果公司开发的一种强类型编程语言,用于iOS、macOS、watchOS和tvOS应用程序的开发。搭建Swift开发环境是开始使用Swift进行编程的第一步。本文将详细介绍如何在不同的操作系统上搭建Swift开发环境。 在macOS上搭建Swift环境 系统要求 …...
![](https://www.ngui.cc/images/no-images.jpg)
科技与出版
科技与出版 ISSN: 1005-0590 CN: 11-3209/G3 常设栏目:特别策划、产业观察、融媒之光、编辑实务、营销方略、学术探索、创作空间等。 稿件要求 (1)来稿应有创新性;立论科学,主题明确,推理严谨;词语准确,…...
![](https://www.ngui.cc/images/no-images.jpg)
5年前端面试之路
作者:星空海绵 顺便吆喝一声,技术大厂,内推捞人,前/后端or测试←感兴趣 --加班偶尔较多,但周末加班两倍工资。 --15-35K,工资一线城市属于一般,但二线城市很可以。 前言 由于公司要进行…...
![](/Images/OutliningIndicators/None.gif)
网站怎么做才能上百度首页/友情链接联盟
Hibernate中进行数据库增删改的代码,太多不想背了,记在这里以后来查好啦~~~~packageorg.lxh.hibernate.demo01;importjava.util.Iterator;importjava.util.List;importorg.hibernate.Query;importorg.hibernate.Session;importorg.hibernate.SessionFact…...
![](https://raw.githubusercontent.com/xinghongfei/awesome-view/master/screenshots/AndroidSwipeLayout.gif)
做学校后台网站/找网站设计公司
awesome-view 项目地址:xinghongfei/awesome-view 简介:自定义 View 有这些足够了 更多:作者 提 Bug 标签: 自定义view- 自定义 View 主要掌握以下四块内容: 绘制机制:掌握 onMeasure,onLayout,…...
![](/images/no-images.jpg)
中企动力邮箱登陆首页/成都优化官网公司
上周,Mike和我们分享了他在数据库设计方面的部分经验和SQL的一些注意点。 一、数据库设计 1.树形结构数据 :分为邻接模型和 物化路径模型 1.1树形数据,需要解决的问题是: 如何将数据高效地以树形的形式展现给用户&…...
![](https://img-blog.csdnimg.cn/c35c120217fe41de99a91e659464554e.png)
wordpress修改成中文/网站制作公司咨询
大家好,我是脚丫先生 (o^^o) 说起,android,这是多么遥远的英文单词。 「第一行代码」 我想学过android开发的小伙伴都应该读过,可谓是入门神作。 看着郭霖大佬的大作,此刻,有那么带点陌生而又带一丝熟悉…...
![](/images/no-images.jpg)
网站设计费用多少/营业推广是一种什么样的促销方式
Gitlab库已损坏前端显示500错误解决方法参考文章: (1)Gitlab库已损坏前端显示500错误解决方法 (2)https://www.cnblogs.com/zhanmeiliang/p/7298188.html 备忘一下。...
![](/images/no-images.jpg)
2010网站建设管理/优化设计
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid6396 假设k为5,那么开5个数组用来分别存放每头怪兽对应的五个防御力 用五个指针去从头开始遍历,如果当前值小于对应的攻击力,就判断一下这头怪兽的五个值是不是都小于其对应…...