最近开发车主相关的App功用, 做一个出行功用大致和高德道路规划功用相当。 对高德的道路规划交做了详尽的调研。 功用拆分开来,道路规划逻辑首要涉及:POI查找和 道路规划; UI部分首要任务在查找输入框开发, 列表开发, 道路图层开发。 道路的查找和道路规划及其规划线路绘制方面高德敞开渠道均有文档详细介绍,就不再细谈了;此篇首要介绍仿高德的道路查找交互界面的开发,本文简称为道路查找框开发。

此道路查找框有两种状况,修改途径点态和无途径点态;

Android-仿高德的路线规划搜索框
Android-仿高德的路线规划搜索框
此外 修改途径点态还包含拖动地址改动顺序功用。 考虑拖动的交互,Android里常选的计划是RecyclerView实现;但是今日咱们选用Tween动画实现拖动的交互作用。 Tween Animation 和属性动画不一样,不改动View的实在属性数据(也就不改动子View实在方位),仅动画作用的显现,可利用此特性来展现拖动时的切换作用,和拖动完毕时回到实在状况的一个切换。 利用数据交流来刷新 拖动后台的UI。

UI全体布局选用XML硬编码的形式引进,使用include的方式复用每条方位POI查找框。

<LinearLayout
                    android:id="@+id/ll_more_edit_line"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_toLeftOf="@+id/ll_edit_action"
                    android:orientation="vertical">
                    <include
                        android:id="@+id/ll_start_location"
                        layout="@layout/edit_search_key" />
                    <include
                        android:id="@+id/ll_end_location"
                        layout="@layout/edit_search_key" />
                </LinearLayout>

edit_search_key.xml文件, 定义每条查找POI的子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="wrap_content"
    android:orientation="horizontal">
    <RelativeLayout
        android:id="@+id/ll_edit_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="horizontal">
        <ImageView
            android:id="@+id/iv_edt"
            android:layout_width="@dimen/dp_32"
            android:layout_height="@dimen/dp_32"
            android:scaleType="centerInside"
            android:src="@drawable/start_location_point" />
        <EditText
            android:id="@+id/edt_input_key"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@+id/iv_edt"
            android:background="@color/transparent"
            android:hint=""
            android:minHeight="@dimen/dp_32"
            android:paddingVertical="@dimen/dp_6"
            android:singleLine="true"
            android:text="@string/confirm"
            android:textColor="@color/text_content_first"
            android:textColorHint="@color/text_content_five"
            android:textSize="@dimen/dp_14" />
        <ImageView
            android:id="@+id/iv_edit_move_tag"
            android:layout_width="@dimen/fit_dp_20"
            android:layout_height="@dimen/fit_dp_20"
            android:layout_alignRight="@+id/edt_input_key"
            android:layout_centerVertical="true"
            android:layout_marginRight="@dimen/fit_dp_8"
            android:src="@drawable/icon_edit_move"
            android:visibility="gone" />
    </RelativeLayout>
    <ImageView
        android:id="@+id/iv_del_edit"
        android:layout_width="@dimen/dp_20"
        android:layout_height="@dimen/dp_20"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="@dimen/dp_8"
        android:scaleType="centerInside"
        android:src="@drawable/ic_close"
        android:visibility="gone" />
</LinearLayout>

增删途径点,经过LinearLayout布局动态AddView()和RemoveView()实现; 增加途径点

    /**
     * @param middleIndex 从0开端,扫除起点和结尾之外的列表
     */
    private fun addMiddleLocation(middleIndex: Int = 0): EditText {
        val params = LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
        val top: Int = resources.getDimensionPixelSize(R.dimen.dp_6)
        val bottom = 0
        params.topMargin = top
        params.bottomMargin = bottom
        val middleViewBinding = createMiddleLocationEditView();
        var middlePos: Int = middleIndex
        if (getMiddleCount() > middleIndex) {
            binding.llMoreEditLine.addView(middleViewBinding.root, middleIndex + 1, params)
        } else {
            val index = binding.llMoreEditLine.childCount - 1;
            binding.llMoreEditLine.addView(middleViewBinding.root, index, params)
            middlePos = getMiddleCount() - 1;
        }
        val middleData = EditLocation.createMiddleLocation()
        middleViewBinding.root.setTag(R.id.cb_item_tag, middleData)
        middleViewBinding.edtInputKey.setTag(R.id.cb_item_tag, middleData)
        locationList.add(middlePos + 1, middleData)
        updateAddMoreUIState()
        post {
            middleViewBinding.edtInputKey.requestFocus()
        }
        return middleViewBinding.edtInputKey
    }

删除途径点

    /**
     * @param middleIndex 从0开端,扫除起点和结尾之外的列表
     */
    private fun removeMiddleLocation(middleIndex: Int) {
        getMiddleItemViewAt(middleIndex)?.let { child ->
            binding.llMoreEditLine.removeView(child)
            (child.getTag(R.id.cb_item_tag) as? EditLocation).let { data ->
                locationList.remove(data)
            }
        }
        val count = getMiddleCount()
        switchUIState(true, count)
        updateAddMoreUIState()
    }

长按事情的触发直接在事情分发的顶层阻拦接触事情判断是否长按:

val mainHandler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                MSG_LONG_CLICK -> {
                    val obj = msg.obj as Point
                    val windowX = obj.x;
                    val windowY = obj.y
                    findLongTouchValidView(windowX, windowY)?.let { findDragView ->
                        onDragStart(findDragView, msg.arg1, msg.arg2)
                    }
                }
            }
        }
    }
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        var dy = 0f
        val tempDragView = dragSelectedView;
        when (ev?.action) {
            MotionEvent.ACTION_DOWN -> {
                yPos = ev.y
                mainHandler.removeMessages(MSG_LONG_CLICK)
                val msg = mainHandler.obtainMessage(MSG_LONG_CLICK)
                msg.arg1 = ev.x.toInt()
                msg.arg2 = ev.y.toInt()
                msg.obj = Point(ev.rawX.toInt(), ev.rawY.toInt())
                mainHandler.sendMessageDelayed(msg, LONG_TIME)
            }
            MotionEvent.ACTION_MOVE -> {
                dy = ev.y - yPos;
                yPos = ev.y
                if (tempDragView != null && dy != 0f) {
                    mainHandler.removeMessages(MSG_LONG_CLICK)
                    onDragMove(tempDragView!!, ev.x.toInt(), ev.y.toInt(), dy)
                }
            }
            MotionEvent.ACTION_CANCEL -> {
                mainHandler.removeMessages(MSG_LONG_CLICK)
                if (tempDragView != null) {
                    onDragEnd()
                }
            }
            MotionEvent.ACTION_UP -> {
                mainHandler.removeMessages(MSG_LONG_CLICK)
                if (tempDragView != null) {
                    onDragEnd()
                }
            }
        }
        if (tempDragView != null) {
            return true
        }
        return super.dispatchTouchEvent(ev);
    }

