自定义ImageView

继续坚持写博客,懒毛病尽快改掉奋斗。今天写了一个图片查看控件。初步功能具有移动,缩放效果。并且限制了移动的范围,使它不能完全移出边界。放大倍数限制默认给了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的操作变换和多点触摸的编写。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值