一、前言

转载请标明出处:http://blog.csdn.net/wlwlwlwl015/article/details/41492031

这一篇blog写的真心不容易,我只想说我这种菜鸟去高仿百度地图去做LBS服务真心有点作死,期间本想放弃,做简单点算了,但不能说服自己。最后通过F6去一行一行的debug(新手朋友注意这是最好的解决问题的方式没有之一),最后成功完成了核心的功能。上一篇blog高仿了百度地图离线地图模块中的“城市列表”部分(模仿百度地图的LBS服务——离线地图篇 Part1),城市实现里“当前城市”、“热门城市”、“全国省市”数据信息的展示,那么本篇blog主要记录的就是如何进行下载了,同样的是高仿百度离线地图的“下载管理”模块。废话不多说,下面就分步骤进行一一介绍了。

二、百度离线地图“下载管理”功能分析

老规矩我们先来看看百度地图离线地图中“下载管理”模块的界面和功能:

通过这上面两幅截图我们分析一下需要做的工作:

1.下载管理分为“正在下载”和“下载完成”两部分,所以整体界面应当分为2个ListView。

2.可以通过进度条实时看到下载任务的进度,下载中可以进行“暂停下载”和“删除”的操作,那么暂停的时候必定也可以执行“开始下载”的操作。具体细节大家可以通过操作百度地图来看。

3.已下载完成的地图提供了“查看地图”和“删除”的功能。

大体上功能就上面提到的这些,具体的细节在下面的代码中再看,下面就按照我的开发顺序来告诉大家先做什么、后做什么、具体怎么做,对于高手来说高仿这个东东可能不算什么,但是对于我来说确实难度挺大,整整两天,吃饭睡觉都在想细节,最终之所以实现了,是因为我首先会理清思路,先考虑我需要做什么并将任务拆分开,然后是这些步骤应当按什么顺序去做,就这样一点一点做,一行一行debug,最终得以完成。所以下面我就列出开发步骤,再逐一说明。

Step 1 初始化

Step 2 编写离线地图事件通知接口及其回调方法中的代码

Step 3 分别编写“正在下载”和“下载完成”的布局以及Adapter

Step 4 编写测试方法,即开始执行下载任务

Step 1 

上面这个说的可能还不够细节,但是没关系,我会在下面通过代码去一一解释清楚。首先来看看初始化,先上代码(注意和上一篇是同一个Activity,所以重复代码就不贴了):

 // 已下载的离线地图数据Listprivate ArrayList<MKOLUpdateElement> isDoingUpdateMapList = null;// 正在下载的数据列表(包括下载中、暂停的)private ArrayList<MKOLUpdateElement> downLoadingMapList = new ArrayList<MKOLUpdateElement>();// 已下载完成的数据列表private ArrayList<MKOLUpdateElement> downLoadedMapList = new ArrayList<MKOLUpdateElement>();// 已下载的离线地图数据Adapter和ListViewprivate DownLoadingListView downLoadingListView; // 正在下载的ListViewprivate DownLoadingAdapter mDownLoadingAdapter = new DownLoadingAdapter();private DownLoadedListView downLoadedListView; // 下载完成的ListViewprivate DownLoadedAdapter mDownLoadedAdapter = new DownLoadedAdapter();

上面的是声明部分,关于“正在下载”和“下载完成”同样是两个自定义的ListView去解决事件冲突问题,解决方案和上一篇blog一样,都是重写onMeasure方法。

同样的声明之后应当进行初始化工作,用来初始化之前的下载任务,比如:两个下载任务都下了一半暂停了,这里我们需要在“正在下载”的ListView中显示出来,参照百度地图应包括以下信息:城市名、数据包大小、下载状态、当前的下载进度等。下面这段代码同样位于initData()方法中:

     // 初始化已下载的城市列表,并根据已下载和下载中进行分类isDoingUpdateMapList = mOfflineMap.getAllUpdateInfo();if (isDoingUpdateMapList == null) {isDoingUpdateMapList = new ArrayList<MKOLUpdateElement>();}if (isDoingUpdateMapList.size() > 0) {for (MKOLUpdateElement element : isDoingUpdateMapList) {// 如果下载进度为100则应放入“已下载”的Listif (element.ratio == 100) {downLoadedMapList.add(element);mDownLoadedAdapter = new DownLoadedAdapter();}// 如果下载进入不为100则应放入“正在下载”的Listif (element.ratio != 100) {downLoadingMapList.add(element);mDownLoadingAdapter = new DownLoadingAdapter();}}}

