Android:实现最简单的单指移动、双指缩放的图片组件

本例实现了最简单的单指移动、双指缩放的图片组件,效果图如下:

     

     

功能:

1.单指移动,双指缩放。

2.可控制缩放范围,防止过大或过小;初始化时自动缩放至组件大小,并居中显示。

3.边界控制,防止图片“移出去了”。

4.可使用在xml中,并自动适应组件大小。

5.代码简洁!!!

核心代码:DragScaleView.java

package com.sina.simplegestureimage;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

/**
 * DragScaleView
 * Created by hanswim on 15-1-23.
 */
public class DragScaleView extends View {
    //监听图片缩放
    private ScaleGestureDetector mScaleDetector;
    //监听图片移动
    private GestureDetector mGestureDetector;

    //当前的缩放比例
    private float mScaleFactor = 1.0f;

    public DragScaleView(Context context) {
        super(context);
    }

    public DragScaleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public DragScaleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void init(Context context) {
        mScaleDetector = new ScaleGestureDetector(context, new SimpleScaleListenerImpl());
        mGestureDetector = new GestureDetector(context, new SimpleGestureListenerImpl());
    }

    private Paint bmpPaint = new Paint();
    //图片资源
    private Bitmap bmp;
    //图片的宽高
    private int bmpWidth, bmpHeight;

    public void setImageResource(int id) {
        bmp = BitmapFactory.decodeResource(getResources(), id);
        bmpWidth = bmp.getWidth();
        bmpHeight = bmp.getHeight();

        initViewSize();

        invalidate();
    }

    //绘制图片的起始位置
    private float mPosX, mPosY;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (bmp == null) {
            return;
        }

        if (!hasGetViewSize) {
            initViewSize();
        }

        canvas.save();
        checkBounds();
        //以图片的中心为基点进行缩放
        canvas.scale(mScaleFactor, mScaleFactor, mPosX + bmpWidth / 2, mPosY + bmpHeight / 2);

        canvas.drawBitmap(bmp, mPosX, mPosY, bmpPaint);
        canvas.restore();
    }

//    private float lastX, lastY;

//    private static final int INVALID_POINTER_ID = -1;
//    private int mActivePointerId = INVALID_POINTER_ID;

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        //双指缩放
        mScaleDetector.onTouchEvent(event);
        //单指移动
        mGestureDetector.onTouchEvent(event);

        return true;

        //也可以自己实现“单指移动图片”
        /*
        int action = MotionEventCompat.getActionMasked(event);

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                final int pointerIndex = MotionEventCompat.getActionIndex(event);
                mActivePointerId = MotionEventCompat.getPointerId(event, pointerIndex);

                lastX = event.getX();
                lastY = event.getY();
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                // Find the index of the active pointer and fetch its position
                final int pointerIndex = MotionEventCompat.findPointerIndex(event, mActivePointerId);
                float currentX = MotionEventCompat.getX(event, pointerIndex);
                float currentY = MotionEventCompat.getY(event, pointerIndex);

                mPosX += (currentX - lastX);
                mPosY += (currentY - lastY);
                invalidate();

                lastX = currentX;
                lastY = currentY;

                break;
            }
            case MotionEvent.ACTION_POINTER_UP: {
                final int pointerIndex = MotionEventCompat.getActionIndex(event);
                final int pointerId = MotionEventCompat.getPointerId(event, pointerIndex);

                if (pointerId == mActivePointerId) {
                    // This was our active pointer going up. Choose a new
                    // active pointer and adjust accordingly.
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    lastX = MotionEventCompat.getX(event, newPointerIndex);
                    lastY = MotionEventCompat.getY(event, newPointerIndex);
                    mActivePointerId = MotionEventCompat.getPointerId(event, newPointerIndex);
                }

                break;
            }

            case MotionEvent.ACTION_UP: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }
        }
        */

    }

    /**
     * 不能超出边界.
     * 原则是:图片较小时任意一条边都不能出了边界,图片较大任意一条边都不能进入边界。宽度和高度分别独立计算。
     */
    private void checkBounds() {
        if (mScaleFactor > widthScale) {
            //宽度方向已经填满
            mPosX = Math.min(mPosX, (mScaleFactor - 1) * (bmpWidth / 2));
            mPosX = Math.max(mPosX, viewWidth - bmpWidth - (mScaleFactor - 1) * (bmpWidth / 2));
        } else {
            mPosX = Math.max(mPosX, (mScaleFactor - 1) * (bmpWidth / 2));
            mPosX = Math.min(mPosX, viewWidth - bmpWidth - (mScaleFactor - 1) * (bmpWidth / 2));
        }

        if (mScaleFactor > heightScale) {
            //高度方向已经填满
            mPosY = Math.min(mPosY, (mScaleFactor - 1) * (bmpHeight / 2));
            mPosY = Math.max(mPosY, viewHeight - bmpHeight - (mScaleFactor - 1) * (bmpHeight / 2));
        } else {
            mPosY = Math.max(mPosY, (mScaleFactor - 1) * (bmpHeight / 2));
            mPosY = Math.min(mPosY, viewHeight - bmpHeight - (mScaleFactor - 1) * (bmpHeight / 2));
        }
    }

    private int viewWidth, viewHeight;
    //组件尺寸只需要获取一次
    private boolean hasGetViewSize;

    private void initViewSize() {
        viewWidth = getWidth();
        viewHeight = getHeight();

        if (viewWidth > 0 && viewHeight > 0) {
            hasGetViewSize = true;

            widthScale = 1.0f * viewWidth / bmpWidth;
            heightScale = 1.0f * viewHeight / bmpHeight;
            //初始缩放比例(使组件刚好铺满)
            mScaleFactor = Math.min(widthScale, heightScale);

            //初始时图片居中绘制
            mPosX = viewWidth / 2 - bmpWidth / 2;
            mPosY = viewHeight / 2 - bmpHeight / 2;
        }
    }

    /**
     * 宽度和高度放大多少倍时,刚好填满此方向的屏幕
     */
    private float widthScale, heightScale;

    //缩放
    private class SimpleScaleListenerImpl extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();
            //缩放倍数范围:0.3~3
            mScaleFactor = Math.max(0.3f, Math.min(mScaleFactor, 3.0f));

            invalidate();
            return true;
        }
    }

    //移动
    private class SimpleGestureListenerImpl extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            mPosX -= distanceX;
            mPosY -= distanceY;

            invalidate();
            return true;
        }
    }
}

