自定义ImageView

来源:互联网 发布:淘宝怎么装修店铺视频 编辑:程序博客网 时间:2024/05/16 02:18

继续坚持写博客,懒毛病尽快改掉奋斗。今天写了一个图片查看控件。初步功能具有移动,缩放效果。并且限制了移动的范围,使它不能完全移出边界。放大倍数限制默认给了4,有特别需要的同学可以自己修改,给个自定义参数来设置。双击图片能还原回初始大小和中间对齐的状态。效果图如下:


上面是初始中间对齐状态。


这个是移动状态


这个是缩放状态。

好了,下面贴上代码:

package com.example.huangyi.test.widge;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.PointF;import android.graphics.RectF;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.Drawable;import android.support.annotation.Nullable;import android.support.v4.view.GestureDetectorCompat;import android.util.AttributeSet;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import com.example.huangyi.test.R;/** * Created by huangyi on 17-5-25. */public class ZoomImageView extends View {    private static final int DEF_SIZE = 200;    private static final float MAX_MULTIPLE = 4; //允许放大的最大倍数    private static final float SIZDE_PADDING = 50; //最大剩余的值,不让全移除边界    private Bitmap mBitmap;    private Paint mPaint;    private Matrix mMatrix;         //用于对图片进行变换的矩阵    private RectF originalRect;     //图片初始时边框所对应的矩形    private RectF translateRect;    //变换后图片边框所对应的矩形    private int pointer = 0;        //触摸点个数    private PointF lastP = new PointF();    //在移动图片中,记录上一次的触摸点    private int lastPId = -1;   //在移动图片中,上次触摸点的id,避免多根手指触摸,然后第一根抬起后造成图片跳动    private float lastDistance = -1;     //缩放图片时,上一次两个手指的距离.    private GestureDetectorCompat gestureDetector;    public ZoomImageView(Context context) {        super(context);        init(context,null);    }    public ZoomImageView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init(context, attrs);    }    public ZoomImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context,attrs);    }    private void init(Context context, AttributeSet attrs){        if(attrs != null){            TypedArray ta = context.getResources().obtainAttributes(attrs, R.styleable.ZoomImageView);            Drawable image = ta.getDrawable(R.styleable.ZoomImageView_ximage);            mBitmap = drawable2Bitmap(image);            ta.recycle();        }        mPaint = new Paint();        mPaint.setAntiAlias(true);        mMatrix = new Matrix();        translateRect = new RectF(0,0,mBitmap.getWidth(),mBitmap.getHeight());        originalRect = new RectF(translateRect);        ZoomGestureListener zoomGestureListener = new ZoomGestureListener();        gestureDetector = new GestureDetectorCompat(getContext(),zoomGestureListener);        gestureDetector.setOnDoubleTapListener(zoomGestureListener);    }    //双击就使图片恢复初始中心对齐状态    class ZoomGestureListener extends GestureDetector.SimpleOnGestureListener{        @Override        public boolean onDoubleTap(MotionEvent e) {            mMatrix.reset();                                            //让图片初始的时候与空间中心对齐            mMatrix.postTranslate(getWidth()/2-mBitmap.getWidth()/2,getHeight()/2-mBitmap.getHeight()/2);            mMatrix.mapRect(translateRect);            postInvalidate();            return true;        }    }    /**     * 把drawable转化为bitmap     * @param d     * @return     */    private Bitmap drawable2Bitmap(Drawable d){        if(d instanceof BitmapDrawable){            return ((BitmapDrawable) d).getBitmap();        }else{            Bitmap b = Bitmap.createBitmap(d.getIntrinsicWidth(),d.getIntrinsicHeight(),Bitmap.Config.ARGB_8888);            Canvas c = new Canvas(b);            d.setBounds(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight());            d.draw(c);            return b;        }    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int wSize = MeasureSpec.getSize(widthMeasureSpec);        int hSize = MeasureSpec.getSize(heightMeasureSpec);        int wMode = MeasureSpec.getMode(widthMeasureSpec);        int hMode = MeasureSpec.getMode(heightMeasureSpec);        //在wrap_content的时候就给一个默认的尺寸        if(wMode == MeasureSpec.AT_MOST){            wSize = Math.min(wSize,DEF_SIZE);        }        if(hMode == MeasureSpec.AT_MOST){            hSize = Math.min(hSize,DEF_SIZE);        }        setMeasuredDimension(MeasureSpec.makeMeasureSpec(wSize,wMode),                MeasureSpec.makeMeasureSpec(hSize,hMode));        mMatrix.reset();                                            //让图片初始的时候与空间中心对齐        mMatrix.postTranslate(wSize/2-mBitmap.getWidth()/2,hSize/2-mBitmap.getHeight()/2);        mMatrix.mapRect(translateRect);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawBitmap(mBitmap,mMatrix,mPaint);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        gestureDetector.onTouchEvent(event);        switch (event.getActionMasked()){            case MotionEvent.ACTION_DOWN:                if(isTouchOnBitmap(event,translateRect)){       //第一个触摸点在图片上才能进行操作                    pointer = 1;                    lastP.set(event.getX(),event.getY());                    lastPId = event.getPointerId(0);                    return true;                }else {                    return false;                }            case MotionEvent.ACTION_POINTER_DOWN:                pointer +=1;                lastDistance = (float) getDistance(event.getX(0),event.getY(0),event.getX(1),event.getY(1));                break;            case MotionEvent.ACTION_MOVE:                if(pointer == 1  && event.getPointerId(0) == lastPId){       //只有一根手指时就是移动                    moveBitmap(event);                }else if(pointer >= 2){ //两根手指就是放大缩小                    scaleBitmap(event);                }                lastP.set(event.getX(),event.getY());                break;            case MotionEvent.ACTION_POINTER_UP:                pointer -=1;                if(event.getPointerId(0) != lastPId){   //松开了最先按下的那个手指,就要更新第一个点的信息,避免跳动                    lastP.set(event.getX(),event.getY());                    lastPId = event.getPointerId(0);                }                break;            case MotionEvent.ACTION_UP:                pointer = 0;                lastDistance = -1;                break;        }        return super.onTouchEvent(event);    }    /**     * 触摸点是否在图片上,只有触摸点在图片上才能移动图片     * @param event     * @param rectF     * @return     */    private boolean isTouchOnBitmap(MotionEvent event,RectF rectF){        return rectF.contains(event.getX(),event.getY());    }    /**     * 单根手指时移动图片     * @param event     */    private void moveBitmap(MotionEvent event){        float disX = event.getX()-lastP.x;        float disY = event.getY()-lastP.y;        if(translateRect.top + disY > getHeight() - SIZDE_PADDING ||            //边界检测,不让全移出边界外                translateRect.bottom + disY < SIZDE_PADDING ||                translateRect.left + disX > getWidth() - SIZDE_PADDING ||                translateRect.right +disX < SIZDE_PADDING){            return;        }        mMatrix.postTranslate(disX,disY);        mMatrix.mapRect(translateRect,originalRect);        //更新translateRect,代表了变换后的图片的位置        postInvalidate();    }    /**     * 两根手指时放大缩小图片     * @param event     */    private void scaleBitmap(MotionEvent event){        float curDis = (float) getDistance(event.getX(0),event.getY(0),event.getX(1),event.getY(1));        if(lastDistance == -1){            lastDistance = curDis;        }        float scale = curDis/lastDistance;        if(translateRect.width()/originalRect.width()*scale > MAX_MULTIPLE){    //倍数不能大于MAX_MULTIPLE            return;        }        lastDistance = curDis;        PointF centerP = getCenter(event.getX(0),event.getY(0),event.getX(1),event.getY(1));        mMatrix.postScale(scale,scale,centerP.x,centerP.y);        mMatrix.mapRect(translateRect,originalRect);                //更新translateRect,代表了变换后的图片的位置        postInvalidate();    }    /**     * 获得两点的距离     * @param x1     * @param y1     * @param x2     * @param y2     * @return     */    private double getDistance(float x1,float y1,float x2,float y2){        return Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2))   ;    }    /**     * 获得两点的中心点     * @param x1     * @param y1     * @param x2     * @param y2     * @return     */    private PointF getCenter(float x1,float y1,float x2,float y2){        return new PointF((x1+x2)/2.0f,(y1+y2)/2.0f);    }}

下面是自定义参数,只写了一个。同学们可以根据需要自己写更多的定制项。

    <declare-styleable name="ZoomImageView">        <attr name="ximage" format="reference"></attr>    </declare-styleable>

布局文件:

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.huangyi.test.MainActivity">    <com.example.huangyi.test.widge.ZoomImageView        android:id="@+id/zoom"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_gravity="center"        app:ximage="@drawable/carton"/></FrameLayout>

整个代码主要就是围绕对Metrix的操作变换和多点触摸的编写。