上一节(自定义初学5——自定义View显示图片)已经说了如何自定义View显示图片。做android时,加载图片是避免不了的,加载网络图片还需要异步加载,最烦人的就是经常出现OOM,为了避免这样的问题,我们一般这样解决:
  1. 根据图片控件的大小对图片进行压缩显示。
  2. 如果图片数量非常多,则会使用LruCache等缓存机制,将所有图片占据的内容维持在一个范围内。

有时加载图片还会遇到特殊情况——就是单个图片非常巨大,还不允许压缩。那么对于这种需求,该如何做呢?

首先不压缩,按照原图尺寸加载,那么屏幕肯定是不够大的,并且考虑到内存的情况,不可能一次性整图加载到内存中,所以肯定是局部加载,而有一个类很好的完成了这个功能,这就是BitmapRegionDecoder

先看下官方的API

写个简单的例子:
public class MainActivity extends Activity {
    private ImageView mImageView;
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
mImageView = (ImageView) findViewById(R.id.imageView1);
        try {
            InputStream inputStream = getAssets().open("dog.jpg");
            // 获得图片的宽、高
            BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
            // tmpOptions.inJustDecodeBounds = true;// 如果设置为true,解码器将返回null
            // 设置显示图片的中心区域
            BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder
                    .newInstance(inputStream, false);
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
//用给定的Rect和options创建位图
            Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(0, 0,
                    600, 600), options);
            mImageView.setImageBitmap(bitmap);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
效果如下:
很明显,BitmapRegionDecoder的功能就是加载图片的部分区域,现在大概原理就清楚了,我们需要定义一个View,然后利用去BitmapRegionDecoder显示一个图片的局部,然后利用手势检测,能够移动图片,好了,开始实现吧。梳理一下我们的View:
  • 提供一个设置图片的入口
  • 重写onTouchEvent,在里面根据用户移动的手势,去更新显示区域的参数
  • 每次更新区域参数后,调用invalidate,onDraw里面去regionDecoder.decodeRegion拿到bitmap,去draw
自定义LargeImageView
public class LargeImageView extends View {
    private BitmapRegionDecoder mDecoder;
    /**
     * 图片的宽度和高度
     */
    private int mImageWidth, mImageHeight;
    /**
     * 绘制的区域
     */
    private volatile Rect mRect = new Rect();
    private MoveGestureDetector mDetector;
    private static final BitmapFactory.Options options = new BitmapFactory.Options();
    static {
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    }
    public void setInputStream(InputStream is) {
        try {
            mDecoder = BitmapRegionDecoder.newInstance(is, false);
            BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
            // 获取图片的宽高
            tmpOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, tmpOptions);
            mImageWidth = tmpOptions.outWidth;
            mImageHeight = tmpOptions.outHeight;
            requestLayout();
            invalidate();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null)
                    is.close();
            } catch (Exception e) {
            }
        }
    }
    public void init() {
        mDetector = new MoveGestureDetector(getContext(),
                new MoveGestureDetector.SimpleMoveGestureDetector() {
                    @Override
                    public boolean onMove(MoveGestureDetector detector) {
                        int moveX = (int) detector.getMoveX();
                        int moveY = (int) detector.getMoveY();
                        if (mImageWidth > getWidth()) {
                            mRect.offset(-moveX, 0);
                            checkWidth();
                            invalidate();
                        }
                        if (mImageHeight > getHeight()) {
                            mRect.offset(0, -moveY);
                            checkHeight();
                            invalidate();
                        }
                        return true;
                    }
                });
    }
    private void checkWidth() {
        Rect rect = mRect;
        int imageWidth = mImageWidth;
        if (rect.right > imageWidth) {
            rect.right = imageWidth;
            rect.left = imageWidth - getWidth();
        }
        if (rect.left < 0) {
            rect.left = 0;
            rect.right = getWidth();
        }
    }
    private void checkHeight() {
        Rect rect = mRect;
        int imageHeight = mImageHeight;
        if (rect.bottom > imageHeight) {
            rect.bottom = imageHeight;
            rect.top = imageHeight - getHeight();
        }
        if (rect.top < 0) {
            rect.top = 0;
            rect.bottom = getHeight();
        }
    }
    public LargeImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDetector.onToucEvent(event);
        return true;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        Bitmap bm = mDecoder.decodeRegion(mRect, options);
        canvas.drawBitmap(bm, 0, 0, null);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        int imageWidth = mImageWidth;
        int imageHeight = mImageHeight;
        // 默认直接显示图片的中心区域,可以自己去调节
        mRect.left = imageWidth / 2 - width / 2;
        mRect.top = imageHeight / 2 - height / 2;
        mRect.right = mRect.left + width;
        mRect.bottom = mRect.top + height;
    }
}