在Activity中使用:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.drag_scale);

        RadioButton landscapeRBtn = (RadioButton) findViewById(R.id.radio_landscape);
        final DragScaleView dragView = (DragScaleView) findViewById(R.id.drag_scale_view);

        dragView.setImageResource(R.drawable.cat_boarder);

        landscapeRBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    dragView.setImageResource(R.drawable.cat_boarder);
                } else {
                    dragView.setImageResource(R.drawable.cat_boarder_p);
                }
            }
        });
    }

======

源代码即将奉上,尽情期待。

时间: 2024-10-27 05:45:28

Android:实现最简单的单指移动、双指缩放的图片组件的相关文章

在viewPager中双指缩放图片,双击缩放图片,单指拖拽图片

我们就把这个问题叫做图片查看器吧,它的主要功能有: 1.双击缩放图片. 2. 双指缩放图片. 3.单指拖拽图片. 为此这个图片查看器需要考虑以下的技术点: 一.双击缩放图片: 1.如果图片高度比屏幕的高度小得多,那么就将图片放大到高度与屏幕高度相等,否则就放大一个特定的倍数. 2.如何判断是否到达这个倍数来停止缩放. 3.判断完且停止放大后,图片可能已经超出了这个倍数需要的大小,如何回归到我们的目标大小. 4.判断完且停止缩小后,图片宽度可能已经小于屏幕宽度,在两边留下了空白,如何重置为原来的大

讲解一下iOS图片单指旋转缩放实现方法

最近做一个项目,里边要做图片处理功能,其中就有图片单指旋转,缩放.由于之前还没做过这样的功能,于是乎找了下相关的资料,终于找到了一种好的实现方案.于是就仿照美图秀秀里边贴纸的功能做了一个demo...以下贴一些主要实现代码.... /*****头文件*********/ #import <UIKit/UIKit.h> @interface ImageEditView : UIView // 背景图片 @property (nonatomic, weak, readonly) UIImageVi

ios UITapGestureRecognizer 单指单击、单指多击、多指单击、多指多击事件操作

转自:http://blog.csdn.net/longzs/article/details/7457108 在ios开发中,需用到对于手指的不同操作,以手指点击为例:分为单指单击.单指多击.多指单击.多指多击.对于这些事件进行不同的操作处理,由于使用系统自带的方法通过判断touches不太容易处理,而且会有事件之间的冲突. 接下来,通过以UITapGestureRecognizer 手势实现此功能需求 代码如下: ViewController.m中的viewDidLoad方法: 1: //单指

simple-spa 一个简单的单页应用实例

上两篇文章说过要写一个简单的单页应用例子的, 迟迟没有兑诺, 实在有愧 哈哈.这篇写给小白用户哈. 正好趁今天风和日丽,事情不多, 把从项目里的代码扣取了一下, 整理了一个简单的例子.因为我们生产项目用到es6 还有构建工具,为了让例子足够简单和原生,除了一个zepto,连require都是我之前写的文章里的实现的,很简单70行代码. 事例地址 github:https://github.com/skyweaver213/simple-spa demo: https://skyweaver213

Android ExpandableListView的简单应用

Expandablelistview1Activity.java package com.wangzhu.demoexpandablelistview; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.Activity; import android.os.Bundle; import android.widg

【原创】android——SQLite实现简单的注册登陆(已经美化)

1,Main_activity的xmL配置 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_pa

如何利用jQuery进行简单表单验证

如何利用jQuery进行简单表单验证:利用jQuery可以进行表单验证,当然有比较复杂的表单验证,这里就不介绍了,下面就简单介绍一下如何使用jQuery进行简单的表单验证,然后大家可以通过改造进行基本的表达验证了.先看一段代码实例: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="author" content="http

Android HttpGet() 请求简单入门实例

HttpClient httpclient = new DefaultHttpClient(); String url = "http://example.com"; List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add( new BasicNameValuePair( "param", "value" ) ); URI uri =

【android】Socket简单用法

原文地址:http://www.cnblogs.com/harrisonpc/archive/2011/03/31/2001565.html Socket通常也称做”套接字“,用于描述IP地址和端口,废话不多说,它就是网络通信过程中端点的抽象表示.值得一提的是,Java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端.这是两个封装得非常好的类,使用起来很方便! 下面将首先创建一个SocketServer的类作为服务端如下,该服务端实现