版权说明 : 《Android自定义导览地图组件(一)》于当前CSDN博客乘月网属同一原创,转载请说明出处,谢谢。

         鉴于Android关于自定义导览地图的相关资料以及开源项目贫乏,应Android同行几位小伙伴们的建议,决定写下这篇文章分享给大家。由于博客篇幅限制,本文将分两到三篇博文叙述。
进入主题:

先看看下面的效果图:

可以看到,地图组件基本上实现当前主流地图应用(如百度,高德地图)的缩放拖拽移动以及定位图标等功能。
实现思路:
         其实一开始想了很多方案:
         1. 单纯的自定义一个View然后Draw绘制地图和定位图标,用各种分工逻辑类拆分单元实现。
         2. 自定义View绘制地图+自定义View绘制定位图标,用各种分工逻辑类拆分单元实现。
         3. 自定一个View,地图是View的背景图+ImageView作为定位图标。
         4. 自定义ViewGroup+自定义Imageview作为地图+ImageView作为定位图标。
         ………….省略后面n种方案,真是绞尽脑汁
         由于本文重点在于提供实现路线,最终敲定了最简单的第4种方案:自定义ViewGroup(MapContainer)+自定义ImageView(MapView)+ImageView(定位图标marker)。不过,通过本文的学习,上述其它方案的实现也就变得简单多了。
分析:
         1. MapContainer提供地图和定位图标的盛放容器,定位图标可以叠加显示于地图之上。我们知道,地图的缩放(缩放会导致定位位置的改变)和移动,定位图标随之移动,作为ViewGroup肯定可以拿到这两种ImageView的对象,一边监听地图View----MapView的变化(获取变化值),再传递并控制地位图标View--marker的显示位置。
         2. 为什么自定义ImageView?因为ImageView可以直接显示地图图片呗,但是缩放移动什么的还得靠自定义来实现。
         3. 定位图标没什么难点,直接用ImageView显示坐标icon就好了,剩下的交给ViewGroup动态控制其位置就好了。
         通过上述,总结下该方案:核心业务就在MapView上,MapContainer打辅助,定位图标ImageView打酱油。
         对于一个图片可移动,缩放的功能实现,自然而然就能想到就是常见的查看大图嘛。
         开干!!!

一、 自定义MapView
         图片加载完成时,自动对图片进行自适应ViewGroup(或屏幕)缩放。
         大多数情况下,图片分辨率都不会是刚好ViewGroup(或屏幕)大小,需要在图片加载时获取其真实显示尺寸并根据当前ViewGroup尺寸做自适应缩放以达到最佳的观感效果。
         那么如何知道图片什么时候加载好了?这里可以实现ViewTreeObserver. OnGlobalLayoutListener接口来订阅监听布局变化。当前视图树中,全局布局发生改变或者某个视图的可视状态发生改变时,会得到消息推送(调用订阅者的OnGlobalLayoutListener下的onGlobalLayoutListener方法),图片加载完成后显示到屏幕上便会触发该方法,完整代码如下:

package cn.icheny.guide_map;import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewTreeObserver;
import android.widget.ImageView;/*** 可手势缩放移动双击缩放ImageView** @author www.icheny.cn* @date 2017/8/15*/public class MapView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener {public MapView(Context context) {this(context, null);}public MapView(Context context, AttributeSet attrs) {this(context, null, 0);}public MapView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();//订阅布局监听getViewTreeObserver().addOnGlobalLayoutListener(this);}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();//取消订阅if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {getViewTreeObserver().removeOnGlobalLayoutListener(this);}}/*** 订阅者的onGlobalLayout方法,监听布局变化*/@Overridepublic void onGlobalLayout() {}
}

对于ImageView可缩放,需要设置:setScaleType(ScaleType.MATRIX)属性 ,初始化图片矩阵Matrix 对象以及设定三个缩放比例系数:

……
float SCALE_MIN = 0.5f;//最小缩小比例值系数
float SCALE_ADAPTIVE = 1f;//自适应ViewGroup(或屏幕)缩放比例值
float SCALE_MID = 2f;//中间放大比例值系数,双击一次的放大值
float SCALE_MAX = 4f;//最大放大比例值系数,双击两次的放大值
private Matrix mScaleMatrix;//缩放矩阵public MapView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);setScaleType(ScaleType.MATRIX);mScaleMatrix = new Matrix();
}
……

