首页 > 其他分享 >View->可拖拽滑动的ImageView + Fling惯性滑动效果 + 回弹效果

View->可拖拽滑动的ImageView + Fling惯性滑动效果 + 回弹效果

时间:2024-06-09 17:32:38浏览次数:17  
标签:val Fling top ImageView var 滑动 event View

XML文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">
    <com.gallery20.app.MyImageView
        android:id="@+id/real_iv"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_marginTop="30dp"
        android:layout_marginBottom="30dp"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
        android:background="#00000000"/>
</LinearLayout>

Activity代码

const val TAG = "Yang"
class MainActivity : AppCompatActivity() {
    var mRealView: MyImageView? = null
    var tempBitmap: Bitmap? = null
    var screenWidth = 0
    var screenHeight = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mRealView = findViewById(R.id.real_iv)
        // 屏幕宽高的一半作为临时RectF, 用于压缩Bitmap
        screenWidth = resources.displayMetrics.widthPixels
        screenHeight = resources.displayMetrics.heightPixels
        val tempRect = RectF(0f, 0f, screenWidth.toFloat() / 2, screenHeight.toFloat() / 2)
        
        CoroutineScope(Dispatchers.IO).launch {
            tempBitmap = getBitmap(resources, tempRect, R.drawable.fake)
            withContext(Dispatchers.Main) {
                mRealView?.setImageBitmap(tempBitmap)
            }
        }
    }
}
fun getBitmap(resources : Resources, destRect : RectF, imageId: Int): Bitmap? {
    var imageWidth = -1
    var imageHeight = -1
    val preOption = BitmapFactory.Options().apply {
        // 只获取图片的宽高
        inJustDecodeBounds = true
        BitmapFactory.decodeResource(resources, imageId, this)
    }
    imageWidth = preOption.outWidth
    imageHeight = preOption.outHeight
    // 计算缩放比例
    val scaleMatrix = Matrix()
    // 确定未缩放Bitmap的RectF
    var srcRect = RectF(0f, 0f, imageWidth.toFloat(), imageHeight.toFloat())
    // 通过目标RectF, 确定缩放数值,存储在scaleMatrix中
    scaleMatrix.setRectToRect(srcRect, destRect, Matrix.ScaleToFit.CENTER)
    // 缩放数值再映射到原始Bitmap上,得到缩放后的RectF
    scaleMatrix.mapRect(srcRect)

    val finalOption = BitmapFactory.Options().apply {
        if (imageHeight > 0 && imageWidth > 0) {
            inPreferredConfig = Bitmap.Config.ARGB_8888
            inSampleSize = calculateInSampleSize(
                imageWidth,
                imageHeight,
                srcRect.width().toInt(),
                srcRect.height().toInt()
            )
        }
    }
    return BitmapFactory.decodeResource(resources, imageId, finalOption)
}

fun calculateInSampleSize(fromWidth: Int, fromHeight: Int, toWidth: Int, toHeight: Int): Int {
    var bitmapWidth = fromWidth
    var bitmapHeight = fromHeight
    if (fromWidth > toWidth|| fromHeight > toHeight) {
        var inSampleSize = 1
        // 计算最大的inSampleSize值,该值是2的幂,并保持原始宽高大于目标宽高
        while (bitmapWidth >= toWidth && bitmapHeight >= toHeight) {
            bitmapWidth /= 2
            bitmapHeight /= 2
            inSampleSize *= 2
        }
        return inSampleSize
    }
    return 1
}

自定义Activity代码

class MyImageView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr){

    private var lastX = 0f
    private var lastY = 0f
    private val scroller = Scroller(context)
	// private val scroller = OverScroller(context)
    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                lastX = event.rawX
                lastY = event.rawY
            }
            MotionEvent.ACTION_MOVE -> {
                val dx = event.rawX - lastX
                val dy = event.rawY - lastY
                val left = left + dx.toInt()
                val top = top + dy.toInt()
                val right = right + dx.toInt()
                val bottom = bottom + dy.toInt()
                layout(left, top, right, bottom)
                lastX = event.rawX
                lastY = event.rawY
            }
            MotionEvent.ACTION_UP -> {
                // 手指抬起时,根据速度进行惯性滑动
                // (int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY)
                // startX, startY:开始滑动的位置
                // velocityX, velocityY:滑动的速度
                // minX, maxX, minY, maxY:滑动的范围
                val parentView = (parent as? View)
                scroller.fling(left, top, 500, 500, 0, parentView?.width!!-width, 0, parentView?.height!!-height)
                invalidate()  // 请求重绘View,这会导致computeScroll()被调用
            }
        }
        return true
    }

    override fun computeScroll() {
        if (scroller.computeScrollOffset()) {
            // 更新View的位置
            val left = scroller.currX
            val top = scroller.currY
            val right = left + width
            val bottom = top + height
            layout(left, top, right, bottom)
            if (!scroller.isFinished) {
                invalidate()  // 继续请求重绘View,直到滑动结束
                Log.i("yang", "computeScroll: $left, $top, $right, $bottom")
            }
        }
    }
}
  • Fling惯性滑动效果是指在手指抬起后,即ACTION_UP事件中计算惯性滑动后,当前View移动的距离
  • 回弹效果的实现在调用Scroller.fling()方法或者OverScroller.fling()方法时定义minX, maxX, minY, maxY定义四个参数的大小。一般minXminY为0,如果能在父View中进行拖拽滑动,不超出边界,则 maxXmaxY为父View的宽高减去需要拖拽滑动View的宽高
  • Scroller在进行越界滑动时没有提供弹性滑动,Scroller会直接闪烁回指定滑动范围内
  • OverScroller在进行越界滑动时提供弹性滑动,OverScroller会继续滑动一段距离,然后慢慢回弹,OverScroller.fling()方法最后两个参数定义了超出滑动范围后水平和竖直方向可弹性减速滑动的最大距离,一般为当前滑动View的宽高

