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

     

  • 相关阅读:
    Using Resource File on DotNet
    C++/CLI VS CSharp
    JIT VS NGen
    [Tip: disable vc intellisense]VS2008 VC Intelisense issue
    UVa 10891 Game of Sum(经典博弈区间DP)
    UVa 10723 Cyborg Genes(LCS变种)
    UVa 607 Scheduling Lectures(简单DP)
    UVa 10401 Injured Queen Problem(简单DP)
    UVa 10313 Pay the Price(类似数字分解DP)
    UVa 10635 Prince and Princess(LCS N*logN)
  • 原文地址:https://www.cnblogs.com/changyiqiang/p/14889742.html
Copyright © 2011-2022 走看看