手势检测抽象类BaseGestureDetector
public abstract class BaseGestureDetector {
     protected boolean mGestureInProgress;
        protected MotionEvent mPreMotionEvent;
        protected MotionEvent mCurrentMotionEvent;
        protected Context mContext;
        public BaseGestureDetector(Context context)
        {
            mContext = context;
        }
        public boolean onToucEvent(MotionEvent event)
        {
            if (!mGestureInProgress)
            {
                handleStartProgressEvent(event);
            } else
            {
                handleInProgressEvent(event);
            }
            return true;
        }
        protected abstract void handleInProgressEvent(MotionEvent event);
        protected abstract void handleStartProgressEvent(MotionEvent event);
        protected abstract void updateStateByEvent(MotionEvent event);
        protected void resetState()
        {
            if (mPreMotionEvent != null)
            {
                mPreMotionEvent.recycle();
                mPreMotionEvent = null;
            }
            if (mCurrentMotionEvent != null)
            {
                mCurrentMotionEvent.recycle();
                mCurrentMotionEvent = null;
            }
            mGestureInProgress = false;
        }
}

手势检测实现类
public class MoveGestureDetector extends BaseGestureDetector {
    private PointF mCurrentPointer;
    private PointF mPrePointer;
    // 用于记录最终结果,并返回
    private PointF mExtenalPointer = new PointF();
    private OnMoveGestureListener mListenter;
    public MoveGestureDetector(Context context, OnMoveGestureListener listener) {
        super(context);
        mListenter = listener;
    }
    @Override
    protected void handleInProgressEvent(MotionEvent event) {
        int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
        switch (actionCode) {
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            mListenter.onMoveEnd(this);
            resetState();
            break;
        case MotionEvent.ACTION_MOVE:
            updateStateByEvent(event);
            boolean update = mListenter.onMove(this);
            if (update) {
                mPreMotionEvent.recycle();
                mPreMotionEvent = MotionEvent.obtain(event);
            }
            break;
        }
    }
    @Override
    protected void handleStartProgressEvent(MotionEvent event) {
        int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
        switch (actionCode) {
        case MotionEvent.ACTION_DOWN:
            resetState();// 防止没有接收到CANCEL or UP ,保险起见
            mPreMotionEvent = MotionEvent.obtain(event);
            updateStateByEvent(event);
            break;
        case MotionEvent.ACTION_MOVE:
            mGestureInProgress = mListenter.onMoveBegin(this);
            break;
        }
    }
    protected void updateStateByEvent(MotionEvent event) {
        final MotionEvent prev = mPreMotionEvent;
        mPrePointer = caculateFocalPointer(prev);
        mCurrentPointer = caculateFocalPointer(event);
        boolean mSkipThisMoveEvent = prev.getPointerCount() != event
                .getPointerCount();
        mExtenalPointer.x = mSkipThisMoveEvent ? 0 : mCurrentPointer.x
                - mPrePointer.x;
        mExtenalPointer.y = mSkipThisMoveEvent ? 0 : mCurrentPointer.y
                - mPrePointer.y;
    }
    /**
     * 根据event计算多指中心点
     * 
     * @param event
     * @return
     */
    private PointF caculateFocalPointer(MotionEvent event) {
        final int count = event.getPointerCount();
        float x = 0, y = 0;
        for (int i = 0; i < count; i++) {
            x += event.getX(i);
            y += event.getY(i);
        }
        x /= count;
        y /= count;
        return new PointF(x, y);
    }
    public float getMoveX() {
        return mExtenalPointer.x;
    }
    public float getMoveY() {
        return mExtenalPointer.y;
    }
    public interface OnMoveGestureListener {
        public boolean onMoveBegin(MoveGestureDetector detector);
        public boolean onMove(MoveGestureDetector detector);
        public void onMoveEnd(MoveGestureDetector detector);
    }
    public static class SimpleMoveGestureDetector implements
            OnMoveGestureListener {
        @Override
        public boolean onMoveBegin(MoveGestureDetector detector) {
            return true;
        }
        @Override
        public boolean onMove(MoveGestureDetector detector) {
            return false;
        }
        @Override
        public void onMoveEnd(MoveGestureDetector detector) {
        }
    }
}
用法不说了,看效果吧:


这只是简单的实现,我们当然还可以实现更多的功能了,我就抛砖引玉吧

源代码
参考:

http://blog.csdn.net/lmj623565791/article/details/49300989
http://www.itstrike.cn/Question/904dfdaa-5cec-4d3c-bbc3-a7cc2e6172d8.html
http://blog.csdn.net/hahajluzxb/article/details/8158852

