zoukankan      html  css  js  c++  java
  • Android中实现一个可拖动的悬浮按钮,点击弹出菜单的功能

    如图:

             

    实现思路
    通过重写控件的onTouchEvent方法监听触摸效果
    通过View的setX()和setY()方法实现移动
    使用属性动画实现边缘吸附效果
    手指按下
    首先是处理手指按压下的事件,这里我们把拖拽标识符设置为false并记录当前点击的屏幕坐标。然后我们在移动事件处

    手指移动
    这里我们把拖拽标识符设置为true,因为手指移动了。然后我们需要计算手指移动了多少偏移量

    //计算手指移动了多少
    int dx=rawX-lastX;
    int dy=rawY-lastY;

    而后的两行代码表示控件需要移动的具体距离,后面有一个简单的边缘检测计算。最终通过调用setX以及setY方法实现控件的移动

    手指松开
    这里如果是拖拽动作我们才需要处理自己的逻辑否则直接跳过即可。在这里我们首先恢复了按钮的按压效果,在源代码中找到setPressed(boolean)方法,这是处理按钮点击效果用的,在这里当手指松开后我们需要恢复按钮原来的效果。然后在判断控件需要往哪边吸附,吸附的过程就是做属性动画而已,原理还是不断的改变setX方法让按钮靠边移动

    代码区
    关于自定义的拖拽View (核心部分)
    继承类(均可实现拖拽)

    有的继承AppCompatImageView下的ImageView
    有的继承FloatingActionButton(如果继承此类需要导入以下依赖)

    compile 'com.android.support:design:26.1.0'
    implementation 'com.android.support:appcompat-v7:26.1.0'
    

    DragFloatActionButton (Kotlin版)

    package com.example.mychartdemo
    
    import android.animation.ObjectAnimator
    import android.annotation.SuppressLint
    import android.content.Context
    import android.util.AttributeSet
    import android.util.Log
    import android.view.MotionEvent
    import android.view.ViewGroup
    import android.view.animation.DecelerateInterpolator
    import android.widget.ImageView
    //https://blog.csdn.net/qq_20451879/article/details/87876673
    //效果可以
    @SuppressLint("AppCompatCustomView")
    class DragFloatActionButton : ImageView {
        private var parentHeight: Int = 0
        private var parentWidth: Int = 0
    
        constructor(context: Context?) : super(context)
        constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
        constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
    
        private var lastX: Int = 0
        private var lastY: Int = 0
    
        public var isDrag: Boolean = false
        public var isRight: Boolean = false
        public var isTop: Boolean = true
        public var isCanMove: Boolean = true
    
        override fun onTouchEvent(event: MotionEvent): Boolean {
            val rawX = event.rawX.toInt()
            val rawY = event.rawY.toInt()
            Log.i("打印X轴、Y轴坐标:","rawX:$rawX  ,rawY: $rawY")
             if (!isCanMove){//不可移动
                 return super.onTouchEvent(event)
             }
    
            when (event.action and MotionEvent.ACTION_MASK) {
                MotionEvent.ACTION_DOWN -> {
                    isPressed = true
                    isDrag = false
                    parent.requestDisallowInterceptTouchEvent(true)
                    lastX = rawX
                    lastY = rawY
                    val parent: ViewGroup
                    if (getParent() != null) {
                        parent = getParent() as ViewGroup
                        parentHeight = parent.height
                        parentWidth = parent.width
                    }
                }
                MotionEvent.ACTION_MOVE -> {
                    isDrag = !(parentHeight <= 0 || parentWidth === 0)
                    /*if (parentHeight <= 0 || parentWidth === 0) {
                        isDrag = false
                    } else {
                        isDrag = true
                    }*/
                    val dx = rawX - lastX
                    val dy = rawY - lastY
                    //这里修复一些华为手机无法触发点击事件
                    val distance = Math.sqrt((dx * dx + dy * dy).toDouble()).toInt()
                    if (distance == 0) {
                        isDrag = false
                    } else {
                        var x = x + dx
                        var y = y + dy
                        //检测是否到达边缘 左上右下
                        x = if (x < 0) 0F else if (x > parentWidth - width) (parentWidth - width).toFloat() else x
                        y = if (getY() < 0) 0F else if (getY() + height > parentHeight) (parentHeight - height).toFloat() else y
                        setX(x)
                        setY(y)
                        lastX = rawX
                        lastY = rawY
                        Log.i("aa", "isDrag=" + isDrag + "getX=" + getX() + ";getY=" + getY() + ";parentWidth=" + parentWidth)
                    }
                }
                MotionEvent.ACTION_UP -> if (!isNotDrag()) {
                    //恢复按压效果
                    isPressed = false
                    //Log.i("getX="+getX()+";screenWidthHalf="+screenWidthHalf);
                    if (rawX >= parentWidth / 2) {
                        //靠右吸附
                        animate().setInterpolator(DecelerateInterpolator())
                            .setDuration(500)
                            .xBy(parentWidth - width - x)
                            .start()
    
                        isRight = true
                    } else {
                        //靠左吸附
                        val oa = ObjectAnimator.ofFloat(this, "x", x, 0F)
                        oa.interpolator = DecelerateInterpolator()
                        oa.duration = 500
                        oa.start()
    
                        isRight = false
                    }
                    //判断是否位于上半部分
                    isTop = rawY <= parentHeight/2
                    Log.i("打印是否位于顶部:",""+isTop)
                }
            }
            //如果是拖拽则消s耗事件,否则正常传递即可。
            return !isNotDrag() || super.onTouchEvent(event)
        }
    
        private fun isNotDrag(): Boolean {
            return !isDrag && (x == 0f || x == (parentWidth - width).toFloat())
        }
    }
    

      主布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity3">
        <com.example.mychartdemo.DragFloatActionButton
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:id="@+id/img_btn"
            android:src="@mipmap/ic_launcher"></com.example.mychartdemo.DragFloatActionButton>
    
    
    </LinearLayout>
    

     MainActivity

    package com.example.mychartdemo
    
    import android.graphics.drawable.BitmapDrawable
    import android.os.Bundle
    import android.util.Log
    import android.view.View
    import android.widget.ImageView
    import android.widget.PopupWindow
    import android.widget.RadioGroup
    import androidx.appcompat.app.AppCompatActivity
    import com.example.mychartdemo.databinding.ActivityMain3Binding
    
    class MainActivity : AppCompatActivity(),View.OnClickListener{
    
        private  var popupWindow: PopupWindow? = null
    
        private lateinit var binding: ActivityMain3Binding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivityMain3Binding.inflate(layoutInflater)
            setContentView(binding.root)
    
            binding.imgBtn.setOnClickListener {
             
                if (popupWindow == null) {
                    initPopWindow()
                    //设置不可拖动
                    binding.imgBtn.isCanMove = false
                } else if (popupWindow != null && popupWindow?.isShowing == true) {
                    popupWindow?.dismiss()
                    popupWindow = null
                    //设置可拖动
                    binding.imgBtn.isCanMove = true
                    
                }
    
            }
    
    
        }
    
        private fun initPopWindow() {
            var view: View? = null
            if(binding.imgBtn.isRight){
               view = View.inflate(this, R.layout.popwindow_view, null)
            }else{
                view = View.inflate(this, R.layout.popwindow_left_view, null)
            }
    
            val popimg_1: ImageView = view.findViewById<ImageView>(R.id.popimg_1)
            popimg_1.setOnClickListener(this)
            val popimg_2: ImageView = view.findViewById<ImageView>(R.id.popimg_2)
            popimg_2.setOnClickListener(this)
            val popimg_3: ImageView = view.findViewById<ImageView>(R.id.popimg_3)
            popimg_3.setOnClickListener(this)
            popupWindow = PopupWindow(
                view,
                DensityUtil.dip2px(this, 160F),
                RadioGroup.LayoutParams.WRAP_CONTENT
            )
            popupWindow?.setTouchable(true) //设置可以点击
            popupWindow?.setOutsideTouchable(false) //点击外部关闭
    
            // popupWindow.setBackgroundDrawable(new ColorDrawable());//设置背景
            // popupWindow.setBackgroundDrawable(new ColorDrawable());//设置背景
            popupWindow?.setBackgroundDrawable(BitmapDrawable())
            if (binding.imgBtn.isTop){
                Log.i("打印是否位于顶部111:",""+binding.imgBtn.isTop)
                popupWindow?.showAsDropDown(binding.imgBtn)
            }else{
                if(binding.imgBtn.isRight){
                    Log.i("打印是否位于顶部222:",""+binding.imgBtn.isTop)
                    popupWindow?.showAsDropDown(binding.imgBtn)
                }else{
                    Log.i("打印是否位于顶部3333:",""+binding.imgBtn.isTop)
                    popupWindow?.showAsDropDown(binding.imgBtn,0,DensityUtil.dip2px(this,-200F))
                }
    
    
            }
    
    
        }
    
        override fun onClick(v: View) {
            when (v.id) {
                R.id.popimg_1 -> {
                    if (popupWindow != null) {
                        popupWindow?.dismiss()
                        popupWindow = null
                        //设置可拖动
                        binding.imgBtn.isCanMove = true
                      Log.i("点击了","111")
                    }else{
                        Log.i("点击了","2222")
                    }
    
                }
                R.id.popimg_2 -> {
                    if (popupWindow != null) {
                        popupWindow?.dismiss()
                        popupWindow = null
                        //设置可拖动
                        binding.imgBtn.isCanMove = true
                    }
                }
                R.id.popimg_3 -> {
                    if (popupWindow != null) {
                        popupWindow?.dismiss()
                        popupWindow = null
                        //设置可拖动
                        binding.imgBtn.isCanMove = true
                    }
                }
            }
        }
    }
    

    popwindow_view 布局文件

      

    <?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">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:orientation="horizontal">
            <TextView
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:text="满刻度"
                android:gravity="center|right"
                android:paddingRight="10dp"></TextView>
            <ImageView
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:src="@mipmap/ic_launcher"
                android:id="@+id/popimg_1"></ImageView>
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:orientation="horizontal">
            <TextView
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:text="数据明细"
                android:gravity="center|right"
                android:paddingRight="10dp"></TextView>
            <ImageView
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:src="@mipmap/ic_launcher"
                android:id="@+id/popimg_2"></ImageView>
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:orientation="horizontal">
            <TextView
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:text="选择日期"
                android:gravity="center|right"
                android:paddingRight="10dp"></TextView>
            <ImageView
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:src="@mipmap/ic_launcher"
                android:id="@+id/popimg_3"></ImageView>
        </LinearLayout>
    
    </LinearLayout>
    

      popwindow_left_view  左边布局文件

    <?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">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:orientation="horizontal">
    
            <ImageView
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:src="@mipmap/ic_launcher"
                android:id="@+id/popimg_1"></ImageView>
            <TextView
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:text="满刻度"
                android:gravity="center|left"
                android:paddingLeft="10dp"></TextView>
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:orientation="horizontal">
            <ImageView
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:src="@mipmap/ic_launcher"
                android:id="@+id/popimg_2"></ImageView>
            <TextView
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:text="数据明细"
                android:gravity="center|left"
                android:paddingLeft="10dp"></TextView>
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:orientation="horizontal">
            <ImageView
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:src="@mipmap/ic_launcher"
                android:id="@+id/popimg_3"></ImageView>
            <TextView
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:text="选择日期"
                android:gravity="center|left"
                android:paddingLeft="10dp"></TextView>
        </LinearLayout>
    
    </LinearLayout>
    

      完成

    参考于:https://blog.csdn.net/qq_20451879/article/details/87876673

     

  • 相关阅读:
    分页SQL取下一页
    取第10行到100行
    Flash Builder4.0新建Flex应用程序切换主题出错
    C++浮点数(float、double)类型数据比较、转换分析总结
    浮点数的大小比较
    052新题1
    一致读原理
    Redis配置文件解读
    Redis系列-远程连接redis并给redis加锁
    svn提交更新原则
  • 原文地址:https://www.cnblogs.com/changyiqiang/p/14889742.html
Copyright © 2011-2022 走看看