拖动的View是一个可以悬浮在容器之上的空间中, 需求定义一个View来显现拖拽的目标View,这里选用的是ImageView; 把即将拖拽的View生成对应Bitmap来显现到悬浮的ImageView中,并把悬浮的方位设定在拖拽View的方位,实现拖拽重合移动作用。 代码片段如下:

/**
     * 开端拖拽,在长按事情触发之后
     */
    private fun onDragStart(dragItemVIew: View, touchX: Int, touchY: Int) {
        try {
            if (itemEditHeight != 0 && isDragEnable()) {
                val itemView = dragItemVIew
                val vImage = vToBitmap(itemView)
                floatView.setImageBitmap(vImage)
                val top = itemView.top + ((itemView.parent as? View)?.top ?: 0)
                Log.w(TAG, "top === $top");
                floatView.tag = top;
                setViewTopMargin(floatView, top)
                floatView.visibility = View.VISIBLE
                itemView.visibility = INVISIBLE
                dragSelectedView = itemView;
                dragInsertViewStack.clear();
                resetItemViewRectInfo()
                ClickVibrator.clickSingle(context, 100L)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
    /**
     * 拖拽移动
     */
    private fun onDragMove(dragItemVIew: View, touchX: Int, touchY: Int, dy: Float) {
        val newTop: Int = floatView.tag as Int + dy.toInt()
        val newBottom: Int = newTop + itemEditHeight;
        if (newTop in minDragTop..maxDragTop) {
            floatView.tag = newTop
            setViewTopMargin(floatView, newTop)
            //磕碰检测
            for (rect in itemRectHashMap.keys) {
                val line = rect.top + rect.height() / 2
                val itemView = itemRectHashMap.get(rect)
                if (itemView == dragSelectedView) {
                    continue
                }
                if (line in newTop..newBottom) {
                    //产生磕碰
                    val dTop = line - newTop;
                    val dBottom = newBottom - line;
                    Log.w(TAG, "move === $dTop -- $dBottom")
                    val fromTop = rect.top;
                    val animDistance = rect.height() + resources.getDimensionPixelSize(R.dimen.dp_6)
                    if (dTop < dBottom) {
                        //item down
                        post {
                            val destTop = fromTop + animDistance
                            itemView?.let {
                                itemRectHashMap.remove(rect)
                                val newRect =
                                    Rect(rect.left, destTop, rect.right, destTop + itemEditHeight)
                                itemRectHashMap.put(newRect, itemView)
                                trans(itemView, rect, newRect)
                            }
                        }
                    } else {
                        //item up
                        post {
                            val destTop = fromTop - animDistance
                            itemView?.let {
                                itemRectHashMap.remove(rect)
                                val newRect =
                                    Rect(rect.left, destTop, rect.right, destTop + itemEditHeight)
                                itemRectHashMap.put(newRect, itemView)
                                trans(itemView, rect, newRect)
                            }
                        }
                    }
                    break;
                }
            }
        }
    }
/**
     * 拖拽完毕
     */
    private fun onDragEnd() {
        val replaceView = if (dragInsertViewStack.isEmpty()) null else dragInsertViewStack.pop()
        if (dragSelectedView != null && replaceView != null) {
            val dragData = dragSelectedView?.getTag(R.id.cb_item_tag) as? EditLocation
            val replaceData = replaceView.getTag(R.id.cb_item_tag) as? EditLocation
            callEditLocationDataChange(dragData, replaceData)
        }
        clearDragUIState()
    }

平移动画实现,需求注意的是:平移的参数首要是相对View的原始方位 核算上下移动的方位。 所以需求改根据方位核算坐标变换。

private fun trans(item: View?, fromRect: Rect, toRect: Rect) {
        val divideTop = binding.rlEditLocationContainer.top
        val fromTop = fromRect.top - divideTop
        val destTop = toRect.top - divideTop
        val originTop = (item?.top ?: 0) + ((item?.parent as? View)?.top ?: 0)
        val animationDis = destTop - fromTop
        val moveOut = originTop == fromTop
        if (moveOut) {
            dragInsertViewStack.push(item)
        } else if (!dragInsertViewStack.isEmpty()) {
            dragInsertViewStack.pop();
        }
        val fromY = if (moveOut) 0 else -1 * animationDis
        val toY = if (moveOut) animationDis else 0;
        Log.w(TAG, "trans start == $originTop --- $fromY,$toY")
        val tran = TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f,
            Animation.ABSOLUTE, fromY.toFloat(), Animation.ABSOLUTE, toY.toFloat()
        )
        tran.duration = (300L)
        tran.fillAfter = true;
        item?.startAnimation(tran)
    }

在拖动完毕时清空所用动画和 核算交流后的数据,从头更新对应方位的显现数据。

private fun callEditLocationDataChange(dragData: EditLocation?, replaceData: EditLocation?) {
        if (dragData == null || replaceData == null) {
            return;
        }
        val tempList = ArrayList<EditLocation>();
        val tempPoiList = ArrayList<EditLocation>();
        tempList.addAll(locationList)
        val dragIndex = tempList.indexOf(dragData)
        val replaceIndex = tempList.indexOf(replaceData);
        if (dragIndex != -1 && replaceIndex != -1 && dragIndex != replaceIndex) {
            tempList.removeAt(dragIndex);
            val insertIndex = tempList.indexOf(replaceData);
            if (insertIndex != -1) {
                val addIndex = if (dragIndex > replaceIndex) insertIndex else insertIndex + 1
                tempList.add(addIndex, dragData)
            }
            if (tempList.size == locationList.size) {
                for (item in tempList) {
                    val temp = EditLocation()
                    temp.poi = item.poi;
                    temp.editContent = item.editContent
                    tempPoiList.add(temp)
                }
                for (i in 0 until locationList.size) {
                    val item = locationList[i]
                    val tempData = tempPoiList[i]
                    item.poi = tempData.poi;
                    item.editContent = tempData.editContent
                    val tv = findLocationTextView(i)
                    updateLocationUI(item, tv)
                }
            }
        }
        tempList.clear()
        tempPoiList.clear()
    }
private fun clearDragUIState() {
        dragSelectedView?.visibility = View.VISIBLE
        floatView.visibility = View.GONE
        dragSelectedView = null
        for (i in 0 until binding.llMoreEditLine.childCount) {
            binding.llMoreEditLine.getChildAt(i)?.clearAnimation()
        }
        dragInsertViewStack.clear()
    }

附上完好代码:


package com.cw.widget.map
import android.content.Context
import android.graphics.*
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.animation.Animation
import android.view.animation.TranslateAnimation
import android.widget.ImageView
import com.cw.widget.R
import com.cw.widget.utils.ClickVibrator
import java.util.*
class DragEditTripLocationView : EditTripLocationListView {
    val TAG = "Car-EditTrip %s"
    constructor(context: Context) : super(context) {}
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {}
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
    }
    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes) {
    }
    companion object {
        const val LONG_TIME = 1000L;
        const val MSG_LONG_CLICK = 100;
        const val isDebug = false;
    }
    val floatView: ImageView
    var itemEditHeight: Int = 0;
    var itemEditWidth: Int = 0;
    var minDragTop: Int = 0;
    var maxDragTop: Int = 0;
    var testPaint: Paint = Paint()
    var dragSelectedView: View? = null;
    val itemRectHashMap = HashMap<Rect, View>();
    val floatPaint = Paint();
    /**
     * 脱拽过程中插入的View
     */
    private val dragInsertViewStack = Stack<View?>();
    val mainHandler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                MSG_LONG_CLICK -> {
                    val obj = msg.obj as Point
                    val windowX = obj.x;
                    val windowY = obj.y
                    findLongTouchValidView(windowX, windowY)?.let { findDragView ->
                        onDragStart(findDragView, msg.arg1, msg.arg2)
                    }
                }
            }
        }
    }
    init {
        floatView = ImageView(context)
        binding.rlEditTwoLocation.addView(
            floatView,
            LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT
        )
        floatView.visibility = View.GONE
        testPaint.color = Color.RED;
        testPaint.style = Paint.Style.STROKE
        floatPaint.color = Color.parseColor("#ffc0df17");
        floatPaint.strokeWidth = 2f;
        floatPaint.style = Paint.Style.STROKE
    }
    private fun setViewTopMargin(v: View, top: Int) {
        (v.layoutParams as? MarginLayoutParams)?.let { p ->
            p.topMargin = top;
            v.layoutParams = p;
        }
    }
    private fun vToBitmap(v: View): Bitmap {
        val w = v.width;
        val h = v.height;
        val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565)
        val canvas = Canvas(bitmap)
        canvas.drawColor(Color.WHITE)
        v.draw(canvas)
        val editWidth = v.findViewById<View>(R.id.ll_edit_content)?.width ?: w
        val corners = resources.getDimensionPixelSize(R.dimen.dp_2)
        canvas.drawRoundRect(
            RectF(0f, 0f, editWidth.toFloat(), h.toFloat()),
            corners.toFloat(),
            corners.toFloat(),
            floatPaint
        )
        canvas.setBitmap(null)
        return bitmap
    }
    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        itemEditWidth = getMiddleItemViewAt(0)?.width ?: 0
        itemEditHeight = getMiddleItemViewAt(0)?.height ?: 0
    }
    override fun dispatchDraw(canvas: Canvas?) {
        super.dispatchDraw(canvas)
        if (isDebug) {
            if (itemRectHashMap.isNotEmpty()) {
                for (item in itemRectHashMap.keys) {
                    canvas?.drawRect(item, testPaint)
                }
            }
        }
    }
    private fun resetItemViewRectInfo() {
        minDragTop = 0;
        maxDragTop = binding.rlEditTwoLocation.height - itemEditHeight;
        itemRectHashMap.clear();
        for (i in 0 until binding.llMoreEditLine.childCount) {
            val itemView = binding.llMoreEditLine.getChildAt(i)
            if (itemView != null && itemView != dragSelectedView) {
                itemRectHashMap.put(getViewRect(itemView), itemView)
            }
        }
    }
    /**
     * 获取相对于整个View 的Rect数据
     */
    private fun getViewRect(v: View): Rect {
        val pTop = binding.rlEditLocationContainer.top + ((v.parent as? View)?.top ?: 0)
        val top = v.top + pTop
        val bottom = v.bottom + pTop;
        val left = 0;
        val right = v.width;
        return Rect(left, top, right, bottom)
    }
    private fun findLongTouchValidView(x: Int, y: Int): View? {
        val rect = Rect();
        for (i in 0 until binding.llMoreEditLine.childCount) {
            binding.llMoreEditLine.getChildAt(i)?.let {
                it.getGlobalVisibleRect(rect)
                if (rect.contains(x, y)) {
                    return it
                }
            }
        }
        return null;
    }
    private fun isDragEnable(): Boolean {
        return binding.rlMoreAddLocation.visibility == View.VISIBLE
    }
    /**
     * 开端拖拽,在长按事情触发之后
     */
    private fun onDragStart(dragItemVIew: View, touchX: Int, touchY: Int) {
        try {
            if (itemEditHeight != 0 && isDragEnable()) {
                val itemView = dragItemVIew
                val vImage = vToBitmap(itemView)
                floatView.setImageBitmap(vImage)
                val top = itemView.top + ((itemView.parent as? View)?.top ?: 0)
                Log.w(TAG, "top === $top");
                floatView.tag = top;
                setViewTopMargin(floatView, top)
                floatView.visibility = View.VISIBLE
                itemView.visibility = INVISIBLE
                dragSelectedView = itemView;
                dragInsertViewStack.clear();
                resetItemViewRectInfo()
                ClickVibrator.clickSingle(context, 100L)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
    /**
     * 拖拽移动
     */
    private fun onDragMove(dragItemVIew: View, touchX: Int, touchY: Int, dy: Float) {
        val newTop: Int = floatView.tag as Int + dy.toInt()
        val newBottom: Int = newTop + itemEditHeight;
        if (newTop in minDragTop..maxDragTop) {
            floatView.tag = newTop
            setViewTopMargin(floatView, newTop)
            //磕碰检测
            for (rect in itemRectHashMap.keys) {
                val line = rect.top + rect.height() / 2
                val itemView = itemRectHashMap.get(rect)
                if (itemView == dragSelectedView) {
                    continue
                }
                if (line in newTop..newBottom) {
                    //产生磕碰
                    val dTop = line - newTop;
                    val dBottom = newBottom - line;
                    Log.w(TAG, "move === $dTop -- $dBottom")
                    val fromTop = rect.top;
                    val animDistance = rect.height() + resources.getDimensionPixelSize(R.dimen.dp_6)
                    if (dTop < dBottom) {
                        //item down
                        post {
                            val destTop = fromTop + animDistance
                            itemView?.let {
                                itemRectHashMap.remove(rect)
                                val newRect =
                                    Rect(rect.left, destTop, rect.right, destTop + itemEditHeight)
                                itemRectHashMap.put(newRect, itemView)
                                trans(itemView, rect, newRect)
                            }
                        }
                    } else {
                        //item up
                        post {
                            val destTop = fromTop - animDistance
                            itemView?.let {
                                itemRectHashMap.remove(rect)
                                val newRect =
                                    Rect(rect.left, destTop, rect.right, destTop + itemEditHeight)
                                itemRectHashMap.put(newRect, itemView)
                                trans(itemView, rect, newRect)
                            }
                        }
                    }
                    break;
                }
            }
        }
    }
    private fun trans(item: View?, fromRect: Rect, toRect: Rect) {
        val divideTop = binding.rlEditLocationContainer.top
        val fromTop = fromRect.top - divideTop
        val destTop = toRect.top - divideTop
        val originTop = (item?.top ?: 0) + ((item?.parent as? View)?.top ?: 0)
        val animationDis = destTop - fromTop
        val moveOut = originTop == fromTop
        if (moveOut) {
            dragInsertViewStack.push(item)
        } else if (!dragInsertViewStack.isEmpty()) {
            dragInsertViewStack.pop();
        }
        val fromY = if (moveOut) 0 else -1 * animationDis
        val toY = if (moveOut) animationDis else 0;
        Log.w(TAG, "trans start == $originTop --- $fromY,$toY")
        val tran = TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f,
            Animation.ABSOLUTE, fromY.toFloat(), Animation.ABSOLUTE, toY.toFloat()
        )
        tran.duration = (300L)
        tran.fillAfter = true;
        item?.startAnimation(tran)
    }
    /**
     * 拖拽完毕
     */
    private fun onDragEnd() {
        val replaceView = if (dragInsertViewStack.isEmpty()) null else dragInsertViewStack.pop()
        if (dragSelectedView != null && replaceView != null) {
            val dragData = dragSelectedView?.getTag(R.id.cb_item_tag) as? EditLocation
            val replaceData = replaceView.getTag(R.id.cb_item_tag) as? EditLocation
            callEditLocationDataChange(dragData, replaceData)
        }
        clearDragUIState()
    }
    private fun callEditLocationDataChange(dragData: EditLocation?, replaceData: EditLocation?) {
        if (dragData == null || replaceData == null) {
            return;
        }
        val tempList = ArrayList<EditLocation>();
        val tempPoiList = ArrayList<EditLocation>();
        tempList.addAll(locationList)
        val dragIndex = tempList.indexOf(dragData)
        val replaceIndex = tempList.indexOf(replaceData);
        if (dragIndex != -1 && replaceIndex != -1 && dragIndex != replaceIndex) {
            tempList.removeAt(dragIndex);
            val insertIndex = tempList.indexOf(replaceData);
            if (insertIndex != -1) {
                val addIndex = if (dragIndex > replaceIndex) insertIndex else insertIndex + 1
                tempList.add(addIndex, dragData)
            }
            if (tempList.size == locationList.size) {
                for (item in tempList) {
                    val temp = EditLocation()
                    temp.poi = item.poi;
                    temp.editContent = item.editContent
                    tempPoiList.add(temp)
                }
                for (i in 0 until locationList.size) {
                    val item = locationList[i]
                    val tempData = tempPoiList[i]
                    item.poi = tempData.poi;
                    item.editContent = tempData.editContent
                    val tv = findLocationTextView(i)
                    updateLocationUI(item, tv)
                }
            }
        }
        tempList.clear()
        tempPoiList.clear()
    }
    private fun clearDragUIState() {
        dragSelectedView?.visibility = View.VISIBLE
        floatView.visibility = View.GONE
        dragSelectedView = null
        for (i in 0 until binding.llMoreEditLine.childCount) {
            binding.llMoreEditLine.getChildAt(i)?.clearAnimation()
        }
        dragInsertViewStack.clear()
    }
    private var yPos = 0f;
    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        var dy = 0f
        val tempDragView = dragSelectedView;
        when (ev?.action) {
            MotionEvent.ACTION_DOWN -> {
                yPos = ev.y
                mainHandler.removeMessages(MSG_LONG_CLICK)
                val msg = mainHandler.obtainMessage(MSG_LONG_CLICK)
                msg.arg1 = ev.x.toInt()
                msg.arg2 = ev.y.toInt()
                msg.obj = Point(ev.rawX.toInt(), ev.rawY.toInt())
                mainHandler.sendMessageDelayed(msg, LONG_TIME)
            }
            MotionEvent.ACTION_MOVE -> {
                dy = ev.y - yPos;
                yPos = ev.y
                if (tempDragView != null && dy != 0f) {
                    mainHandler.removeMessages(MSG_LONG_CLICK)
                    onDragMove(tempDragView!!, ev.x.toInt(), ev.y.toInt(), dy)
                }
            }
            MotionEvent.ACTION_CANCEL -> {
                mainHandler.removeMessages(MSG_LONG_CLICK)
                if (tempDragView != null) {
                    onDragEnd()
                }
            }
            MotionEvent.ACTION_UP -> {
                mainHandler.removeMessages(MSG_LONG_CLICK)
                if (tempDragView != null) {
                    onDragEnd()
                }
            }
        }
        if (tempDragView != null) {
            return true
        }
        return super.dispatchTouchEvent(ev);
    }
}
package com.cw.widget.map
import android.app.Activity
import android.content.Context
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.Paint
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.TextView
import com.blankj.utilcode.util.KeyboardUtils
import com.blankj.utilcode.util.ToastUtils
import com.cw.widget.R
import com.cw.widget.bean.POIType
import com.cw.widget.bean.SearchPOI
import com.cw.widget.databinding.EditSearchKeyBinding
import com.cw.widget.databinding.EditTripLocationListBinding
/**
 * 新建,修改行程的修改View
 */
