最近在研究使用android实现平板和电脑端一些应用的效果,话不多说先上个图
可以看到,实现了中间的绿色区域换到父布局最左侧的功能。在拖动的过程中,父布局会出现上下左右四个箭头按钮,当光标移动到箭头上并放下时,拖动的视图会移动到指定的方向上去。
实现思路:
中间的绿色组件,经历了以下几个过程:
1.长按实现视图的拖拽。
2.拖拽移动过程中,父布局出现四个方向的箭头按钮,当光标在箭头上时显示黄色圆圈表示被选中。
3.选中以后,将拖拽的组件移动到视图的最左侧。
技术难点:
1.首先是如何创造一个拖动的效果。因为ConstraintLayout中我们在布局里已经定义了各个子childview的约束关系,所以直接改变拖动的视图位置是不合适的,这里先隐藏了拖拽的view(设置visible=invisible),然后创建一个imageview来现实这个view的图层,再根据手指光标的移动来展示这个imageview,这样看起来就是一个view被拖走了的效果。实际上并没有移动。
2.其次最大的难点是如何改变ConstraintLayout里的约束关系,这里有一个很重要的技术点就是通过layoutparams得到各个方向的约束对象的id:
-
var lp: LayoutParams = child.layoutParams
as LayoutParams
-
var leftToLeftId = lp.leftToLeft
比如要移动中间的view到左边,那么中间的view的左右约束依赖要改成parent和左边,原本左右两个view的依赖也要做相应的修改。约束依赖的修改这里就不赘述了,使用的 ConstraintSet进行修改。
以下是实现的代码,目前并没有实现完整的功能,只是作为一个demo展示:
-
package com.ng.ui.other.drag
-
-
import android.annotation.SuppressLint
-
import android.content.Context
-
import android.graphics.Rect
-
import android.util.AttributeSet
-
import android.view.MotionEvent
-
import android.view.View
-
import android.widget.ImageView
-
import androidx.constraintlayout.widget.ConstraintLayout
-
import androidx.constraintlayout.widget.ConstraintSet
-
import androidx.core.view.contains
-
import com.ng.ui.R
-
import 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<ZChildLayout>()
-
-
//当前操作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<ImageView>
-
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<Rect> = 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)
-
}
-
-
-
}
转载:https://blog.csdn.net/qq_22770457/article/details/108505605
查看评论