第2行的getAllUpdateInfo()方法很好用,是这个下载模块的核心方法,官方的解释是:返回各城市离线地图更新信息。对于这个解释我是觉得很笼统,不明白,通过我的使用我认为这个方法的作用就是:返回当前已下载的(包括开始下载的、暂停的、正在下载的以及完成的)的所有数据信息,并且是以城市为单位的List集合。根据官方文档可以看出返回值类型是MKOLUpdateElement,这个类也正式封装了一个下载任务应有的所有关键信息,而在上面的代码中我正是通过element.ratio来分割List,因为ratio正是下载进度的意思:

所以当ratio为100的时候,我就将这个对象放到“下载完成”的ListView,否则说明还没下载完,就放到“正在下载”的ListView。这样我们就可以在进入应用之后看到之前未完成的、已完成的下载记录了。OK,初始化很简单,结束了。

Step 2 

初始化完成之后,现在就来说道说道离线地图中唯一的一个监听:public interface MKOfflineMapListener

同样的来看看官方文档中对它的解释:

离线地图事件通知接口。该接口返回新安装离线地图、下载更新、数据版本更新等结果,用户需要实现该接口以处理相应事件。

不知道是不是我书读的少,总觉得官方的解释不够通俗,看了依然不知道怎么用。但是官方对这个接口的回调方法还是解释的比较清楚的:

void onGetOfflineMapState(int type,int state)

type - 事件类型: MKOfflineMap.TYPE_NEW_OFFLINE, MKOfflineMap.TYPE_DOWNLOAD_UPDATE, MKOfflineMap.TYPE_VER_UPDATE.

state - 事件状态: 当type为TYPE_NEW_OFFLINE时,表示新安装的离线地图数目. 当type为TYPE_DOWNLOAD_UPDATE时,表示更新的城市ID.