自定义View显示超大图片相关推荐

  1. Android 自定义View 圆形圆角图片

    [Android 自定义View 圆形圆角图片] 基于Xfermode 实现 1.概述 在很久以前也写过一个利用Xfermode 实现圆形.圆角图片的(Android 完美实现图片圆角和圆形(对实现进 ...

  2. Android 中自定义View 裁剪扇形图片

    Android 中自定义View 裁剪扇形图片 当需要裁剪图片为扇形区域时,使用Canvas.clipPath(path)方法可以裁剪为扇形区域 ps:此方法会导致绘制图片边缘有锯齿,暂无解决方法(知 ...

  3. 自定义初学5——自定义View显示图片

    前面已经简单介绍过一些自定义View的实现,现在再利用自定义View实现显示一张图片的功能 1.首先编写attrs.xml文件 <resources>     <declare-st ...

  4. 仿海报工厂效果的自定义View(在图片上输入文字)

    下面这个view来自"易水南风",我在其代码上加入了文字的输入 下载地址 https://github.com/yxkrrhx/PotserView 近期做了一个自定义View,效 ...

  5. 自定义view绘图篇--图片和文字的绘制

    图片的绘制功能可以让我们直接添加一张图片到画布,文字的绘制可以点缀我们view的功能.本章节继续深入,总结下其中的流程. 知识点 一.绘制图片 1.Picture类 (1)Picture类的功能 我们 ...

  6. android 自定义Gifview显示gif图片

    在android 开发中,由于Imagview 不能显示gif图片,所以就得自定义View 来实现显示gif图片 而android.graphics.Movie可以把gif图片解析成Movie帧来显示 ...

  7. android 自定义view 加载图片,Android自定义View基础开发之图片加载进度条

    学会了Paint,Canvas的基本用法之后,我们就可以动手开始实践了,先写个简单的图片加载进度条看看. 按照惯例,先看效果图,再决定要不要往下看: 既然看到这里了,应该是想了解这个图片加载进度条了, ...

  8. Android 自定义View消除锯齿实现图片旋转,添加边框及文字说明

    先看看图片的效果,左边是原图,右边是旋转之后的图:   之所以把这个写出来是因为在一个项目中需要用到这样的效果,我试过用FrameLayout布局如上的画面,然后旋转FrameLayout,随之而来也 ...

  9. Android人脸识别绘制人脸框自定义View显示

    创建一个自定义的View,它将绘制人脸框,并重写 onDraw 方法以绘制矩形. 下面是一个示例代码: public class FaceBoundsView extends View {privat ...

  10. github用相对路径显示图片_我写了一个开源工具, 让Github的README.md可以正常显示超大图片...

    最终效果对比 图片替换前: 图片显示有好有坏,能否显示,全凭运气 图片替换后: 所有大图正常显示! 本项目永久开源地址 痛点: Github的README.md展示图片效果并不完美 为了让项目演示更生 ...

最新文章

  1. Exchange 2010 恢复误删除的邮箱账户及其邮箱
  2. 解决Silverlight在ChildWindow中进行DragDrop操作问题
  3. oracle删除unique key,【PK】Oracle 10g删除主键约束后无法删除唯一约束索引问题的模拟与分析...
  4. Windows上编译github源码方式运行Node-RED,以及离线迁移安装Node-RED
  5. 关于2017届学长制作分享软件share(失物招领)的使用体验和需改进的内容
  6. 第五届省赛(软件类)真题----Java大学C组答案及解析
  7. SQL Tuning Advisor使用实例
  8. 微信小程序开发教程(六)配置——app.json、page.json详解
  9. 立刻、马上对你的电脑做这三件事!
  10. 9_flutter_SimpleDialog(对话框),FloatingActionButton(浮动按钮),Slider(滑动器)
  11. 如何全面认识大数据分析的基础知识
  12. android 序列化传参数,Android序列化之Parcelable和Serializable的使用详解
  13. supervisor入门应用
  14. Webrtc 屏幕共享
  15. FastReport.NET v2022.2.7
  16. 金武彩印机械设备有限公司仓储管理系统设计与实现
  17. 基于python语言设计的词云定制器
  18. 联想小新pro16锐龙版和酷睿版的区别 哪个好
  19. 浏览器的判断和Window系统是64位还是32位的判断
  20. 3月份GitHub上最热门的JavaScript开源项目(下)

热门文章

  1. echarts柱形图x轴y轴互换_echarts图表x,y轴的设置
  2. 如何批量保存苏宁易购里的商品图片
  3. 网络服务器主机和普通家用电脑主机的区别在哪里?
  4. Java-SE 学习笔记 第一章-Java历史及其基本介绍
  5. 周杰伦要出新专辑了?上 Instagram 看看
  6. html向下的箭头符号,向下的箭头符号
  7. Word排版之论文的诞生(目录篇)
  8. 对于无线网络经常掉线的问题
  9. 3D~RPG游戏的制作
  10. 华退学博士王垠:离开是为了获得力量后再回来