Android自定义导览地图组件(一)

2023-11-17

版权说明 : 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);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        //订阅布局监听
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        //取消订阅
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
    }

    /**
     * 订阅者的onGlobalLayout方法,监听布局变化
     */
    @Override
    public 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函数
     */
    @Override
    public 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
     */
    @Override
    public 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_MIN
            if (scale * scaleFactor < SCALE_MIN && scaleFactor < 1) {
                //强制锁定缩小后缩放比例为SCALE_MIN(scale*scaleFactor=SCALE_MIN)
                scaleFactor = SCALE_MIN / scale;
            }
            //进一步考虑即将放大后的缩放比例(scale*scaleFactor)高于规定SCALE_MIN-SCALE_MAX范围的最大值SCALE_MAX
            if (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=0
            deltaX = -rect.left;
        }
        //图片右边坐标小于width,即右边有空隙
        if (rect.right < width) {
            //向右移动width - rect.left个单位到View最右边,rect.right=width
            deltaX = width - rect.right;
        }
    }
    //图片高度大于等于View高,同理
    if (rect.height() >= height) {
        //图片上面坐标大于0,即上面有空隙
        if (rect.top > 0) {
            //向上移动rect.top个单位到View最上边,rect.top=0
            deltaY = -rect.top;
        }
        //图片下面坐标小于height,即下面有空隙
        if (rect.bottom < height) {
            //向下移动height - rect.bottom个单位到View最下边,rect.bottom=height
            deltaY = 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
     */
    @Override
    public 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;
            }
        }

        @Override
        public 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() {
        @Override
        public 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 {
……
    @Override
    public 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和mLastY
            break;
    }
}

//上一次触点中心坐标
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;
}

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


 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android自定义导览地图组件(一) 的相关文章

  • 为什么 cordova.file.documentsDirectory 为空?

    我正在尝试使用 cordova plugin file transfer 在http ngcordova com docs plugins fileTransfer http ngcordova com docs plugins fileT
  • 不同 Android 设备上 box2D 中出现奇怪的“口吃”

    我正在用 C 同时开发引擎和游戏 并使用 box2D 作为物理后端 我正在不同的 Android 设备上进行测试 在三分之二的设备上 游戏运行良好 物理效果也很好 然而 在我的 Galaxy Tab 10 1 上 我偶尔会遇到某种 口吃 的
  • Android自定义控件命名空间问题

    我一直在为 Android 开发自定义控件 尽管我尝试按照建议进行操作here https stackoverflow com questions 4495511 how to pass custom component parameter
  • 编译后从字节代码中删除注释

    我们正在使用一个包含使用 JAXB 注释进行注释的 bean 的库 我们使用这些类的方式完全不依赖于 JAXB 换句话说 我们不需要 JAXB 也不依赖注释 但是 由于注释存在 它们最终会被处理注释的其他类引用 这要求我将 JAXB 捆绑到
  • 从 BroadcastReceiver 获取方法来更新 UI

    我正在尝试根据变量的变化更新用户界面BroadcastReceiver 因此 我需要调用一个扩展类的方法 以获取我提到的变量 BroadcastReceiver in MainActivity取决于但我无法以任何方式获得真正的返回值 扩展的
  • Android 5.0 Lollipop 中屏幕固定关闭时如何收到通知?

    我有一个在后台运行的应用程序 并在手机上发生特定事件时启动活动 我发现在 Android 5 0 中 当用户使用另一个应用程序打开屏幕固定时 startActivity intent 调用将被完全忽略 我的应用程序不知道该活动尚未启动 因此
  • 在浏览器中打开 URL,即使我的应用程序为其注册了意图过滤器

    我的应用程序为某些 URL 注册了一个意图过滤器 因为它可以处理来自这些 URL 的数据 但是 在应用程序内部 我想提供一个按钮来在浏览器中打开这样的 URL 也就是说 如果设置了默认浏览器 则在默认浏览器中打开它 否则提供一个选择器 就像
  • Android 8.1 中 Activity 自行旋转并恢复正常

    我的应用程序在所有 Android 版本上运行良好 但我注意到在 Android 8 1 0 Oreo 中 当我将屏幕从纵向活动转到横向活动时 以及当我按后退按钮时 它会显示异常行为 屏幕自动从横向旋转并恢复正常 看起来 Activity
  • 在 Android 中始终以横向模式打开相机

    在我的 Android 应用程序中 单击按钮后我希望相机以横向模式打开 即使我将手机旋转为纵向模式 相机也应始终处于横向模式或纵向模式 使用此代码在横向模式下打开相机 Intent cameraIntent new Intent Media
  • Android BLE 扫描在后台几分钟后停止

    当我为公司开发新冠肺炎接触者追踪应用程序时 我在后台遇到了 Android 扫描停止问题 这是我尝试过的 添加前台服务 禁用手机中所有与电池相关的优化选项 启用后台运行的应用程序 测试设备 搭载 Android 10 的 Galaxy S2
  • 使用 mupdf android 库导航到特定页面

    我如何使用 muPDF 库导航到特定页面 或者有没有办法让图书馆不记得我最后在那个pdf文件中浏览的是哪一页 Uri uri Uri parse path Intent intent new Intent MainActivity getC
  • 如何更改对话框片段内的片段

    我想做一个空的DialogFragment with a LinearLayout然后更改里面的片段LinearLayout 例如 第一个片段是 3 个按钮 facebook google 电子邮件登录 的登录 当有人按下电子邮件时 第 2
  • 如何使用 Swipe 视图实现 Android TabLayout 设计支持库

    我将使用 android TabLayout 设计支持库 但我不知道如何使用滑动视图 这是我的代码 XML
  • 如何知道用户是否在 Android 应用程序中输入了错误的密码(锁定屏幕)

    我正在开发一个 Android 应用程序 如果用户在 Android 锁定屏幕中输入错误的密码 则必须完成其中一项活动 例如 如果用户输入错误的密码 则会发送电子邮件 我将不胜感激任何帮助 提前致谢 Kshitij 锁屏在完全沙箱环境中运行
  • 在片段之间切换时底部导航栏会向下推

    在我的活动中 我有一个底部导航栏和框架布局来显示片段 一切正常 但问题是当我开始按顺序从 1 4 移动时 底部导航栏保持在其位置 但当我突然从 4 跳到2 然后底部导航栏就会超出屏幕 当再次单击同一项目时 它就会回到正常位置 该视频将清楚地
  • 如何在 onDraw() 方法中定义与像素无关的高度

    我扩展了 View 来构建自定义小部件 我想用独立的像素单位定义小部件的高度 我认为可以通过将像素密度乘以所需的高度来完成 但我不知道该怎么做 到目前为止我所拥有的 最小化 public class Timeline extends Vie
  • 将 Crashlytics 集成到图书馆项目

    我有一个图书馆项目 自定义视图库项目 它没有任何活动 服务 我想将 Crashlytics SDK 集成到我的库中 当我尝试通过 Android Studio 的 Crashlytics 插件 工具栏中的图标 添加它时 它只是停留在 Che
  • 从 sqlite 和 mysql 加载数据微调器

    我试试这个tutorial http nielpoenya blogspot com 2012 08 tutorial android spinner dari database html加载Spinner from sqlite and
  • 改造方法调用可能会产生“java.lang.NullPointerException”

    使用 Retrofit 2 3 0 我在 Android Studio 中收到以下消息 有关如何删除此 IDE 错误消息的任何建议 谢谢 来自Response文档 http square github io retrofit 2 x ret
  • 查询联系人 - 有时返回空游标

    我正在尝试查询联系人的显示名称 Override public void onActivityResult int requestCode int resultCode Intent data switch requestCode case