open class EditTripLocationListView : FrameLayout {
    constructor(context: Context) : super(context) {}
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {}
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
    }
    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes) {
    }
    val binding: EditTripLocationListBinding
    val MAX_MIDDLE_NUM = 3;
    val locationList: ArrayList<EditLocation>
    private val startLocation: EditLocation
    private val endLocation: EditLocation
    var onEventListener: OnEventListener? = null
    /**
     * 文字改变疏忽改动
     */
    var textChangeIgnoreTask: TextChangeIgnore? = null
    init {
        startLocation = EditLocation.createStartLocation(context)
        endLocation = EditLocation.createEndLocation()
        locationList = ArrayList<EditLocation>()
        locationList.add(startLocation)
        locationList.add(endLocation)
        binding = EditTripLocationListBinding.inflate(LayoutInflater.from(context))
        addView(binding.root, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
        binding.llStartLocation.ivEdt.setImageResource(R.drawable.start_location_point)
        binding.llStartLocation.edtInputKey.setText("")
        binding.llStartLocation.edtInputKey.hint = "请输入起点"
        binding.llStartLocation.edtInputKey.contentDescription = "起点"
        setUpEditViewKeyboard(binding.llStartLocation.edtInputKey)
        val startWatcher = EditTextWatcher(this, binding.llStartLocation.edtInputKey)
        binding.llStartLocation.edtInputKey.addTextChangedListener(startWatcher)
        binding.llStartLocation.root.setTag(R.id.cb_item_tag, startLocation)
        binding.llStartLocation.edtInputKey.setTag(R.id.cb_item_tag, startLocation)
        updateLocationUI(startLocation)
        binding.llEndLocation.ivEdt.setImageResource(R.drawable.end_location_point)
        binding.llEndLocation.root.setTag(R.id.cb_item_tag, endLocation)
        binding.llEndLocation.edtInputKey.setTag(R.id.cb_item_tag, endLocation)
        binding.llEndLocation.edtInputKey.contentDescription = "结尾"
        setUpEditViewKeyboard(binding.llEndLocation.edtInputKey)
        val endWatcher = EditTextWatcher(this, binding.llEndLocation.edtInputKey)
        binding.llEndLocation.edtInputKey.addTextChangedListener(endWatcher)
        binding.llEndLocation.edtInputKey.setText("")
        binding.llEndLocation.edtInputKey.hint = "请输入结尾"
        switchUIState(false)
        post {
            if (binding.llStartLocation.edtInputKey.text.isNullOrEmpty()) {
                binding.llStartLocation.edtInputKey.requestFocus()
            } else {
                binding.llEndLocation.edtInputKey.requestFocus()
            }
        }
        binding.ivAddLocation.setOnClickListener {
            val index = getMiddleCount()
            if (index < MAX_MIDDLE_NUM) {
                var addIndex = index
                (findFocusEditTextView()?.getTag(R.id.cb_item_tag) as? EditLocation)?.let {
                    val findIndex = locationList.indexOf(it)
                    if (findIndex != -1) {
                        addIndex = findIndex;
                    }
                }
                addMiddleLocation(addIndex)
                switchUIState(true)
            } else {
                ToastUtils.showShort("最多可增加${MAX_MIDDLE_NUM}个途经点")
            }
        }
        binding.rlMoreAdd.setOnClickListener {
            binding.ivAddLocation.performClick()
        }
        binding.llStartLocation.ivDelEdit.setOnClickListener {
            if (locationList.size > 2 &&
                locationList[1].locationType == EditLocation.TYPE_MIDDLE
            ) {
                startLocation.poi = locationList[1].poi
                removeMiddleLocation(0)
            } else {
                startLocation.poi = null
            }
            updateLocationUI(startLocation)
        }
        binding.llEndLocation.ivDelEdit.setOnClickListener {
            val index = locationList.size - 2
            if (locationList.size > 2 &&
                locationList[index].locationType == EditLocation.TYPE_MIDDLE
            ) {
                endLocation.poi = locationList[index].poi
                val middleIndex = getMiddleCount() - 1
                removeMiddleLocation(middleIndex)
            } else {
                endLocation.poi = null
            }
            updateLocationUI(endLocation)
        }
        binding.ivBackFinish.setOnClickListener { v ->
            onEventListener?.onCloseClick(v)
        }
        binding.ivLocationChangeMore.setOnClickListener {
            binding.ivSwitchLocation.performClick();
        }
        binding.ivSwitchLocation.setOnClickListener { v ->
            val temp = startLocation.poi
            val tempEditContent = startLocation.editContent
            startLocation.poi = endLocation.poi
            startLocation.editContent = endLocation.editContent
            endLocation.poi = temp
            endLocation.editContent = tempEditContent
            updateLocationUI(startLocation)
            updateLocationUI(endLocation)
            val start = 1;
            val end = locationList.lastIndex - 1
            //途经点交流次数
            val switchCount = locationList.size / 2 - 1
            for (i in 0 until switchCount) {
                val index = start + i;
                val switchIndex = end - i;
                if (switchIndex != index && switchIndex >= 0) {
                    val temp = locationList.getOrNull(index)?.poi
                    locationList.getOrNull(index)?.poi = locationList.getOrNull(switchIndex)?.poi
                    locationList.getOrNull(switchIndex)?.poi = temp;
                    locationList.getOrNull(index)?.let {
                        updateLocationUI(it, findMidLocationTextView(index))
                    }
                    locationList.getOrNull(switchIndex)?.let {
                        updateLocationUI(it, findMidLocationTextView(switchIndex))
                    }
                }
            }
            findFirstNoneEditTextView()?.let {
                if (!it.text.isNullOrEmpty()) {
                    callOnTextChanged(it, it.text)
                }
                requestFocus(it, true)
            }
        }
        binding.btnEditLocationComplete.setOnClickListener {
            val dataList = ArrayList<EditLocation>()
            for (item in locationList) {
                if (item.poi != null) {
                    dataList.add(item)
                }
            }
            if (startLocation.poi == null && !startLocation.editContent.isNullOrEmpty()) {
                ToastUtils.showShort("请挑选起点")
                requestFocus(binding.llStartLocation.edtInputKey)
                return@setOnClickListener
            }
            if (endLocation.poi == null && !endLocation.editContent.isNullOrEmpty()) {
                ToastUtils.showShort("请挑选结尾")
                requestFocus(binding.llEndLocation.edtInputKey)
                return@setOnClickListener
            }
            if (dataList.size < 2) {
                if (startLocation?.poi == null) {
                    ToastUtils.showShort("请挑选起点")
                    requestFocus(binding.llStartLocation.edtInputKey)
                } else if (endLocation?.poi == null) {
                    ToastUtils.showShort("请挑选结尾")
                    requestFocus(binding.llEndLocation.edtInputKey)
                }
                return@setOnClickListener
            }
            if (getMiddleCount() + 2 != dataList.size) {
                for (i in 0 until getMiddleCount()) {
                    val editInText = !findMidLocationTextView(i + 1)?.text.isNullOrEmpty()
                    val poiNoSelected = locationList.getOrNull(i + 1)?.poi == null
                    if (editInText && poiNoSelected) {
                        ToastUtils.showShort("请挑选第${i + 1}个途经点")
                        return@setOnClickListener
                    }
                }
            }
            val resultList = ArrayList<EditLocation>()
            var latestData: EditLocation? = null
            for (data in dataList) {
                if (data.poi?.poiName != latestData?.poi?.poiName ||
                    data.poi?.poiType != latestData?.poi?.poiType
                ) {
                    resultList.add(data)
                }
                latestData = data
            }
            if (resultList.size == 2) {
                if (isSamePOI(resultList[0].poi, resultList[1].poi)) {
                    ToastUtils.showShort("起点与结尾不能相同")
                    return@setOnClickListener
                }
            } else if (resultList.size < 2) {
                ToastUtils.showShort("起点与结尾不能相同")
                return@setOnClickListener
            }
            hideSoftInput()
            onEventListener?.onLocationEditComplete(resultList)
        }
    }
    /**
     * 获取途经点个数
     */
    protected fun getMiddleCount(): Int {
        val allCount = binding.llMoreEditLine.childCount - 2
        if (allCount >= 0) {
            return allCount;
        }
        return 0;
    }
    private fun isSamePOI(poi1: SearchPOI?, poi2: SearchPOI?): Boolean {
        return poi1 == poi2 || poi1?.poiName == poi2?.poiName ||
                (poi1?.poiType == POIType.userPosition && POIType.userPosition == poi2?.poiType)
                || (poi1?.poiType == POIType.carPosition && POIType.carPosition == poi2?.poiType)
    }
    private fun requestFocus(view: TextView, isSelectedText: Boolean = false) {
        post {
            view.requestFocus()
            if (view is EditText && isSelectedText) {
                val length = view.text.length;
                (view as EditText).setSelection(0, length)
                KeyboardUtils.showSoftInput(view)
            }
        }
    }
    fun setLocationList(list: List<EditLocation>) {
        list.getOrNull(0)?.let {
            startLocation.poi = it.poi
            updateLocationUI(startLocation)
        }
        list.getOrNull(list.lastIndex)?.let {
            endLocation.poi = it.poi
            updateLocationUI(endLocation)
        }
        locationList.clear()
        locationList.add(startLocation)
        locationList.add(endLocation)
        removeAllMiddleView()
        switchUIState(list.size > 2)
        for (i in 1 until list.lastIndex) {
            val v = addMiddleLocation(i - 1)
            val item = list[i]
            locationList[i].poi = item.poi
            updateLocationUI(locationList[i], v)
        }
    }
    private fun removeAllMiddleView() {
        var hasCount = binding.llMoreEditLine.childCount
        while (hasCount > 2) {
            val index = hasCount - 1 - 1;
            binding.llMoreEditLine.getChildAt(index)?.let { v ->
                binding.llMoreEditLine.removeView(v)
            }
            hasCount = binding.llMoreEditLine.childCount
        }
    }
    fun setIndexFocus(focusIndex: Int) {
        locationList.getOrNull(focusIndex)?.let {
            if (it.locationType == EditLocation.TYPE_START) {
                requestFocus(binding.llStartLocation.edtInputKey, true)
            } else if (it.locationType == EditLocation.TYPE_END) {
                requestFocus(binding.llEndLocation.edtInputKey, true)
            } else {
                getMiddleItemViewAt(focusIndex - 1)
                    ?.findViewById<EditText>(R.id.edt_input_key)
                    ?.let { v ->
                        requestFocus(v, true)
                    }
            }
        }
    }
    /**
     * @param middleIndex 从0开端,扫除起点和结尾之外的列表
     */
    protected fun getMiddleItemViewAt(middleIndex: Int): View? {
        return binding.llMoreEditLine.getChildAt(middleIndex + 1)
    }
    fun showSoftInput() {
        if (context is Activity) {
            KeyboardUtils.showSoftInput(context as Activity)
        } else {
            KeyboardUtils.showSoftInput();
        }
    }
    fun showSoftInput(editText: View) {
        KeyboardUtils.showSoftInput(editText)
    }
    fun hideSoftInput() {
        if (context is Activity) {
            KeyboardUtils.hideSoftInput(context as Activity)
        } else {
            KeyboardUtils.hideSoftInput(this)
        }
    }
    /**
     * 设置POI ; 有光标时就填充对应的项;没有光标时优先填充未填写项,顺序:结尾、起点、途经点
     */
    fun setPoi(poi: SearchPOI) {
        val v = findFocusEditTextView()
        if (v != null) {
            val tagData = v.getTag(R.id.cb_item_tag)
            if (tagData is EditLocation) {
                tagData.poi = poi
                updateLocationUI(tagData, v)
            }
        } else if (endLocation.poi == null) {
            endLocation.poi = poi
            updateLocationUI(endLocation)
        } else if (startLocation.poi == null) {
            startLocation.poi = poi
            updateLocationUI(startLocation)
        } else {
            val index = findNonePoiMiddleLocation()
            if (index <= 0) {
                endLocation.poi = poi
                updateLocationUI(endLocation)
            } else {
                locationList[index].poi = poi
                getMiddleItemViewAt(index - 1)
                    ?.findViewById<EditText>(R.id.edt_input_key)
                    ?.let { v ->
                        updateLocationUI(locationList[index], v)
                    }
            }
        }
        //光标自动聚焦到下一个空的输入框
        findFirstNoneEditTextView()?.let {
            requestFocus(it)
            callOnTextChanged(it, it.text)
        } ?: let {
            callOnTextChanged(null, "")
            hideSoftInput()
        }
        //无途经点
        if (getMiddleCount() <= 0) {
            if (startLocation.poi != null && endLocation.poi != null) {
                if (isSamePOI(startLocation.poi, endLocation.poi)) {
                    ToastUtils.showShort("起点与结尾不能相同")
                } else {
                    val resultList = ArrayList<EditLocation>();
                    resultList.add(startLocation)
                    resultList.add(endLocation)
                    hideSoftInput()
                    onEventListener?.onLocationEditComplete(resultList)
                }
            }
        }
    }
    private fun setUpEditViewKeyboard(editText: EditText?) {
        editText?.imeOptions = EditorInfo.IME_ACTION_SEARCH
        editText?.setOnEditorActionListener { v, actionId, event ->
            val isClick = event?.keyCode == KeyEvent.KEYCODE_ENTER;
            if (actionId == EditorInfo.IME_ACTION_SEARCH || isClick) {
                onEventListener?.onEditTextChange(v as? EditText, v.text)
                return@setOnEditorActionListener true;
            }
            return@setOnEditorActionListener false;
        }
        editText?.isLongClickable = false;
        editText?.setOnFocusChangeListener { v, hasFocus ->
            if (hasFocus) {
                (v as? EditText)?.let { edit ->
                    edit.postDelayed({
                        edit.setSelection(0, edit.text?.length ?: 0)
                    }, 100);
                }
            }
        }
        editText?.setOnClickListener { v ->
            (v as? EditText)?.let { edit ->
                edit.setSelection(0, edit.text?.length ?: 0)
            }
        }
    }
    private fun findNonePoiMiddleLocation(): Int {
        for (index in 1..locationList.size - 2) {
            val item = locationList[index]
            if (item.poi == null) {
                return index
            }
        }
        return -1;
    }
    private fun findFirstNoneEditTextView(): EditText? {
        if (startLocation.poi == null) {
            return binding.llStartLocation.edtInputKey
        }
        if (endLocation.poi == null) {
            return binding.llEndLocation.edtInputKey
        }
        for (i in 0 until getMiddleCount()) {
            val itemView = getMiddleItemViewAt(i)
            val editText = itemView?.findViewById<EditText>(R.id.edt_input_key)
            if (editText != null && (editText.getTag(R.id.cb_item_tag) as? EditLocation)?.poi == null) {
                return editText
            }
        }
        return null
    }
    private fun findFocusEditTextView(): EditText? {
        if (binding.llStartLocation.edtInputKey.hasFocus()) {
            return binding.llStartLocation.edtInputKey
        }
        if (binding.llEndLocation.edtInputKey.hasFocus()) {
            return binding.llEndLocation.edtInputKey
        }
        for (i in 0 until getMiddleCount()) {
            val itemView = getMiddleItemViewAt(i)
            val editText = itemView?.findViewById<EditText>(R.id.edt_input_key)
            if (editText != null && editText.hasFocus()) {
                return editText
            }
        }
        return null
    }
    private fun findMidLocationTextView(index: Int): EditText? {
        val middleIndex = index - 1;
        val itemView = getMiddleItemViewAt(middleIndex)
        return itemView?.findViewById<EditText>(R.id.edt_input_key)
    }
    protected fun findLocationTextView(index: Int): EditText? {
        val itemView = binding.llMoreEditLine.getChildAt(index)
        return itemView?.findViewById<EditText>(R.id.edt_input_key)
    }
    protected fun updateLocationUI(location: EditLocation, textView: TextView? = null) {
        var addressName: String = location.poi?.poiName ?: ""
        if (location.poi?.poiType == POIType.userPosition) {
            addressName = "我的方位"
        } else if (location.poi?.poiType == POIType.carPosition) {
            addressName = "车辆方位"
        }
        var editView: TextView? = null
        if (location.locationType == EditLocation.TYPE_START) {
            editView = binding.llStartLocation.edtInputKey;
        } else if (location.locationType == EditLocation.TYPE_END) {
            editView = binding.llEndLocation.edtInputKey
        } else {
            editView = textView
        }
        if (editView != null && !addressName.isNullOrEmpty()) {
            textChangeIgnoreTask = TextChangeIgnore(editView, addressName)
        }
        val showText = addressName.ifEmpty { (location.editContent ?: "") }
        editView?.text = showText
        editView?.text?.length?.let { l ->
            (editView as? EditText)?.setSelection(l)
        }
    }
    private fun setViewMargin(v: View, left: Int, top: Int, right: Int, bottom: Int) {
        v.layoutParams?.let {
            val p = it as MarginLayoutParams
            if (p.leftMargin != left ||
                p.topMargin != top ||
                p.rightMargin != right ||
                p.bottomMargin != bottom
            ) {
                p.setMargins(left, top, right, bottom)
                v.layoutParams = p
            }
        }
    }
    private fun switchUIState(isHasMiddleLocation: Boolean, middleCount: Int = 1) {
        if (isHasMiddleLocation) {
            binding.rlEditLocationContainer.setBackgroundResource(R.drawable.bg_transparent)
            binding.llStartLocation.llEditContent.setBackgroundResource(R.drawable.bg_edit_box)
            binding.llEndLocation.llEditContent.setBackgroundResource(R.drawable.bg_edit_box)
            binding.llStartLocation.ivDelEdit.visibility =
                if (middleCount == 0) View.GONE else View.VISIBLE
            binding.llEndLocation.ivDelEdit.visibility =
                if (middleCount == 0) View.GONE else View.VISIBLE
            binding.llEditAction.visibility = View.GONE
            binding.llStartLocation.ivEditMoveTag.visibility = View.VISIBLE
            binding.llEndLocation.ivEditMoveTag.visibility = View.VISIBLE
            binding.ivLocationChangeMore.visibility = View.VISIBLE
            binding.rlMoreAddLocation.visibility = View.VISIBLE
            binding.twoLineSplit.visibility = View.GONE
            setViewMargin(
                binding.llEndLocation.root, 0, resources.getDimensionPixelSize(R.dimen.dp_6),
                0, 0
            )
        } else {
            binding.rlEditLocationContainer.setBackgroundResource(R.drawable.bg_edit_box)
            binding.llStartLocation.llEditContent.setBackgroundResource(R.drawable.bg_transparent)
            binding.llEndLocation.llEditContent.setBackgroundResource(R.drawable.bg_transparent)
            binding.llStartLocation.ivDelEdit.visibility = View.GONE
            binding.llEndLocation.ivDelEdit.visibility = View.GONE
            binding.llStartLocation.ivEditMoveTag.visibility = View.GONE
            binding.llEndLocation.ivEditMoveTag.visibility = View.GONE
            binding.llEditAction.visibility = View.VISIBLE
            binding.ivLocationChangeMore.visibility = View.GONE
            binding.rlMoreAddLocation.visibility = View.GONE
            removeAllMiddleView()
            binding.twoLineSplit.visibility = View.VISIBLE
            setViewMargin(
                binding.llEndLocation.root, 0, 0,
                0, 0
            )
        }
    }
    /**
     * @param middleIndex 从0开端,扫除起点和结尾之外的列表
     */
    private fun addMiddleLocation(middleIndex: Int = 0): EditText {
        val params = LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
        val top: Int = resources.getDimensionPixelSize(R.dimen.dp_6)
        val bottom = 0
        params.topMargin = top
        params.bottomMargin = bottom
        val middleViewBinding = createMiddleLocationEditView();
        var middlePos: Int = middleIndex
        if (getMiddleCount() > middleIndex) {
            binding.llMoreEditLine.addView(middleViewBinding.root, middleIndex + 1, params)
        } else {
            val index = binding.llMoreEditLine.childCount - 1;
            binding.llMoreEditLine.addView(middleViewBinding.root, index, params)
            middlePos = getMiddleCount() - 1;
        }
        val middleData = EditLocation.createMiddleLocation()
        middleViewBinding.root.setTag(R.id.cb_item_tag, middleData)
        middleViewBinding.edtInputKey.setTag(R.id.cb_item_tag, middleData)
        locationList.add(middlePos + 1, middleData)
        updateAddMoreUIState()
        post {
            middleViewBinding.edtInputKey.requestFocus()
        }
        return middleViewBinding.edtInputKey
    }
    private fun updateAddMoreUIState() {
        val couldAddSize = MAX_MIDDLE_NUM - getMiddleCount()
        binding.tvAddNum.text = if (couldAddSize <= 0) "已到达上限" else "还可增加${couldAddSize}个"
        if (couldAddSize <= 0) {
            binding.rlMoreAdd.tag = false
            setViewColorFilter(binding.rlMoreAdd, 0.5f)
        } else {
            binding.rlMoreAdd.tag = true;
            setViewColorFilter(binding.rlMoreAdd, 1f)
        }
    }
    private fun setViewColorFilter(view: View, alphaScale: Float) {
        val paint = Paint()
        val cm = ColorMatrix()
        cm.setScale(1f, 1f, 1f, alphaScale)
        paint.colorFilter = ColorMatrixColorFilter(cm)
        view.setLayerType(View.LAYER_TYPE_HARDWARE, paint)
    }
    /**
     * @param middleIndex 从0开端,扫除起点和结尾之外的列表
     */
    private fun removeMiddleLocation(middleIndex: Int) {
        getMiddleItemViewAt(middleIndex)?.let { child ->
            binding.llMoreEditLine.removeView(child)
            (child.getTag(R.id.cb_item_tag) as? EditLocation).let { data ->
                locationList.remove(data)
            }
        }
        /*val index = middleIndex + 1
        val location = locationList.getOrNull(index)
        if (location != null && location.locationType == EditLocation.TYPE_MIDDLE) {
            locationList.removeAt(index)
        }*/
        val count = getMiddleCount()
        switchUIState(true, count)
        updateAddMoreUIState()
    }
    private fun createMiddleLocationEditView(): EditSearchKeyBinding {
        val tempBinding = EditSearchKeyBinding.inflate(LayoutInflater.from(context))
        val itemView: View = tempBinding.root
        tempBinding.ivEdt.setImageResource(R.drawable.middle_location_point)
        tempBinding.llEditContent.setBackgroundResource(R.drawable.bg_edit_box)
        tempBinding.edtInputKey.setText("")
        tempBinding.edtInputKey.hint = "请输入途经点"
        setUpEditViewKeyboard(tempBinding.edtInputKey)
        val middleWatcher = EditTextWatcher(this, tempBinding.edtInputKey)
        tempBinding.edtInputKey.addTextChangedListener(middleWatcher)
        tempBinding.ivEditMoveTag.visibility = View.VISIBLE
        tempBinding.ivDelEdit.visibility = View.VISIBLE
        tempBinding.ivDelEdit.setTag(itemView)
        tempBinding.ivDelEdit.setOnClickListener { v ->
            if (v.tag != null && v.tag is View) {
                val index = binding.llMoreEditLine.indexOfChild(v.tag as View) - 1
                removeMiddleLocation(index)
            }
        }
        return tempBinding
    }
    private fun callOnTextChanged(editText: EditText?, text: CharSequence?) {
        if (textChangeIgnoreTask?.isIgnore(editText, text?.toString() ?: "") != true) {
            (editText?.getTag(R.id.cb_item_tag) as? EditLocation)?.let {
                it.editContent = text?.toString();
                it.poi = null;
            }
            onEventListener?.onEditTextChange(editText, text)
        }
        textChangeIgnoreTask = null
    }
    interface OnEventListener {
        fun onEditTextChange(editText: EditText?, text: CharSequence?)
        fun onCloseClick(v: View)
        fun onLocationEditComplete(locationList: List<EditLocation>)
    }
    class TextChangeIgnore(val tagView: View, val ignoreText: String) {
        fun isIgnore(changeView: View?, changeText: String): Boolean {
            return tagView == changeView && changeText == ignoreText;
        }
    }
    class EditTextWatcher(val v: EditTripLocationListView, val editText: EditText) : TextWatcher {
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        }
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        }
        override fun afterTextChanged(s: Editable?) {
            var inputText = s?.toString() ?: ""
            if (inputText.length > 200) {
                inputText = inputText.substring(0, 200)
                editText.setText(inputText)
                val selection = editText.text.length
                editText.setSelection(selection)
            }
            v.callOnTextChanged(editText, inputText)
        }
    }
}
class EditLocation {
    companion object {
        const val TYPE_START = 0;
        const val TYPE_MIDDLE = 1;
        const val TYPE_END = 2;
        fun createStartLocation(context: Context): EditLocation {
            val location = EditLocation()
            location.locationType = TYPE_START
            val userPoi = SearchPOI()
            userPoi.poiType = POIType.userPosition;
            location.poi = userPoi;
            return location
        }
        fun createMiddleLocation(): EditLocation {
            val location = EditLocation()
            location.locationType = TYPE_MIDDLE
            return location
        }
        fun createEndLocation(): EditLocation {
            val location = EditLocation()
            location.locationType = TYPE_END
            return location
        }
    }
    var locationType: Int = TYPE_START
    var poi: SearchPOI? = null
    /**
     * 修改的文字
     */
    var editContent: String? = null;
}

终究的作用: