自定义 View:如何实现手动拖动的图片控件
 作者:Changing Lin
- 2021 年 11 月 12 日
- 本文字数:3014 字 - 阅读完需:约 10 分钟 
1.知识点
- 属性动画:ObjectAnimator 
- 动画懒加载 
- 手势拖动:GestureDetector.onScroll 
2.原理
- 使用属性动画可以快速的修改某一个属性,并让其从起始值变化到结束值 
- 懒加载的优点是:实例只有在使用的时候才会初始化,一定程度上节约了资源 
- GestureDetector.onScroll 参数 distanceX 表示:旧的点-新的点 
/**         * Notified when a scroll occurs with the initial on down {@link MotionEvent} and the         * current move {@link MotionEvent}. The distance in x and y is also supplied for         * convenience.         *         * @param e1 滑动事件按下的起始点         * @param e2 滑动事件的当前位置         * @param distanceX 两次调用onScroll之间在X轴上滑动的距离         * @param distanceY 两次调用onScroll之间在Y轴上滑动的距离         * @return true if the event is consumed, else false         */        boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);复制代码
 3.代码
import android.animation.ObjectAnimator;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Paint;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import android.widget.OverScroller;
import androidx.annotation.Nullable;import androidx.core.view.GestureDetectorCompat;
public class ScalableImageView extends View implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {
    public static final String TAG = "ScalableImageView";    public static final float IMAGE_WIDTH = Util.dp2px(300); // 指定图片的尺寸    public static final float OVER_SCALE_FACTOR = 1.5f;
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);    Bitmap bitmap;
    float originalOffsetX;    float originalOffsetY;
    float offsetX, offsetY;
    float smallScale;    float bigScale;
    boolean isBig = false;    float scaleFraction = 0f;
    ObjectAnimator scaleAnimator;
    GestureDetectorCompat detector;
    public void setScaleFraction(float scaleFraction) {        this.scaleFraction = scaleFraction;        invalidate();    }
    public float getScaleFraction() {        return scaleFraction;    }
    {        bitmap = Util.getAvatar(getResources(), (int) IMAGE_WIDTH, R.drawable.audi);
    }
    public ScalableImageView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);
        detector = new GestureDetectorCompat(context, this);        scroller = new OverScroller(context);    }
    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);
        originalOffsetX = (getWidth() - bitmap.getWidth()) / 2.0f;        originalOffsetY = (getHeight() - bitmap.getHeight()) / 2.0f;
        if ((float) bitmap.getWidth() / bitmap.getHeight() > (float) getWidth() / getHeight()) {            smallScale = (float) getWidth() / bitmap.getWidth();            bigScale = (float) getHeight() / bitmap.getHeight()*OVER_SCALE_FACTOR;        } else {            smallScale = (float) getHeight() / bitmap.getHeight();            bigScale = (float) getWidth() / bitmap.getWidth()*OVER_SCALE_FACTOR;        }
    }
    private ObjectAnimator getScaleAnimator(){        if (null == scaleAnimator){            scaleAnimator = ObjectAnimator.ofFloat(this, "scaleFraction", 0, 1);            scaleAnimator.setStartDelay(0);            scaleAnimator.setDuration(500);        }        return scaleAnimator;    }    @Override    protected void onDetachedFromWindow() {        super.onDetachedFromWindow();        if (null != scaleAnimator) scaleAnimator.cancel();    }
    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);
        canvas.translate(offsetX, offsetY);        float scale = smallScale+ (bigScale-smallScale)*scaleFraction;        canvas.scale(scale, scale, getWidth() / 2, getHeight() / 2);        canvas.drawBitmap(bitmap, originalOffsetX, originalOffsetY, paint);    }
    @Override    public boolean onTouchEvent(MotionEvent event) {        return detector.onTouchEvent(event);    }
    @Override    public boolean onDown(MotionEvent e) {        Log.e(TAG, "onDown: "+e);        return true;    }
    @Override    public void onShowPress(MotionEvent e) {        Log.e(TAG, "onShowPress: "+e);    }
    @Override    public boolean onSingleTapUp(MotionEvent e) {        Log.e(TAG, "onSingleTapUp: "+e);        return false;    }
    @Override    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {        Log.e(TAG, "onScroll: "+distanceX);        if (isBig) {            offsetX -= distanceX;            offsetX = Math.min(offsetX, (bitmap.getWidth()*bigScale-getWidth())/2);            offsetX = Math.max(offsetX, -(bitmap.getWidth()*bigScale-getWidth())/2);            offsetY -= distanceY;            offsetY = Math.min(offsetY, (bitmap.getHeight()*bigScale-getHeight())/2);            offsetY = Math.max(offsetY, -(bitmap.getHeight()*bigScale-getHeight())/2);            invalidate();        }        return false;    }
    @Override    public void onLongPress(MotionEvent e) {        Log.e(TAG, "onLongPress: "+e);    }
    @Override    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {        Log.e(TAG, "onFling: "+velocityX);//        scroller.fling();        return false;    }
    @Override    public boolean onSingleTapConfirmed(MotionEvent e) {        Log.e(TAG, "onSingleTapConfirmed: "+e);        return false;    }
    @Override    public boolean onDoubleTap(MotionEvent e) {        Log.e(TAG, "onDoubleTap: "+e);        isBig = !isBig;        if (isBig){            getScaleAnimator().start();        } else {            getScaleAnimator().reverse();        }
        return false;    }
    @Override    public boolean onDoubleTapEvent(MotionEvent e) {        Log.e(TAG, "onDoubleTapEvent: "+e);        return false;    }}复制代码
 划线
评论
复制
发布于: 1 小时前阅读数: 3
版权声明: 本文为 InfoQ 作者【Changing Lin】的原创文章。
原文链接:【http://xie.infoq.cn/article/aff531c456382b2a965b60e5d】。文章转载请联系作者。


Changing Lin
关注
获得机遇的手段远超于固有常规之上~ 2020.04.29 加入
我能做的,就是调整好自己的精神状态,以最佳的面貌去面对那些未曾经历过得事情,对生活充满热情和希望。











 
    
评论