正如上述,onGlobalLayout()在很多情况下都会被触发,然而我们只需要确定一旦图片加载完成便对其自适应屏幕的缩放处理,所以我们需要一个Flag进行判定,并确定能够获取Drawable对象则表示图片刚好加载完成:

……
private boolean isPicLoaded = false;//图片是否已加载/*** 订阅者的onGlobalLayout函数*/
@Override
public void onGlobalLayout() {if (!isPicLoaded) {Drawable drawable = getDrawable();if (null == drawable) {//图片不存在就继续监听return;}isPicLoaded=true;//图片存在,已加载完成,停止监听}
}
……

OK,检测到图片加载完成,便可以进行缩放处理。为了少点废话,接下来“代码+注释”为主:

    /*** 订阅者的onGlobalLayout函数*/@Overridepublic void onGlobalLayout() {if (!isPicLoaded) {Drawable drawable = getDrawable();if (null == drawable) {//图片不存在就继续监听return;}isPicLoaded = true;//图片存在,已加载完成,停止监听//获取图片固有的宽高(不是指本身属性:分辨率,因为android系统在加载显示图片前可能对其压缩)int iWidth = drawable.getIntrinsicWidth();int iHeight = drawable.getIntrinsicHeight();//获取当前View(ImageView)的宽高,即父View给予的宽高int width = getWidth();int height = getHeight();//对比图片宽高和当前View的宽高,针对性的缩放if (iWidth >= width && iHeight <= height) {//如果图片固宽大于View宽,固高小于View高,SCALE_ADAPTIVE = height * 1f / iHeight;   // 那么只需针对高度等比例放大图片(这里有别于查看大图的处理方式)} else if (iWidth <= width && iHeight >= height) {//固宽小于View宽,固高大于View高,针对宽度放大SCALE_ADAPTIVE = width * 1f / iWidth;} else if (iWidth >= width && iHeight >= height || iWidth <= width && iHeight <= height) {//固宽和固高都大于或都小于View的宽高,SCALE_ADAPTIVE = Math.max(width * 1f / iWidth, height * 1f / iHeight);//只取对宽和对高之间最小的缩放比例值(这里有别于查看大图的处理方式)}//先将图片移动到View中心位置mScaleMatrix.postTranslate((width - iWidth) * 1f / 2, (height - iHeight) * 1f / 2);//再对图片从View的中心点缩放mScaleMatrix.postScale(SCALE_ADAPTIVE, SCALE_ADAPTIVE, width * 1f / 2, height * 1f / 2);//执行偏移和缩放setImageMatrix(mScaleMatrix);//根据当前图片的缩放情况,重新调整图片的最大最小缩放值SCALE_MAX *= SCALE_ADAPTIVE;SCALE_MID *= SCALE_ADAPTIVE;SCALE_MIN *= SCALE_ADAPTIVE;}}

折腾完自适应,开始折腾手势缩放,实现缩放手势探测器接口OnScaleGestureListener来完成图片随手势的动态缩放。初始化ScaleGestureDetector对象并重写当前View的onTouchEvent()方法,将touch事件交给ScaleGestureDetector处理:

……
private ScaleGestureDetector mScaleGestureDetector;//缩放手势探测测器public MapView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);setScaleType(ScaleType.MATRIX);mScaleMatrix = new Matrix();mScaleGestureDetector = new ScaleGestureDetector(context, this);
}/*** 缩放手势开始时调用该方法** @param detector* @return*/
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {//返回为true,则缩放手势事件往下进行,否则到此为止,即不会执行onScale和onScaleEnd方法return true;
}/*** 缩放手势进行时调用该方法* 缩放控制范围:SCALE_MIN——SCALE_MAX** @param detector*/
@Override
public boolean onScale(ScaleGestureDetector detector) {return true;
}/*** 缩放手势完成后调用该方法** @param detector*/
@Override
public void onScaleEnd(ScaleGestureDetector detector) {}@Override
public boolean onTouchEvent(MotionEvent event) {if (mScaleGestureDetector != null) {//绑定缩放手势探测器,由其处理touch事件mScaleGestureDetector.onTouchEvent(event);}return true;
}
……

接下来是处理onScale事件代码:

    /*** 缩放手势进行时调用该方法* 缩放控制范围:SCALE_MIN——SCALE_MAX** @param detector*/@Overridepublic boolean onScale(ScaleGestureDetector detector) {if (getDrawable() == null) {return true;//没有图片就不用折腾了}//缩放因子(即将缩放的值)float scaleFactor = detector.getScaleFactor();//当前图片已缩放的值(如果onScale第一次被调用,scale就是自适应后的缩放值:SCALE_ADAPTIVE)float scale = getDrawableScale();//当前缩放值在最大放大值以内且手势检测缩放因子为缩小手势(小于1),或当前缩放值在最小缩小值以内且缩放因子为放大手势,允许缩放if (scale <= SCALE_MAX && scaleFactor < 1 || scale >= SCALE_MIN && scaleFactor > 1) {//进一步考虑即将缩小后的缩放比例(scale*scaleFactor)低于规定SCALE_MIN-SCALE_MAX范围的最小值SCALE_MINif (scale * scaleFactor < SCALE_MIN && scaleFactor < 1) {//强制锁定缩小后缩放比例为SCALE_MIN(scale*scaleFactor=SCALE_MIN)scaleFactor = SCALE_MIN / scale;}//进一步考虑即将放大后的缩放比例(scale*scaleFactor)高于规定SCALE_MIN-SCALE_MAX范围的最大值SCALE_MAXif (scale * scaleFactor > SCALE_MAX && scaleFactor > 1) {//强制锁定放大后缩放比例为SCALE_MAX(scale*scaleFactor=SCALE_MAX)scaleFactor = SCALE_MAX / scale;}//设定缩放值和缩放位置,这里缩放位置便是手势焦点的位置mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());//执行缩放setImageMatrix(mScaleMatrix);}return true;}

好了,写了那么多,上个图看看运行效果,舒缓下紧张的气氛:

还是有效果的,滋滋滋~  不过,这个白边还有图片中心缩放着缩放着就跑偏了着实让人强迫症大发。所以,应该在执行缩放前调用checkBoderAndCenter()方法用于检测并修复留白边,图片中心跑偏的问题:

@Override
public boolean onScale(ScaleGestureDetector detector) {
……//设定缩放值和缩放位置,这里缩放位置便是手势焦点的位置mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());//检查即将缩放后造成的留空隙和图片不居中的问题,及时调整缩放参数checkBoderAndCenter();//执行缩放setImageMatrix(mScaleMatrix);}return true;
}/*** 处理缩放和移动后图片边界与屏幕有间隙或者不居中的问题*/
private void checkBoderAndCenter() {RectF rect = getMatrixRect();int width = getWidth();int height = getHeight();float deltaX = 0;//X轴方向偏移量float deltaY = 0;//Y轴方向偏移量//图片宽度大于等于View宽if (rect.width() >= width) {//图片左边坐标大于0,即左边有空隙if (rect.left > 0) {//向左移动rect.left个单位到View最左边,rect.left=0deltaX = -rect.left;}//图片右边坐标小于width,即右边有空隙if (rect.right < width) {//向右移动width - rect.left个单位到View最右边,rect.right=widthdeltaX = width - rect.right;}}//图片高度大于等于View高,同理if (rect.height() >= height) {//图片上面坐标大于0,即上面有空隙if (rect.top > 0) {//向上移动rect.top个单位到View最上边,rect.top=0deltaY = -rect.top;}//图片下面坐标小于height,即下面有空隙if (rect.bottom < height) {//向下移动height - rect.bottom个单位到View最下边,rect.bottom=heightdeltaY = height - rect.bottom;}}//图片宽度小于View宽if (rect.width() < width) {//计算需要移动到X方向View中心的距离deltaX = width * 1f / 2 - rect.right + rect.width() * 1f / 2;}//图片高度小于View高度if (rect.height() < height) {//计算需要移动到Y方向View中心的距离deltaY = height * 1f / 2 - rect.bottom + rect.height() * 1f / 2;}mScaleMatrix.postTranslate(deltaX, deltaY);
}/*** 根据当前图片矩阵变换成的四个角的坐标,即left,top,right,bottom** @return*/
private RectF getMatrixRect() {RectF rect = new RectF();Drawable drawable = getDrawable();if (drawable != null) {rect.set(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());}mScaleMatrix.mapRect(rect);return rect;
}

效果图不贴了,节省写博时间。上图我们也可以看到:图片缩小到宽高小于View的宽高后,手指离开屏幕,此时图片无法自动恢复(放大)到自适应View宽高的大小,只能依靠手动缩放恢复,甚是心累。所以,需求来了…...缩放之后,检测这样的情况,再用一个动画让图片平滑恢复自适应View大小,接下来onScaleEnd登场:

    /*** 缩放手势完成后调用该方法** @param detector*/@Overridepublic void onScaleEnd(ScaleGestureDetector detector) {Drawable drawable = getDrawable();if (drawable == null) return;//当前缩放值float scale = getDrawableScale();//当前缩放值小于自适应缩放缩放比例,即图片小于View宽高if (scale < SCALE_ADAPTIVE) {postDelayed(new AutoScaleTask(SCALE_ADAPTIVE, getWidth() * 1f / 2, getHeight() * 1f), 2);}}/*** 自动缩放任务*/private class AutoScaleTask implements Runnable {float targetScale;//目标缩放值float x;//缩放焦点的x坐标float y;//缩放焦点的y坐标static final float TMP_AMPLIFY = 1.05f;//放大梯度static final float TMP_SHRINK = 0.96f;//缩小梯度float tmpScale = 1.0f;//缩小梯度public AutoScaleTask(float targetScale, float x, float y) {this.targetScale = targetScale;this.x = x;this.y = y;//当前缩放值小于目标缩放值,目标是放大图片if (getDrawableScale() < targetScale) {//设定缩放梯度为放大梯度tmpScale = TMP_AMPLIFY;} else {  //当前缩放值小于(等于可以忽略)目标缩放值,目标是缩小图片//设定缩放梯度为缩小梯度tmpScale = TMP_SHRINK;}}@Overridepublic void run() {//设定缩放参数mScaleMatrix.postScale(tmpScale, tmpScale, x, y);//检查即将缩放后造成的留空隙和图片不居中的问题,及时调整缩放参数checkBoderAndCenter();setImageMatrix(mScaleMatrix);//当前缩放值float scale = getDrawableScale();//如果tmpScale>1即放大任务状态,且当前缩放值还是小于目标缩放值或// tmpScale<1即缩小任务状态,且当前缩放值还是大于目标缩放值就继续执行缩放任务if (tmpScale > 1 && scale < targetScale || scale > targetScale && tmpScale < 1) {postDelayed(this, 2);} else {//缩放的略微过头了,需要强制设定为目标缩放值tmpScale = targetScale / scale;mScaleMatrix.postScale(tmpScale, tmpScale, x, y);checkBoderAndCenter();setImageMatrix(mScaleMatrix);}}}

好了,大工造成!来看看效果吧:

上面所述的留白,图片中心移位以及缩小后不能自动恢复自适应View(或屏幕)大小的问题在效果图中已经不复存在了,滋滋滋~

上文代码提到自动缩放任务AutoScaleTask,咱趁热打铁来实现双击缩放功能。一般查看地图(或大图)时,双击,如果当前缩放比例小于一级放大(scale<SCALE_MID)比例就自动放大到一级放大(SCALE_ MID), 如果比例大于等于一级放大(SCALE_ MID)比例且小于二级放大(SCALE_MAX)比例就自动放大到二级放大(SCALE_MAX),如果等于二级放大(SCALE_ MID)比例就缩小到自适应View大小(SCALE_ADAPTIVE),在自动缩放过程中不再响应双击事件,直到自动缩放结束,这就意味着需要一个flag进行锁定。思路已经很清晰,就差Android手势探测器GestureDetector帮我们判断双击手势。嗯,是这样!贴代码:

……
private boolean isAutoScaling = false;//是否处于自动缩放中,用于是否响应双击手势的flag
private GestureDetector mGestureDetector;//手势探测器
public ZoomImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
……mGestureDetector = initGestureDetector(context);
}/*** 初始化手势探测器** @param context* @return GestureDetector*/
private GestureDetector initGestureDetector(Context context) {GestureDetector.SimpleOnGestureListener listner = new GestureDetector.SimpleOnGestureListener() {@Overridepublic boolean onDoubleTap(MotionEvent e) {if (!isAutoScaling) {//如果不在自动缩放isAutoScaling = true;float x = e.getX();//双击触点x坐标float y = e.getY();//双击触点y坐标float scale = getDrawableScale();if (scale < SCALE_MID) {//当前缩放比例小于一级缩放比例//一级放大postDelayed(new AutoScaleTask(SCALE_MID, x, y), 10);} else if (scale >= SCALE_MID && scale < SCALE_MAX) {//当前缩放比例在一级缩放和二级缩放比例之间//二级放大postDelayed(new AutoScaleTask(SCALE_MAX, x, y), 10);} else if (scale == SCALE_MAX) {//当前缩放比例等于二级缩放比例//缩小至自适应view比例postDelayed(new AutoScaleTask(SCALE_ADAPTIVE, x, y), 10);} else {isAutoScaling = false;}}return super.onDoubleTap(e);}};return new GestureDetector(context, listner);
}@Override
public boolean onTouchEvent(MotionEvent event) {
……if (mGestureDetector != null) {//绑定手势探测器,由其处理touch事件mGestureDetector.onTouchEvent(event);}return true;
}private class AutoScaleTask implements Runnable {
……@Overridepublic void run() {……if (tmpScale > 1 && scale < targetScale || scale > targetScale && tmpScale < 1) {……} else {//缩放的略微过头了,需要强制设定为目标缩放值……isAutoScaling = false;}}
}

终于进入最后一个功能实现:手势移动。这里依然借助系统给我们算好的拖拽临界值ScaledTouchSlop决定是否拖动图片。重写onTouchEvent()处理touch事件判断用户拖动(ActionMove)值是否达到临界值,是则从手势触点(Point)的中心点(因为可能不止一个手指在拖动,需要求中心点)出发移动图片:

@Override
public boolean onTouchEvent(MotionEvent event) {
……//不在自动缩放中才可以拖动图片(这个判断可有可无,根据需求来)if (!isAutoScaling) {//绑定touch事件,处理移动图片逻辑moveByTouchEvent(event);}return true;
}/*** 通过Touch事件移动图片** @param event*/
private void moveByTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_MOVE://手势移动RectF rect = getMatrixRect();if (rect.width() <= getWidth() && rect.height() <= getHeight()) {//图片宽高小于等于View宽高,即图片可以完全显示于屏幕中,那就没必要拖动了return;}//计算多个触点的中心坐标int x = 0;int y = 0;int pointerCount = event.getPointerCount();//获取触点数(手指数)for (int i = 0; i < pointerCount; i++) {x += event.getX(i);y += event.getY(i);}//得到最终的中心坐标x /= pointerCount;y /= pointerCount;//如果触点数(手指数)发生变化,需要重置上一次中心坐标和数量的参考值if (mLastPointCount != pointerCount) {mLastX = x;mLastY = y;mLastPointCount = pointerCount;}int deltaX = x - mLastX;//X方向的位移int deltaY = y - mLastY;//Y方向的位移//如果可以拖拽if (isCanDrag(deltaX, deltaY)) {//图片宽小于等于view宽,则X方向不需要移动if (rect.width() <= getWidth()) {deltaX = 0;}//图片高小于等于view高,则Y方向不需要移动if (rect.height() <= getHeight()) {deltaY = 0;}//完成缩放mScaleMatrix.postTranslate(deltaX, deltaY);checkBoderAndCenter();setImageMatrix(mScaleMatrix);}//交换中心坐标值,作为下次移动事件的参考值mLastX = x;mLastY = y;break;case MotionEvent.ACTION_CANCEL://取消case MotionEvent.ACTION_UP://释放mLastPointCount = 0;//触点数置零,便于下次判断是否重置mLastX和mLastYbreak;}
}//上一次触点中心坐标
int mLastX;//上一次拖动图片的触点数(手指数)
int mLastY;
//上一次拖动图片的触点数(手指数)
int mLastPointCount;/*** 是否可以移动图片** @param deltaX* @param deltaY*/
private boolean isCanDrag(int deltaX, int deltaY) {return Math.sqrt(deltaX * deltaX + deltaY * deltaY) >= mTouchSlop;
}

好了,几经周折终于把查看大图的功能搞定了,这里就不贴效果图了,下一篇再见!

Android自定义导览地图组件(一)相关推荐

  1. android自定义本地邮箱联想组件(基于MultiAutoCompleteTextView)

    在做android客户端应用程序时,总会遇到输入邮箱的功能,最近由于项目需要,要做本地的邮箱联想功能,思考下后,决定用MultiAutoCompleteTextView来实现这一功能. android ...

  2. Android自定义九宫格手势解锁组件

    项目来源 别的不说,先看一下效果图: 九宫格手势图案解锁功能在很多应用中都在使用,本文介绍的组件来自于开源项目PatternLocker的翻写,原工程是使用Kotlin开发的,由于我们项目是使用jav ...

  3. Android自定义电池电量显示组件(kotlin,java)

    最近产品研发需求需要显示在线设备的电池电量状态,然后UI给出的效果的图是这样的 于是就开始了自定义个,因为是项目特定的UI所以很多属性都没有直接抽取到styles里面了,直接上代码(因为项目是使用ko ...

  4. android 自定义扫地机器人地图,扫地机如何正确操作进行APP建图?

    扫地机如何正确操作进行APP建图? 2020-09-18 11:48:31 0点赞 1收藏 0评论 扫地机APP建图是一个非常重要的步骤 ,获取完成的空间地图后才能进行后续的多种智能化操作,所以需要掌 ...

  5. 可以随身携带的景区导览系统是什么样的?

    8月4日,全国旅游标准化技术委员会对其发布的行业标准<旅游景区智慧化建设指南>(以下简称<指南>)公开征求意见.<指南>给出了旅游景区智慧化建设的总体架构.基础设施 ...

  6. 高德地图组件在Android的应用以及Android与JavaScript的交互(一)

    最近在慕课网学习了关于高德地图组件的课程(其实就是一个广告,内容和官网的API完全一样),发现这个JavaScript API比Android API简单方便多了,于是就打算放在Android APP ...

  7. android开发之高德地图不能定位,只显示格子

    android 在引用高德地图组件进行开发的时候,不能定位,只显示格子,如下图所示: 参考了网上的一下大家的解决方案,主要是 (1)检查申请的KEY是否正确.PS:申请方法请参考这篇博客,写的很全很详 ...

  8. 智慧景区导览系统可以为游客提供哪些服务?

    智慧景区导览系统是建立在无线通信.物联网等技术基础之上的智能导览系统,将景区导览电子化.智能化,该系统覆盖景区全景及景区附近地图,能够快速提供线路规划,准确查询景区附近吃.住.游.购信息及景区内公共设 ...

  9. 旅游景区地图导览系统,传统导览智慧新升级

    地图在景区导览中一直扮演着重要角色. 从传统导览的纸质地图,再到智慧导览的电子地图,游客都可以从景区地图上了解到景点名称.游玩路线.服务设施等内容,帮助游客更好地游览景区. 相比传统的纸质地图导览,电 ...

最新文章

  1. 宏观经济学思维导图_巧用思维导图,提升初三化学专题复习课实效
  2. 石墨烯区块链(1)系统架构
  3. android 手机自动化测试,Appium进行Android手机真机自动化测试
  4. 组合数学 —— 康托展开
  5. flinksql on zeppelin安装及使用
  6. 改变iOS app的icon(iOS10.3)
  7. diamond简介和使用
  8. 在web of science / 知网 上生成题录
  9. excel导出动态表头以及二级三级表头,还有数据库动态的数据来源
  10. python学习笔记------乌龟吃鱼小游戏
  11. 李宏毅老师《机器学习》课程笔记-1深度学习简介
  12. Managed Direct3D开发经验浅析
  13. # Alpha冲刺之事后诸葛亮
  14. mac java 创建文件夹_在mac电脑上创建java的一些简单操作
  15. [逆向工程] 二进制拆弹Binary Bombs 快乐拆弹 详解
  16. python实现zigzag_Zigzag Iterator的Pythonic方式?
  17. openstack部署过程4
  18. std::function 学习笔记(3)
  19. sai椭圆尺子等比例放大或者缩小
  20. linux exp parfile用法,逻辑备份与恢复-使用EXP进行逻辑备份

热门文章

  1. 电脑桌面快捷方式更换图片
  2. 【AI初识境】从头理解神经网络-内行与外行的分水岭
  3. 在腾讯云中配置服务器外网可以访问
  4. 使用遗传算法实现迷宫游戏(genetic maze)
  5. java实现二维码生成功能
  6. 使用PowerDesigner逆向工程生成pdm文件(超全)
  7. 预防死锁,检测死锁,避免死锁,解除死锁....
  8. 正则表达式习题解答-Python核心编程3 第一章
  9. Top 10 顶级项目管理工具
  10. Linux dirname命令