随机推荐

  • windows系统pycharm安装,opencv安装,anaconda安装

    1 python IDE安装 3 9 https www python org getit 2 pycharm安装 社区版最新 https www jetbrains com pycharm 3 anaconda3安装 https www
  • Electron 自定义 Dock 图标

    转载自https cloud tencent com developer article 1650700 学透 Electron 自定义 Dock 图标 Mac OS 做为前端开发者的首选操作系统相信大家再熟悉不过了 在电脑主界面的底部可以
  • epoll在多线程中的应用-EPOLLEXCLUSIVE和REUSEPORT(一)

    以下均为对epoll在多线程中的使用的一些笔记 如果有不对的地方 烦请指出 主要对于我所遇到的问题进行讨论 不会讨论代码如何改写 探讨如何解决这个问题 一 引言 这些问题均是我在编写我的Web服务器遇到的 我在编写多线程Web服务器的时候
  • Docker 镜像库国内加速的几种方法

    概述 在国内 拉取 Docker 镜像速度慢 时不时断线 无账号导致限流等 比较痛苦 这里提供加速 优化的几种方法 梳理一下 会碰到以下情况 国内下载速度慢 时不时断线 是因为网络被限制了 没有公共镜像库账号导致限流 是因为 Docker
  • 「网页开发|前端开发|Vue」01 快速入门:快速写一个Vue的HelloWorld项目

    本文主要介绍如何用vue开发的标准化工具vue cli快速搭建一个符合实际业务项目结构的hello world网页项目并理解vue的代码文件结构以及页面渲染流程 文章目录 一 准备工作 安装node js 二 项目搭建 创建项目目录 全局安
  • 谁来教我渗透测试——黑客应该掌握的Windows基础

    今天我们看看作为一个黑客对于Windows应该掌握哪些基础知识 主要内容包含以下四个方面 系统目录 服务 端口和注册表 黑客常用的DOS命令及批处理文件的编写 黑客常用的快捷键 以及如何优化系统 登录密码破解 手动清除木马病毒 系统目录 服
  • 2014年总结

    总结的意义在于认清未来的方向 2014年工作 1 ETL Data Warehouse Data Mining 数据挖掘内容很多 如何与企业需求相结合是重点 2 简单的工作流系统开发 3 体会ArgGIS在物流运输企业中的应用 无论云计算以
  • 色彩空间与像素格式

    转载来自 https www cnblogs com leisure chn p 10290575 html 1 色彩空间基础 颜色是不同波长的光对人眼刺激产生的色彩感觉 色彩空间 Color Space 是颜色的数学表示 根据不同的表示方
  • PSO优化LSTM

    有两个py文件 PSO 1和LSTM 1 在资源那里下载 有数据 环境 python TF2 优化的参数有 神隐藏神经元个数 dropout比率 batch size 这个可以根据自己的意愿改 规定上限和下限 UP 64 0 14 32 D
  • java跨时区问题【相差8小时】

    情况一 后端传递给前端 前端展示到页面中的时间与系统时间相差8小时 解决方法 在该类的日期属性字段上加上注解 JsonFormat pattern yyyy MM dd HH mm ss timezone GMT 8 情况二 展示数据时间与
  • 解决Chrome, NET::ERR_CERT_AUTHORITY_INVALID

    文章目录 前言 解决方法一 解决方法二 总结 前言 解决方法一 首先清理一下缓存 三个点 gt 设置 gt 清除浏览数据 即可 如果还解决不了 因为Chrome是默认使用HSTS传输 严格的http传输方式 解决方法二 在Chrome浏览框
  • C++如何切割String对象

    C 如何切割String对象 C 相较于Java Python 并没有提供的字符串分割的函数split 因此需要自己进行编写 在实际的工作中这一功能会被经常使用 所以进行简单的记录一下 核心函数 代码实现的函数是调用String库中的fin
  • 数学:矩阵求导

    矩阵Y对标量x求导 Y y ij dY dx dy ji dx 求导后 Y变转置了 标量y对矩阵X求导 dy dX Dy Dx ij 求导后 不需要转置 重要结论 y U XV u i x ij v j 于是 dy dX u i v j U
  • ACM-子串(字符串处理)

    问题描述 有一些由英文字符组成的大小写敏感的字符串 请写一个程序 找到一个最长的字符串 x 使得 对于已经给出的字符串中的任意一个 y x 或者是 y 的子串 或者 x 中的字符反序之后得到的新字符串是 y 的子串 输入数据 输入 输入的第
  • vue使用高德地图导航路线失败,获取驾车数据失败:INVALID_USER_SCODE(解决方法)

    高德地图使用路线导航 最近做大屏项目 里面有地图定位 路线导航等方面的功能 在做路线定位是一直都是获取不到导航的路线 经过好长时间的百度看高德api文档终于找到解决方法 解决方法 添加高德地图安全密钥 添加高德安全密钥 安全密钥是和key一
  • 计算机毕业设计-基于SSM的网上书店管理系统

    项目背景 本网上系统是针对目前网上的实际需求 从实际工作出发 对过去的网上系统存在的问题进行分析 结合计算机系统的结构 概念 模型 原理 方法 在计算机各种优势的情况下 采用目前最流行的B S结构和java中流行的ssm框架和eclipse
  • jq ajax fail,javascript - jQuery.ajax fail handler not called - Stack Overflow

    Deprecation Notice The jqXHR success jqXHR error and jqXHR complete callbacks are removed as of jQuery 3 0 You can use j
  • 【Linux】-关于调试器gdb的介绍和使用

    作者 小树苗渴望变成参天大树 作者宣言 认真写好每一篇博客 作者gitee gitee 如 果 你 喜 欢 作 者 的 文 章 就 给 作 者 点 点 关 注 吧 文章目录 前言 一 Linux中的debug和release 二 gdb的使
  • Java作用域

    try catch里面的是局部变量 java变量的作用域分为四个级别 类级 对象实例级 方法级 块级 块级变量就是定义在一个块 内部的变量都是局部变量 try 中try后面的 就是一个块级作用域 所以内部定义的变量是局部变量 在Java中
  • Android自定义导览地图组件(一)

    丨版权说明 Android自定义导览地图组件 一 于当前CSDN博客和乘月网属同一原创 转载请说明出处 谢谢 鉴于Android关于自定义导览地图的相关资料以及开源项目贫乏 应Android同行几位小伙伴们的建议 决定写下这篇文章分享给大家