上面的蓝色字体都是官方文档的原话,这里我们也清楚了这个回调方法的两个参数都表示什么意思了。根据需求,我们需要实时监控下载进度并反馈到UI,所以这里我们只需要关注TYPE_DOWNLOAD_UPDATE这个类型的事件即可,而正好此时的state就表示更新城市的ID,那么这个监听的作用就很明显了,就是通过它来实时获取下载进度并更新我们界面上的ProgressBar即可。可能有些人不清楚什么时候会触发这个监听,那么通过打印语句来观察控制台不难发现,当type为TYPE_DOWNLOAD_UPDATE时,只要下载状态发生变化,即会触发监听。文字解释比较费劲,下面我贴上监听代码,大家可以开启一个下载任务(后面说),并像第9行一样通过打印来观察一下都有什么变化:

     mOfflineMap.init(new MKOfflineMapListener() {@Overridepublic void onGetOfflineMapState(int type, int state) {// TODO Auto-generated method stubswitch (type) {case MKOfflineMap.TYPE_DOWNLOAD_UPDATE:// 得到当前正在下载的城市的具体更新信息MKOLUpdateElement update = mOfflineMap.getUpdateInfo(state);Log.e(TAG, update.cityName + " ," + update.ratio);if (update != null) {// 此监听器在下载任务进行期间大概每下载1%触发一次,注意是大概List<MKOLUpdateElement> elements = downLoadingMapList;for (MKOLUpdateElement element : elements) {if (update.cityID == element.cityID) {element.ratio = update.ratio;if (update.ratio == 100) {// 当下载进度到100时,Item从“下载中”的List移动到“已下载的List”downLoadingMapList.remove(element);downLoadedMapList.add(element);mDownLoadedAdapter.notifyDataSetChanged();}break;}}}mDownLoadingAdapter.notifyDataSetChanged();break;case MKOfflineMap.TYPE_NEW_OFFLINE:// 有新离线地图安装Log.e(TAG, "TYPE_NEW_OFFLINE");break;case MKOfflineMap.TYPE_VER_UPDATE:// 版本更新提示break;}}});

相信认真看了代码的朋友肯定也大致明白了个123吧,有问题可以留言,虽然我是新手,可我自己写的东西还是解释的清的。

Step 3 

下面就是最重要的适配器了,我们来分别看看“下载中”的Adapter和“下载完成”的Adapter:

 // 下载管理——正在下载——适配器class DownLoadingAdapter extends BaseAdapter {@Overridepublic int getCount() {// TODO Auto-generated method stubreturn downLoadingMapList.size();}@Overridepublic Object getItem(int position) {// TODO Auto-generated method stubreturn downLoadingMapList.get(position);}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn position;}@Overridepublic View getView(final int position, View convertView,ViewGroup parent) {// TODO Auto-generated method stubfinal MKOLUpdateElement element = downLoadingMapList.get(position);ViewHolder holder = null;if (convertView == null) {convertView = mInflater.inflate(R.layout.down_loading_item,null);holder = new ViewHolder();holder.cityName = (TextView) convertView.findViewById(R.id.id_city_name);holder.dataPakSize = (TextView) convertView.findViewById(R.id.id_data_size);holder.downLoadState = (TextView) convertView.findViewById(R.id.id_down_state);holder.downLoadRatio = (TextView) convertView.findViewById(R.id.id_down_raito);holder.downLoadProgress = (ProgressBar) convertView.findViewById(R.id.id_down_progress);holder.downPullIcon = (ImageButton) convertView.findViewById(R.id.id_expand_down_icon);holder.upPullIcon = (ImageButton) convertView.findViewById(R.id.id_expand_up_icon);holder.pauseDownBtn = (Button) convertView.findViewById(R.id.id_pause_down_btn);holder.startDownBtn = (Button) convertView.findViewById(R.id.id_start_down_btn);holder.deleteMapBtn = (Button) convertView.findViewById(R.id.id_delete_map);holder.btnGroup = (LinearLayout) convertView.findViewById(R.id.id_btn_group);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}holder.cityName.setText(element.cityName);holder.dataPakSize.setText(NumberFormatUtil.dataSizeFormatter(element.serversize) + "M");String stateInfo = "";// 如果是正在下载的状态if (element.status == MKOLUpdateElement.DOWNLOADING) {stateInfo = "正在下载";holder.startDownBtn.setVisibility(View.GONE);holder.pauseDownBtn.setVisibility(View.VISIBLE);holder.downLoadState.setTextColor(Color.BLUE);}//如果是暂停的状态if (element.status == MKOLUpdateElement.SUSPENDED) {stateInfo = "已暂停";holder.downLoadState.setTextColor(Color.RED);holder.startDownBtn.setVisibility(View.VISIBLE);holder.pauseDownBtn.setVisibility(View.GONE);}holder.downLoadState.setText(stateInfo);//设置当前进度的百分数holder.downLoadRatio.setText(element.ratio + "%");//设置ProgressBar的进度holder.downLoadProgress.setProgress(element.ratio);// 暂停下载holder.pauseDownBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubint cityId = element.cityID;mOfflineMap.pause(cityId);Toast.makeText(OfflineActitivty.this,"暂停下载" + element.cityName + "离线地图:",Toast.LENGTH_SHORT).show();isDoingUpdateMapList = mOfflineMap.getAllUpdateInfo();if (isDoingUpdateMapList == null) {isDoingUpdateMapList = new ArrayList<MKOLUpdateElement>();}if (isDoingUpdateMapList.size() > 0) {downLoadingMapList.clear();for (MKOLUpdateElement element : isDoingUpdateMapList) {if (element.ratio != 100) {downLoadingMapList.add(element);}}}mDownLoadingAdapter.notifyDataSetChanged();}});// 开始下载holder.startDownBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubint cityId = element.cityID;mOfflineMap.start(cityId);Toast.makeText(OfflineActitivty.this,"开始下载" + element.cityName + "离线地图:",Toast.LENGTH_SHORT).show();isDoingUpdateMapList = mOfflineMap.getAllUpdateInfo();if (isDoingUpdateMapList == null) {isDoingUpdateMapList = new ArrayList<MKOLUpdateElement>();}if (isDoingUpdateMapList.size() > 0) {downLoadingMapList.clear();for (MKOLUpdateElement element : isDoingUpdateMapList) {if (element.ratio != 100) {downLoadingMapList.add(element);}}}mDownLoadingAdapter.notifyDataSetChanged();mDownLoadedAdapter.notifyDataSetChanged();}});// 删除地图holder.deleteMapBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubint cityId = element.cityID;mOfflineMap.remove(cityId);Toast.makeText(OfflineActitivty.this,"已删除" + element.cityName + "离线地图:",Toast.LENGTH_SHORT).show();downLoadingMapList.remove(position);mDownLoadingAdapter.notifyDataSetChanged();}});}}});return convertView;}private class ViewHolder {TextView cityName;TextView dataPakSize;TextView downLoadState;TextView downLoadRatio;ProgressBar downLoadProgress;ImageButton downPullIcon;ImageButton upPullIcon;Button pauseDownBtn;Button startDownBtn;Button deleteMapBtn;LinearLayout btnGroup;}}
 // 下载管理——已下载——适配器class DownLoadedAdapter extends BaseAdapter {@Overridepublic int getCount() {// TODO Auto-generated method stubreturn downLoadedMapList.size();}@Overridepublic Object getItem(int position) {// TODO Auto-generated method stubreturn downLoadedMapList.get(position);}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn position;}@Overridepublic View getView(final int position, View convertView,ViewGroup parent) {// TODO Auto-generated method stubfinal MKOLUpdateElement element = downLoadedMapList.get(position);ViewHolder holder = null;if (convertView == null) {convertView = mInflater.inflate(R.layout.down_loaded_item, null);holder = new ViewHolder();holder.cityName = (TextView) convertView.findViewById(R.id.id_city_name);holder.isHasNewData = (TextView) convertView.findViewById(R.id.id_is_has_new);holder.dataPakSize = (TextView) convertView.findViewById(R.id.id_data_pak_size);holder.deleteMapBtn = (Button) convertView.findViewById(R.id.id_btn_delete_map);holder.seeMapDetailBtn = (Button) convertView.findViewById(R.id.id_see_map_detail);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}holder.cityName.setText(element.cityName);holder.dataPakSize.setText(NumberFormatUtil.dataSizeFormatter(element.serversize) + "M");// 删除地图holder.deleteMapBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stub// 删除已下载的离线Mapint cityId = element.cityID;mOfflineMap.remove(cityId);Toast.makeText(OfflineActitivty.this,"已删除" + element.cityName + "离线地图:",Toast.LENGTH_SHORT).show();downLoadedMapList.remove(position);mDownLoadedAdapter.notifyDataSetChanged();}});// 查看地图holder.seeMapDetailBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubIntent intent = new Intent();intent.putExtra("x", element.geoPt.longitude);intent.putExtra("y", element.geoPt.latitude);intent.setClass(OfflineActitivty.this,BaseMapActivity.class);startActivity(intent);}});return convertView;}private class ViewHolder {private TextView cityName;private TextView isHasNewData;private TextView dataPakSize;private Button deleteMapBtn;private Button seeMapDetailBtn;}}

可以看到由于“下载中”有开始和暂停两个按钮,所以比“下载完成”略微麻烦一些,而下载完成之后即可以查看地图,即根据经纬坐标去加载改城市的地图,代码很简单就是官方Demo中的那个BaseMapActivity。item的布局也很简单,模仿百度地图的样式拼凑一下就好了,后面会贴上动态效果图,其实到这里离线地图模块的核心功能都已完成,最后看看如何通过点击Item去开启一个下载任务吧。

Step 4 

通过上面的三个步骤就已经完成了离线地图的所有准备工作了,下面只剩下点击列表项开启下载任务了,依然是参考百度地图,简单的流程是这样的:

点击任意列表中的任意项(包括热门城市的ListView、全国省市的ExpandableListView的子项),如果被点击的城市没有下载,那么新开一个下载任务在“正在下载”的列表,如果被点击的城市“正在下载”或“已暂停”,那么只需用切换到“下载管理列表”即可,最后如果被点击的城市“已下载完成”,那么同样的只是切换一下即可。

下面贴上剩余全部代码,包括初始化ListView以及添加Item点击:

// 初始化ListViewprivate void initListView() {// 热门城市hotCitieslistView = (HotCitiesListView) findViewById(R.id.id_hotcities_lv);mHotCityAdapter = new HotCityAdapter();hotCitieslistView.setAdapter(mHotCityAdapter);// 全国省市allCitieslistView = (NationalCitiesListView) findViewById(R.id.id_allcities_exp_lv);mNationalCityAdapter = new NationalCityAdapter();allCitieslistView.setAdapter(mNationalCityAdapter);// 下载管理——下载中downLoadingListView = (DownLoadingListView) findViewById(R.id.id_download_manager_lv);downLoadingListView.setAdapter(mDownLoadingAdapter);// 下载管理——下载完成downLoadedListView = (DownLoadedListView) findViewById(R.id.id_download_manager_lv_2);downLoadedListView.setAdapter(mDownLoadedAdapter);// 设置热门城市的Item点击事件hotCitieslistView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {// // TODO Auto-generated method stubOfflineMapItemBean offlineMapItemBean = mHotCityDatas.get(position);int cityId = offlineMapItemBean.getCityId();String cityName = offlineMapItemBean.getCityName();MKOLUpdateElement element = mOfflineMap.getUpdateInfo(cityId);// 如果进度为0,则下载。否则仅仅切换过去if (element == null) {// 开始下载mOfflineMap.start(cityId);offlineMapItemBean.setDownloadStatus(DownLoadStatus.DOWNLOADING);// 切换到下载管理tb.setChecked(false);llayout1.setVisibility(View.VISIBLE);llayout2.setVisibility(View.GONE);Toast.makeText(OfflineActitivty.this,"开始下载" + cityName + "离线地图",Toast.LENGTH_SHORT).show();// 更新界面显示isDoingUpdateMapList = mOfflineMap.getAllUpdateInfo();if (isDoingUpdateMapList == null) {isDoingUpdateMapList = new ArrayList<MKOLUpdateElement>();}// 现在就有更新信息了element = mOfflineMap.getUpdateInfo(cityId);downLoadingMapList.add(element);mDownLoadingAdapter.notifyDataSetChanged();mHotCityAdapter.notifyDataSetChanged();} else {// 仅仅跳转// 切换到下载管理tb.setChecked(false);llayout1.setVisibility(View.VISIBLE);llayout2.setVisibility(View.GONE);}}});// 设置全国省市的Item点击事件allCitieslistView.setOnChildClickListener(new OnChildClickListener() {@Overridepublic boolean onChildClick(ExpandableListView parent, View v,int groupPosition, int childPosition, long id) {OfflineMapItemBean offlineMapItemBean = allCityDatas.get(groupPosition).getChildCities().get(childPosition);int cityId = offlineMapItemBean.getCityId();String cityName = offlineMapItemBean.getCityName();// Toast.makeText(OfflineActitivty.this, cityId+","+cityName,// Toast.LENGTH_SHORT).show();MKOLUpdateElement element = mOfflineMap.getUpdateInfo(cityId);// 如果进度为0,则下载。否则仅仅切换过去if (element == null) {// 开始下载mOfflineMap.start(cityId);offlineMapItemBean.setDownloadStatus(DownLoadStatus.DOWNLOADING);// 切换到下载管理tb.setChecked(false);llayout1.setVisibility(View.VISIBLE);llayout2.setVisibility(View.GONE);Toast.makeText(OfflineActitivty.this,"开始下载" + cityName + "离线地图:", Toast.LENGTH_SHORT).show();// 更新界面显示isDoingUpdateMapList = mOfflineMap.getAllUpdateInfo();if (isDoingUpdateMapList == null) {isDoingUpdateMapList = new ArrayList<MKOLUpdateElement>();}// 现在就有更新信息了element = mOfflineMap.getUpdateInfo(cityId);downLoadingMapList.add(element);mDownLoadingAdapter.notifyDataSetChanged();mHotCityAdapter.notifyDataSetChanged();} else {// 仅仅跳转// 切换到下载管理tb.setChecked(false);llayout1.setVisibility(View.VISIBLE);llayout2.setVisibility(View.GONE);}return false;}});}

最后做一些说明,包括我整体代码的不足和重点需要注意的地方

1.肯定有的朋友发现我在写“热门城市”的列表的时候还通过自定义的OfflineMapItemBean对数据进行了封装,在后面做“下载列表”的时候压根就没有再封装了,而是直接用SDK中的MKOLUpdateElement对象去取值了。这块是我做的不好,因为下载列表要拆分成“正在下载”和“已完成”两部分,每次获取更新对象再遍历拆分感觉也不是个办法,所以干脆就不封装了,我也没有想到更好的办法去处理,有更好的方法的朋友可以给我指点一二。

2.当ScrollView嵌套ListView之后item的点击事件是失效的,包括ExpandableListView的子item也是一样。对于这个我选择了在item的布局文件中添加一个属性:android:descendantFocusability="blocksDescendants"来解决的,但这不是个好办法,据说会让ViewHolder失效,问了其他朋友说是让ScrollView中的onTouch返回false之类的,我尝试了没有达到预期效果,如果哪位大神知道最优的解决方案还请给小弟指点一二,感激不尽。

3.由于项目周期紧,还有很多小效果没来得及实现,比如:下载状态(已暂停、已完成等)应当在“下载管理”和“城市列表”中能实时的同步的更新等,但是总体上看效果还是挺不错的吧,下面就贴上效果图:

由于在模拟器上调试时无法开始下载(offlineMap.start方法调用之后MKOLUpdateElement返回的更新对象的status始终是WAITING的状态,而在真机调试时调用start之后更新对象的status会立刻变成DOWNING,模拟器的网络也没有问题,不知道是不是BUG),所以这里只贴上两张真机运行截图好了,UI比较粗糙,但是核心功能都没问题,到了这里离线地图的全部内容就已经算是记录完毕了。

三、总结

写到这里关于我们项目中的LBS服务就已经全部介绍完毕了,在做地图模块的同时学到了许多新的东西,在这里也要感谢鸿洋大神对我的帮助,目前准备和同事一起再做一个以LBS为主的APP,等做好之后可能还会写一个博客吧,这个APP的点子不错,目前暂且先不透漏,透漏了也没几个人会看到,哈哈。路漫漫其修远兮,吾将上下而求索,我的Android之路才刚刚开始,今后还应当更加努力!加油!Raito!

Android 高仿百度地图的LBS服务——离线地图篇 Part 2 (v 3.1.1)相关推荐

  1. Android 高仿百度地图的LBS服务——基础地图篇(v 3.1.1)

    一.前言 转载请标明出处:http://blog.csdn.net/wlwlwlwl015/article/details/41076537 因为项目需要集成地图功能,所以交给我负责研究并继续完善百度 ...

  2. 糗事百科 android源码,Android高仿糗事百科(含服务端)

    Android高仿糗事百科(含服务端) 积分: 23 智慧币 积分: 3 智慧币 [1.00元 ] 包含内容: 源码,全套工具 详情描述 如遇视频不清晰,请最大化观看演示 以下仅列出部分功能,全部功能 ...

  3. 基于百度音乐Api的Android高仿音乐播放App可在线试听下载歌词浏览

    基于百度音乐Api的Android高仿音乐播放App可在线试听下载歌词浏览 首先这也是学习过程中开发的一个练手项目,基于百度音乐Api,api在网上搜的,如果想看他的实现,可以下载我的项目来参考.风格 ...

  4. android高仿京东app

    仿京东app 采用组件化架构 屏幕适配方案可以较好解决多分辨率及同分辨率不同dpi适配: 全新组件化架构升级,相比之前的方案模块间更为解耦且使用更为方便: 本项目为仿京东项目,资源为抓包获取,项目框架 ...

  5. Android 高仿App项目归纳整理,持续更新中…

    Android 高仿App项目归纳整理,持续更新中- Android高仿App项目整理,包含高仿了一些大公司的app,有基于Java,Kotlin,Flutter等语言的.对于开发我们自已的项目时可以 ...

  6. android+高仿视频录制,android高仿微信视频编辑页

    android高仿微信视频编辑页-视频多张图片提取 上一篇中介绍了有关视频提取图片的知识点,如果对这个不太了解 建议看下android提取视频多张图片和视频信息之前这篇. 这里实现的是仿微信的视频编辑 ...

  7. PHP+Mysql高仿百度知道签到源码演示与下载

    PHP+Mysql高仿百度知道签到源码演示与下载 演示下载地址:http://www.erdangjiade.com/js/512.html 效果图片: 签到表 --  -- 表的结构 `sign`  ...

  8. android qq红点,Android高仿QQ小红点功能

    先给大家展示下效果图: 绘制贝塞尔曲线: 主要是当在一定范围内拖拽时算出固定圆和拖拽圆的外切直线以及对应的切点,就可以通过path.quadTo()来绘制二阶贝塞尔曲线了~ 整体思路: 1.当小红点静 ...

  9. Android 高仿QQ5.2双向側滑菜单DrawerLayout实现源代码

    Android 高仿QQ5.2双向側滑菜单DrawerLayout实现源代码 左右側滑效果图 1.主页的实现 直接将DrawerLayout作为根布局,然后其内部第一个View为内容区域,第二个Vie ...

最新文章

  1. Java必备:java入门、java学习
  2. 阿里云主机安装Memcached扩展优化WordPress
  3. linux用户不能su: This account is currently not available
  4. freemodbus线圈中的位操作
  5. LeetCode - Easy - 14. Longest Common Prefix
  6. flex与flash元件交互
  7. 3d页游开发_大翅膀等于页游风?天谕手游扭转印象,阿云嘎同款黑翅膀最有排面...
  8. ROS on ARM--RK3066上本地编译ROS Groovy
  9. 质性研究工具_MAXQDA在质性数据分析中的使用——免费的中文在线研讨会!
  10. linux中shell curl命令获取http状态码--------强大的网络传输工具
  11. 数据库期末总结笔记( 零基础 )--数据库安全性与完整性-范式-E-R图
  12. C#内存共享通讯示例
  13. C#中的多线程 - 多线程的使用 z
  14. 电池电量检测方法-库仑计-基于LTC2941
  15. linux qt qpa linuxfb,Qt 5.4带有Tslib的Linux触摸屏输入在Raspberry Pi上无法使用LinuxFB QPA平台插件...
  16. Py之lime:lime库的简介、安装、使用方法之详细攻略
  17. 海外问卷调查,招募合伙人
  18. 11 OPENVINO intermediate course experiment 3 增加性别和年龄识别
  19. 利用pandas进行数据分析
  20. LSD直线检测和霍夫线变换的学习建议

热门文章

  1. python开发区_最新章节测试答案2020智慧树知道Python程序设计
  2. linux设备树 驱动,(9条消息)zynq linux驱动之使用设备树开发
  3. 用比较wu的话解释程序专业术语
  4. 失落的C语言结构体封装艺术
  5. hibernate的sessionfactory
  6. BitBlt函数解析
  7. EXCEL的IF函数学习
  8. 如果PDF阅读器显示,真正在准备阅读怎么办?
  9. 2023年如何在Google做外贸
  10. 把彩色照片用css样式改成灰色照片