效果图

  • Scroller惯性滑动效果
    在这里插入图片描述
  • OverScroller惯性滑动效果
    在这里插入图片描述

标签:val,Fling,top,ImageView,var,滑动,event,View
From: https://blog.csdn.net/sunshine_guo/article/details/139468450

相关文章

  • (算法)水果成篮——<滑动窗口>
    1.题⽬链接:904.⽔果成篮2.题⽬描述: 3.解法(滑动窗⼝): 算法思路:研究的对象是⼀段连续的区间,可以使⽤「滑动窗⼝」思想来解决问题。让滑动窗⼝满⾜:窗⼝内⽔果的种类只有两种。做法: 右端⽔果进⼊窗⼝的时候,⽤哈希表统计这个⽔果的频次。这个⽔果进来后,判断哈希表的......
  • Android 水平滚动List 一项Item占满一页宽 设定单次滑动一次切换一次Item
    背景:水平滚动的List,一项Item占满页面宽度,相当于数量不定的选项卡,每个选项卡占满一页,左右滑动时,如何限制一次只能滑动一个Item步骤:1.水平滚动布局linearLayoutManager=newLinearLayoutManager(this);linearLayoutManager.setOrientation(LinearLayoutMana......
  • 122. 滑动窗口最大值(卡码网周赛第二十期(23年用友提前批笔试真题))
    122.滑动窗口最大值(卡码网周赛第二十期(23年用友提前批笔试真题))题目描述给定一个整数数组nums和一个整数k,k表示滑动窗口的大小。你需要找出每个滑动窗口中的最大值与最小值的差,并返回这些差的最大值。输入数组的长度为n,1<=n<=10000,数组中的每个元素范围为[-......
  • uniapp-two-day-two之基础内容and滑动组件和滚动栏
    基础内容又是码农无聊的一天,今天也就上了一节早课,下课想学习的服了结果玩了半天手机,终于是在下午学上了,真的是很难控制自己。闲聊结束。text标签text中有上面这几个属性,其中在我看来selectable是挺重要的一个属性,是吧现在不都说是cv工程师吗?可不就是这个来组成了我们工程师......
  • 滑动窗口最大值-力扣
    在做这道题时,首先想到的解法是使用队列来做,维护一个队列,每次保存滑动窗口大小的长度,并判断此时队列中的最大值,但这样做,在k的值较大时,出现了超时问题,代码如下:classSolution{public:vector<int>maxSlidingWindow(vector<int>&nums,intk){vector<int>r......
  • CSS 头部固定,中间滑动
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width......
  • (nice!!!)LeetCode 3097. 或值至少为 K 的最短子数组 II(位运算、滑动窗口)
    3097.或值至少为K的最短子数组II思路:既然求的是区间,那么我们自然就想到前缀和、滑动窗口、双指针。结合本题的特点:或运算,会发现如果一段连续的区间进行或运算,最多只会有32次运算可以改变,这是因为int型的二进制范围是-2^31~2^31-1,每次增加一个二进制形式的1。所......
  • Day 13| 239. 滑动窗口最大值、 347.前 K 个高频元素
    239.滑动窗口最大值(一刷至少需要理解思路)之前讲的都是栈的应用,这次该是队列的应用了。本题算比较有难度的,需要自己去构造单调队列,建议先看视频来理解。题目链接/文章讲解/视频讲解:https://programmercarl.com/0239.滑动窗口最大值.html思考用单调队列实现,太难了,超过能力范......
  • 滑动窗口最大值
    给你一个整数数组nums,有一个大小为k的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。示例1:输入:nums=[1,3,-1,-3,5,3,6,7],k=3输出:[3,3,5,5,6,7]解释:滑动窗口的位置......
  • 统计子矩阵+二维前缀和+滑动窗口
    题目链接:0统计子矩阵-蓝桥云课(lanqiao.cn)代码#include<iostream>usingnamespacestd;constintN=505;intnum[N][N];intmain(){ intn,m,k; cin>>n>>m>>k; intcount=0; for(inti=1;i<=n;i++){ for(intj=1;j......