美文网首页Android学习
Android加载高清大图

Android加载高清大图

作者: Elder | 来源:发表于2017-02-16 20:51 被阅读1816次

    前两天在面试当中被问到有没有做过加载高清大图,当时确实没有做过,听面试官提到可以动态加载图片的显示区域。回来后在网上找到了一篇鸿洋大神的博文悔啊-_-!为什么早点没有看到。废话不多说代码如下:

    一、BitmapRegionDecoder

    BitmapRegionDecoder主要用于显示图片的某一块矩形区域,所以可以利用它来完成大图片的动态区域显示。

    • 简单用法:
      • BitmapRegionDecoder提供了一系列的newInstance方法来构造对象,支持传入文件路径,文件描述符,文件的inputstrem等。
        例如:
     BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder
                                             .newInstance(is, false);
    
    • BitmapRegionDecoder.decodeRegion()方法,通过传入矩形区域即可显示图片的指定区域。
     Bitmap bitmap = bitmapRegionDecoder.decodeRegion(rect, options);
    

    参数一为矩形区域,参数二为BitmapFactory.Options。

    • 一个简单的使用例子:
     // load Assets image
            try {
                InputStream is = getAssets().open("qm.jpg");
                BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
                tmpOptions.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(is, null, tmpOptions);
                int width = tmpOptions.outWidth;
                int height = tmpOptions.outHeight;
    
                // 设置显示图片的中心区域
                BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder
                                                 .newInstance(is, false);
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                // JPG图片没有Aphla通道使用该颜色模式更加节省内存
                options.inPreferredConfig = Bitmap.Config.RGB_565;
                // 设置显示区域的矩形大小
                Rect rect = new Rect(
                        width / 2 - 100, height / 2 - 100,
                        width / 2 + 100, height / 2 + 100);
                // 通过BitmapRegionDecoder来解析显示区域的图像
                Bitmap bitmap = bitmapRegionDecoder.decodeRegion(rect, options);
                mShow.setImageBitmap(bitmap);
            } catch (IOException e) {
                e.printStackTrace();
            }
    

    一个简单的使用例子,整体自定义View的设计思想,即是通过滑动时获得的坐标不断设置矩形区域的偏移坐标,然后不断绘制View来完成动态显示大图的效果。具体代码大家可以自行参考鸿洋大神的博客,我这里将我注释的代码分享给大家。

    二、LargeImageView

    • 首先手势检测的两个类:BaseGestureDetector、MoveGestureDetector。
    public abstract class BaseGestureDetector {
    
        protected boolean mGestureInProgress;
    
        protected MotionEvent mPreMotionEvent;
        protected MotionEvent mCurrentMotionEvent;
    
        protected Context mContext;
    
        public BaseGestureDetector(Context context) {
            mContext = context;
        }
    
        public boolean onTouchEvent(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 mDeltaPointer = new PointF();
    
        //用于记录最终结果,并返回
        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);
    
    //        Log.e("TAG", mPrePointer.toString() + " ,  " + mCurrentPointer);
    
            boolean mSkipThisMoveEvent = prev.getPointerCount() != event.getPointerCount();
    
    //        Log.e("TAG", "mSkipThisMoveEvent = " + mSkipThisMoveEvent);
            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)
            {
            }
        }
    
    }
    

    关于这两个检测手势移动的类我还没有研究,各位同学可以自行研究。

    • 最后自定义View的代码如下:
    public class LargeImageView extends View {
        private BitmapRegionDecoder mDecoder;
        /**
         * 图片的宽高
         */
        private int mImageWidth,mImageHeight;
        /**
         * 矩形显示的区域(volatile修饰的对象线程安全)
         */
        private volatile Rect mRect = new Rect();
        /**
         * 手势检测
         */
        private MoveGestureDetector mDetector;
    
        private static final BitmapFactory.Options options = 
                                  new BitmapFactory.Options();
    
        static {
            options.inPreferredConfig = Bitmap.Config.RGB_565;
        }
    
        public LargeImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        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;
    //            Log.d("xns", "width:" + mImageWidth + " ,height:" + mImageHeight);
                // 重新布局View
                requestLayout();
                invalidate();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (is != null) {
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        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();
                    // 只要图片在X或Y方向上大于View的宽高才进行矩形区域的偏移
                    if (mImageWidth > getWidth()) {
                        mRect.offset(-moveX, 0);
                        checkWidth();
                        // 偏移后重新绘制显示图片
                        invalidate();
                    }
                    if (mImageHeight > getHeight())
                    {
                        mRect.offset(0, -moveY);
                        checkHeight();
                        invalidate();
                    }
                    return true;
                }
    
            });
        }
    
        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();
            }
        }
    
        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();
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            //Log.d("xns", "x:" + event.getX() + " ,y:" + event.getY());
            mDetector.onTouchEvent(event);
            return true;
        }
    
        @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;
    
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
    
            Bitmap bitmap = mDecoder.decodeRegion(mRect, options);
            canvas.drawBitmap(bitmap, 0, 0, null);
        }
    }
    
    • 使用代码如下:
    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
        private LargeImageView mLIv;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mLIv = (LargeImageView) findViewById(R.id.iv_large);
    
            // load Assets image
            try {
                InputStream is = getAssets().open("qm.jpg");
                // 调用自定义大图加载View
                mLIv.setInputStream(is);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    写在最后

    最近找工作找的很烦躁,最后写给自己的寄语,以此激励自己一下,静心思考,勇往直前。

    相关文章

      网友评论

      本文标题:Android加载高清大图

      本文链接:https://www.haomeiwen.com/subject/xpshwttx.html