最近在研究使用android实现平板和电脑端一些应用的效果,话不多说先上个图

可以看到,实现了中间的绿色区域换到父布局最左侧的功能。在拖动的过程中,父布局会出现上下左右四个箭头按钮,当光标移动到箭头上并放下时,拖动的视图会移动到指定的方向上去。

实现思路:

中间的绿色组件,经历了以下几个过程:

1.长按实现视图的拖拽。

2.拖拽移动过程中,父布局出现四个方向的箭头按钮,当光标在箭头上时显示黄色圆圈表示被选中。

3.选中以后,将拖拽的组件移动到视图的最左侧。

技术难点:

1.首先是如何创造一个拖动的效果。因为ConstraintLayout中我们在布局里已经定义了各个子childview的约束关系,所以直接改变拖动的视图位置是不合适的,这里先隐藏了拖拽的view(设置visible=invisible),然后创建一个imageview来现实这个view的图层,再根据手指光标的移动来展示这个imageview,这样看起来就是一个view被拖走了的效果。实际上并没有移动。

2.其次最大的难点是如何改变ConstraintLayout里的约束关系,这里有一个很重要的技术点就是通过layoutparams得到各个方向的约束对象的id:

var lp: LayoutParams = child.layoutParams as LayoutParamsvar leftToLeftId = lp.leftToLeft

比如要移动中间的view到左边,那么中间的view的左右约束依赖要改成parent和左边,原本左右两个view的依赖也要做相应的修改。约束依赖的修改这里就不赘述了,使用的 ConstraintSet进行修改。

以下是实现的代码,目前并没有实现完整的功能,只是作为一个demo展示:

package com.ng.ui.other.dragimport android.annotation.SuppressLintimport android.content.Contextimport android.graphics.Rectimport android.util.AttributeSetimport android.view.MotionEventimport android.view.Viewimport android.widget.ImageViewimport androidx.constraintlayout.widget.ConstraintLayoutimport androidx.constraintlayout.widget.ConstraintSetimport androidx.core.view.containsimport com.ng.ui.Rimport kotlinx.android.synthetic.main.activity_drag.view.*import java.util.*/** * 描述:  可拖动layout * @author Jzn * @date 2020/9/8 */class ZLayout : ConstraintLayout,ZLayoutStretchListener {    //子layout列表    private var mChildLayoutList = arrayListOf()    //当前操作view    private var mOperationView: ConstraintLayout? = null    private var mOperationIndex: Int = -1    //绘制    private var mIsDrawing = false    private lateinit var mAnimItemView: ImageView    //操作符    private lateinit var mLeftArrow: ImageView    private lateinit var mRightArrow: ImageView    private lateinit var mUpArrow: ImageView    private lateinit var mDownArrow: ImageView    private lateinit var mArrowList: ArrayList    private var mArrowResList = arrayListOf(R.drawable.ic_left, R.drawable.ic_up, R.drawable.ic_right, R.drawable.ic_down)    //操作符区域    private var mLeftRect: Rect = Rect()    private var mRightRect: Rect = Rect()    private var mUpRect: Rect = Rect()    private var mDownRect: Rect = Rect()    private var mArrowRectList: ArrayList = arrayListOf()    //父布局id    private var mRootId = 0    //    constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs) {        initAll()    }    private fun initAll() {        mRootId = id        mAnimItemView = ImageView(context)        mAnimItemView.scaleType = ImageView.ScaleType.FIT_XY        mLeftArrow = ImageView(context)        mRightArrow = ImageView(context)        mUpArrow = ImageView(context)        mDownArrow = ImageView(context)        mLeftArrow.setImageResource(mArrowResList[0])        mUpArrow.setImageResource(mArrowResList[1])        mRightArrow.setImageResource(mArrowResList[2])        mDownArrow.setImageResource(mArrowResList[3])        mArrowList = arrayListOf(mLeftArrow, mUpArrow, mRightArrow, mDownArrow)        mArrowRectList = arrayListOf(mLeftRect, mUpRect, mRightRect, mDownRect)    }    @SuppressLint("DrawAllocation")    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {        super.onLayout(changed, left, top, right, bottom)        var mArrowWidth = mLeftArrow.width        var mArrowHeight = mLeftArrow.height        //确定操作符位置        mLeftArrow.x = 0f        mLeftArrow.y = height / 2.toFloat() - mArrowHeight / 2        mRightArrow.x = width.toFloat() - mArrowWidth        mRightArrow.y = height / 2.toFloat() - mArrowHeight / 2        mUpArrow.x = width / 2.toFloat() - mArrowWidth / 2        mUpArrow.y = 0f        mDownArrow.x = width / 2.toFloat() - mArrowWidth / 2        mDownArrow.y = height.toFloat() - mArrowHeight        //确定操作符区域        //扩大一点区域,方便选中        mArrowWidth += 100        mArrowHeight += 100        mLeftRect = Rect(0, height / 2 - mArrowHeight / 2, mArrowWidth, height / 2 + mArrowHeight / 2)        mRightRect = Rect(width - mArrowWidth, height / 2 - mArrowHeight / 2, width, height / 2 + mArrowHeight / 2)        mUpRect = Rect(width / 2 - mArrowWidth / 2, 0, width / 2 + mArrowWidth / 2, mArrowHeight)        mDownRect = Rect(width / 2 - mArrowWidth / 2, height - mArrowHeight, width / 2 + mArrowWidth / 2, height)        mArrowRectList = arrayListOf(mLeftRect, mUpRect, mRightRect, mDownRect)    }    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec)        val childCount = childCount        mChildLayoutList.clear()        for (i in 0 until childCount) {            val childView: View = getChildAt(i)            if (childView is ZChildLayout) {                mChildLayoutList.add(childView)            }            childView.measure(widthMeasureSpec, heightMeasureSpec)        }        mChildLayoutList.forEachIndexed { index, child ->            child.setCallBack(index, this)        }    }    //拖动的位置    private var mLiftX = 0f    private var mLiftY = 0f    //起始点位置    private var mStartX = 0f    private var mStartY = 0f    //位移    private var mIntervalX = 0f    private var mIntervalY = 0f    override fun onStartLift(motionEvent: MotionEvent) {        mStartX = motionEvent.x        mStartY = motionEvent.y        mChildLayoutList.forEachIndexed { index, child ->        }    }    override fun onLift(index: Int, view: View, motionEvent: MotionEvent) {        //MLog.d("$index $motionEvent")        mOperationView = view as ConstraintLayout        mLiftX = motionEvent.rawX        mLiftY = motionEvent.rawY - ViewUtils.getStatusBarHeight(context)        mIntervalX = mStartX - motionEvent.x        mIntervalY = mStartY - motionEvent.y        val bitmap = ViewUtils.getBitmapFromView(view)        view.visibility = View.INVISIBLE        val tarGetLocation = IntArray(2)        view.getLocationOnScreen(tarGetLocation)        if (!mIsDrawing) {            mAnimItemView.setImageBitmap(bitmap)        }        mAnimItemView.x = tarGetLocation[0].toFloat() - mIntervalX        mAnimItemView.y = tarGetLocation[1].toFloat() - ViewUtils.getStatusBarHeight(context) - mIntervalY        //显示悬浮框        showView(mAnimItemView)        mIsDrawing = true        //显示操作视图        showOptionView()    }    //在父布局的四个角显示操作符按钮    private fun showOptionView() {        mArrowList.forEach {            showView(it)        }        mArrowRectList.forEachIndexed { index, rect ->            if (rect.contains(mLiftX.toInt(), mLiftY.toInt())) {                mArrowList[index].setImageResource(R.drawable.ic_change)                mOperationIndex = index            } else {                mArrowList[index].setImageResource(mArrowResList[index])            }        }    }    //重新排序    //先试验单一的左右关系    private fun onOption(index: Int) {        var constraintSet: ConstraintSet = ConstraintSet()        constraintSet.clone(root_layout)        //左 上 右 下        when (index) {            0 -> {                //原来的左右改为到一起                //找到原来左边约束父布局的view//                mChildLayoutList.forEachIndexed { index, child ->//                    var lp: LayoutParams = child.layoutParams as LayoutParams//                    var leftToLeftId = lp.leftToLeft////                    MLog.d("index: " + index)////                    MLog.d(" left to left : " + lp.leftToLeft)//                    MLog.d(" left to right : " + lp.leftToRight)////                    MLog.d(" right to left : " + lp.rightToLeft)//                    MLog.d(" right to right : " + lp.rightToRight)////                    if (leftToLeftId.equals(0)) {//                        MLog.d("当前操作的:" + mOperationView!!.id)//                        MLog.d(" 找到的 : " + child.id)////                    }////                    //left to parent//                    constraintSet.connect(mOperationView!!.id,ConstraintSet.LEFT,ConstraintSet.PARENT_ID,ConstraintSet.LEFT,20)//                    constraintSet.connect(mOperationView!!.id,ConstraintSet.RIGHT,child.id,ConstraintSet.LEFT,20)//                    constraintSet.applyTo(root_layout)//                    return////                }                var left = mChildLayoutList[0]                var center = mChildLayoutList[1]                var right = mChildLayoutList[2]                constraintSet.connect(center.id,ConstraintSet.LEFT,ConstraintSet.PARENT_ID,ConstraintSet.LEFT,0)                constraintSet.connect(center.id,ConstraintSet.RIGHT,left.id,ConstraintSet.LEFT,0)                constraintSet.connect(left.id,ConstraintSet.LEFT,center.id,ConstraintSet.RIGHT,0)                constraintSet.connect(left.id,ConstraintSet.RIGHT,right.id,ConstraintSet.LEFT,0)                constraintSet.connect(right.id,ConstraintSet.LEFT,left.id,ConstraintSet.RIGHT,0)                constraintSet.connect(right.id,ConstraintSet.RIGHT,ConstraintSet.PARENT_ID,ConstraintSet.RIGHT,0)                constraintSet.applyTo(root_layout)            }            1 -> {            }            2 -> {            }            3 -> {            }        }        //完成以后重置mOperationIndex        mOperationIndex = -1    }    //在父布局的四个角显示操作符按钮    private fun hideOptionView() {        mArrowList.forEach {            hideView(it)        }    }    override fun onFinishLift() {        mIsDrawing = false        //判断此时的状态        hideView(mAnimItemView)        hideOptionView()        if (mOperationView != null) {            mOperationView!!.visibility = View.VISIBLE        }        if (mOperationIndex != -1) {            onOption(mOperationIndex)        }    }    //移除view    private fun hideView(view: View) {        if ( contains(view)) {            removeView(view)        }    }    //显示view    private fun showView(view: View) {        val lp = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)        hideView(view)        addView(view, lp)    }}

 

更多相关文章

  1. Android集成ShareSDK第三方分享和登录
  2. Android使用Gallery实现照片拖动的特效
  3. Android之自定义最简单的竖向引导页
  4. Android(安卓)利用ViewPager+GridView,仿美团首页导航栏分类布局
  5. android listview仿iphone弹簧特效
  6. Android使用底部导航2018-08-16
  7. Android(安卓)Configuration change引发的问题及解决方法
  8. Android实现多个跑马灯效果,多个文本框TextView的跑马灯
  9. Android(安卓)UI 的几个简单技巧

随机推荐

  1. Android 生态详解
  2. Android 之 复习大纲
  3. 最封闭的开源系统:话说 Android 的八宗罪
  4. android spinner修改 样式
  5. 关于Android的开发经验总结
  6. Android RelativeLayout 相对布局解析
  7. Android的Window类
  8. Android 电池管理系统
  9. Android(安卓)跨进程启动Activity黑屏(白
  10. Android教程之一:Window下搭建Android开发