Browse Source

add poc2.0需求

lihao1 3 years ago
parent
commit
9c00a60fb0
100 changed files with 8876 additions and 353 deletions
  1. 1 0
      build.gradle
  2. 1 1
      cadengine/build.gradle
  3. 0 189
      cadengine/src/main/java/cn/sagacloud/android/cadengine/FloorScene.kt
  4. 16 6
      cadengine/src/main/java/cn/sagacloud/android/cadengine/items/EquipItem.kt
  5. 1 1
      cadengine/src/main/java/cn/sagacloud/android/cadengine/items/PipeNodeItem.kt
  6. 12 6
      cadengine/src/main/java/cn/sagacloud/android/cadengine/items/PointItem.kt
  7. 28 17
      cadengine/src/main/java/cn/sagacloud/android/cadengine/items/QrcodeItem.kt
  8. 3 3
      cadengine/src/main/java/cn/sagacloud/android/cadengine/items/TunableSpaceItem.kt
  9. BIN
      cadengine/src/main/res/drawable/equip_blue.png
  10. BIN
      cadengine/src/main/res/drawable/equip_green.png
  11. BIN
      cadengine/src/main/res/drawable/equip_red.png
  12. BIN
      cadengine/src/main/res/drawable/qrcode.png
  13. 31 5
      demo/build.gradle
  14. BIN
      demo/libs/httpclient-4.3.5.jar
  15. BIN
      demo/libs/httpcore-4.3.2.jar
  16. BIN
      demo/libs/universal-image-loader-1.9.4-with-sources.jar
  17. 23 11
      demo/src/main/AndroidManifest.xml
  18. 28 0
      demo/src/main/java/com/sybotan/android/demo/activities/BaseActivity.java
  19. 185 0
      demo/src/main/java/com/sybotan/android/demo/activities/EquipDetailActivity.kt
  20. 520 85
      demo/src/main/java/com/sybotan/android/demo/activities/GraphyActivity.kt
  21. 153 0
      demo/src/main/java/com/sybotan/android/demo/activities/LookBigPicActivity.java
  22. 292 0
      demo/src/main/java/com/sybotan/android/demo/activities/Repair_ImgSelActivity.java
  23. 462 0
      demo/src/main/java/com/sybotan/android/demo/activities/Repair_ImgSelFragment.java
  24. 152 0
      demo/src/main/java/com/sybotan/android/demo/activities/SpaceDetailActivity.kt
  25. 1 1
      demo/src/main/java/com/sybotan/android/demo/activities/WebViewActivity.kt
  26. 16 0
      demo/src/main/java/com/sybotan/android/demo/adapter/BaseViewHolder.java
  27. 104 0
      demo/src/main/java/com/sybotan/android/demo/adapter/ImageListRVAdapter.java
  28. 159 0
      demo/src/main/java/com/sybotan/android/demo/adapter/PictureAdapter.java
  29. 77 0
      demo/src/main/java/com/sybotan/android/demo/adapter/RelationExpandAdapter.kt
  30. 119 0
      demo/src/main/java/com/sybotan/android/demo/adapter/SelectPopListAdapter.java
  31. 168 0
      demo/src/main/java/com/sybotan/android/demo/adapter/StaticInfoAdapter.java
  32. 129 0
      demo/src/main/java/com/sybotan/android/demo/adapter/ViewPagerImageAdapter.java
  33. 21 0
      demo/src/main/java/com/sybotan/android/demo/bean/result/Photos.java
  34. 7 6
      demo/src/main/java/com/sybotan/android/demo/graphy/GraphyHelper.kt
  35. 7 1
      demo/src/main/java/com/sybotan/android/demo/retrofit/Constant.java
  36. 30 0
      demo/src/main/java/com/sybotan/android/demo/tools/AnimateUtil.java
  37. 947 0
      demo/src/main/java/com/sybotan/android/demo/tools/CommonUtils.java
  38. 78 0
      demo/src/main/java/com/sybotan/android/demo/tools/DefaultOnDoubleTapListener.java
  39. 121 0
      demo/src/main/java/com/sybotan/android/demo/tools/IPhotoView.java
  40. 97 0
      demo/src/main/java/com/sybotan/android/demo/tools/LocalDataOperation.java
  41. 46 0
      demo/src/main/java/com/sybotan/android/demo/tools/NetUtil.java
  42. 69 0
      demo/src/main/java/com/sybotan/android/demo/tools/OfflineSendRequest.java
  43. 924 0
      demo/src/main/java/com/sybotan/android/demo/tools/PhotoViewAttacher.java
  44. 115 0
      demo/src/main/java/com/sybotan/android/demo/tools/PictureUtils.java
  45. 393 0
      demo/src/main/java/com/sybotan/android/demo/tools/SDCardHelper.java
  46. 107 0
      demo/src/main/java/com/sybotan/android/demo/tools/ScreenUtils.java
  47. 153 0
      demo/src/main/java/com/sybotan/android/demo/tools/ToastUtils.java
  48. 267 0
      demo/src/main/java/com/sybotan/android/demo/tools/UploadImage.java
  49. 294 0
      demo/src/main/java/com/sybotan/android/demo/view/CommonNumberControl.java
  50. 86 0
      demo/src/main/java/com/sybotan/android/demo/view/EquipListDialog.java
  51. 366 0
      demo/src/main/java/com/sybotan/android/demo/view/ListItemView.java
  52. 46 0
      demo/src/main/java/com/sybotan/android/demo/view/MaxHeightRecyclerView.java
  53. 96 0
      demo/src/main/java/com/sybotan/android/demo/view/MyDialogViewSingle.java
  54. 96 0
      demo/src/main/java/com/sybotan/android/demo/view/ObjectRelationDialog.java
  55. 244 0
      demo/src/main/java/com/sybotan/android/demo/view/SelectListPopupWindow.java
  56. 21 11
      demo/src/main/java/com/sybotan/android/demo/viewmodel/GraphyVM.kt
  57. 18 0
      demo/src/main/res/anim/svfade_in_center.xml
  58. 18 0
      demo/src/main/res/anim/svfade_out_center.xml
  59. 11 0
      demo/src/main/res/anim/svslide_in_bottom.xml
  60. 11 0
      demo/src/main/res/anim/svslide_in_top.xml
  61. 11 0
      demo/src/main/res/anim/svslide_out_bottom.xml
  62. 11 0
      demo/src/main/res/anim/svslide_out_top.xml
  63. 6 0
      demo/src/main/res/drawable/bg_button_green.xml
  64. 6 0
      demo/src/main/res/drawable/bg_gray_f2f3f6.xml
  65. 8 0
      demo/src/main/res/drawable/bg_rect_stroke_gray_2.xml
  66. 12 0
      demo/src/main/res/drawable/bg_rect_stroke_gray_top_bottom.xml
  67. 6 0
      demo/src/main/res/drawable/bg_round_search.xml
  68. 10 0
      demo/src/main/res/drawable/bg_toast.xml
  69. 7 0
      demo/src/main/res/drawable/bg_white.xml
  70. 7 0
      demo/src/main/res/drawable/bg_yellow_700.xml
  71. BIN
      demo/src/main/res/drawable/checkbox_selected.png
  72. 7 0
      demo/src/main/res/drawable/checkbox_style.xml
  73. BIN
      demo/src/main/res/drawable/checkbox_unselected.png
  74. 8 0
      demo/src/main/res/drawable/common_bg_divider.xml
  75. 8 0
      demo/src/main/res/drawable/common_bg_divider_f7f7f7_10.xml
  76. 13 0
      demo/src/main/res/drawable/common_line_ffffff_single_16.xml
  77. 9 0
      demo/src/main/res/drawable/common_line_ffffff_single_all.xml
  78. 11 0
      demo/src/main/res/drawable/common_line_ffffff_top_8.xml
  79. 16 0
      demo/src/main/res/drawable/corners_line_black.xml
  80. 12 0
      demo/src/main/res/drawable/shape_rectangle_green_3.xml
  81. 115 0
      demo/src/main/res/layout/activity_equip_detail.xml
  82. 221 10
      demo/src/main/res/layout/activity_graphy.xml
  83. 19 0
      demo/src/main/res/layout/activity_look_big_pic.xml
  84. 74 0
      demo/src/main/res/layout/activity_newcreate_dialog.xml
  85. 79 0
      demo/src/main/res/layout/activity_space_detail.xml
  86. 29 0
      demo/src/main/res/layout/child_expand_relation.xml
  87. 68 0
      demo/src/main/res/layout/common_fragment_img_sel.xml
  88. 28 0
      demo/src/main/res/layout/common_item_img_sel.xml
  89. 34 0
      demo/src/main/res/layout/common_number_control.xml
  90. 57 0
      demo/src/main/res/layout/dialog_equip_list.xml
  91. 65 0
      demo/src/main/res/layout/dialog_list_choice.xml
  92. 133 0
      demo/src/main/res/layout/dialog_object_relation.xml
  93. 35 0
      demo/src/main/res/layout/geometry_info.xml
  94. 28 0
      demo/src/main/res/layout/group_expand_relation.xml
  95. 34 0
      demo/src/main/res/layout/item_node_nine_pic.xml
  96. 70 0
      demo/src/main/res/layout/layout_common_list_item.xml
  97. 72 0
      demo/src/main/res/layout/layout_common_list_item1.xml
  98. 94 0
      demo/src/main/res/layout/layout_common_list_item2.xml
  99. 83 0
      demo/src/main/res/layout/layout_common_list_item3.xml
  100. 0 0
      demo/src/main/res/layout/layout_common_list_item4.xml

+ 1 - 0
build.gradle

@@ -71,6 +71,7 @@ allprojects {
                 password = 'test123'
             }
         }
+        maven{url"https://jitpack.io"}
 
     }
     tasks.withType(Javadoc) {

+ 1 - 1
cadengine/build.gradle

@@ -26,7 +26,7 @@ version rootProject.version
 
 apply plugin: 'com.android.library'
 apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
+//apply plugin: 'kotlin-android-extensions'
 
 apply plugin: 'org.jetbrains.dokka-android'
 //apply plugin: 'maven'

+ 0 - 189
cadengine/src/main/java/cn/sagacloud/android/cadengine/FloorScene.kt

@@ -416,195 +416,6 @@ open class FloorScene : SGraphyScene() {
     }
 
     override fun onSingleTapUp(e: SMotionEvent): Boolean {
-        //如果只是查看模式,可以选中空间,进行空间内业务操作
-        if ("normal".equals(type)) {
-            var hasChose = false
-            for (space in tunableSpaceList) {
-                val polygonContainsPoint = Lasso.isPolygonContainsPoint(
-                        space.mPoints,
-                        android.graphics.Point(e.x.toInt(), -e.y.toInt())
-                )
-                if (polygonContainsPoint) {
-                    if (hasChose) {
-                        if (space.calculateArea() < choseSpace!!.calculateArea()) {
-                            space.outPaint.color = Color.rgb(0xbd, 0xe8, 0xfe)
-                            space.outPaint.strokeWidth = 500f
-                            space.paint.color = Opt.spaceChoseColor
-                            mapType = "space"
-                            choseSpace = space
-                            hasChose = true
-
-                        }
-                    } else {
-                        space.outPaint.color = Color.rgb(0xbd, 0xe8, 0xfe)
-                        space.paint.color = Opt.spaceChoseColor
-                        space.outPaint.strokeWidth = 500f
-                        mapType = "space"
-                        choseSpace = space
-                        hasChose = true
-                    }
-                } else {
-                    space.paint.color = Opt.spaceColor
-                    space.outPaint.strokeWidth = 50f
-                    space.outPaint.color = Color.rgb(0xff, 0xff, 0xff)
-                }
-            }
-            if (!hasChose) {
-                mapType = "nothing"
-                choseSpace = null
-            }
-        }
-
-
-        var outSide = false
-        //计算控制点的边界
-        //是否已经拥有该点
-        //打点业务
-        if ("dot".equals(type)) {
-            for (space in spaceList) {
-                val polygonContainsPoint = Lasso.isPolygonContainsPoint(
-                        space.mPoints,
-                        android.graphics.Point(e.x.toInt(), -e.y.toInt())
-                )
-                outSide = true
-                space.paint.color = Opt.spaceChoseColor
-                space.outPaint.color = Color.rgb(0xbd, 0xe8, 0xfe)
-            }
-//            val point = Point()
-//            point.mX = e.x
-//            point.mY = e.y
-//            var item = PointItem(point, null, defaultPointScale)
-//            item!!.isVisible = true
-//            item!!.zOrder = 100000f
-//            pointItemList.add(item!!)
-
-            //添加问题点业务
-            for (item in pointItemList) {
-                item.isVisible = isShowSpace
-                item.zOrder = 100000f
-                addItem(item)
-            }
-            if (!outSide) {
-                return true
-            }
-        }
-
-        //绘制管道业务
-        else if ("pipe".equals(type)) {
-            var pipe = PipeLine()
-            var pointf = PointF()
-            pointf.x = e.x
-            pointf.y = -e.y
-            pipeLineList.add(pointf)
-            pipe.points!!.addAll(pipeLineList)
-            var item = PipeLineItem(pipe, null)
-            item!!.isVisible = true
-            item!!.zOrder = 100000f
-            drawingPipeItemList.add(item)
-            for (item in drawingPipeItemList) {
-                item.isVisible = isShowSpace
-                item.zOrder = 100000f
-                addItem(item)
-            }
-
-            //绘制二维码
-        } else if ("qrcode".equals(type)) {
-            point.mX = e.x
-            point.mY = e.y
-
-            if (item != null) {
-                removeItem(item!!)
-            }
-            item = QrcodeItem(point, null, defaultPointScale)
-            item!!.isVisible = true
-            item!!.zOrder = 100000f
-            addItem(item!!)
-
-            //绘制设备
-        } else if ("equip".equals(type)) {
-            point.mX = e.x
-            point.mY = e.y
-
-            if (eqItem != null) {
-                removeItem(eqItem!!)
-            }
-
-            var dist = 500.00
-            for (index in 0..choseSpace!!.mPoints.size - 1) {
-
-
-                val point1 = choseSpace!!.mPoints.get(index)
-                var point2 = PointF()
-                if (index == choseSpace!!.mPoints.size - 1) {
-                    point2 = choseSpace!!.mPoints.get(0)
-                } else {
-                    point2 = choseSpace!!.mPoints.get(index + 1)
-                }
-
-
-                val l1 = Math.sqrt(Math.pow(Math.abs(point1.x - point2.x).toDouble(), 2.0) + Math.pow(Math.abs(point1.y - point2.y).toDouble(), 2.0)).toFloat()
-                val l2 = Math.sqrt(Math.pow(Math.abs(point1.x - e.x).toDouble(), 2.0) + Math.pow(Math.abs(point1.y + e.y).toDouble(), 2.0)).toFloat()
-                val l3 = Math.sqrt(Math.pow(Math.abs(point2.x - e.x).toDouble(), 2.0) + Math.pow(Math.abs(point2.y + e.y).toDouble(), 2.0)).toFloat()
-                val halfP = (l1 + l2 + l3) / 2
-                val s = Math.sqrt(halfP * (halfP - l1) * (halfP - l2) * (halfP - l3).toDouble())
-                val h = 2 * s / l1
-                if (h < dist) {
-                    dist = h
-                    var k1: Float
-                    if (point1.x - point2.x == 0f) {
-                        k1 = 0f
-                    } else {
-                        k1 = (point1.y - point2.y) / (point1.x - point2.x)
-                    }
-
-                    val b1 = point1.y - k1 * point1.x
-
-                    var k2: Float
-                    if (k1 == 0f) {
-                        k2 = 0f
-                    } else {
-                        k2 = -1 / k1
-                    }
-
-                    val b2 = e.y - k2 * e.x
-
-                    val x1 = (b2 - b1) / (k1 - k2)
-                    val y1 = k2 * (e.x) + b2
-
-                    if (point1.x - point2.x == 0f) {
-                        point.mX = point1.x
-                        if (Math.abs(k1) == 0f) {
-                            point.mY = e.y
-                        } else {
-                            if (point1.y - point2.y == 0f) {
-                                point.mY = -point1.y
-                            } else {
-                                point.mY = y1
-                            }
-                        }
-
-                    } else {
-                        if (Math.abs(k1) == 0f) {
-                            point.mX = e.x
-                        } else {
-                            point.mX = x1
-                        }
-                        if (point1.y - point2.y == 0f) {
-                            point.mY = -point1.y
-                        } else {
-                            point.mY = y1
-                        }
-                    }
-
-                }
-
-            }
-
-            eqItem = EquipItem(point, null, defaultPointScale)
-            eqItem!!.isVisible = true
-            eqItem!!.zOrder = 10000012f
-            addItem(eqItem!!)
-        }
 
         topUp.onSingUp(e.x, e.y)
         return super.onSingleTapUp(e)

+ 16 - 6
cadengine/src/main/java/cn/sagacloud/android/cadengine/items/EquipItem.kt

@@ -1,13 +1,13 @@
 package cn.sagacloud.android.cadengine.items
 
 
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
+import android.content.Context
+import android.graphics.*
 import android.graphics.drawable.BitmapDrawable
 import android.util.Log
 import android.view.GestureDetector
 import android.view.MotionEvent
+import cn.sagacloud.android.cadengine.R
 import cn.sagacloud.android.cadengine.types.Point
 import com.sybotan.android.graphy.SGraphyItem
 import com.sybotan.android.graphy.SMotionEvent
@@ -19,18 +19,24 @@ import java.util.*
  * Created by lihao.
  * Date: 2021/6/4
  */
-class EquipItem(val data: Point, parent: SGraphyItem? = null, defaultScale: Float? = null) :
+class EquipItem(
+    var context: Context,
+    val data: Point,
+    parent: SGraphyItem? = null,
+    defaultScale: Float? = null
+) :
     SGraphyItem(parent) {
     /** 新建 画笔 */
     val mPaint_inner = Paint()
     val mPaint_center = Paint()
     var mDefaultScale = 0f
+    var mBitmap: Bitmap
 
     init {
         if (defaultScale != null) {
             mDefaultScale = defaultScale
         }
-
+        mBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.equip_blue)
         /** 新建 */
         //内圈
         mPaint_inner.color = Color.parseColor("#66272727")
@@ -61,7 +67,11 @@ class EquipItem(val data: Point, parent: SGraphyItem? = null, defaultScale: Floa
 
     override fun onDraw(canvas: Canvas) {
         data.scalePoint = mDefaultScale / scene!!.view!!.scale
-        canvas.drawCircle(data.mX, data.mY, 500f, mPaint_center)
+        val matrix = Matrix()
+        matrix.postScale(3.0000007f, 3.0000007f)
+        matrix.postTranslate(data.mX - 400f, data.mY - 400f)
+        canvas.drawBitmap(mBitmap, matrix, mPaint_center)
+//        canvas.drawCircle(data.mX, data.mY, 500f, mPaint_center)
 //        canvas.drawCircle(data.mX, -data.mY, 1500f, mPaint_inner)
         super.onDraw(canvas)
     } // Function onDraw()

+ 1 - 1
cadengine/src/main/java/cn/sagacloud/android/cadengine/items/PipeNodeItem.kt

@@ -86,7 +86,7 @@ class PipeNodeItem(val data: PointF, parent: SGraphyItem? = null) : SGraphyItem(
      * @param   rect            绘制区域
      */
     override fun onDraw(canvas: Canvas) {
-        canvas.drawCircle(data.x, -data.y, 400f, mPaint_inner)
+//        canvas.drawCircle(data.x, -data.y, 400f, mPaint_inner)
     } // Function onDraw()
 
 }

+ 12 - 6
cadengine/src/main/java/cn/sagacloud/android/cadengine/items/PointItem.kt

@@ -17,8 +17,8 @@ import java.util.*
  * Created by lihao.
  * Date: 2021/6/4
  */
-class PointItem(val data: Point, parent: SGraphyItem? = null, defaultScale: Float) :
-        SGraphyItem(parent) {
+class PointItem(val data: Point, parent: SGraphyItem? = null, defaultScale: Float? = null) :
+    SGraphyItem(parent) {
     private lateinit var actionMove: ActionMove
 
     /** 新建 画笔 */
@@ -42,8 +42,14 @@ class PointItem(val data: Point, parent: SGraphyItem? = null, defaultScale: Floa
     //var mScale = 1f
 
     init {
-        flags = EnumSet.of(SGraphyItemFlag.ItemIsMovable, SGraphyItemFlag.ItemIsSelectable, SGraphyItemFlag.ItemIsFocusable)
-        mDefaultScale = defaultScale;
+//        flags = EnumSet.of(
+//            SGraphyItemFlag.ItemIsMovable,
+//            SGraphyItemFlag.ItemIsSelectable,
+//            SGraphyItemFlag.ItemIsFocusable
+//        )
+        if (defaultScale != null) {
+            mDefaultScale = defaultScale
+        }
         /** 按时 */
         not_overTime_inner.color = Color.parseColor("#ffffff")
         not_overTime_inner.isAntiAlias = true
@@ -81,7 +87,7 @@ class PointItem(val data: Point, parent: SGraphyItem? = null, defaultScale: Floa
         mPaint_inner.isAntiAlias = true
         mPaint_inner.style = Paint.Style.FILL
         //外圈
-        mPaint_outer.color = Color.parseColor("#260c90eb")
+        mPaint_outer.color = Color.parseColor("#0CC2EB")
         mPaint_outer.isAntiAlias = true
         mPaint_outer.style = Paint.Style.FILL
     }
@@ -118,7 +124,7 @@ class PointItem(val data: Point, parent: SGraphyItem? = null, defaultScale: Floa
     }
 
     fun setOnActionMove(actionMove: ActionMove) {
-        this.actionMove=actionMove
+        this.actionMove = actionMove
     }
 
 }

+ 28 - 17
cadengine/src/main/java/cn/sagacloud/android/cadengine/items/QrcodeItem.kt

@@ -1,10 +1,9 @@
 package cn.sagacloud.android.cadengine.items
 
 
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
-import android.graphics.RectF
+import android.content.Context
+import android.graphics.*
+import cn.sagacloud.android.cadengine.R
 import cn.sagacloud.android.cadengine.types.Point
 import com.sybotan.android.graphy.SGraphyItem
 import com.sybotan.android.graphy.enums.SGraphyItemFlag
@@ -14,7 +13,12 @@ import java.util.*
  * Created by lihao.
  * Date: 2021/6/4
  */
-class QrcodeItem(val data: Point, parent: SGraphyItem? = null, defaultScale: Float? = null) :
+class QrcodeItem(
+    var context: Context,
+    val data: Point,
+    parent: SGraphyItem? = null,
+    defaultScale: Float? = null
+) :
     SGraphyItem(parent) {
     /** 新建 画笔 */
     val mPaint_inner = Paint()
@@ -33,18 +37,20 @@ class QrcodeItem(val data: Point, parent: SGraphyItem? = null, defaultScale: Flo
     val checkReviewing_inner = Paint()
     val checkReviewing_outer = Paint()
     var mDefaultScale = 0f
+
     //var defaultScale = 1.0f
     //var mScale = 1f
+    var mBitmap: Bitmap
 
     init {
         if (defaultScale != null) {
             mDefaultScale = defaultScale
         }
-        flags = EnumSet.of(
-            SGraphyItemFlag.ItemIsMovable,
-            SGraphyItemFlag.ItemIsSelectable,
-            SGraphyItemFlag.ItemIsFocusable
-        )
+//        flags = EnumSet.of(
+//            SGraphyItemFlag.ItemIsMovable,
+//            SGraphyItemFlag.ItemIsSelectable,
+//            SGraphyItemFlag.ItemIsFocusable
+//        )
         /** 按时 */
         not_overTime_inner.color = Color.parseColor("#ffffff")
         not_overTime_inner.isAntiAlias = true
@@ -85,6 +91,7 @@ class QrcodeItem(val data: Point, parent: SGraphyItem? = null, defaultScale: Flo
         mPaint_outer.color = Color.parseColor("#260c90eb")
         mPaint_outer.isAntiAlias = true
         mPaint_outer.style = Paint.Style.FILL
+        mBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.qrcode)
     }
 
     override fun boundingRect(): RectF {
@@ -98,13 +105,17 @@ class QrcodeItem(val data: Point, parent: SGraphyItem? = null, defaultScale: Flo
      * @param   rect            绘制区域
      */
     override fun onDraw(canvas: Canvas) {
-        data.scalePoint = mDefaultScale / scene!!.view!!.scale
-        mPaint_inner.color = if (this.isSelected) {
-            Color.RED
-        } else {
-            Color.BLUE
-        }
-        canvas.drawCircle(data.mX, data.mY, 500f, mPaint_inner)
+//        data.scalePoint = mDefaultScale / scene!!.view!!.scale
+//        mPaint_inner.color = if (this.isSelected) {
+//            Color.RED
+//        } else {
+//            Color.BLUE
+//        }
+//        canvas.drawCircle(data.mX, data.mY, 500f, mPaint_inner)
+        val matrix = Matrix()
+        matrix.postScale(4.0000007f, 4.0000007f)
+        matrix.postTranslate(data.mX - 400f, data.mY - 400f)
+        canvas.drawBitmap(mBitmap, matrix, mPaint_center)
         super.onDraw(canvas)
     } // Function onDraw()
 

+ 3 - 3
cadengine/src/main/java/cn/sagacloud/android/cadengine/items/TunableSpaceItem.kt

@@ -205,13 +205,13 @@ class TunableSpaceItem(val data: TunableSpace, parent: SGraphyItem? = null) : SG
             } else {
                 if (data.jobStatus.equals("01") || data.jobStatus.equals("04")) {
                     //黄
-                    paint.color = Color.parseColor("#D0D1BE")
+                    paint.color = Color.parseColor("#FFDAAD")
                 } else if (data.jobStatus.equals("02")) {
                     //红
-                    paint.color = Color.parseColor("#745764")
+                    paint.color = Color.parseColor("#FFADAD")
                 } else if (data.jobStatus.equals("03") || data.jobStatus.equals("05")) {
                     //绿
-                    paint.color = Color.parseColor("#3F6C5E")
+                    paint.color = Color.parseColor("#50E3C2")
                 }
             }
             outPaint.color = Color.parseColor("#7E96FF")

BIN
cadengine/src/main/res/drawable/equip_blue.png


BIN
cadengine/src/main/res/drawable/equip_green.png


BIN
cadengine/src/main/res/drawable/equip_red.png


BIN
cadengine/src/main/res/drawable/qrcode.png


+ 31 - 5
demo/build.gradle

@@ -77,6 +77,19 @@ android {
         jvmTarget = JVM_TARGET
     }
 
+    packagingOptions {
+        exclude 'META-INF/DEPENDENCIES.txt'
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+        exclude 'META-INF/NOTICE'
+        exclude 'META-INF/LICENSE'
+        exclude 'META-INF/DEPENDENCIES'
+        exclude 'META-INF/notice.txt'
+        exclude 'META-INF/license.txt'
+        exclude 'META-INF/dependencies.txt'
+        exclude 'META-INF/LGPL2.1'
+    }
+
     compileOptions {
         targetCompatibility JavaVersion.VERSION_1_8
         sourceCompatibility JavaVersion.VERSION_1_8
@@ -98,6 +111,9 @@ android {
 dependencies {
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation files('libs/zbardecoder.jar')
+    implementation files('libs/universal-image-loader-1.9.4-with-sources.jar')
+    implementation files('libs/httpclient-4.3.5.jar')
+    implementation files('libs/httpcore-4.3.2.jar')
     testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: JUNIT_VERSION
     androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
         exclude group: 'com.android.support', module: 'support-annotations'
@@ -119,6 +135,16 @@ dependencies {
     // implementation group: 'androidx.legacy', name: 'legacy-support-v4', version: ANDROID_SUPPORT_VERSION
     // implementation group: 'androidx.constraintlayout', name: 'constraintlayout', version: CONSTRAINT_VERSION
 
+    implementation 'com.github.bumptech.glide:glide:4.0.0'
+    implementation 'com.github.bumptech.glide:okhttp3-integration:4.0.0'
+    kapt 'com.github.bumptech.glide:compiler:4.0.0'
+    implementation 'com.contrarywind:Android-PickerView:4.1.3'
+    implementation 'com.bm.photoview:library:1.4.1'
+    implementation 'com.yuyh.imgsel:library:1.3.8'
+    implementation 'com.github.chrisbanes:PhotoView:1.2.6'
+    implementation 'com.squareup.okhttp3:logging-interceptor:3.1.2'
+    implementation 'commons-logging:commons-logging:1.2'
+
     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     // anko依赖
     // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-commons
@@ -172,17 +198,17 @@ dependencies {
     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     // Sybotan依赖
     implementation group: 'cn.sagacloud', name: 'saga-kotlin-base', version: SAGA_KOTLIN_VERSION
-//    implementation project(':sybotan-android-base')
-//    implementation project(':sybotan-android-database')
+    implementation project(':sybotan-android-base')
+    implementation project(':sybotan-android-database')
 //    implementation project(':sybotan-android-devices')
 //    implementation project(':sybotan-android-views')
-//    implementation project(':sybotan-android-graphy')
+    implementation project(':sybotan-android-graphy')
 //    implementation project(':sybotan-android-works')
     implementation project(':cadengine')
     // Sybotan依赖
     implementation group: 'cn.sagacloud', name: 'saga-kotlin-database', version: SAGA_KOTLIN_VERSION
-    implementation group: 'cn.sagacloud', name: 'sybotan-android-base', version: SYBOTAN_ANDROID_VERSION
-    implementation group: 'cn.sagacloud', name: 'sybotan-android-graphy', version: SYBOTAN_ANDROID_VERSION
+//    implementation group: 'cn.sagacloud', name: 'sybotan-android-base', version: SYBOTAN_ANDROID_VERSION
+//    implementation group: 'cn.sagacloud', name: 'sybotan-android-graphy', version: SYBOTAN_ANDROID_VERSION
     implementation 'androidx.multidex:multidex:2.0.1'
     implementation 'io.reactivex.rxjava2:rxjava:2.2.9'
     implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

BIN
demo/libs/httpclient-4.3.5.jar


BIN
demo/libs/httpcore-4.3.2.jar


BIN
demo/libs/universal-image-loader-1.9.4-with-sources.jar


+ 23 - 11
demo/src/main/AndroidManifest.xml

@@ -28,8 +28,17 @@
         android:supportsRtl="true"
         android:theme="@style/AppTheme">
         <activity
+            android:name=".activities.PocActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
             android:name=".activities.poc.RelationActivity"
-            android:exported="true"></activity>
+            android:exported="true" />
         <activity
             android:name=".activities.poc.RecordInfoActivity"
             android:exported="true" />
@@ -38,7 +47,7 @@
             android:exported="true" />
         <activity
             android:name=".activities.poc.DeviceActivity"
-            android:exported="true"></activity>
+            android:exported="true" />
         <activity
             android:name=".activities.poc.SceneActivity"
             android:exported="true" />
@@ -47,7 +56,7 @@
             android:exported="true" />
         <activity
             android:name=".activities.MainActivity"
-            android:exported="true"></activity>
+            android:exported="true" />
         <activity
             android:name=".activities.GraphyActivity"
             android:exported="true" />
@@ -55,14 +64,17 @@
             android:name=".activities.WebViewActivity"
             android:exported="true" />
         <activity
-            android:name=".activities.PocActivity"
-            android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
+            android:name=".activities.EquipDetailActivity"
+            android:exported="true" />
+        <activity
+            android:name=".activities.SpaceDetailActivity"
+            android:exported="true" />
+        <activity
+            android:name=".activities.LookBigPicActivity"
+            android:exported="true" />
+        <activity
+            android:name=".activities.Repair_ImgSelActivity"
+            android:exported="true" />
         <activity
             android:name="com.zbar.lib.CaptureActivity"
             android:exported="true" />

+ 28 - 0
demo/src/main/java/com/sybotan/android/demo/activities/BaseActivity.java

@@ -1,9 +1,16 @@
 package com.sybotan.android.demo.activities;
 
 import android.os.Bundle;
+import android.util.Log;
 
+import com.sybotan.android.demo.DemoApp;
+import com.sybotan.android.demo.tools.LocalDataOperation;
+import com.sybotan.android.demo.tools.NetUtil;
 import com.trello.rxlifecycle2.components.support.RxAppCompatActivity;
 
+import java.io.File;
+import java.util.Objects;
+
 /**
  * Created by lihao.
  * Date: 2021/6/8
@@ -16,4 +23,25 @@ public class BaseActivity extends RxAppCompatActivity {
 
     }
 
+    /**
+     * 获取图片路径-离线缓存模块的图片存储
+     */
+    public String getOfflineImagePath() {
+        String saveDir = LocalDataOperation.getInstance().getOfflineImageFilePath(this);
+        // 新建目录
+        File dir = new File(saveDir);
+        if (!dir.exists()) dir.mkdirs();
+        return saveDir + File.separator + System.currentTimeMillis() + ".png";
+    }
+
+    /**
+     * 判断是否有网络
+     * @return
+     */
+    public static Boolean isHaveInternet() {
+        int netMobile = NetUtil.getNetWorkState(Objects.requireNonNull(DemoApp.Companion.getContext()));
+        Log.e("网络状态", netMobile + "");
+        return netMobile == 1 || netMobile == 0;
+    }
+
 }

+ 185 - 0
demo/src/main/java/com/sybotan/android/demo/activities/EquipDetailActivity.kt

@@ -0,0 +1,185 @@
+package com.sybotan.android.demo.activities
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.os.Bundle
+import android.text.TextUtils
+import android.util.Log
+import android.view.MotionEvent
+import android.view.View
+import android.widget.EditText
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.sybotan.android.demo.R
+import com.sybotan.android.demo.adapter.StaticInfoAdapter
+import com.sybotan.android.demo.bean.result.Photos
+import com.sybotan.android.demo.retrofit.Constant
+import com.sybotan.android.demo.tools.LocalDataOperation
+import com.sybotan.android.demo.tools.LocalDataOperation.*
+import com.sybotan.android.demo.tools.PictureUtils
+import com.sybotan.android.demo.tools.ToastUtils
+import com.sybotan.android.demo.tools.UploadImage
+import com.yuyh.library.imgsel.ImageLoader
+import com.yuyh.library.imgsel.ImgSelConfig
+import com.yuyh.library.imgsel.utils.LogUtils
+import io.reactivex.ObservableEmitter
+import org.json.JSONException
+import org.json.JSONObject
+import java.io.File
+
+/**
+ * Created by lihao.
+ * Date: 2021/9/27
+ */
+class EquipDetailActivity : BaseActivity(), View.OnTouchListener {
+    //选择图片
+    private val REQUEST_CODE_PIC = 0X102
+
+    private lateinit var recycler: RecyclerView
+    private lateinit var mtextContent: EditText
+
+    // 自定义图片加载器
+    private val loader =
+        ImageLoader { context: Context?, path: String?, imageView: ImageView? ->
+            Glide.with(
+                context
+            ).load(path).into(imageView)
+        }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_equip_detail)
+        initView()
+        initData()
+        return
+    } // Function onCreate()
+
+    private fun initView() {
+        recycler = findViewById(R.id.recycler)
+        mtextContent = findViewById(R.id.mtextContent)
+        recycler.layoutManager = LinearLayoutManager(this)
+        mtextContent.setOnTouchListener(this)
+    }
+
+    //重写onTouch方法
+    override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
+        //触摸的是EditText控件,且当前EditText可以滚动,则将事件交给EditText处理;否则将事件交由其父类处理
+        if (view.id == R.id.mtextContent && canVerticalScroll(mtextContent)) {
+            view.parent.requestDisallowInterceptTouchEvent(true)
+            if (motionEvent.action == MotionEvent.ACTION_UP) {
+                view.parent.requestDisallowInterceptTouchEvent(false)
+            }
+        }
+        return false
+    }
+
+    /**
+     * EditText竖直方向是否可以滚动
+     * @param editText  需要判断的EditText
+     * @return  true:可以滚动   false:不可以滚动
+     */
+    private fun canVerticalScroll(editText: EditText): Boolean {
+        return if (editText.canScrollVertically(-1) || editText.canScrollVertically(1)) {
+            //垂直方向上可以滚动
+            true
+        } else false
+    }
+
+    private fun initData() {
+        var list = ArrayList<Object>()
+        list.add(Object())
+        val adapter = StaticInfoAdapter(this, list as List<Any>?)
+        adapter.setOnAddPic { addPic() }
+        recycler.adapter = adapter
+    }
+
+    private fun addPic() {
+        val config: ImgSelConfig = ImgSelConfig.Builder(this, loader) // 是否多选
+            .multiSelect(true) // 是否记住上次选中记录, 仅当multiSelect为true的时候配置,默认为true
+            .rememberSelected(false) // 确定按钮文字颜色
+            .btnTextColor(Color.WHITE) // 使用沉浸式状态栏
+            .statusBarColor(resources.getColor(R.color.blue_2E3679)) // 返回图标ResId
+            .backResId(R.mipmap.back_gray).title("选择图片").titleColor(Color.WHITE).titleBgColor(
+                resources.getColor(R.color.blue_2E3679)
+            ) //                .allImagesText("All Images")
+            .needCrop(false).cropSize(1, 1, 200, 200) // 第一个是否显示相机
+            .needCamera(true) // 最大选择图片数量
+            .build()
+        //选择图片activity
+        //选择图片activity
+        Repair_ImgSelActivity.startActivity(this, config, REQUEST_CODE_PIC)
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        if (resultCode == RESULT_OK && requestCode == REQUEST_CODE_PIC) {
+            if (data != null) {
+                val pathList: List<String>? =
+                    data.getStringArrayListExtra(Repair_ImgSelActivity.INTENT_RESULT)
+                doPicDatas(pathList)
+            }
+        }
+    }
+
+    private fun doPicDatas(pathList: List<String>?) {
+        getInstance().dealLocalData<Photos>(this) { name, params ->
+            when (name) {
+                SHOW_DIALOG_IN_MAIN -> ToastUtils.showMyToast("图片上传中")
+                DEAL_IO_DATA -> {
+                    val emitter =
+                        params as ObservableEmitter<String>
+                    for (s in pathList!!) {
+                        Log.e("已选择图片的路径为-----", s!!)
+                        val path: String =
+                            PictureUtils.compressImage(s, getOfflineImagePath(), 100)
+                        val picFile = File(path)
+                        if (picFile.exists()) {
+                            val key = picFile.name
+                            if (isHaveInternet()) {
+                                val url: String = Constant.ServerImgIPUp + key
+                                val str = UploadImage.uploadFiles(picFile, url)
+                                if (!TextUtils.isEmpty(str)) {
+                                    var resultString: String? = null
+                                    try {
+                                        val `object` = JSONObject(str)
+                                        resultString = `object`.getString("Result")
+                                    } catch (e: JSONException) {
+                                        e.printStackTrace()
+                                    }
+                                    if ("success" == resultString) {
+                                        val photos = Photos()
+                                        photos.itemType = 2
+                                        photos.path = path
+                                        photos.key = key
+//                                        viewModel.picPathList.add(photos)
+//                                        viewModel.content.pictures.add(key)
+                                    }
+                                }
+                            } else {
+                                val photos = Photos()
+                                photos.itemType = 2
+                                photos.path = path
+                                photos.key = key
+//                                viewModel.picPathList.add(photos)
+//                                viewModel.content.pictures.add(key)
+                            }
+                        }
+                    }
+                    emitter.onNext("")
+                }
+                DEAL_DATA_NEXT -> {
+                    ToastUtils.showMyToast("图片上传完毕")
+                }
+                DEAL_DATA_ERROR -> {
+                    ToastUtils.showMyToast("图片上传失败")
+                }
+                else -> {
+                }
+            }
+        }
+    }
+}

+ 520 - 85
demo/src/main/java/com/sybotan/android/demo/activities/GraphyActivity.kt

@@ -24,21 +24,23 @@
 package com.sybotan.android.demo.activities
 
 import android.annotation.SuppressLint
+import android.content.Context
 import android.content.Intent
 import android.graphics.Color
 import android.graphics.PointF
 import android.os.Bundle
 import android.os.Environment
+import android.text.TextUtils
 import android.util.Log
 import android.view.Gravity
 import android.view.MotionEvent
 import android.view.View
-import android.webkit.WebSettings
 import android.webkit.WebView
 import android.webkit.WebViewClient
 import android.widget.TextView
 import android.widget.Toast
 import android.widget.*
+import androidx.appcompat.widget.LinearLayoutCompat
 import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
@@ -53,6 +55,7 @@ import com.afollestad.materialdialogs.MaterialDialog
 import com.arashivision.sdkcamera.camera.InstaCameraManager
 import com.arashivision.sdkcamera.camera.callback.ICameraChangedCallback
 import com.arashivision.sdkcamera.camera.callback.ICaptureStatusListener
+import com.bumptech.glide.Glide
 import com.google.gson.Gson
 import com.lzy.okgo.OkGo
 import com.lzy.okgo.callback.FileCallback
@@ -60,22 +63,29 @@ import com.lzy.okgo.model.Response
 import com.sybotan.android.demo.R
 import com.sybotan.android.demo.activities.poc.DeviceActivity
 import com.sybotan.android.demo.activities.poc.RelationActivity
+import com.sybotan.android.demo.adapter.PictureAdapter
 import com.sybotan.android.demo.adapter.PipeAdapter
 import com.sybotan.android.demo.bean.result.*
 import com.sybotan.android.demo.graphy.GraphyHelper
 import com.sybotan.android.demo.retrofit.BaseViewModelInterface
-import com.sybotan.android.demo.tools.DensityUtils
-import com.sybotan.android.demo.tools.WordWrapView
-import com.sybotan.android.demo.tools.NetworkManager
+import com.sybotan.android.demo.retrofit.Constant
+import com.sybotan.android.demo.tools.*
+import com.sybotan.android.demo.view.MaxHeightRecyclerView
 import com.sybotan.android.demo.viewmodel.GraphyVM
 import com.sybotan.android.graphy.SGraphyView
 import com.sybotan.android.graphy.events.SGraphyViewMoveEvent
+import com.yuyh.library.imgsel.ImageLoader
+import com.yuyh.library.imgsel.ImgSelConfig
 import com.zbar.lib.CaptureActivity
+import io.reactivex.ObservableEmitter
 import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
 import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.startActivity
 import org.jetbrains.anko.textColor
 import org.jetbrains.anko.uiThread
+import org.json.JSONException
+import org.json.JSONObject
 import java.io.*
 import java.text.SimpleDateFormat
 import java.util.*
@@ -101,6 +111,16 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
         var isAddBaseMap: Boolean = false
     }
 
+    //选择图片
+    private val REQUEST_CODE_PIC = 0X102
+
+    // 自定义图片加载器
+    private val loader =
+        ImageLoader { context: Context?, path: String?, imageView: ImageView? ->
+            Glide.with(
+                context
+            ).load(path).into(imageView)
+        }
     private lateinit var spacePipeModel: java.util.ArrayList<SpacePipeModel>
 
     /** viewModel */
@@ -122,7 +142,7 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
     lateinit var from: String
 
     /** 实际距离 */
-    val distance = 2500f
+    val distance = 5000f
 
     /** 缩放后的同比例距离 */
     var realDistance = 0f
@@ -136,6 +156,18 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
     /** 底图item */
     lateinit var mapItem: BaseMapItem
 
+    /**
+     * 问题点坐标
+     */
+    val ProblemDot = Point()
+    var addPoint: PointItem? = null
+
+    /**
+     * 问题图片列表
+     */
+    val beans: ArrayList<Photos> = ArrayList()
+    lateinit var pictureAdapter: PictureAdapter
+
     @Subscribe
     fun test(msg: SGraphyViewMoveEvent) {
         Log.d(TAG, "msg=$msg")
@@ -145,20 +177,22 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
     private lateinit var graphyView: SGraphyView
     private lateinit var quesDetail: RelativeLayout
     private lateinit var spaceStructure: LinearLayout
+    private lateinit var doneSpaceStructure: LinearLayout
     private lateinit var spaceName: TextView
+    private lateinit var spaceName3: TextView
+    private lateinit var spaceName4: TextView
     private lateinit var spaceName2: TextView
-    private lateinit var equipLl:LinearLayout
-    private lateinit var drawLl:LinearLayout
-    private lateinit var equipLocation:TextView
-    private lateinit var qrcodeLl:LinearLayout
-    private lateinit var addDot:TextView
-    private lateinit var nfc:TextView
-    private lateinit var qrcodeLocation:TextView
+    private lateinit var equipLl: LinearLayout
+    private lateinit var drawLl: LinearLayout
+    private lateinit var equipLocation: TextView
+    private lateinit var qrcodeLl: LinearLayout
+    private lateinit var addDot: TextView
+    private lateinit var nfc: TextView
+    private lateinit var qrcodeLocation: TextView
     private lateinit var modelCheckBox: CheckBox
     private lateinit var modelSubmit: TextView
     private lateinit var sceneCheckBox: CheckBox
     private lateinit var sceneSubmit: TextView
-    private lateinit var codeEdit: EditText
     private lateinit var qrcode: TextView
     private lateinit var qrcodeCancel: TextView
     private lateinit var pipeCancel: TextView
@@ -169,6 +203,7 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
     private lateinit var drawEquip: TextView
     private lateinit var drawPipe: TextView
     private lateinit var piprRv: RecyclerView
+    private lateinit var problemPicRecycler: MaxHeightRecyclerView
     private lateinit var equipCancel: TextView
     private lateinit var equipNext: TextView
     private lateinit var equipEdit: EditText
@@ -181,15 +216,24 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
     private lateinit var eqPipeCnct: TextView
     private lateinit var equipWwv: WordWrapView
     private lateinit var pipeWwv: WordWrapView
-    private lateinit var roof : RadioButton
+    private lateinit var roof: RadioButton
     private lateinit var floor: RadioButton
     private lateinit var wall: RadioButton
     private lateinit var air: RadioButton
     private lateinit var quesTv: TextView
     private lateinit var quesSubmit: TextView
+    private lateinit var addSpaceDetail: TextView
+    private lateinit var addSpaceDetail2: TextView
+    private lateinit var resetSpaceState: TextView
     private lateinit var qrcodeEdit: EditText
     private lateinit var webview: WebView
     private lateinit var quesIma: ImageView
+    private lateinit var problemLl: LinearLayoutCompat
+    private lateinit var problemClose: TextView
+    private lateinit var problemDone: TextView
+    private lateinit var problemSave: TextView
+    private lateinit var equipDetail: TextView
+    private lateinit var equipDelete: TextView
 
     /**
      * 创建Activity时的回调函数
@@ -241,7 +285,7 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
         /** 网络请求回调处理 */
         initViewModel()
 
-        initGrid()
+//        initGrid()
         return
     } // Function onCreate()
 
@@ -309,10 +353,12 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
     private fun initView() {
         graphyView = findViewById(R.id.graphyView)
         quesDetail = findViewById(R.id.quesDetail)
-        quesDetail = findViewById(R.id.quesDetail)
         spaceStructure = findViewById(R.id.spaceStructure)
+        doneSpaceStructure = findViewById(R.id.doneSpaceStructure)
         spaceName = findViewById(R.id.spaceName)
         spaceName2 = findViewById(R.id.spaceName2)
+        spaceName3 = findViewById(R.id.spaceName3)
+        spaceName4 = findViewById(R.id.spaceName4)
         equipLl = findViewById(R.id.equipLl)
         drawLl = findViewById(R.id.drawLl)
         equipLocation = findViewById(R.id.equipLocation)
@@ -324,7 +370,6 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
         modelSubmit = findViewById(R.id.modelSubmit)
         sceneCheckBox = findViewById(R.id.sceneCheckBox)
         sceneSubmit = findViewById(R.id.sceneSubmit)
-        codeEdit = findViewById(R.id.codeEdit)
         qrcode = findViewById(R.id.qrcode)
         qrcodeCancel = findViewById(R.id.qrcodeCancel)
         pipeCancel = findViewById(R.id.pipeCancel)
@@ -335,6 +380,7 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
         drawEquip = findViewById(R.id.drawEquip)
         drawPipe = findViewById(R.id.drawPipe)
         piprRv = findViewById(R.id.piprRv)
+        problemPicRecycler = findViewById(R.id.problemPicRecycler)
         equipCancel = findViewById(R.id.equipCancel)
         equipNext = findViewById(R.id.equipNext)
         equipEdit = findViewById(R.id.equipEdit)
@@ -348,14 +394,23 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
         equipWwv = findViewById(R.id.equipWwv)
         pipeWwv = findViewById(R.id.pipeWwv)
         roof = findViewById(R.id.roof)
-        floor= findViewById(R.id.floor)
-        wall= findViewById(R.id.wall)
+        floor = findViewById(R.id.floor)
+        wall = findViewById(R.id.wall)
         air = findViewById(R.id.air)
         quesTv = findViewById(R.id.quesTv)
         quesSubmit = findViewById(R.id.quesSubmit)
         qrcodeEdit = findViewById(R.id.qrcodeEdit)
         webview = findViewById(R.id.webview)
         quesIma = findViewById(R.id.quesIma)
+        addSpaceDetail = findViewById(R.id.addSpaceDetail)
+        addSpaceDetail2 = findViewById(R.id.addSpaceDetail2)
+        resetSpaceState = findViewById(R.id.resetSpaceState)
+        problemLl = findViewById(R.id.problemLl)
+        problemClose = findViewById(R.id.problemClose)
+        problemDone = findViewById(R.id.problemDone)
+        problemSave = findViewById(R.id.problemSave)
+        equipDetail = findViewById(R.id.equipDetail)
+        equipDelete = findViewById(R.id.equipDelete)
 
     }
 
@@ -370,6 +425,81 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
         from = floor.getString("from")!!
     }
 
+    private fun addPic() {
+        val config: ImgSelConfig = ImgSelConfig.Builder(this, loader) // 是否多选
+            .multiSelect(true) // 是否记住上次选中记录, 仅当multiSelect为true的时候配置,默认为true
+            .rememberSelected(false) // 确定按钮文字颜色
+            .btnTextColor(Color.WHITE) // 使用沉浸式状态栏
+            .statusBarColor(resources.getColor(R.color.blue_2E3679)) // 返回图标ResId
+            .backResId(R.mipmap.back_gray).title("选择图片").titleColor(Color.WHITE).titleBgColor(
+                resources.getColor(R.color.blue_2E3679)
+            ) //                .allImagesText("All Images")
+            .needCrop(false).cropSize(1, 1, 200, 200) // 第一个是否显示相机
+            .needCamera(true) // 最大选择图片数量
+            .build()
+        //选择图片activity
+        //选择图片activity
+        Repair_ImgSelActivity.startActivity(this, config, REQUEST_CODE_PIC)
+    }
+
+    private fun doPicDatas(pathList: List<String>?) {
+        LocalDataOperation.getInstance().dealLocalData<Photos>(this) { name, params ->
+            when (name) {
+                LocalDataOperation.SHOW_DIALOG_IN_MAIN -> ToastUtils.showMyToast("图片上传中")
+                LocalDataOperation.DEAL_IO_DATA -> {
+                    val emitter =
+                        params as ObservableEmitter<String>
+                    for (s in pathList!!) {
+                        Log.e("已选择图片的路径为-----", s!!)
+                        val path: String =
+                            PictureUtils.compressImage(s, getOfflineImagePath(), 100)
+                        val picFile = File(path)
+                        if (picFile.exists()) {
+                            val key = picFile.name
+                            if (isHaveInternet()) {
+                                val url: String = Constant.ServerImgIPUp + key
+                                val str = UploadImage.uploadFiles(picFile, url)
+                                if (!TextUtils.isEmpty(str)) {
+                                    var resultString: String? = null
+                                    try {
+                                        val `object` = JSONObject(str)
+                                        resultString = `object`.getString("Result")
+                                    } catch (e: JSONException) {
+                                        e.printStackTrace()
+                                    }
+                                    if ("success" == resultString) {
+                                        val photos = Photos()
+                                        photos.itemType = 0
+                                        photos.path = path
+                                        photos.key = key
+                                        beans.add(photos)
+
+                                    }
+                                }
+                            } else {
+                                val photos = Photos()
+                                photos.itemType = 0
+                                photos.path = path
+                                photos.key = key
+                                beans.add(photos)
+                            }
+                        }
+                    }
+                    emitter.onNext("")
+                }
+                LocalDataOperation.DEAL_DATA_NEXT -> {
+                    ToastUtils.showMyToast("图片上传完毕")
+                    pictureAdapter.notifyDataSetChanged()
+                }
+                LocalDataOperation.DEAL_DATA_ERROR -> {
+                    ToastUtils.showMyToast("图片上传失败")
+                }
+                else -> {
+                }
+            }
+        }
+    }
+
 
     /**
      * 防止用户连续点击
@@ -405,28 +535,241 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
         scene.setOnSingleTopUp(object : FloorScene.OnSingleTopUp {
             @SuppressLint("SetJavaScriptEnabled")
             override fun onSingUp(x: Float, y: Float) {
-                //设置
-                val settings: WebSettings = webview.getSettings()
-                settings.javaScriptEnabled = true
-                settings.defaultTextEncodingName = "utf-8"
-                settings.builtInZoomControls = true
-                settings.loadsImagesAutomatically = true
-                settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
-                webview.webViewClient = MyWebViewClient()
-                webview.loadUrl("http://60.205.177.43:8000/APP/#/")
-                if (isFastDoubleClick()) {
-                    if (isAddBaseMap) {
-                        scene.removeItem(mapItem)
-                        isAddBaseMap = false
-                        webview.visibility = View.GONE
-                    } else {
-                        mapItem.zOrder = 1000000f
-                        mapItem.isVisible = true
-                        scene.addItem(mapItem)
-                        isAddBaseMap = true
-                        webview.visibility = View.VISIBLE
+                //如果只是查看模式,可以选中空间,进行空间内业务操作
+                if ("normal".equals(scene.type)) {
+                    var hasChose = false
+                    for (space in scene.tunableSpaceList) {
+                        val polygonContainsPoint = Lasso.isPolygonContainsPoint(
+                            space.mPoints,
+                            android.graphics.Point(x.toInt(), -y.toInt())
+                        )
+                        if (polygonContainsPoint) {
+                            if (hasChose) {
+                                if (space.calculateArea() < scene.choseSpace!!.calculateArea()) {
+                                    space.outPaint.color = Color.rgb(0xbd, 0xe8, 0xfe)
+                                    space.outPaint.strokeWidth = 500f
+                                    space.paint.color = Opt.spaceChoseColor
+                                    scene.mapType = "space"
+                                    scene.choseSpace = space
+                                    hasChose = true
+
+                                }
+                            } else {
+                                space.outPaint.color = Color.rgb(0xbd, 0xe8, 0xfe)
+                                space.paint.color = Opt.spaceChoseColor
+                                space.outPaint.strokeWidth = 500f
+                                scene.mapType = "space"
+                                scene.choseSpace = space
+                                hasChose = true
+                            }
+                        } else {
+                            space.paint.color = Opt.spaceColor
+                            space.outPaint.strokeWidth = 50f
+                            space.outPaint.color = Color.rgb(0xff, 0xff, 0xff)
+                        }
+                    }
+                    if (!hasChose) {
+                        scene.mapType = "nothing"
+                        scene.choseSpace = null
+                    }
+                }
+
+
+                var outSide = false
+                //计算控制点的边界
+                //是否已经拥有该点
+                /** 打点业务 */
+                if ("dot".equals(scene.type)) {
+//                    for (space in scene.spaceList) {
+//                        val polygonContainsPoint = Lasso.isPolygonContainsPoint(
+//                            space.mPoints,
+//                            android.graphics.Point(x.toInt(), -y.toInt())
+//                        )
+//                        outSide = true
+//                        space.paint.color = Opt.spaceChoseColor
+//                        space.outPaint.color = Color.rgb(0xbd, 0xe8, 0xfe)
+//                    }
+//                    val point = Point()
+//                    point.mX = x
+//                    point.mY = y
+//                    var item = PointItem(point)
+//                    item.isVisible = true
+//                    item.zOrder = 100000f
+//                    scene.pointItemList.add(item)
+
+                    if (addPoint != null) {
+                        scene.removeItem(addPoint!!)
+                        scene.pointItemList.remove(addPoint)
+                    }
+                    ProblemDot.mX = x
+                    ProblemDot.mY = y
+                    addPoint = graphyHelper.addPoint(ProblemDot)
+
+                    //添加问题点业务
+                    for (item in scene.pointItemList) {
+                        item.isVisible = scene.isShowSpace
+                        item.zOrder = 100000f
+                        scene.addItem(item)
+                    }
+                    if (!outSide) {
+                        return
                     }
                 }
+
+                //打点业务
+//                if ("dot".equals(scene.type)) {
+//                    if (addPoint != null) {
+//                        scene.removeItem(addPoint!!)
+//                    }
+//                    ProblemDot.mX = x
+//                    ProblemDot.mY = y
+//                    addPoint = graphyHelper.addPoint(ProblemDot)
+//                    return
+//                }
+
+                //绘制管道业务
+                else if ("pipe".equals(scene.type)) {
+                    var pipe = PipeLine()
+                    var pointf = PointF()
+                    pointf.x = x
+                    pointf.y = -y
+                    scene.pipeLineList.add(pointf)
+                    pipe.points!!.addAll(scene.pipeLineList)
+                    var item = PipeLineItem(pipe, null)
+                    item!!.isVisible = true
+                    item!!.zOrder = 100000f
+                    scene.drawingPipeItemList.add(item)
+                    for (item in scene.drawingPipeItemList) {
+                        item.isVisible = scene.isShowSpace
+                        item.zOrder = 100000f
+                        scene.addItem(item)
+                    }
+
+                    //绘制二维码
+                } else if ("qrcode".equals(scene.type)) {
+                    scene.point.mX = x
+                    scene.point.mY = y
+
+                    if (scene.item != null) {
+                        scene.removeItem(scene.item!!)
+                    }
+                    scene.item =
+                        QrcodeItem(this@GraphyActivity, scene.point, null, scene.defaultPointScale)
+                    scene.item!!.isVisible = true
+                    scene.item!!.zOrder = 100000f
+                    scene.addItem(scene.item!!)
+
+                    //绘制设备
+                } else if ("equip".equals(scene.type)) {
+                    scene.point.mX = x
+                    scene.point.mY = y
+
+                    if (scene.eqItem != null) {
+                        scene.removeItem(scene.eqItem!!)
+                    }
+
+                    /* //吸附算法
+                    var dist = 500.00
+                    for (index in 0..scene.choseSpace!!.mPoints.size - 1) {
+
+
+                        val point1 = scene.choseSpace!!.mPoints.get(index)
+                        var point2 = PointF()
+                        if (index == scene.choseSpace!!.mPoints.size - 1) {
+                            point2 = scene.choseSpace!!.mPoints.get(0)
+                        } else {
+                            point2 = scene.choseSpace!!.mPoints.get(index + 1)
+                        }
+
+
+                        val l1 = Math.sqrt(Math.pow(Math.abs(point1.x - point2.x).toDouble(), 2.0) + Math.pow(Math.abs(point1.y - point2.y).toDouble(), 2.0)).toFloat()
+                        val l2 = Math.sqrt(Math.pow(Math.abs(point1.x - x).toDouble(), 2.0) + Math.pow(Math.abs(point1.y + y).toDouble(), 2.0)).toFloat()
+                        val l3 = Math.sqrt(Math.pow(Math.abs(point2.x - x).toDouble(), 2.0) + Math.pow(Math.abs(point2.y + y).toDouble(), 2.0)).toFloat()
+                        val halfP = (l1 + l2 + l3) / 2
+                        val s = Math.sqrt(halfP * (halfP - l1) * (halfP - l2) * (halfP - l3).toDouble())
+                        val h = 2 * s / l1
+                        if (h < dist) {
+                            dist = h
+                            var k1: Float
+                            if (point1.x - point2.x == 0f) {
+                                k1 = 0f
+                            } else {
+                                k1 = (point1.y - point2.y) / (point1.x - point2.x)
+                            }
+
+                            val b1 = point1.y - k1 * point1.x
+
+                            var k2: Float
+                            if (k1 == 0f) {
+                                k2 = 0f
+                            } else {
+                                k2 = -1 / k1
+                            }
+
+                            val b2 = y - k2 * x
+
+                            val x1 = (b2 - b1) / (k1 - k2)
+                            val y1 = k2 * (x) + b2
+
+                            if (point1.x - point2.x == 0f) {
+                                scene.point.mX = point1.x
+                                if (Math.abs(k1) == 0f) {
+                                    scene.point.mY = y
+                                } else {
+                                    if (point1.y - point2.y == 0f) {
+                                        scene.point.mY = -point1.y
+                                    } else {
+                                        scene.point.mY = y1
+                                    }
+                                }
+
+                            } else {
+                                if (Math.abs(k1) == 0f) {
+                                    scene.point.mX = x
+                                } else {
+                                    scene.point.mX = x1
+                                }
+                                if (point1.y - point2.y == 0f) {
+                                    scene.point.mY = -point1.y
+                                } else {
+                                    scene.point.mY = y1
+                                }
+                            }
+
+                        }
+
+                    }*/
+
+                    scene.eqItem =
+                        EquipItem(this@GraphyActivity, scene.point, null, scene.defaultPointScale)
+                    scene.eqItem!!.isVisible = true
+                    scene.eqItem!!.zOrder = 10000012f
+                    scene.addItem(scene.eqItem!!)
+                }
+
+
+                //设置
+//                val settings: WebSettings = webview.getSettings()
+//                settings.javaScriptEnabled = true
+//                settings.defaultTextEncodingName = "utf-8"
+//                settings.builtInZoomControls = true
+//                settings.loadsImagesAutomatically = true
+//                settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
+//                webview.webViewClient = MyWebViewClient()
+//                webview.loadUrl("http://60.205.177.43:8000/APP/#/")
+//                if (isFastDoubleClick()) {
+//                    if (isAddBaseMap) {
+//                        scene.removeItem(mapItem)
+//                        isAddBaseMap = false
+//                        webview.visibility = View.GONE
+//                    } else {
+//                        mapItem.zOrder = 1000000f
+//                        mapItem.isVisible = true
+//                        scene.addItem(mapItem)
+//                        isAddBaseMap = true
+//                        webview.visibility = View.VISIBLE
+//                    }
+//                }
                 if ("pipe".equals(scene.type)) {
                     return
                 }
@@ -444,10 +787,15 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
                         quesDetail.visibility = View.GONE
                         if (!scene.choseSpace!!.data.jobStatus.equals("03")) {
                             spaceStructure.visibility = View.VISIBLE
-                            spaceName.text = scene.choseSpace!!.data.localName
+                            doneSpaceStructure.visibility = View.GONE
                             graphyVM.getProblem(scene.choseSpace)
-
+                        } else {
+                            doneSpaceStructure.visibility = View.VISIBLE
+                            spaceStructure.visibility = View.GONE
                         }
+                        spaceName.text = scene.choseSpace!!.data.localName
+                        spaceName3.text = scene.choseSpace!!.data.localName
+                        spaceName4.text = scene.choseSpace!!.data.localName
                         graphyVM.getQrcode(scene.choseSpace)
                     } else {
                         if (equipLl.visibility == View.GONE) {
@@ -462,10 +810,12 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
                     //未选中空间
                 } else if (scene.mapType.equals("nothing")) {
                     spaceStructure.visibility = View.GONE
+                    doneSpaceStructure.visibility = View.GONE
                     qrcodeLl.visibility = View.GONE
                     quesDetail.visibility = View.GONE
                     equipLl.visibility = View.GONE
                     drawLl.visibility = View.GONE
+                    problemLl.visibility = View.GONE
                     for (item in scene.pointItemList) {
                         scene.removeItem(item)
                     }
@@ -480,17 +830,10 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
                     scene.qrcodeItemList.clear()
                 }
 
-                //打点业务
-                if ("dot".equals(scene.type)) {
-                    scene.type = "normal"
-                    addDot.alpha = 1f
-                    scene.choseSpace?.let { graphyVM.addProblem(it, x, y) }
-                    return
-                }
-
                 //查看模式下,判断问题点选中逻辑
                 if ("normal".equals(scene.type)) {
                     realDistance = distance * scene.defaultPointScale / graphyView.scale
+                    var hasChoose = false
                     for (point in scene.pointItemList) {
                         val sqrt = Math.sqrt(
                             Math.pow(
@@ -499,11 +842,15 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
                             ) + Math.pow(Math.abs(y - point.data.mY).toDouble(), 2.0)
                         ).toInt()
                         if (sqrt < realDistance) {
+                            hasChoose = true
                             showDotDetail(point)
                             break
                         }
                     }
-
+                    if (!hasChoose) {
+                        problemLl.visibility = View.GONE
+                    }
+                    var hasChooseEquip = false
                     for (point in scene.equipList) {
                         val sqrt = Math.sqrt(
                             Math.pow(
@@ -512,14 +859,26 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
                             ) + Math.pow(Math.abs(y - point.data.mY).toDouble(), 2.0)
                         ).toInt()
                         if (sqrt < realDistance) {
-                            Toast.makeText(
-                                this@GraphyActivity,
-                                "${point.data.type}\n${point.data.remark}\n${point.data.position}",
-                                Toast.LENGTH_LONG
-                            ).show()
+                            hasChooseEquip = true
+                            equipDetail.alpha = 1f
+                            equipDelete.alpha = 1f
+                            equipDetail.isEnabled = true
+                            equipDelete.isEnabled = true
+                            equipDetail.setOnClickListener {
+                                startActivity<EquipDetailActivity>()
+                            }
+                            equipDelete.setOnClickListener {
+                                point.data.type
+                            }
                             break
                         }
                     }
+                    if (!hasChooseEquip) {
+                        equipDetail.alpha = 0.5f
+                        equipDelete.alpha = 0.5f
+                        equipDetail.isEnabled = false
+                        equipDelete.isEnabled = false
+                    }
                     return
                 }
 
@@ -528,6 +887,7 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
                     qrcodeLl.visibility = View.VISIBLE
                     qrcodeLocation.setText("(${scene.point.mX},${scene.point.mY})")
                     spaceStructure.visibility = View.GONE
+                    doneSpaceStructure.visibility = View.GONE
                     return
                 }
             }
@@ -536,7 +896,8 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
 
     private fun checkSdCardEnabled(): Boolean {
         if (!InstaCameraManager.getInstance().isSdCardEnabled()) {
-            Toast.makeText(this, "No SD card inserted or Not enough free space", Toast.LENGTH_SHORT).show()
+            Toast.makeText(this, "No SD card inserted or Not enough free space", Toast.LENGTH_SHORT)
+                .show()
             return false
         }
         return true
@@ -581,18 +942,21 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
 
         /** 设置空间的任务状态完成 */
         sceneSubmit.setOnClickListener {
-            graphyVM.setJob(scene.choseSpace, codeEdit.text.toString(), "05")
+            graphyVM.setJob(scene.choseSpace, "05")
         }
 
         /** 设置空间的任务状态完成 */
         modelSubmit.setOnClickListener {
-            graphyVM.setJob(scene.choseSpace, codeEdit.text.toString(), "03")
+            graphyVM.setJob(scene.choseSpace, "03")
         }
 
         /** 打点按钮 */
         addDot.setOnClickListener {
             scene.type = "dot"
-            addDot.alpha = 0.3f
+            spaceStructure.visibility = View.GONE
+            problemLl.visibility = View.VISIBLE
+            initProblemPicture()
+
         }
 
         /** 绑定二维码按钮 */
@@ -632,7 +996,12 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
         pipeNext.setOnClickListener {
             for (model in pipeTypeList) {
                 if (model.isCheck) {
-                    graphyVM.addPipe(model, scene.pipeLineList, pipeEdit.text.toString(), scene.choseSpace)
+                    graphyVM.addPipe(
+                        model,
+                        scene.pipeLineList,
+                        pipeEdit.text.toString(),
+                        scene.choseSpace
+                    )
                     break
                 }
             }
@@ -707,6 +1076,42 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
         eqEqVv.setOnClickListener { skipRelationship("eq_eq_vv", "eq_eq") }
         /** 连接 */
         eqPipeCnct.setOnClickListener { skipRelationship("eq_pipe_cnct", "eq_pipe") }
+        /** 跳转空间详情 */
+        addSpaceDetail.setOnClickListener { startActivity<SpaceDetailActivity>() }
+        /** 跳转空间详情 */
+        addSpaceDetail2.setOnClickListener { startActivity<SpaceDetailActivity>() }
+        /** 重置空间状态 */
+        resetSpaceState.setOnClickListener { }
+        /** 问题-关闭 */
+        problemClose.setOnClickListener {
+            if (addPoint != null) {
+                scene.removeItem(addPoint!!)
+                scene.pointItemList.remove(addPoint)
+            }
+            scene.type = "normal"
+            problemLl.visibility = View.GONE
+        }
+        /** 问题-已解决 */
+        problemDone.setOnClickListener {
+
+        }
+        /** 问题-保存 */
+        problemSave.setOnClickListener {
+            scene.choseSpace?.let { graphyVM.addProblem(it, ProblemDot.mX, ProblemDot.mY) }
+            scene.type = "normal"
+            problemLl.visibility = View.GONE
+        }
+    }
+
+    private fun initProblemPicture() {
+        beans.clear()
+        val photo = Photos()
+        photo.itemType = 1
+        beans.add(photo)
+        pictureAdapter = PictureAdapter(this, beans, "", false, 0, 85)
+        pictureAdapter.setOnPicAdd { addPic() }
+        problemPicRecycler.setLayoutManager(GridLayoutManager(this, 3))
+        problemPicRecycler.adapter = pictureAdapter
     }
 
     private fun skipRelationship(code: String, type: String) {
@@ -798,8 +1203,11 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
                     for (model in list) {
                         var position = gson.fromJson(model.position, Position::class.java)
                         val polygonContainsPoint = Lasso.isPolygonContainsPoint(
-                                mPoints,
-                                android.graphics.Point(position.x.toFloat().toInt(), position.y.toFloat().toInt())
+                            mPoints,
+                            android.graphics.Point(
+                                position.x.toFloat().toInt(),
+                                position.y.toFloat().toInt()
+                            )
                         )
                         if (polygonContainsPoint) {
                             val point = Point()
@@ -845,7 +1253,7 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
                         point.mX = position.x.toFloat()
                         point.mY = position.y.toFloat()
 
-                        graphyHelper.addQrcode(point)
+                        graphyHelper.addQrcode(this, point)
                     }
                 }
 
@@ -974,7 +1382,9 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
         }, this, buildingId, floorId)
         graphyVM.getSpaceJob(from)
     }
-    val graphyHelper : GraphyHelper = GraphyHelper(scene)
+
+    val graphyHelper: GraphyHelper = GraphyHelper(scene, this)
+
     /**
      * 刷新地图上管线
      */
@@ -1097,13 +1507,13 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
 
 
         spaceStructure.visibility = View.GONE
+        doneSpaceStructure.visibility = View.GONE
         quesDetail.visibility = View.GONE
         drawLl.visibility = View.GONE
         equipLl.visibility = View.GONE
 
         scene.type = "normal"
         modelCheckBox.isChecked = false
-        codeEdit.setText("")
         equipLocation.setText("")
         roof.isChecked = false
         floor.isChecked = false
@@ -1117,23 +1527,28 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
      */
     private fun showDotDetail(point: PointItem) {
         spaceStructure.visibility = View.GONE
-        quesDetail.visibility = View.VISIBLE
-        quesTv.setText(
-            "这是描述问题的文本,左侧是拍照图片。" +
-                    "\n问题编码:${point.data.id}" +
-                    "\nx轴:${point.data.mX}" +
-                    "\ny轴:${point.data.mY}" +
-                    "\n点击下方“问题已解决”按钮后可关闭该问题点。"
-        )
-        quesTv.setOnClickListener {
-//            Toast.makeText(this@GraphyActivity, InstaCameraManager.getInstance(). + "======", Toast.LENGTH_LONG).show()
-        }
+        doneSpaceStructure.visibility = View.GONE
+//        quesDetail.visibility = View.VISIBLE
+//        quesTv.setText(
+//            "这是描述问题的文本,左侧是拍照图片。" +
+//                    "\n问题编码:${point.data.id}" +
+//                    "\nx轴:${point.data.mX}" +
+//                    "\ny轴:${point.data.mY}" +
+//                    "\n点击下方“问题已解决”按钮后可关闭该问题点。"
+//        )
+//        quesTv.setOnClickListener {
+////            Toast.makeText(this@GraphyActivity, InstaCameraManager.getInstance(). + "======", Toast.LENGTH_LONG).show()
+//        }
+
+        problemLl.visibility = View.VISIBLE
+        initProblemPicture()
 
         /** 问题已解决按钮 */
-        quesSubmit.setOnClickListener {
+        problemDone.setOnClickListener {
             graphyVM.closeProblem(point, scene.choseSpace)
             spaceStructure.visibility = View.VISIBLE
             quesDetail.visibility = View.GONE
+            problemLl.visibility = View.GONE
             scene.pointItemList.remove(point)
             scene.removeItem(point)
             if (scene.pointItemList.isEmpty()) {
@@ -1141,6 +1556,7 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
                     scene.removeItem(item)
                 }
             }
+            quesDetail.visibility = View.GONE
             graphyVM.getSpaceJob(from)
         }
     }
@@ -1185,6 +1601,15 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
                     graphyVM.getSpaceEq(scene.choseSpace)
                 }
             }
+            REQUEST_CODE_PIC -> {
+                if (resultCode == RESULT_OK) {
+                    if (data != null) {
+                        val pathList: List<String>? =
+                            data.getStringArrayListExtra(Repair_ImgSelActivity.INTENT_RESULT)
+                        doPicDatas(pathList)
+                    }
+                }
+            }
             else -> {
             }
         }
@@ -1241,7 +1666,9 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
         if (urls == null || urls.size == 0) {
             return
         }
-        val localFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString() + "/SDK_DEMO_CAPTURE"
+        val localFolder =
+            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+                .toString() + "/SDK_DEMO_CAPTURE"
         val fileNames = arrayOfNulls<String>(urls.size)
         val localPaths = arrayOfNulls<String>(urls.size)
         var needDownload = false
@@ -1263,11 +1690,11 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
             return
         }
         val dialog: MaterialDialog = MaterialDialog.Builder(this)
-                .title(R.string.osc_dialog_title_downloading)
-                .content(getString(R.string.osc_dialog_msg_downloading, urls.size, 0, 0))
-                .cancelable(false)
-                .canceledOnTouchOutside(false)
-                .show()
+            .title(R.string.osc_dialog_title_downloading)
+            .content(getString(R.string.osc_dialog_msg_downloading, urls.size, 0, 0))
+            .cancelable(false)
+            .canceledOnTouchOutside(false)
+            .show()
         val successfulCount = AtomicInteger(0)
         val errorCount = AtomicInteger(0)
         for (i in localPaths.indices) {
@@ -1285,10 +1712,18 @@ class GraphyActivity : BaseActivity(), ICaptureStatusListener, ICameraChangedCal
                 }
 
                 private fun checkDownloadCount() {
-                    dialog.setContent(getString(R.string.osc_dialog_msg_downloading, urls.size, successfulCount.toInt(), errorCount.toInt()))
+                    dialog.setContent(
+                        getString(
+                            R.string.osc_dialog_msg_downloading,
+                            urls.size,
+                            successfulCount.toInt(),
+                            errorCount.toInt()
+                        )
+                    )
                     if (successfulCount.toInt() + errorCount.toInt() >= urls.size) {
 //                                PlayAndExportActivity.launchActivity(this@CaptureActivity, localPaths)
-                        val time = SimpleDateFormat("HH:mm:ss").format(Date(System.currentTimeMillis()))
+                        val time =
+                            SimpleDateFormat("HH:mm:ss").format(Date(System.currentTimeMillis()))
                         quesTv.text = quesTv.text.toString() + "\n$time:下载完成"
                         dialog.dismiss()
                     }

+ 153 - 0
demo/src/main/java/com/sybotan/android/demo/activities/LookBigPicActivity.java

@@ -0,0 +1,153 @@
+package com.sybotan.android.demo.activities;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+
+import com.bm.library.PhotoView;
+import com.bumptech.glide.Glide;
+import com.sybotan.android.demo.R;
+import com.sybotan.android.demo.tools.CommonUtils;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.io.File;
+
+/**
+ * created by lihao on 2018/11/14
+ * 查看大图片
+ */
+public class LookBigPicActivity extends BaseActivity {
+
+
+    PhotoView mImageView;
+    private String mUrl;
+    private String mId;
+    private boolean mCanEdit;
+    private int position;
+    //图片key
+    private String mKey;
+    private String mPath;
+    private String localPath;
+    private int type;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_look_big_pic);
+        mImageView = findViewById(R.id.look_big_pic_iv);
+        mUrl = getIntent().getStringExtra("url");
+        mId = getIntent().getStringExtra("id");
+        mKey = getIntent().getStringExtra("key");
+        mPath = getIntent().getStringExtra("path");
+        localPath = getIntent().getStringExtra("localPath");
+        mCanEdit = getIntent().getBooleanExtra("canEdit", false);
+        position = getIntent().getIntExtra("position", 0);
+        type = getIntent().getIntExtra("type", 0);
+        initView();
+    }
+
+    /**
+     * 图片压缩
+     */
+    public static Bitmap getScaledBitmap(String filePath, int width) {
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFile(filePath, options);
+
+        int sampleSize = options.outWidth > width ? options.outWidth / width
+                + 1 : 1;
+        options.inJustDecodeBounds = false;
+        options.inSampleSize = sampleSize;
+        return BitmapFactory.decodeFile(filePath, options);
+    }
+
+    private void initView() {
+        if (!TextUtils.isEmpty(localPath)) {
+            WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
+            mImageView.setImageBitmap(getScaledBitmap(localPath, wm.getDefaultDisplay().getWidth()));
+            return;
+        }
+        if (!TextUtils.isEmpty(mPath)) {
+            Bitmap bitmap = getLoacalBitmap(mPath);
+            mImageView.setImageBitmap(bitmap);
+        } else {
+            //GlideUtils.loadImage(this, mUrl, mImageView, false);
+            if (TextUtils.isEmpty(mUrl)) {
+                return;
+            }
+            showImgWithThumbnail(this, mUrl, 1.0f, mImageView, getIntent().getBooleanExtra("isFile", false));
+        }
+    }
+
+    /**
+     * 加载图片若引起oom则缩略加载
+     *
+     * @param context
+     * @param url
+     * @param thumb
+     * @param mImageView
+     */
+    public static void showImgWithThumbnail(Context context, String url, float thumb, ImageView mImageView, boolean isFile) {
+        try {
+            if (isFile) {
+                Glide.with(context).load(new File(url)).thumbnail(thumb).into(mImageView);
+            } else {
+                Glide.with(context).load(url).thumbnail(thumb).into(mImageView);
+            }
+        } catch (OutOfMemoryError e) {
+            thumb -= 0.2f;
+            if (thumb <= 0) {
+                thumb = 0.1f;
+            }
+            showImgWithThumbnail(context, url, thumb, mImageView, isFile);
+        }
+    }
+
+    /**
+     * 加载本地图片
+     * http://bbs.3gstdy.com
+     *
+     * @param url
+     * @return
+     */
+    public static Bitmap getLoacalBitmap(String url) {
+        try {
+            BitmapFactory.Options options = new BitmapFactory.Options();
+            Bitmap bmp = BitmapFactory.decodeFile(url, options);
+            return bmp;
+        } catch (Error e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static void launch(Context activity, String url, boolean canEdit) {
+        activity.startActivity(new Intent(activity, LookBigPicActivity.class)
+                .putExtra("url", url).putExtra("canEdit", canEdit));
+    }
+
+    public static void launch(Context activity, String url, boolean canEdit, boolean isFile) {
+        activity.startActivity(new Intent(activity, LookBigPicActivity.class)
+                .putExtra("url", url).putExtra("canEdit", canEdit)
+                .putExtra("isFile", isFile));
+    }
+
+    public static void launch(Context activity, String url, boolean canEdit, int type, String time, String projectName, boolean isFile) {
+        activity.startActivity(new Intent(activity, LookBigPicActivity.class)
+                .putExtra("url", url).putExtra("canEdit", canEdit)
+                .putExtra("type", type)
+                .putExtra("time", time)
+                .putExtra("projectName", projectName)
+                .putExtra("isFile", isFile));
+    }
+}

+ 292 - 0
demo/src/main/java/com/sybotan/android/demo/activities/Repair_ImgSelActivity.java

@@ -0,0 +1,292 @@
+package com.sybotan.android.demo.activities;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.yuyh.library.imgsel.ImgSelConfig;
+import com.yuyh.library.imgsel.R;
+import com.yuyh.library.imgsel.common.Callback;
+import com.yuyh.library.imgsel.common.Constant;
+import com.yuyh.library.imgsel.utils.FileUtils;
+import com.yuyh.library.imgsel.utils.StatusBarCompat;
+
+import java.io.File;
+import java.util.ArrayList;
+
+/**
+ * https://github.com/smuyyh/ImageSelector
+ * 选择相册图片列表通用页
+ *
+ * @author yuyh.
+ * @date 2016/8/5.
+ */
+public class Repair_ImgSelActivity extends FragmentActivity implements View.OnClickListener, Callback {
+
+    public static final String INTENT_RESULT = "result";
+    private static final int IMAGE_CROP_CODE = 1;
+    private static final int STORAGE_REQUEST_CODE = 1;
+
+    private ImgSelConfig config;
+
+    private RelativeLayout rlTitleBar;
+    private TextView tvTitle;
+    private Button btnConfirm;
+    private ImageView ivBack;
+    private String cropImagePath;
+
+    private Repair_ImgSelFragment fragment;
+
+    private ArrayList<String> result = new ArrayList<>();
+
+    public static void startActivity(Activity activity, ImgSelConfig config, int RequestCode) {
+        Intent intent = new Intent(activity, Repair_ImgSelActivity.class);
+        Constant.config = config;
+        activity.startActivityForResult(intent, RequestCode);
+    }
+
+    public static void startActivity(Fragment fragment, ImgSelConfig config, int RequestCode) {
+        Intent intent = new Intent(fragment.getActivity(), Repair_ImgSelActivity.class);
+        Constant.config = config;
+        fragment.startActivityForResult(intent, RequestCode);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_img_sel);
+        config = Constant.config;
+        // Android 6.0 checkSelfPermission
+        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+                != PackageManager.PERMISSION_GRANTED) {
+            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+                    STORAGE_REQUEST_CODE);
+        } else {
+            fragment = Repair_ImgSelFragment.instance();
+            getSupportFragmentManager().beginTransaction()
+                    .add(R.id.fmImageList, fragment, null)
+                    .commit();
+        }
+
+        initView();
+        if (!FileUtils.isSdCardAvailable()) {
+            Toast.makeText(this, getString(R.string.sd_disable), Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    private void initView() {
+        rlTitleBar = (RelativeLayout) findViewById(R.id.rlTitleBar);
+        tvTitle = (TextView) findViewById(R.id.tvTitle);
+
+        btnConfirm = (Button) findViewById(R.id.btnConfirm);
+        btnConfirm.setOnClickListener(this);
+
+        ivBack = (ImageView) findViewById(R.id.ivBack);
+        ivBack.setOnClickListener(this);
+
+        if (config != null) {
+            if (config.backResId != -1) {
+                ivBack.setImageResource(config.backResId);
+            }
+
+            if (config.statusBarColor != -1) {
+                StatusBarCompat.compat(this, config.statusBarColor);
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
+                        && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+                    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+                    //getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+                }
+            }
+            rlTitleBar.setBackgroundColor(config.titleBgColor);
+            tvTitle.setTextColor(config.titleColor);
+            tvTitle.setText(config.title);
+            btnConfirm.setBackgroundColor(config.btnBgColor);
+            btnConfirm.setTextColor(config.btnTextColor);
+
+            if (config.multiSelect) {
+                if (!config.rememberSelected) {
+                    Constant.imageList.clear();
+                }
+                btnConfirm.setText(String.format(getString(R.string.confirm_format), config.btnText, Constant.imageList
+                        .size(), config.maxNum));
+            } else {
+                Constant.imageList.clear();
+                btnConfirm.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        int id = v.getId();
+        if (id == R.id.btnConfirm) {
+            if (Constant.imageList != null && !Constant.imageList.isEmpty()) {
+                exit();
+            } else {
+                Toast.makeText(this, getString(R.string.minnum), Toast.LENGTH_SHORT).show();
+            }
+        } else if (id == R.id.ivBack) {
+            onBackPressed();
+        }
+    }
+
+    @Override
+    public void onSingleImageSelected(String path) {
+        if (config.needCrop) {
+            crop(path);
+        } else {
+            Constant.imageList.add(path);
+            exit();
+        }
+    }
+
+    @Override
+    public void onImageSelected(String path) {
+        btnConfirm.setText(String.format(getString(R.string.confirm_format), config.btnText, Constant.imageList.size(),
+                config.maxNum));
+    }
+
+    @Override
+    public void onImageUnselected(String path) {
+        btnConfirm.setText(String.format(getString(R.string.confirm_format), config.btnText, Constant.imageList.size(),
+                config.maxNum));
+    }
+
+    @Override
+    public void onCameraShot(File imageFile) {
+        if (imageFile != null) {
+            if (config.needCrop) {
+                crop(imageFile.getAbsolutePath());
+            } else {
+                Constant.imageList.add(imageFile.getAbsolutePath());
+                config.multiSelect = false; // 多选点击拍照,强制更改为单选
+                exit();
+            }
+        }
+    }
+
+    @Override
+    public void onPreviewChanged(int select, int sum, boolean visible) {
+        if (visible) {
+            tvTitle.setText(select + "/" + sum);
+        } else {
+            tvTitle.setText(config.title);
+        }
+    }
+
+    private void crop(String imagePath) {
+        File file = new File(FileUtils.createRootPath(this) + "/" + System.currentTimeMillis() + ".jpg");
+
+        cropImagePath = file.getAbsolutePath();
+        Intent intent = new Intent("com.android.camera.action.CROP");
+        intent.setDataAndType(getImageContentUri(new File(imagePath)), "image/*");
+        intent.putExtra("crop", "true");
+        intent.putExtra("aspectX", config.aspectX);
+        intent.putExtra("aspectY", config.aspectY);
+        intent.putExtra("outputX", config.outputX);
+        intent.putExtra("outputY", config.outputY);
+        intent.putExtra("scale", true);
+        intent.putExtra("return-data", false);
+        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
+        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
+        intent.putExtra("noFaceDetection", true);
+        startActivityForResult(intent, IMAGE_CROP_CODE);
+    }
+
+    public Uri getImageContentUri(File imageFile) {
+        String filePath = imageFile.getAbsolutePath();
+        Cursor cursor = getContentResolver().query(
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+                new String[]{MediaStore.Images.Media._ID},
+                MediaStore.Images.Media.DATA + "=? ",
+                new String[]{filePath}, null);
+
+        if (cursor != null && cursor.moveToFirst()) {
+            int id = cursor.getInt(cursor
+                    .getColumnIndex(MediaStore.MediaColumns._ID));
+            Uri baseUri = Uri.parse("content://media/external/images/media");
+            return Uri.withAppendedPath(baseUri, "" + id);
+        } else {
+            if (imageFile.exists()) {
+                ContentValues values = new ContentValues();
+                values.put(MediaStore.Images.Media.DATA, filePath);
+                return getContentResolver().insert(
+                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == IMAGE_CROP_CODE && resultCode == RESULT_OK) {
+            Constant.imageList.add(cropImagePath);
+            config.multiSelect = false; // 多选点击拍照,强制更改为单选
+            exit();
+        }
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+
+    public void exit() {
+        Intent intent = new Intent();
+        result.clear();
+        result.addAll(Constant.imageList);
+        intent.putStringArrayListExtra(INTENT_RESULT, result);
+        setResult(RESULT_OK, intent);
+
+        if (!config.multiSelect) {
+            Constant.imageList.clear();
+        }
+
+        finish();
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        switch (requestCode) {
+            case STORAGE_REQUEST_CODE:
+                if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    getSupportFragmentManager()
+                            .beginTransaction()
+                            .add(R.id.fmImageList, Repair_ImgSelFragment.instance(), null)
+                            .commitAllowingStateLoss();
+                } else {
+                    Toast.makeText(this, getString(R.string.permission_storage_denied), Toast.LENGTH_SHORT).show();
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (fragment == null || !fragment.hidePreview()) {
+            Constant.imageList.clear();
+            super.onBackPressed();
+        }
+    }
+}

+ 462 - 0
demo/src/main/java/com/sybotan/android/demo/activities/Repair_ImgSelFragment.java

@@ -0,0 +1,462 @@
+package com.sybotan.android.demo.activities;
+
+/**
+ * 选择相册图片列表fragment通用页
+ * Created by cunwen on 17/9/5.
+ */
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.ListPopupWindow;
+import androidx.core.content.ContextCompat;
+import androidx.core.content.FileProvider;
+import androidx.fragment.app.Fragment;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.CursorLoader;
+import androidx.loader.content.Loader;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.viewpager.widget.ViewPager;
+
+import com.sybotan.android.demo.R;
+import com.sybotan.android.demo.adapter.ImageListRVAdapter;
+import com.sybotan.android.demo.view.MyDialogViewSingle;
+import com.yuyh.library.imgsel.ImgSelConfig;
+import com.yuyh.library.imgsel.adapter.FolderListAdapter;
+import com.yuyh.library.imgsel.adapter.PreviewAdapter;
+import com.yuyh.library.imgsel.bean.Folder;
+import com.yuyh.library.imgsel.bean.Image;
+import com.yuyh.library.imgsel.common.Callback;
+import com.yuyh.library.imgsel.common.Constant;
+import com.yuyh.library.imgsel.common.OnFolderChangeListener;
+import com.yuyh.library.imgsel.common.OnItemClickListener;
+import com.yuyh.library.imgsel.utils.FileUtils;
+import com.yuyh.library.imgsel.utils.LogUtils;
+import com.yuyh.library.imgsel.widget.CustomViewPager;
+import com.yuyh.library.imgsel.widget.DividerGridItemDecoration;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class Repair_ImgSelFragment extends Fragment implements View.OnClickListener, ViewPager.OnPageChangeListener {
+
+    private RecyclerView rvImageList;
+    private Button btnAlbumSelected;
+    private View rlBottom;
+    private CustomViewPager viewPager;
+
+    private ImgSelConfig config;
+    private Callback callback;
+    private List<Folder> folderList = new ArrayList<>();
+    private List<Image> imageList = new ArrayList<>();
+
+    private ListPopupWindow folderPopupWindow;
+    private ImageListRVAdapter imageListAdapter;
+    private FolderListAdapter folderListAdapter;
+    private PreviewAdapter previewAdapter;
+
+    private boolean hasFolderGened = false;
+
+    private static final int LOADER_ALL = 0;
+    private static final int LOADER_CATEGORY = 1;
+    private static final int REQUEST_CAMERA = 5;
+
+    private static final int CAMERA_REQUEST_CODE = 1;
+
+    private File tempFile;
+
+    public static Repair_ImgSelFragment instance() {
+        Repair_ImgSelFragment fragment = new Repair_ImgSelFragment();
+        Bundle bundle = new Bundle();
+        fragment.setArguments(bundle);
+        return fragment;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.common_fragment_img_sel, container, false);
+        rvImageList = (RecyclerView) view.findViewById(R.id.rvImageList);
+        btnAlbumSelected = (Button) view.findViewById(R.id.btnAlbumSelected);
+        btnAlbumSelected.setOnClickListener(this);
+        rlBottom = view.findViewById(R.id.rlBottom);
+        viewPager = (CustomViewPager) view.findViewById(R.id.viewPager);
+        viewPager.addOnPageChangeListener(this);
+        return view;
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        config = Constant.config;
+        try {
+            callback = (Callback) getActivity();
+        } catch (Exception e) {
+
+        }
+
+        btnAlbumSelected.setText(config.allImagesText);
+
+        rvImageList.setLayoutManager(new GridLayoutManager(rvImageList.getContext(), 3));
+        rvImageList.addItemDecoration(new DividerGridItemDecoration(rvImageList.getContext()));
+        if (config.needCamera)
+            imageList.add(new Image());
+
+        imageListAdapter = new ImageListRVAdapter(getActivity(), imageList, config);
+        imageListAdapter.setShowCamera(config.needCamera);
+        imageListAdapter.setMutiSelect(config.multiSelect);
+        rvImageList.setAdapter(imageListAdapter);
+        imageListAdapter.setOnItemClickListener(new OnItemClickListener() {
+            @Override
+            public int onCheckedClick(int position, Image image) {
+                return checkedImage(position, image);
+            }
+
+            @Override
+            public void onImageClick(int position, Image image) {
+                if (config.needCamera && position == 0) {
+                    showCameraAction();
+                } else {
+                    if (config.multiSelect) {
+                        viewPager.setAdapter((previewAdapter = new PreviewAdapter(getActivity(), imageList, config)));
+                        previewAdapter.setListener(new OnItemClickListener() {
+                            @Override
+                            public int onCheckedClick(int position, Image image) {
+                                return checkedImage(position, image);
+                            }
+
+                            @Override
+                            public void onImageClick(int position, Image image) {
+                                hidePreview();
+                            }
+                        });
+                        if (config.needCamera) {
+                            callback.onPreviewChanged(position, imageList.size() - 1, true);
+                        } else {
+                            callback.onPreviewChanged(position + 1, imageList.size(), true);
+                        }
+                        viewPager.setCurrentItem(config.needCamera ? position - 1 : position);
+                        viewPager.setVisibility(View.VISIBLE);
+                    } else {
+                        if (callback != null) {
+                            callback.onSingleImageSelected(image.path);
+                        }
+                    }
+                }
+            }
+        });
+
+        folderListAdapter = new FolderListAdapter(getActivity(), folderList, config);
+
+        getActivity().getSupportLoaderManager().initLoader(LOADER_ALL, null, mLoaderCallback);
+    }
+
+    private int checkedImage(int position, Image image) {
+        if (image != null) {
+            if (Constant.imageList.contains(image.path)) {
+                Constant.imageList.remove(image.path);
+                if (callback != null) {
+                    callback.onImageUnselected(image.path);
+                }
+            } else {
+                if (config.maxNum <= Constant.imageList.size()) {
+//                    Toast.makeText(getActivity(), String.format(getString(R.string.maxnum), config.maxNum), Toast.LENGTH_SHORT).show();
+//                    Toast.makeText(getActivity(), "哈哈", Toast.LENGTH_SHORT).show();
+
+                    MyDialogViewSingle dialog = new MyDialogViewSingle(getActivity()) {
+                        @Override
+                        protected void onBottomClick() {
+                            dismiss();
+                        }
+                    };
+                    dialog.show();
+                    dialog.setContentviewText(String.format(getString(R.string.maxnum), config.maxNum));
+                    dialog.setButtonviewText("我知道了");
+
+                    return 0;
+                }
+
+                Constant.imageList.add(image.path);
+                if (callback != null) {
+                    callback.onImageSelected(image.path);
+                }
+            }
+            return 1;
+        }
+        return 0;
+    }
+
+    private LoaderManager.LoaderCallbacks<Cursor> mLoaderCallback = new LoaderManager.LoaderCallbacks<Cursor>() {
+
+        private final String[] IMAGE_PROJECTION = {
+                MediaStore.Images.Media.DATA,
+                MediaStore.Images.Media.DISPLAY_NAME,
+                MediaStore.Images.Media.DATE_ADDED,
+                MediaStore.Images.Media._ID};
+
+        @Override
+        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+            if (id == LOADER_ALL) {
+                CursorLoader cursorLoader = new CursorLoader(getActivity(),
+                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_PROJECTION,
+                        null, null, IMAGE_PROJECTION[2] + " DESC");
+                return cursorLoader;
+            } else if (id == LOADER_CATEGORY) {
+                CursorLoader cursorLoader = new CursorLoader(getActivity(),
+                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_PROJECTION,
+                        IMAGE_PROJECTION[0] + " like '%" + args.getString("path") + "%'", null, IMAGE_PROJECTION[2] + " DESC");
+                return cursorLoader;
+            }
+            return null;
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+            if (data != null) {
+                int count = data.getCount();
+                if (count > 0) {
+                    List<Image> tempImageList = new ArrayList<>();
+                    data.moveToFirst();
+                    do {
+                        String path = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[0]));
+                        String name = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[1]));
+                        long dateTime = data.getLong(data.getColumnIndexOrThrow(IMAGE_PROJECTION[2]));
+                        Image image = new Image(path, name, dateTime);
+                        if (!image.path.endsWith("gif")){
+                            File file = new File(path);
+                            if (file.exists()) {
+                                tempImageList.add(image);
+                            }
+                        }
+                        if (!hasFolderGened) {
+                            File imageFile = new File(path);
+                            File folderFile = imageFile.getParentFile();
+                            if (folderFile == null) {
+                                System.out.println(path);
+                                return;
+                            }
+                            Folder folder = new Folder();
+                            folder.name = folderFile.getName();
+                            folder.path = folderFile.getAbsolutePath();
+                            folder.cover = image;
+                            if (!folderList.contains(folder)) {
+                                List<Image> imageList = new ArrayList<>();
+                                imageList.add(image);
+                                folder.images = imageList;
+                                folderList.add(folder);
+                            } else {
+                                Folder f = folderList.get(folderList.indexOf(folder));
+                                f.images.add(image);
+                            }
+                        }
+
+                    } while (data.moveToNext());
+
+                    imageList.clear();
+                    if (config.needCamera){
+                        imageList.add(new Image());
+                    }
+                    imageList.addAll(tempImageList);
+
+
+                    imageListAdapter.notifyDataSetChanged();
+
+                    if (Constant.imageList != null && Constant.imageList.size() > 0) {
+                        //imageListAdapter.setDefaultSelected(Constant.imageList);
+                    }
+
+                    folderListAdapter.notifyDataSetChanged();
+
+                    hasFolderGened = true;
+                }
+            }
+        }
+
+        @Override
+        public void onLoaderReset(Loader<Cursor> loader) {
+
+        }
+    };
+
+    private void createPopupFolderList(int width, int height) {
+        folderPopupWindow = new ListPopupWindow(getActivity());
+        folderPopupWindow.setBackgroundDrawable(getContext().getResources().getDrawable(R.color.gray_a6a6a6));
+        folderPopupWindow.setAdapter(folderListAdapter);
+        folderPopupWindow.setContentWidth(width);
+        folderPopupWindow.setWidth(width);
+        folderPopupWindow.setHeight(height);
+        folderPopupWindow.setAnchorView(rlBottom);
+        folderPopupWindow.setModal(true);
+        folderListAdapter.setOnFloderChangeListener(new OnFolderChangeListener() {
+            @Override
+            public void onChange(int position, Folder folder) {
+                folderPopupWindow.dismiss();
+                if (position == 0) {
+                    getActivity().getSupportLoaderManager().restartLoader(LOADER_ALL, null, mLoaderCallback);
+                    btnAlbumSelected.setText(config.allImagesText);
+                } else {
+                    imageList.clear();
+                    if (config.needCamera)
+                        imageList.add(new Image());
+                    imageList.addAll(folder.images);
+                    imageListAdapter.notifyDataSetChanged();
+
+                    btnAlbumSelected.setText(folder.name);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v.getId() == btnAlbumSelected.getId()) {
+            if (folderPopupWindow == null) {
+                WindowManager wm = getActivity().getWindowManager();
+                int width = wm.getDefaultDisplay().getWidth();
+                createPopupFolderList(width / 3 * 2, width / 3 * 2);
+            }
+
+            if (folderPopupWindow.isShowing()) {
+                folderPopupWindow.dismiss();
+            } else {
+                folderPopupWindow.show();
+                if (folderPopupWindow.getListView() != null) {
+                    folderPopupWindow.getListView().setDivider(new ColorDrawable(ContextCompat.getColor(getActivity(), R.color.bottom_bg)));
+                }
+                int index = folderListAdapter.getSelectIndex();
+                index = index == 0 ? index : index - 1;
+                folderPopupWindow.getListView().setSelection(index);
+            }
+        }
+    }
+
+    private void showCameraAction() {
+
+        if (config.maxNum <= Constant.imageList.size()) {
+//            Toast.makeText(getActivity(), String.format(getString(R.string.maxnum), config.maxNum), Toast.LENGTH_SHORT).show();
+//            Toast.makeText(getActivity(), "哈哈", Toast.LENGTH_SHORT).show();
+            MyDialogViewSingle dialog = new MyDialogViewSingle(getActivity()) {
+                @Override
+                protected void onBottomClick() {
+                    dismiss();
+                }
+            };
+            dialog.show();
+            dialog.setContentviewText(String.format(getString(R.string.maxnum), config.maxNum));
+            dialog.setButtonviewText("我知道了");
+            return;
+        }
+
+        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
+                != PackageManager.PERMISSION_GRANTED) {
+            requestPermissions(new String[]{Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE);
+            return;
+        }
+
+        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+
+        if (cameraIntent.resolveActivity(getActivity().getPackageManager()) != null) {
+            tempFile = new File(FileUtils.createRootPath(getActivity()) + "/" + System.currentTimeMillis() + ".jpg");
+            LogUtils.e(tempFile.getAbsolutePath());
+            FileUtils.createFile(tempFile);
+
+            Uri uri = FileProvider.getUriForFile(getActivity(),
+                    FileUtils.getApplicationId(getActivity()) + ".provider", tempFile);
+
+            List<ResolveInfo> resInfoList = getActivity().getPackageManager()
+                    .queryIntentActivities(cameraIntent, PackageManager.MATCH_DEFAULT_ONLY);
+            for (ResolveInfo resolveInfo : resInfoList) {
+                String packageName = resolveInfo.activityInfo.packageName;
+                getActivity().grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                        | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            }
+
+            cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); //Uri.fromFile(tempFile)
+            startActivityForResult(cameraIntent, REQUEST_CAMERA);
+        } else {
+            Toast.makeText(getActivity(), getString(R.string.open_camera_failure), Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_CAMERA) {
+            if (resultCode == Activity.RESULT_OK) {
+                if (tempFile != null) {
+                    if (callback != null) {
+                        callback.onCameraShot(tempFile);
+                    }
+                }
+            } else {
+                if (tempFile != null && tempFile.exists()) {
+                    tempFile.delete();
+                }
+            }
+        }
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        switch (requestCode) {
+            case CAMERA_REQUEST_CODE:
+                if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    showCameraAction();
+                } else {
+                    Toast.makeText(getActivity(), getString(R.string.permission_camera_denied), Toast.LENGTH_SHORT).show();
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+    }
+
+    @Override
+    public void onPageSelected(int position) {
+        if (config.needCamera) {
+            callback.onPreviewChanged(position + 1, imageList.size() - 1, true);
+        } else {
+            callback.onPreviewChanged(position + 1, imageList.size(), true);
+        }
+    }
+
+    @Override
+    public void onPageScrollStateChanged(int state) {
+
+    }
+
+    public boolean hidePreview() {
+        if (viewPager.getVisibility() == View.VISIBLE) {
+            viewPager.setVisibility(View.GONE);
+            callback.onPreviewChanged(0, 0, false);
+            imageListAdapter.notifyDataSetChanged();
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

+ 152 - 0
demo/src/main/java/com/sybotan/android/demo/activities/SpaceDetailActivity.kt

@@ -0,0 +1,152 @@
+package com.sybotan.android.demo.activities
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.os.Bundle
+import android.text.TextUtils
+import android.util.Log
+import android.widget.ImageView
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.sybotan.android.demo.R
+import com.sybotan.android.demo.adapter.StaticInfoAdapter
+import com.sybotan.android.demo.bean.result.Photos
+import com.sybotan.android.demo.retrofit.Constant
+import com.sybotan.android.demo.tools.LocalDataOperation
+import com.sybotan.android.demo.tools.PictureUtils
+import com.sybotan.android.demo.tools.ToastUtils
+import com.sybotan.android.demo.tools.UploadImage
+import com.yuyh.library.imgsel.ImageLoader
+import com.yuyh.library.imgsel.ImgSelConfig
+import io.reactivex.ObservableEmitter
+import org.json.JSONException
+import org.json.JSONObject
+import java.io.File
+
+/**
+ * Created by lihao.
+ * Date: 2021/9/27
+ */
+class SpaceDetailActivity : BaseActivity() {
+
+    //选择图片
+    private val REQUEST_CODE_PIC = 0X102
+
+    // 自定义图片加载器
+    private val loader =
+        ImageLoader { context: Context?, path: String?, imageView: ImageView? ->
+            Glide.with(
+                context
+            ).load(path).into(imageView)
+        }
+    private lateinit var recycler: RecyclerView
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_space_detail)
+        initView()
+        initData()
+        return
+    } // Function onCreate()
+
+    private fun initData() {
+        var list = ArrayList<Object>()
+        list.add(Object())
+        val adapter = StaticInfoAdapter(this, list as List<Any>?)
+        adapter.setOnAddPic { addPic() }
+        recycler.adapter = adapter
+    }
+
+    private fun initView() {
+        recycler = findViewById(R.id.recycler)
+        recycler.layoutManager = LinearLayoutManager(this)
+    }
+
+    private fun addPic() {
+        val config: ImgSelConfig = ImgSelConfig.Builder(this, loader) // 是否多选
+            .multiSelect(true) // 是否记住上次选中记录, 仅当multiSelect为true的时候配置,默认为true
+            .rememberSelected(false) // 确定按钮文字颜色
+            .btnTextColor(Color.WHITE) // 使用沉浸式状态栏
+            .statusBarColor(resources.getColor(R.color.blue_2E3679)) // 返回图标ResId
+            .backResId(R.mipmap.back_gray).title("选择图片").titleColor(Color.WHITE).titleBgColor(
+                resources.getColor(R.color.blue_2E3679)
+            ) //                .allImagesText("All Images")
+            .needCrop(false).cropSize(1, 1, 200, 200) // 第一个是否显示相机
+            .needCamera(true) // 最大选择图片数量
+            .build()
+        //选择图片activity
+        //选择图片activity
+        Repair_ImgSelActivity.startActivity(this, config, REQUEST_CODE_PIC)
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        if (resultCode == RESULT_OK && requestCode == REQUEST_CODE_PIC) {
+            if (data != null) {
+                val pathList: List<String>? =
+                    data.getStringArrayListExtra(Repair_ImgSelActivity.INTENT_RESULT)
+                doPicDatas(pathList)
+            }
+        }
+    }
+
+    private fun doPicDatas(pathList: List<String>?) {
+        LocalDataOperation.getInstance().dealLocalData<Photos>(this) { name, params ->
+            when (name) {
+                LocalDataOperation.SHOW_DIALOG_IN_MAIN -> ToastUtils.showMyToast("图片上传中")
+                LocalDataOperation.DEAL_IO_DATA -> {
+                    val emitter =
+                        params as ObservableEmitter<String>
+                    for (s in pathList!!) {
+                        Log.e("已选择图片的路径为-----", s!!)
+                        val path: String =
+                            PictureUtils.compressImage(s, getOfflineImagePath(), 100)
+                        val picFile = File(path)
+                        if (picFile.exists()) {
+                            val key = picFile.name
+                            if (isHaveInternet()) {
+                                val url: String = Constant.ServerImgIPUp + key
+                                val str = UploadImage.uploadFiles(picFile, url)
+                                if (!TextUtils.isEmpty(str)) {
+                                    var resultString: String? = null
+                                    try {
+                                        val `object` = JSONObject(str)
+                                        resultString = `object`.getString("Result")
+                                    } catch (e: JSONException) {
+                                        e.printStackTrace()
+                                    }
+                                    if ("success" == resultString) {
+                                        val photos = Photos()
+                                        photos.itemType = 0
+                                        photos.path = path
+                                        photos.key = key
+//                                        viewModel.picPathList.add(photos)
+//                                        viewModel.content.pictures.add(key)
+                                    }
+                                }
+                            } else {
+                                val photos = Photos()
+                                photos.itemType = 0
+                                photos.path = path
+                                photos.key = key
+//                                viewModel.picPathList.add(photos)
+//                                viewModel.content.pictures.add(key)
+                            }
+                        }
+                    }
+                    emitter.onNext("")
+                }
+                LocalDataOperation.DEAL_DATA_NEXT -> {
+                    ToastUtils.showMyToast("图片上传完毕")
+                }
+                LocalDataOperation.DEAL_DATA_ERROR -> {
+                    ToastUtils.showMyToast("图片上传失败")
+                }
+                else -> {
+                }
+            }
+        }
+    }
+}

+ 1 - 1
demo/src/main/java/com/sybotan/android/demo/activities/WebViewActivity.kt

@@ -62,7 +62,7 @@ class WebViewActivity : BaseActivity() {
 //            }
 //            customView {
 //                verticalLayout {
-//                    editText("I'm a text") {
+//                    editText("I'm a view_text.xml") {
 //                        hint = "Name"
 //                    }
 //                    padding = dip(20)

+ 16 - 0
demo/src/main/java/com/sybotan/android/demo/adapter/BaseViewHolder.java

@@ -0,0 +1,16 @@
+package com.sybotan.android.demo.adapter;
+
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Created by Administrator on 2015/11/9.
+ */
+
+public class BaseViewHolder extends RecyclerView.ViewHolder {
+
+    public BaseViewHolder(View itemView) {
+        super(itemView);
+    }
+}

+ 104 - 0
demo/src/main/java/com/sybotan/android/demo/adapter/ImageListRVAdapter.java

@@ -0,0 +1,104 @@
+package com.sybotan.android.demo.adapter;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.sybotan.android.demo.R;
+import com.yuyh.easyadapter.recyclerview.EasyRVAdapter;
+import com.yuyh.easyadapter.recyclerview.EasyRVHolder;
+import com.yuyh.library.imgsel.ImgSelConfig;
+import com.yuyh.library.imgsel.bean.Image;
+import com.yuyh.library.imgsel.common.Constant;
+import com.yuyh.library.imgsel.common.OnItemClickListener;
+
+import java.util.List;
+
+/**
+ * Created by wangmaobo on 2018/12/10.
+ */
+public class ImageListRVAdapter extends EasyRVAdapter<Image> {
+    private boolean showCamera;
+    private boolean mutiSelect;
+
+    private ImgSelConfig config;
+    private Context context;
+    private OnItemClickListener listener;
+
+    public ImageListRVAdapter(Context context, List<Image> list, ImgSelConfig config) {
+        super(context, list, R.layout.common_item_img_sel, com.yuyh.library.imgsel.R.layout.item_img_sel_take_photo);
+        this.context = context;
+        this.config = config;
+    }
+
+    @Override
+    protected void onBindData(final EasyRVHolder viewHolder, final int position, final Image item) {
+
+        if (position == 0 && showCamera) {
+            ImageView iv = viewHolder.getView(com.yuyh.library.imgsel.R.id.ivTakePhoto);
+            iv.setImageResource(R.mipmap.icon_takephoto);
+            iv.setOnClickListener(v -> {
+                if (listener != null)
+                    listener.onImageClick(position, item);
+            });
+            return;
+        }
+
+        if (mutiSelect) {
+            viewHolder.getView(com.yuyh.library.imgsel.R.id.ivPhotoCheaked).setOnClickListener(v -> {
+                if (listener != null) {
+                    int ret = listener.onCheckedClick(position, item);
+                    if (ret == 1) { // 局部刷新
+                        if (Constant.imageList.contains(item.path)) {
+                            viewHolder.setImageResource(R.id.ivPhotoCheaked, R.mipmap.choseicon_green);
+                        } else {
+                            viewHolder.setImageResource(R.id.ivPhotoCheaked, R.mipmap.choseicon_gray);
+                        }
+                    }
+                }
+            });
+        }
+
+        viewHolder.setOnItemViewClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (listener != null)
+                    listener.onImageClick(position, item);
+            }
+        });
+
+        final ImageView iv = viewHolder.getView(com.yuyh.library.imgsel.R.id.ivImage);
+        config.loader.displayImage(context, item.path, iv);
+
+        if (mutiSelect) {
+            viewHolder.setVisible(com.yuyh.library.imgsel.R.id.ivPhotoCheaked, true);
+            if (Constant.imageList.contains(item.path)) {
+                viewHolder.setImageResource(R.id.ivPhotoCheaked, R.mipmap.choseicon_green);
+            } else {
+                viewHolder.setImageResource(R.id.ivPhotoCheaked, R.mipmap.choseicon_gray);
+            }
+        } else {
+            viewHolder.setVisible(com.yuyh.library.imgsel.R.id.ivPhotoCheaked, false);
+        }
+    }
+
+    public void setShowCamera(boolean showCamera) {
+        this.showCamera = showCamera;
+    }
+
+    public void setMutiSelect(boolean mutiSelect) {
+        this.mutiSelect = mutiSelect;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        if (position == 0 && showCamera) {
+            return 1;
+        }
+        return 0;
+    }
+
+    public void setOnItemClickListener(OnItemClickListener listener) {
+        this.listener = listener;
+    }
+}

+ 159 - 0
demo/src/main/java/com/sybotan/android/demo/adapter/PictureAdapter.java

@@ -0,0 +1,159 @@
+package com.sybotan.android.demo.adapter;
+
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.text.TextUtils;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.sybotan.android.demo.R;
+import com.sybotan.android.demo.activities.LookBigPicActivity;
+import com.sybotan.android.demo.bean.result.Photos;
+import com.sybotan.android.demo.retrofit.Constant;
+import com.sybotan.android.demo.tools.CommonUtils;
+import com.sybotan.android.demo.tools.DensityUtils;
+import com.sybotan.android.demo.tools.LocalDataOperation;
+import com.tencent.smtt.sdk.ui.dialog.widget.RoundImageView;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.List;
+
+/**
+ * created by lihao on 2018/12/11
+ * 九图
+ */
+public class PictureAdapter extends BaseRecyclerViewAdapter<Photos> {
+    private int width;
+    private int currentPosition;
+    private String id;
+    private boolean canEdit;
+    private PicAdd picAdd;
+
+    public PictureAdapter(Context context, List<Photos> beans, String id, boolean canEdit, int currentPosition, int width) {
+        super(context, beans);
+        this.id = id;
+        this.canEdit = canEdit;
+        this.currentPosition = currentPosition;
+        this.width = width;
+    }
+
+    @Override
+    protected int getItemLayoutId(int viewType) {
+        return R.layout.item_node_nine_pic;
+    }
+
+    @Override
+    protected void onBindDataToView(ViewHolder holder, Photos photos) {
+        ViewGroup.LayoutParams layoutParams = holder.getView(R.id.node_nine_pic_iv).getLayoutParams();
+        layoutParams.width = DensityUtils.dp2px(mContext, width);
+        layoutParams.height = DensityUtils.dp2px(mContext, width);
+        holder.getView(R.id.node_nine_pic_iv).setLayoutParams(layoutParams);
+        switch (photos.itemType) {
+            case 0:
+                if (canEdit) {
+                    holder.setVisible(R.id.node_nine_pic_delete);
+                    holder.getView(R.id.node_nine_pic_delete).setOnClickListener(v -> {
+                        if (!CommonUtils.isFastDoubleClick()) {
+//                            EventBus.getDefault().post(new DeletePicEvent(id, photos.key, currentPosition, photos.path));
+                        }
+                    });
+                } else {
+                    holder.setInVisible(R.id.node_nine_pic_delete);
+                }
+                String url = Constant.ServerImgIPDown + photos.key;
+                FileInputStream fis = null;
+                try {
+                    fis = new FileInputStream(photos.path);
+                } catch (FileNotFoundException e) {
+                    e.printStackTrace();
+                    loadImageViewCornersByPath(mContext, url, holder.getView(R.id.node_nine_pic_iv));
+                }
+
+                Bitmap bitmap = BitmapFactory.decodeStream(fis);
+                ImageView imageView = (ImageView) holder.getView(R.id.node_nine_pic_iv);
+                imageView.setImageBitmap(bitmap);
+
+                holder.getView(R.id.node_nine_pic_iv).setOnClickListener(v -> {
+                    Intent intent = new Intent(mContext, LookBigPicActivity.class);
+                    intent.putExtra("url", url);
+                    intent.putExtra("key", photos.key);
+                    intent.putExtra("id", id);
+                    intent.putExtra("canEdit", canEdit);
+                    intent.putExtra("position", currentPosition);
+                    mContext.startActivity(intent);
+                });
+                holder.setText(R.id.node_nine_pic_position, holder.getCurrentPosition() + ".");
+                break;
+            case 2:
+                if (canEdit) {
+                    holder.setVisible(R.id.node_nine_pic_delete);
+                    holder.getView(R.id.node_nine_pic_delete).setOnClickListener(v -> {
+                        if (!CommonUtils.isFastDoubleClick()) {
+//                            EventBus.getDefault().post(new DeletePicEvent(id, photos.key, currentPosition, photos.path));
+                        }
+                    });
+                } else {
+                    holder.setInVisible(R.id.node_nine_pic_delete);
+                }
+                holder.setText(R.id.node_nine_pic_position, holder.getCurrentPosition() + 1 + ".");
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                photos.bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
+                byte[] bytes = baos.toByteArray();
+                RoundImageView view = holder.getView(R.id.node_nine_pic_iv);
+                view.setImageBitmap(photos.bitmap);
+                holder.getView(R.id.node_nine_pic_iv).setOnClickListener(v -> {
+                    Intent intent = new Intent(mContext, LookBigPicActivity.class);
+                    intent.putExtra("path", photos.path);
+                    intent.putExtra("key", photos.key);
+//                    intent.putExtra("id", id);
+                    intent.putExtra("canEdit", canEdit);
+                    intent.putExtra("position", currentPosition);
+                    mContext.startActivity(intent);
+                });
+                break;
+            case 1:
+                holder.setText(R.id.node_nine_pic_position, "");
+                holder.setInVisible(R.id.node_nine_pic_delete);
+                holder.getView(R.id.node_nine_pic_iv).setOnClickListener(v -> {
+                    if (picAdd != null) {
+                        picAdd.onPicAdd();
+                    }
+                });
+                break;
+        }
+    }
+
+    public void loadImageViewCornersByPath(Context mContext, String path, ImageView mImageView) {
+        if (TextUtils.isEmpty(path) || (path.contains("key=null") || path.endsWith("key="))) {
+            mImageView.setImageResource(R.mipmap.image_error_icon);
+            return;
+        }
+        Glide.with(mContext)
+                .load(path)
+                .into(mImageView);
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return mBeans.get(position).itemType;
+    }
+
+    public interface PicAdd {
+        void onPicAdd();
+    }
+
+    public void setOnPicAdd(PicAdd picAdd) {
+        this.picAdd = picAdd;
+    }
+}

+ 77 - 0
demo/src/main/java/com/sybotan/android/demo/adapter/RelationExpandAdapter.kt

@@ -0,0 +1,77 @@
+package com.sybotan.android.demo.adapter
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseExpandableListAdapter
+import android.widget.TextView
+import com.sybotan.android.demo.R
+import com.sybotan.android.demo.entitiies.Build
+import com.sybotan.android.demo.entitiies.Floor
+
+
+class RelationExpandAdapter(
+    context: Context,
+    groups: List<Build>,
+    items: List<List<Floor>>
+) : BaseExpandableListAdapter() {
+    private var mContext: Context = context
+    private var mGroups: List<Build> = groups
+    private var mItems: List<List<Floor>> = items
+    private var mLayoutInflater: LayoutInflater = LayoutInflater.from(context)
+
+    override fun getGroupCount(): Int {
+        return mGroups.size
+    }
+
+    override fun getChildrenCount(groupPosition: Int): Int {
+        return mItems[groupPosition].size
+    }
+
+    override fun getGroup(groupPosition: Int): Build {
+        return mGroups[groupPosition]
+    }
+
+    override fun getChild(groupPosition: Int, childPosition: Int): Floor {
+        return mItems[groupPosition][childPosition]
+    }
+
+    override fun getGroupId(groupPosition: Int): Long {
+        return groupPosition.toLong()
+    }
+
+    override fun getChildId(groupPosition: Int, childPosition: Int): Long {
+        return childPosition.toLong()
+    }
+
+    override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
+        return true
+    }
+
+    override fun hasStableIds(): Boolean {
+        return false
+    }
+
+    override fun getGroupView(groupPosition: Int, isExpanded: Boolean, convertView: View?, parent: ViewGroup?): View {
+        var holder = convertView
+        if (holder == null) {
+            holder = mLayoutInflater.inflate(R.layout.group_expand_relation, parent, false)
+        }
+        val group: Build = mGroups[groupPosition]
+        val tvGroup = holder?.findViewById(R.id.title) as TextView
+        tvGroup.text = group.localName
+        return holder
+    }
+
+    override fun getChildView(groupPosition: Int, childPosition: Int, isLastChild: Boolean, convertView: View?, parent: ViewGroup?): View {
+        var holder = convertView
+        if (holder == null) {
+            holder = mLayoutInflater.inflate(R.layout.child_expand_relation, parent, false)
+        }
+        val itemTv = holder?.findViewById(R.id.title) as TextView
+        val item = mItems[groupPosition][childPosition]
+        itemTv.text = item.localName
+        return holder
+    }
+}

+ 119 - 0
demo/src/main/java/com/sybotan/android/demo/adapter/SelectPopListAdapter.java

@@ -0,0 +1,119 @@
+package com.sybotan.android.demo.adapter;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+
+import com.sybotan.android.demo.R;
+import com.sybotan.android.demo.retrofit.Constant;
+import com.sybotan.android.demo.view.SelectListPopupWindow;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by cunwen on 16/6/21.
+ */
+public class SelectPopListAdapter extends RecyclerView.Adapter<SelectPopListAdapter.BuildViewHolder> {
+    private List<?> items = new ArrayList<>();
+    private Context context;
+    private int selectPosition;
+    private String type;
+    private boolean isRed;
+    private int redPosition;
+    private Boolean flag;
+
+    public void setItems(List<?> items, Context context, int position, String type) {
+        this.items = items;
+        this.context = context;
+        this.selectPosition = position;
+        this.type = type;
+    }
+
+
+    @Override
+    public BuildViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View mRootView = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.personal_select_text_item, parent, false);
+        return new BuildViewHolder(mRootView);
+    }
+
+    @Override
+    public void onBindViewHolder(final BuildViewHolder holder, @SuppressLint("RecyclerView") final int position) {
+        final Object item = items.get(position);
+        if (type.equals(Constant.TYPE1)) {
+            holder.choice_name.setText((String) item);
+        }
+        if (selectPosition > 0 && selectPosition - 1 == position) {
+            holder.choice_name.setTextColor(context.getResources().getColor(R.color.blue_2f48b7));
+        } else {
+            holder.choice_name.setTextColor(context.getResources().getColor(R.color.black_272727));
+        }
+        if (isRed && position == redPosition) {
+            holder.choice_name.setTextColor(context.getResources().getColor(R.color.red_D44D44));
+        } else if (isRed) {
+            holder.choice_name.setTextColor(context.getResources().getColor(R.color.black_272727));
+        }
+        holder.choice_layout.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mOnItemClickListener != null) {
+                    mOnItemClickListener.onItemClick(holder.choice_layout, position);
+                }
+            }
+        });
+//        if (flag) {
+//            holder.choice_name.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+//        } else {
+        holder.choice_name.setGravity(Gravity.CENTER);
+//        }
+    }
+
+    @Override
+    public int getItemCount() {
+        return items.size();
+    }
+
+    public void setRedText(int i, boolean isRed) {
+        this.isRed = true;
+        this.redPosition = i;
+        notifyDataSetChanged();
+    }
+
+    public void setFlag(Boolean flag) {
+        this.flag = flag;
+        notifyDataSetChanged();
+    }
+
+
+    static class BuildViewHolder extends BaseViewHolder {
+        TextView choice_name;
+        LinearLayout choice_layout;
+
+        public BuildViewHolder(View mRootView) {
+            super(mRootView);
+            choice_name = (TextView) mRootView.findViewById(R.id.choice_name);
+            choice_layout = (LinearLayout) mRootView.findViewById(R.id.choice_layout);
+
+        }
+    }
+
+    private OnRecyclerViewItemClickListener mOnItemClickListener = null;
+
+    public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
+        this.mOnItemClickListener = listener;
+    }
+
+    public interface OnRecyclerViewItemClickListener {
+        void onItemClick(View view, int position);
+    }
+}

+ 168 - 0
demo/src/main/java/com/sybotan/android/demo/adapter/StaticInfoAdapter.java

@@ -0,0 +1,168 @@
+package com.sybotan.android.demo.adapter;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bigkoo.pickerview.builder.TimePickerBuilder;
+import com.bigkoo.pickerview.view.TimePickerView;
+import com.sybotan.android.demo.R;
+import com.sybotan.android.demo.bean.result.Photos;
+import com.sybotan.android.demo.view.ListItemView;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+/**
+ * Created by lihao.
+ * Date: 2021/9/26
+ */
+public class StaticInfoAdapter extends BaseRecyclerViewAdapter<Object> {
+    public static final int TEXT = 1;
+    public static final int INT = 2;
+    public static final int DOUBLE = 3;
+    public static final int ENUM = 4;
+    public static final int DATE = 5;
+    public static final int MTEXT = 6;
+    public static final int PICTURE = 7;
+
+    private TimePickerView pvTime;
+    private AddPic addPic;
+
+    public StaticInfoAdapter(Context context, List<Object> beans) {
+        super(context, beans);
+    }
+
+    @Override
+    protected int getItemLayoutId(int viewType) {
+        switch (viewType) {
+            case TEXT:
+                return R.layout.view_text;
+            case INT:
+                return R.layout.view_int;
+            case DOUBLE:
+                return R.layout.view_double;
+            case ENUM:
+                return R.layout.view_enum;
+            case DATE:
+                return R.layout.view_date;
+            case MTEXT:
+                return R.layout.view_mtext;
+            case PICTURE:
+                return R.layout.view_picture;
+
+
+        }
+        return 0;
+    }
+
+    @Override
+    protected void onBindDataToView(ViewHolder holder, Object o) {
+        List<Photos> beans = new ArrayList<>();
+        Photos photo = new Photos();
+        photo.itemType = 1;
+        beans.add(photo);
+        PictureAdapter pictureAdapter = new PictureAdapter(mContext, beans, "", false, holder.getCurrentPosition(), 85);
+        pictureAdapter.setOnPicAdd(new PictureAdapter.PicAdd() {
+            @Override
+            public void onPicAdd() {
+                if (addPic != null) {
+                    addPic.onAddPic();
+                }
+            }
+        });
+        RecyclerView recycler = holder.getView(R.id.recycler);
+        recycler.setLayoutManager(new GridLayoutManager(mContext, 3));
+        recycler.setAdapter(pictureAdapter);
+//        switch (o.hashCode()) {
+//            case TEXT:
+//                break;
+//            case INT:
+//                break;
+//            case DOUBLE:
+//                break;
+//            case ENUM:
+//                break;
+//            case DATE:
+//                ListItemView listItemView = holder.getView(R.id.view_date);
+//                listItemView.getView(R.id.tv_right).setOnClickListener(v -> {
+//                    pvTime.show(true);
+//                });
+//                break;
+//            case MTEXT:
+//                break;
+//            case PICTURE:
+//                List<Photos> beans = new ArrayList<>();
+//                Photos photo = new Photos();
+//                photo.itemType = 1;
+//                beans.add(photo);
+//                PictureAdapter pictureAdapter = new PictureAdapter(mContext, beans, "", false, holder.getCurrentPosition(), 85);
+//                pictureAdapter.setOnPicAdd(new PictureAdapter.PicAdd() {
+//                    @Override
+//                    public void onPicAdd() {
+//                        if (addPic != null) {
+//                            addPic.onAddPic();
+//                        }
+//                    }
+//                });
+//                RecyclerView recycler = holder.getView(R.id.recycler);
+//                recycler.setLayoutManager(new GridLayoutManager(mContext, 3));
+//                recycler.setAdapter(pictureAdapter);
+//                break;
+//        }
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return PICTURE;
+    }
+
+    public void initTimePicker() {
+        Calendar currentDate = Calendar.getInstance();
+        Calendar endDate = Calendar.getInstance();
+        endDate.set(3000, 12, 31);
+        //初始化时间选择器
+        pvTime = new TimePickerBuilder(mContext, (date, v) -> {//选中事件回调
+            long timeBegin = 0;
+            long timeEnd = 0;
+        }).setLayoutRes(R.layout.pickerview_custom_time, v -> {
+            Button btnSubmit = v.findViewById(R.id.btnSubmit);
+            Button btnCancel = v.findViewById(R.id.btnCancel);
+            //确定
+            btnSubmit.setOnClickListener(v1 -> {
+                pvTime.returnData();
+                pvTime.dismiss();
+            });
+            //取消
+            btnCancel.setOnClickListener(v2 -> {
+                pvTime.dismiss();
+            });
+        }).setType(new boolean[]{true, true, true, false, false, false})
+                .setLabel("年", "月", "日", "", "", "") //设置空字符串以隐藏单位提示   hide label
+                .setDividerColor(Color.DKGRAY)
+                .setContentTextSize(20)
+                .setDate(currentDate)
+                .setRangDate(currentDate, endDate)
+//                .setDecorView((ViewGroup) findViewById(R.id.root))//非dialog模式下,设置ViewGroup,
+                // pickerView将会添加到这个ViewGroup中
+                .setBackgroundId(0xb0000000)
+                .setOutSideCancelable(false)
+                .build();
+        pvTime.setKeyBackCancelable(false);//系统返回键监听屏蔽掉
+    }
+
+    public interface AddPic {
+        void onAddPic();
+    }
+
+    public void setOnAddPic(AddPic addPic) {
+        this.addPic = addPic;
+    }
+
+}

+ 129 - 0
demo/src/main/java/com/sybotan/android/demo/adapter/ViewPagerImageAdapter.java

@@ -0,0 +1,129 @@
+package com.sybotan.android.demo.adapter;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.viewpager.widget.PagerAdapter;
+
+
+import com.bm.library.PhotoView;
+import com.bumptech.glide.Glide;
+import com.sybotan.android.demo.retrofit.API;
+import com.sybotan.android.demo.retrofit.Constant;
+import com.sybotan.android.demo.tools.CommonUtils;
+import com.sybotan.android.demo.tools.PhotoViewAttacher;
+import com.sybotan.android.demo.tools.ToastUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+
+/**
+ * viewpager展示多张图片通用适配器
+ * Created by wangmaobo on 2018/8/15.
+ */
+public class ViewPagerImageAdapter extends PagerAdapter {
+    private ArrayList<String> mImages = new ArrayList<>();
+    private ArrayList<HashMap<String, Object>> mBitmaps = new ArrayList<>();
+    private Context mContext;
+    private DismissListener mListener;
+    private boolean isFile;
+
+    public ViewPagerImageAdapter(Context context, ArrayList<String> list, boolean isFile) {
+        mContext = context;
+        mImages = list;
+        this.isFile = isFile;
+    }
+
+    public ViewPagerImageAdapter(Context context, ArrayList<HashMap<String, Object>> list) {
+        mContext = context;
+        mBitmaps = list;
+    }
+
+    public ViewPagerImageAdapter setListener(DismissListener listener) {
+        mListener = listener;
+        return this;
+    }
+
+    @Override
+    public int getCount() {
+        return mImages.size() > mBitmaps.size() ? mImages.size() : mBitmaps.size();
+    }
+
+    @Override
+    public View instantiateItem(ViewGroup container, int position) {
+        PhotoView photoView = new PhotoView(container.getContext());
+
+        PhotoViewAttacher attacher = new PhotoViewAttacher(photoView);
+        attacher.setOnPhotoTapListener(new PhotoViewAttacher.OnPhotoTapListener() {
+            @Override
+            public void onPhotoTap(View view, float v, float v1) {
+                if (null != mListener) {
+                    mListener.dismissM();
+                }
+            }
+
+            @Override
+            public void onOutsidePhotoTap() {
+                if (null != mListener) {
+                    mListener.dismissM();
+                }
+            }
+        });
+        if (isFile) {
+            loadImage(mContext, mImages.get(position), photoView, isFile);
+        } else if (!CommonUtils.IsNull(mBitmaps)) {
+            if (null != mBitmaps.get(position).get("Image_Bit") && mBitmaps.get(position).get("Image_Bit") instanceof Bitmap) {
+                photoView.setImageBitmap((Bitmap) mBitmaps.get(position).get("Image_Bit"));
+            }
+        } else {
+            String loadUrl = mImages.get(position);
+            if (TextUtils.isEmpty(loadUrl)) {
+                ToastUtils.showMyToast("图片已损坏");
+                return photoView;
+            }
+            if (!loadUrl.startsWith("http")) {
+                loadUrl = Constant.ServerImgIPDown + mImages.get(position);
+            }
+            loadImage(mContext, loadUrl, photoView, isFile);
+        }
+        container.addView(photoView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+        return photoView;
+    }
+
+    public static void loadImage(Context context, String path, ImageView imageView, boolean isFile) {
+        if (isFile) {
+            Glide.with(context)
+                    .load(new File(path))
+                    .into(imageView);
+        } else {
+            if (path.contains("key=null") || path.endsWith("key=")) {
+                return;
+            }
+            Glide.with(context)
+                    .load(path)
+//                    .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
+//                    .signature(new ObjectKey(String.valueOf(System.currentTimeMillis())))
+                    .into(imageView);
+        }
+    }
+
+    @Override
+    public void destroyItem(ViewGroup container, int position, Object object) {
+        container.removeView((View) object);
+    }
+
+    @Override
+    public boolean isViewFromObject(View view, Object object) {
+        return view == object;
+    }
+
+    public interface DismissListener {
+        void dismissM();
+    }
+}

+ 21 - 0
demo/src/main/java/com/sybotan/android/demo/bean/result/Photos.java

@@ -0,0 +1,21 @@
+package com.sybotan.android.demo.bean.result;
+
+import android.graphics.Bitmap;
+
+import java.io.Serializable;
+
+/**
+ * created by lihao on 2018/11/19
+ */
+public class Photos implements Serializable {
+    public String type;
+    public String name;
+    public String key;
+    public String creator_id;
+    public String creator_name;
+    public String create_time;
+    public Bitmap bitmap;
+
+    public int itemType;//0-图片 1-添加按钮 2-bitmap
+    public String path;//本地bitmap路径
+}

+ 7 - 6
demo/src/main/java/com/sybotan/android/demo/graphy/GraphyHelper.kt

@@ -1,25 +1,26 @@
 package com.sybotan.android.demo.graphy
 
+import android.content.Context
 import android.graphics.PointF
 import cn.sagacloud.android.cadengine.FloorScene
 import cn.sagacloud.android.cadengine.items.*
 import cn.sagacloud.android.cadengine.types.PipeLine
 import cn.sagacloud.android.cadengine.types.Point
-import java.util.ArrayList
+import com.sybotan.android.demo.activities.GraphyActivity
 
 /**
  * Created by lihao.
  * Date: 2021/9/22
  */
-class GraphyHelper(var scene: FloorScene) {
+class GraphyHelper(var scene: FloorScene, var context: Context) {
 
     /**
      * 绘制新设备
      */
     fun addEquip(point: Point): EquipItem {
-        val item = EquipItem(point)
+        val item = EquipItem(context, point)
         item.isVisible = true
-        item.zOrder = 100000f
+        item.zOrder = 1000001f
         scene.equipList.add(item)
         scene.addItem(item)
         return item
@@ -94,8 +95,8 @@ class GraphyHelper(var scene: FloorScene) {
     /**
      * 绘制二维码
      */
-    fun addQrcode(point: Point) {
-        val item = QrcodeItem(point)
+    fun addQrcode(context: Context, point: Point) {
+        val item = QrcodeItem(context, point)
         item.isVisible = true
         item.zOrder = 100000f
         scene.qrcodeItemList.add(item)

+ 7 - 1
demo/src/main/java/com/sybotan/android/demo/retrofit/Constant.java

@@ -1,4 +1,5 @@
 package com.sybotan.android.demo.retrofit;
+
 /**
  * Created by weimo on 2020/7/13.
  */
@@ -11,7 +12,12 @@ public class Constant {
     //服务器地址
     public static final String ServerIP = "http://192.168.0.11:8082/";
     //图片服务器地址
-    public static final String ServerImgIP = "http://api.sagacloud.cn/dp-auxiliary/image-service/common/image_get?systemId=dataPlatform&key=";
+    public static final String ServerImgIPUp = "http://develop.persagy.com/image-service/common/file_upload?systemId=dataPlatform&secret=9e0891a7a8c8e885&key=";
+    //图片服务器地址下载
+    public static final String ServerImgIPDown = "http://develop.persagy.com/image-service/common/file_get?systemId=dataPlatform&secret=9e0891a7a8c8e885&key=";
     //地图服务器地址
     public static final String ServerMapIP = "http://adm.sagacloud.cn/image-service/common/file_get?systemId=revit&key=";
+
+
+    public static final String TYPE1 = "String";
 }

+ 30 - 0
demo/src/main/java/com/sybotan/android/demo/tools/AnimateUtil.java

@@ -0,0 +1,30 @@
+package com.sybotan.android.demo.tools;
+
+import android.view.Gravity;
+
+import com.sybotan.android.demo.R;
+
+
+/**
+ * Created by cunwen on 17/6/29.
+ */
+
+public class AnimateUtil {
+
+    private static final int INVALID = -1;
+
+    public static int getAnimationResource(int gravity, boolean isInAnimation) {
+        switch (gravity) {
+            case Gravity.TOP:
+                return isInAnimation ? R.anim.svslide_in_top : R.anim.svslide_out_top;
+            case Gravity.BOTTOM:
+                return isInAnimation ? R.anim.svslide_in_bottom : R.anim.svslide_out_bottom;
+            case Gravity.CENTER:
+                return isInAnimation ? R.anim.svfade_in_center : R.anim.svfade_out_center;
+            default:
+                // This case is not implemented because we don't expect any other gravity at the moment
+        }
+        return INVALID;
+    }
+
+}

+ 947 - 0
demo/src/main/java/com/sybotan/android/demo/tools/CommonUtils.java

@@ -0,0 +1,947 @@
+package com.sybotan.android.demo.tools;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.text.Layout;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 通用工具类
+ * Created by wangmaobo on 2018/4/26.
+ */
+public class CommonUtils {
+    /**
+     * 判断是否为空
+     *
+     * @param object
+     * @return
+     */
+    public static boolean IsNull(Object object) {
+        if (null == object) {
+            return true;
+        } else {
+            if (object instanceof String) {
+                if (TextUtils.isEmpty((String) object)) {
+                    return true;
+                }
+            } else if (object instanceof List) {
+                if (((List) object).size() <= 0) {
+                    return true;
+                }
+            } else if (object instanceof Map) {
+                if (((Map) object).size() <= 0) {
+                    return true;
+                }
+            } else if (object instanceof Set) {
+                if (((Set) object).size() <= 0) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 获取时间段
+     *
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public static String getTimeMoment(String startTime, String endTime) {
+        long start = dateToMillisecond("yyyyMMddHHmmss", startTime);
+        long end = dateToMillisecond("yyyyMMddHHmmss", endTime);
+        long moment = end - start;
+        String result = secondToTimeDHMS(moment);
+        return result;
+    }
+
+    /**
+     * 将自定义格式的日期转化为毫秒
+     *
+     * @param format
+     * @param data
+     * @return
+     */
+    public static long dateToMillisecond(String format, String data) {
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        try {
+            long startTime = sdf.parse(data).getTime();
+            return startTime;
+        } catch (ParseException e) {
+            Log.e("tag", "抛异常了,转化失败");
+            e.printStackTrace();
+        }
+        return -1;
+    }
+
+    /**
+     * 毫将秒数转换为日时分秒,
+     *
+     * @param second
+     * @return
+     */
+    public static String secondToTime(long second) {
+        second = second / 1000;
+        long days = second / 86400;
+        second = second % 86400;
+        long hours = second / 3600;
+        second = second % 3600;
+        long minutes = second / 60;
+        //second = second % 60;
+        if (days > 0) {
+            if (hours > 0) {
+                return days + "天" + hours + "小时" + minutes + "分";
+            } else {
+                return days + "天" + minutes + "分";
+            }
+        } else {
+            if (hours > 0) {
+                return hours + "小时" + minutes + "分";
+            } else {
+                return minutes + "分";
+            }
+
+        }
+    }
+
+    /**
+     * 毫将秒数转换为日时分秒,
+     *
+     * @param second
+     * @return
+     */
+    public static String secondToTimeDHMS(long second) {
+        second = second / 1000;
+        long days = second / 86400;
+        second = second % 86400;
+        long hours = second / 3600;
+        second = second % 3600;
+        long minutes = second / 60;
+        second = second % 60;
+        StringBuilder stringBuilder = new StringBuilder();
+        if (days > 0) {
+            stringBuilder.append(days + "天");
+        }
+        if (hours > 0) {
+            stringBuilder.append(hours + "小时");
+        }
+        if (minutes > 0) {
+            stringBuilder.append(minutes + "分");
+        }
+        if (second > 0) {
+            stringBuilder.append(second + "秒");
+        }
+        return stringBuilder.toString();
+    }
+
+    /**
+     * list 集合去重-基本类型
+     */
+    public static ArrayList removeRepeatElement(ArrayList list) {
+        HashSet hs = new HashSet(list);
+        list.clear();
+        list.addAll(hs);
+        return list;
+    }
+
+    /**
+     * @param context
+     * @param target  目标view
+     * @param pxSpace 留白空间-单位dp
+     */
+    public static void setTextViewMaxWidth(Context context, TextView target, int pxSpace) {
+        int screenWidth = ScreenUtils.getScreenWidth(context);
+        target.setMaxWidth(screenWidth - pxSpace);
+    }
+
+    /**
+     * Description: 获取当前版本信息
+     *
+     * @param context
+     * @return 数组 0: 版本号 1: 版本名称
+     */
+    public static String[] getVersionMsg(Context context) {
+        // 获取packagemanager的实例
+        PackageManager packageManager = context.getPackageManager();
+        // getPackageName()是你当前类的包名,0代表是获取版本信息
+        PackageInfo packInfo = null;
+        try {
+            packInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        String versionCode = String.valueOf(packInfo.versionCode);
+        String versionName = packInfo.versionName;
+        return new String[]{versionCode, versionName};
+    }
+
+    /**
+     * 适用于父控件为相对布局
+     *
+     * @param view
+     * @param margin
+     */
+    public static void setLeftMargin(View view, int margin) {
+        RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) view.getLayoutParams();
+        lp.leftMargin = margin;
+        view.setLayoutParams(lp);
+    }
+
+    public static void setRightMargin(View view, int margin) {
+        RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) view.getLayoutParams();
+        lp.rightMargin = margin;
+        view.setLayoutParams(lp);
+    }
+
+    public static String MD5(String url) {
+        Log.e("tag_MD5", url);
+        String cacheKey;
+        try {
+            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
+            mDigest.update(url.getBytes());
+            byte[] bytes = mDigest.digest();
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < bytes.length; i++) {
+                String hex = Integer.toHexString(0xFF & bytes[i]);
+                if (hex.length() == 1) {
+                    sb.append('0');
+                }
+                sb.append(hex);
+            }
+            return sb.toString();
+        } catch (NoSuchAlgorithmException e) {
+            cacheKey = String.valueOf(url.hashCode());
+        }
+        return cacheKey;
+    }
+
+    private static long lastClickTime;
+
+    /**
+     * 防止用户连续点击
+     *
+     * @return true:连续点击 false:单次点击
+     */
+    public static boolean isFastDoubleClick() {
+        long time = System.currentTimeMillis();
+        long timeD = time - lastClickTime;
+        if (0 < timeD && timeD < 500) {
+            return true;
+        }
+        lastClickTime = time;
+        return false;
+    }
+
+    private static long last1Time;
+
+    /**
+     * 防止用户连续点击
+     *
+     * @return true:连续点击 false:单次点击
+     */
+    public static boolean is1Time() {
+        long time = System.currentTimeMillis();
+        long timeD = time - last1Time;
+        if (0 < timeD && timeD < 1000) {
+            return true;
+        }
+        last1Time = time;
+        return false;
+    }
+
+    private static long last10Time;
+
+    /**
+     * 防止用户连续点击
+     *
+     * @return true:连续点击 false:单次点击
+     */
+    public static boolean is10Time() {
+        long time = System.currentTimeMillis();
+        long timeD = time - last10Time;
+        if (0 < timeD && timeD < 10 * 1000) {
+            return true;
+        }
+        last10Time = time;
+        return false;
+    }
+
+    /**
+     * @param view   要动态设置高度的RecyclerView,最多放四个imageview
+     * @param margin 边距
+     * @note 根据屏幕宽度和边距动态设置高度
+     */
+    public static int setImageRecyclerViewHeight(RecyclerView view, int margin) {
+        int screenWidth = ScreenUtils.getScreenWidth(view.getContext());
+        int maxWidth = screenWidth - margin;
+        int singWidth = maxWidth / 4;
+        ViewGroup.LayoutParams mParams = view.getLayoutParams();
+        mParams.height = singWidth;
+        mParams.width = maxWidth;
+        view.setLayoutParams(mParams);
+        return singWidth;
+    }
+
+    /**
+     * @param view
+     * @param margin
+     * @param singleNum
+     * @param maxNum
+     * @return
+     */
+    public static int setImageRecyclerViewHeight(RecyclerView view, int margin, int singleNum, int maxNum, boolean isPreview) {
+        if (singleNum == 0) {
+            return 0;
+        }
+        int sumLine = maxNum / singleNum;
+        int extra = maxNum % singleNum;
+        if (extra != 0) {
+            sumLine += 1;
+        }
+        int screenWidth = ScreenUtils.getScreenWidth(view.getContext());
+        int maxWidth = screenWidth - margin;
+        int singWidth = maxWidth / singleNum;
+        ViewGroup.LayoutParams mParams = view.getLayoutParams();
+        if (isPreview) {
+            sumLine = sumLine <= 0 ? 1 : sumLine;
+            mParams.height = singWidth * sumLine + (DensityUtils.dp2px(view.getContext(), 4) * sumLine);//分割线的高度
+            Log.e("tag", "sumLine=" + sumLine + ",singleWidth=" + singWidth);
+        }
+        mParams.width = maxWidth;
+        view.setLayoutParams(mParams);
+        return singWidth;
+    }
+
+    public static String formatDate(String time) {
+        if (time.length() >= 14) {
+            String year, mouth, day, hour, minute;
+            year = time.substring(0, 4);
+            mouth = time.substring(4, 6);
+            day = time.substring(6, 8);
+            hour = time.substring(8, 10);
+            minute = time.substring(10, 12);
+            return year + "-" + mouth + "-" + day + " " + hour + ":" + minute;
+        }
+        return null;
+    }
+
+    /**
+     * 将某种格式的string日期转化为某种格式的string日期
+     *
+     * @param format_pre   入参日期的格式
+     * @param format_after 出参日期的格式
+     * @param time         入参日期
+     * @return 出参日期
+     */
+    public static String formatDate(String format_pre, String format_after, String time) {
+        if (TextUtils.isEmpty(time)) {
+            return "--";
+        }
+        String result = null;
+        SimpleDateFormat sdf_pre = new SimpleDateFormat(format_pre);
+        SimpleDateFormat sdf_after = new SimpleDateFormat(format_after);
+        try {
+            Date date = sdf_pre.parse(time);
+            result = sdf_after.format(date);
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    /**
+     * 制定的日期与当前日期对比
+     *
+     * @param time
+     * @return 负值为比当前时间晚,正值为比当前时间早
+     */
+    public static long compareTime(String time) {
+        DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//创建日期转换对象HH:mm:ss为时分秒,年月日为yyyy-MM-dd
+        try {
+            Date dt1 = df.parse(time);//将字符串转换为date类型
+            Date dt2 = df.parse(df.format(new Date()));
+            Calendar c1 = Calendar.getInstance();
+            Calendar c2 = Calendar.getInstance();
+            c1.setTime(dt1);
+            c2.setTime(dt2);
+
+            int result = c2.compareTo(c1);
+            Log.e("tag", "差值:" + result);
+            return result;
+        } catch (ParseException e) {
+        }
+        return -1;
+    }
+
+    /**
+     * 制定的日期与当前日期对比
+     *
+     * @param time
+     * @return 负值为比当前时间晚,正值为比当前时间早
+     */
+    public static long compareTimeTwo(String time) {
+        DateFormat df = new SimpleDateFormat("yyyyMMdd");//创建日期转换对象HH:mm:ss为时分秒,年月日为yyyy-MM-dd
+        try {
+            Date dt1 = df.parse(time);//将字符串转换为date类型
+            Date dt2 = df.parse(df.format(new Date()));
+            Calendar c1 = Calendar.getInstance();
+            Calendar c2 = Calendar.getInstance();
+            c1.setTime(dt1);
+            c2.setTime(dt2);
+
+            int result = c2.compareTo(c1);
+            Log.e("tag", "差值:" + result);
+            return result;
+        } catch (ParseException e) {
+        }
+        return -1;
+    }
+
+    //===================================数据部分===============================================
+    public static String getCheckDescription(String recommend_control_code, boolean isOrder) {
+        StringBuilder sb = new StringBuilder();
+        sb.append((isOrder ? "工单" : "任务节点") + "-");
+        switch (recommend_control_code) {
+            case "auditMatters":
+            case "approvalMatters":
+                sb.append("申请事项");
+                break;
+            case "auditAddingPeople":
+            case "approvalAddingPeople":
+                sb.append("申请加人");
+                break;
+            case "auditReplacePeople":
+            case "approvalReplacePeople":
+                sb.append("申请换人");
+                break;
+            case "auditDelay":
+            case "approvalDelay":
+                sb.append("申请延期");
+                break;
+            case "auditClose":
+            case "approvalClose":
+                sb.append("申请结束");
+                break;
+            case "auditStop":
+            case "approvalStop":
+                sb.append("申请终止");
+                break;
+            case "auditPause":
+            case "approvalPause":
+                sb.append("申请暂停");
+                break;
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 是否应该显示 查看更多
+     *
+     * @param tv_content 被观察者对象
+     * @param checkAll   需要显隐的查看更多view
+     */
+    public static void showCheckAll(TextView tv_content, TextView checkAll) {
+        //checkAll.setVisibility(View.VISIBLE);
+        tv_content.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                Layout l = tv_content.getLayout();
+                if (l != null) {
+                    int lines = l.getLineCount();
+                    if (lines > 0) {
+                        if (l.getEllipsisCount(lines - 1) > 0) {
+                            checkAll.setVisibility(View.VISIBLE);
+                            checkAll.setOnClickListener(v1 -> {
+                                if (checkAll.getText().equals("点击收起")) {
+                                    checkAll.setText("展开更多");
+                                    tv_content.setEllipsize(TextUtils.TruncateAt.END);
+                                    tv_content.setMaxLines(2);
+                                } else {
+                                    //显示TextView的全部内容其实就是取消这类隐藏文字的属性
+                                    tv_content.setEllipsize(null);
+                                    tv_content.setMaxLines(100);
+                                    checkAll.setText("点击收起");
+                                }
+                                //checkAll.setVisibility(View.GONE);
+                            });
+                        } else {
+                            checkAll.setVisibility(View.GONE);
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * 获取版本号
+     *
+     * @param context 上下文
+     * @return 版本号
+     */
+    public static int getVersionCode(Context context) {
+
+        //获取包管理器
+        PackageManager pm = context.getPackageManager();
+        //获取包信息
+        try {
+            PackageInfo packageInfo = pm.getPackageInfo(context.getPackageName(), 0);
+            //返回版本号
+            return packageInfo.versionCode;
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+
+        return 0;
+
+    }
+
+    public static String formatString(String resource) {
+        if (TextUtils.isEmpty(resource)) {
+            return "--";
+        }
+        return resource;
+    }
+
+    public static void formatStringAndView(String resource, TextView view) {
+        if (TextUtils.isEmpty(resource)) {
+            view.setVisibility(View.GONE);
+        } else {
+            view.setText(resource);
+            view.setVisibility(View.VISIBLE);
+        }
+    }
+
+    /**
+     * 结束时间是否在 当前时间之后
+     * @param endTime
+     * @return
+     */
+    public  static  boolean setComparisonOfSize(Date  endTime){
+        Date date = new Date(CommonUtils.translateMillionToCertain(System.currentTimeMillis(), "yyyyMMddHHmmss") );
+       return endTime.before(date);
+
+    }
+
+    /**
+     * 父控件为LinearLayout
+     *
+     * @param view
+     * @param left
+     * @param top
+     * @param right
+     * @param bottom
+     */
+    public static void setMargins(View view, int left, int top, int right, int bottom) {
+        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
+        params.setMargins(DensityUtils.dp2px(view.getContext(), left), top, DensityUtils.dp2px(view.getContext(), right), bottom);
+        view.setLayoutParams(params);
+    }
+
+    public static void setMarginsRl(View view, int left, int top, int right, int bottom) {
+        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
+        params.setMargins(DensityUtils.dp2px(view.getContext(), left), top, DensityUtils.dp2px(view.getContext(), right), bottom);
+        view.setLayoutParams(params);
+    }
+
+    public static String getCertainDate(int day) {
+        Calendar calendar = Calendar.getInstance();
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
+        calendar.add(Calendar.DATE, day);
+        String result = sdf.format(calendar.getTime());
+        return result;
+    }
+
+    /**
+     * 将制定毫秒的long转化为制定格式的日期
+     *
+     * @param mill
+     * @param format
+     * @return
+     */
+    public static String translateMillionToCertain(long mill, String format) {
+        Date date = new Date(mill);
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        return sdf.format(date);
+    }
+
+    /**
+     * 判断是否是当前年
+     *
+     * @param yyyyMMddHHmmss
+     * @return
+     */
+    public static boolean isThisYear(String yyyyMMddHHmmss) {
+        try {
+            String nowDate = new SimpleDateFormat("yyyyMMddHHmmss").format(new java.sql.Date(System.currentTimeMillis()));
+            return nowDate.substring(0, 4).equals(yyyyMMddHHmmss.substring(0, 4));
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /**
+     * 判断是否是当前天
+     *
+     * @param yyyyMMddHHmmss
+     * @return
+     */
+    public static boolean isThisDay(String yyyyMMddHHmmss) {
+        try {
+            String nowDate = new SimpleDateFormat("yyyyMMddHHmmss").format(new java.sql.Date(System.currentTimeMillis()));
+            return nowDate.substring(0, 8).equals(yyyyMMddHHmmss.substring(0, 8));
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    public static String translateDateToCertain(Date date, String format) {//yyyyMMddHHmmss
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        return sdf.format(date);
+    }
+
+    public static void setTextNotNull(TextView tv, String text) {
+        if (TextUtils.isEmpty(text)) {
+            text = "--";
+        }
+        tv.setText(text);
+    }
+
+    public static <T> T mapToObject(Map<String, Object> map, Class<T> beanClass) throws Exception {
+        if (map == null)
+            return null;
+
+        T obj = beanClass.newInstance();
+
+        Field[] fields = obj.getClass().getDeclaredFields();
+        for (Field field : fields) {
+            int mod = field.getModifiers();
+            if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
+                continue;
+            }
+
+            field.setAccessible(true);
+            field.set(obj, map.get(field.getName()));
+        }
+
+        return obj;
+    }
+
+    /**
+     * 获取百度的网络时间
+     *
+     * @return
+     */
+    public static String getNetworkTime() {
+        try {
+            URL url = new URL("http://www.baidu.com");
+            URLConnection conn = url.openConnection();
+            conn.connect();
+            long dateL = conn.getDate();
+            Date date = new Date(dateL);
+            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+            String format = dateFormat.format(date);
+            if (dateL == 0 || format.equals("19700101080000")) {
+                format = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date(System.currentTimeMillis()));
+            }
+            return format;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date(System.currentTimeMillis()));
+    }
+
+    /**
+     * 过滤掉中文
+     *
+     * @param str 待过滤中文的字符串
+     * @return 过滤掉中文后字符串
+     */
+    public static String filterChinese(String str) {
+        // 用于返回结果
+        String result = str;
+        boolean flag = isContainChinese(str);
+        if (flag) {// 包含中文
+            // 用于拼接过滤中文后的字符
+            StringBuffer sb = new StringBuffer();
+            // 用于校验是否为中文
+            boolean flag2 = false;
+            // 用于临时存储单字符
+            char chinese = 0;
+            // 5.去除掉文件名中的中文
+            // 将字符串转换成char[]
+            char[] charArray = str.toCharArray();
+            // 过滤到中文及中文字符
+            for (int i = 0; i < charArray.length; i++) {
+                chinese = charArray[i];
+                flag2 = isChinese(chinese);
+                if (!flag2) {// 不是中日韩文字及标点符号
+                    sb.append(chinese);
+                }
+            }
+            result = sb.toString();
+        }
+        return result;
+    }
+
+    /**
+     * 判定输入的是否是汉字
+     *
+     * @param c 被校验的字符
+     * @return true代表是汉字
+     */
+    public static boolean isChinese(char c) {
+        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
+        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
+                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
+                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
+                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
+                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
+                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 判断字符串中是否包含中文
+     *
+     * @param str 待校验字符串
+     * @return 是否为中文
+     * @warn 不能校验是否为中文标点符号
+     */
+    public static boolean isContainChinese(String str) {
+        Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
+        Matcher m = p.matcher(str);
+        if (m.find()) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 判断字符是否是数字
+     *
+     * @param str 字符串
+     * @return 是否是数字
+     */
+    public static boolean isNumeric(String str) {
+        for (int i = str.length(); --i >= 0; ) {
+            if (!Character.isDigit(str.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 获取版本名称
+     *
+     * @param context 上下文
+     * @return 版本名称
+     */
+    public static String getVersionName(Context context) {
+
+        //获取包管理器
+        PackageManager pm = context.getPackageManager();
+        //获取包信息
+        try {
+            PackageInfo packageInfo = pm.getPackageInfo(context.getPackageName(), 0);
+            //返回版本号
+            return packageInfo.versionName;
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+
+        return "";
+
+    }
+
+    public static String upNum(int i) {
+        switch (i) {
+            case 1:
+                return "一";
+            case 2:
+                return "二";
+            case 3:
+                return "三";
+            case 4:
+                return "四";
+            case 5:
+                return "五";
+            case 6:
+                return "六";
+            case 7:
+                return "七";
+            case 8:
+                return "八";
+            case 9:
+                return "九";
+            case 10:
+                return "十";
+            case 11:
+                return "十一";
+            case 12:
+                return "十二";
+            case 13:
+                return "十三";
+            case 14:
+                return "十四";
+            case 15:
+                return "十五";
+            case 16:
+                return "十六";
+            case 17:
+                return "十七";
+            case 18:
+                return "十八";
+            case 19:
+                return "十九";
+            case 20:
+                return "二十";
+        }
+        return "";
+    }
+
+    public static int getNum(String s) {
+        String regEx = "[^0-9]";
+        Pattern p = Pattern.compile(regEx);
+        Matcher m = p.matcher(s);
+        return Integer.parseInt(m.replaceAll("").trim());
+    }
+
+    /**
+     * 是否包含英文和数字
+     */
+    public static boolean isContainLetterAndNum(String str) {
+        Pattern pattern = Pattern.compile("^(?=.*[a-zA-Z])(?=.*\\d)[a-zA-Z0-9_\\-!@#\\^\\$\\!\\*]{6,}$");
+        Matcher matcher = pattern.matcher(str);
+        return matcher.find();
+    }
+
+    /**
+     * 是否包含英文和数字(8-15位)--万达青训
+     *
+     * @param str
+     * @return
+     */
+    public static boolean isContainLetterAndNumForWanda(String str) {
+        Pattern pattern = Pattern.compile("^(?=.*[a-zA-Z])(?=.*\\d)[a-zA-Z0-9_\\-!@#\\^\\$\\!\\*]{8,15}$");
+        Matcher matcher = pattern.matcher(str);
+        return matcher.find();
+    }
+
+    // 判断一个字符串是否含有数字
+    private static boolean hasDigit(String content) {
+        boolean flag = false;
+        Pattern p = Pattern.compile(".*\\d+.*");
+        Matcher m = p.matcher(content);
+        if (m.matches()) {
+            flag = true;
+        }
+        return flag;
+    }
+
+    /*
+     * 判断字符串中是否含有英文,包含返回true
+     */
+    private static boolean isENChar(String string) {
+        boolean flag = false;
+        Pattern p = Pattern.compile("[a-zA-z]");
+        if (p.matcher(string).find()) {
+            flag = true;
+        }
+        return flag;
+    }
+
+    public static boolean CheckPhone(String phone) {//判断手机号格式  
+//        Pattern pattern = Pattern.compile("^[1][3-8][0-9]{9}$");
+//        Matcher matcher = pattern.matcher(phone);
+//        return matcher;
+        return phone.length() == 11 && CommonUtils.isNumeric(phone);
+    }
+
+    /**
+     * 拨打电话
+     *
+     * @param mContext
+     * @param phone
+     */
+    public static void callPhone(Context mContext, String phone) {
+        Intent intent = new Intent(Intent.ACTION_DIAL);
+        Uri data = Uri.parse("tel:" + phone);
+        intent.setData(data);
+        mContext.startActivity(intent);
+    }
+
+    public static boolean isEquals(ArrayList<String> a, ArrayList<String> b) {
+        if (a.size() != b.size()) {
+            return false;
+        }
+        for (int i = 0; i != (a.size() < b.size() ? a.size() : b.size()); i++)
+            if (!a.get(i).equals(b.get(i))) return false;
+        return true;
+    }
+
+    public static String changeTime(long time) {
+        StringBuilder stringBuilder = new StringBuilder();
+        int day = (int) Math.floor(time / (1000 * 60 * 60 * 24));
+        if (day > 0) {
+            stringBuilder.append(day).append("天");;
+        }
+        int hour = (int) Math.floor(( time / (1000 * 60 * 60)) % 24);
+        if (hour > 0) {
+            stringBuilder.append(hour).append("小时");;
+        }
+        int minutes = (int) Math.floor(( time / (1000 * 60)) % 60);
+        if (minutes > 0) {
+            stringBuilder.append(minutes).append("分钟");
+        }
+        if (TextUtils.isEmpty(stringBuilder.toString())) {
+            stringBuilder.append("1分钟");
+        }
+        return stringBuilder.toString();
+    }
+
+    public static String subZeroAndDot(String s) {
+        if (s.indexOf(".") > 0) {
+            s = s.replaceAll("0+?$", "");//去掉多余的0
+            s = s.replaceAll("[.]$", "");//如最后一位是.则去掉
+        }
+        return s;
+    }
+
+}

+ 78 - 0
demo/src/main/java/com/sybotan/android/demo/tools/DefaultOnDoubleTapListener.java

@@ -0,0 +1,78 @@
+package com.sybotan.android.demo.tools;
+
+import android.graphics.RectF;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.widget.ImageView;
+
+/**
+ * Created by wangmaobo on 2019/1/25.
+ */
+public class DefaultOnDoubleTapListener implements GestureDetector.OnDoubleTapListener {
+    private PhotoViewAttacher photoViewAttacher;
+
+    public DefaultOnDoubleTapListener(PhotoViewAttacher photoViewAttacher) {
+        this.setPhotoViewAttacher(photoViewAttacher);
+    }
+
+    public void setPhotoViewAttacher(PhotoViewAttacher newPhotoViewAttacher) {
+        this.photoViewAttacher = newPhotoViewAttacher;
+    }
+
+    public boolean onSingleTapConfirmed(MotionEvent e) {
+        if (this.photoViewAttacher == null) {
+            return false;
+        } else {
+            ImageView imageView = this.photoViewAttacher.getImageView();
+            if (null != this.photoViewAttacher.getOnPhotoTapListener()) {
+                RectF displayRect = this.photoViewAttacher.getDisplayRect();
+                if (null != displayRect) {
+                    float x = e.getX();
+                    float y = e.getY();
+                    if (displayRect.contains(x, y)) {
+                        float xResult = (x - displayRect.left) / displayRect.width();
+                        float yResult = (y - displayRect.top) / displayRect.height();
+                        this.photoViewAttacher.getOnPhotoTapListener().onPhotoTap(imageView, xResult, yResult);
+                        return true;
+                    }
+
+                    this.photoViewAttacher.getOnPhotoTapListener().onOutsidePhotoTap();
+                }
+            }
+
+            if (null != this.photoViewAttacher.getOnViewTapListener()) {
+                this.photoViewAttacher.getOnViewTapListener().onViewTap(imageView, e.getX(), e.getY());
+            }
+
+            return false;
+        }
+    }
+
+    public boolean onDoubleTap(MotionEvent ev) {
+        if (this.photoViewAttacher == null) {
+            return false;
+        } else {
+            try {
+                float scale = this.photoViewAttacher.getScale();
+                float x = ev.getX();
+                float y = ev.getY();
+                if (scale < this.photoViewAttacher.getMediumScale()) {
+                    this.photoViewAttacher.setScale(this.photoViewAttacher.getMediumScale(), x, y, true);
+                } else if (scale >= this.photoViewAttacher.getMediumScale() && scale < this.photoViewAttacher.getMaximumScale()) {
+                    this.photoViewAttacher.setScale(this.photoViewAttacher.getMaximumScale(), x, y, true);
+                } else {
+                    this.photoViewAttacher.setScale(this.photoViewAttacher.getMinimumScale(), x, y, true);
+                }
+            } catch (ArrayIndexOutOfBoundsException var5) {
+                ;
+            }
+
+            return true;
+        }
+    }
+
+    public boolean onDoubleTapEvent(MotionEvent e) {
+        return false;
+    }
+}
+

+ 121 - 0
demo/src/main/java/com/sybotan/android/demo/tools/IPhotoView.java

@@ -0,0 +1,121 @@
+package com.sybotan.android.demo.tools;
+
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by Fernflower decompiler)
+//
+
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.view.GestureDetector.OnDoubleTapListener;
+import android.view.View.OnLongClickListener;
+import android.widget.ImageView.ScaleType;
+
+
+public interface IPhotoView {
+    float DEFAULT_MAX_SCALE = 3.0F;
+    float DEFAULT_MID_SCALE = 1.75F;
+    float DEFAULT_MIN_SCALE = 1.0F;
+    int DEFAULT_ZOOM_DURATION = 200;
+
+    boolean canZoom();
+
+    RectF getDisplayRect();
+
+    boolean setDisplayMatrix(Matrix var1);
+
+    Matrix getDisplayMatrix();
+
+    void getDisplayMatrix(Matrix var1);
+
+    /** @deprecated */
+    @Deprecated
+    float getMinScale();
+
+    float getMinimumScale();
+
+    /** @deprecated */
+    @Deprecated
+    float getMidScale();
+
+    float getMediumScale();
+
+    /** @deprecated */
+    @Deprecated
+    float getMaxScale();
+
+    float getMaximumScale();
+
+    float getScale();
+
+    ScaleType getScaleType();
+
+    void setAllowParentInterceptOnEdge(boolean var1);
+
+    /** @deprecated */
+    @Deprecated
+    void setMinScale(float var1);
+
+    void setMinimumScale(float var1);
+
+    /** @deprecated */
+    @Deprecated
+    void setMidScale(float var1);
+
+    void setMediumScale(float var1);
+
+    /** @deprecated */
+    @Deprecated
+    void setMaxScale(float var1);
+
+    void setMaximumScale(float var1);
+
+    void setScaleLevels(float var1, float var2, float var3);
+
+    void setOnLongClickListener(OnLongClickListener var1);
+
+    void setOnMatrixChangeListener(PhotoViewAttacher.OnMatrixChangedListener var1);
+
+    void setOnPhotoTapListener(PhotoViewAttacher.OnPhotoTapListener var1);
+
+    /** @deprecated */
+    @Deprecated
+    PhotoViewAttacher.OnPhotoTapListener getOnPhotoTapListener();
+
+    void setOnViewTapListener(PhotoViewAttacher.OnViewTapListener var1);
+
+    void setRotationTo(float var1);
+
+    void setRotationBy(float var1);
+
+    /** @deprecated */
+    @Deprecated
+    PhotoViewAttacher.OnViewTapListener getOnViewTapListener();
+
+    void setScale(float var1);
+
+    void setScale(float var1, boolean var2);
+
+    void setScale(float var1, float var2, float var3, boolean var4);
+
+    void setScaleType(ScaleType var1);
+
+    void setZoomable(boolean var1);
+
+    /** @deprecated */
+    void setPhotoViewRotation(float var1);
+
+    Bitmap getVisibleRectangleBitmap();
+
+    void setZoomTransitionDuration(int var1);
+
+    IPhotoView getIPhotoViewImplementation();
+
+    void setOnDoubleTapListener(OnDoubleTapListener var1);
+
+    void setOnScaleChangeListener(PhotoViewAttacher.OnScaleChangeListener var1);
+
+    void setOnSingleFlingListener(PhotoViewAttacher.OnSingleFlingListener var1);
+}
+

+ 97 - 0
demo/src/main/java/com/sybotan/android/demo/tools/LocalDataOperation.java

@@ -0,0 +1,97 @@
+package com.sybotan.android.demo.tools;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import com.sybotan.android.demo.activities.BaseActivity;
+import com.sybotan.android.demo.retrofit.BaseViewModelInterface;
+
+import java.io.File;
+import java.util.List;
+
+import io.reactivex.Observable;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.Observer;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Created by wangjie on 2019/10/15
+ * 利用Observable处理耗时操作时的线程切换类
+ * 诸如:本地文件读写、数据库操作等
+ */
+public class LocalDataOperation {
+    //在子线程处理耗时操作
+    public static final String DEAL_IO_DATA = "DEAL_IO_DATA";
+    //可以具体开始子线程的耗时操作前显示操作等待弹窗之类的
+    public static final String SHOW_DIALOG_IN_MAIN = "SHOW_DIALOG_IN_MAIN";
+    //子线程数据处理完毕,回到主线程
+    public static final String DEAL_DATA_NEXT = "DEAL_DATA_NEXT";
+    //子线程数据处理出现异常
+    public static final String DEAL_DATA_ERROR = "DEAL_DATA_ERROR";
+    //承接查验离线自动上传
+    public String ALL_NEED_SUBMIT_CHECK = "ALL_NEED_SUBMIT_CHECK";
+    //离线保存的工单待执行列表数据
+    public String ALL_NEED_SUBMIT_ORDER = "ALL_NEED_SUBMIT_ORDER";
+    //离线数据上传接口类
+    public OfflineSendRequest offlineSendRequest;
+
+
+    private LocalDataOperation() {
+        offlineSendRequest = new OfflineSendRequest();
+    }
+
+    private static class SingletonHolder {
+        private static final LocalDataOperation INSTANCE = new LocalDataOperation();
+    }
+
+    public static LocalDataOperation getInstance() {
+        return SingletonHolder.INSTANCE;
+    }
+
+    /**
+     * 在子线程中处理耗时操作,处理完回到主线程
+     *
+     * @param mActivity
+     * @param object
+     * @param <T>
+     */
+    public <T> void dealLocalData(BaseActivity mActivity, BaseViewModelInterface object) {
+        Observable<T> observable = Observable.create((ObservableOnSubscribe<T>) e -> {
+            object.SendDircetive(DEAL_IO_DATA, e);
+        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
+        observable.compose(mActivity.bindToLifecycle()).subscribe(new Observer<T>() {
+            @Override
+            public void onSubscribe(Disposable d) {
+                object.SendDircetive(SHOW_DIALOG_IN_MAIN, d);
+            }
+
+            @Override
+            public void onNext(T s) {
+                object.SendDircetive(DEAL_DATA_NEXT, s);
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                object.SendDircetive(DEAL_DATA_ERROR, e);
+            }
+
+            @Override
+            public void onComplete() {
+
+            }
+        });
+    }
+
+    /**
+     * 离线操作的图片存储路径
+     *
+     * @param context
+     * @return
+     */
+    public String getOfflineImageFilePath(Context context) {
+        return SDCardHelper.getSDCardBaseDir() + "/Android/data/" + context.getPackageName() + "/offlineImages";
+    }
+
+}

+ 46 - 0
demo/src/main/java/com/sybotan/android/demo/tools/NetUtil.java

@@ -0,0 +1,46 @@
+package com.sybotan.android.demo.tools;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+
+/**
+ * Created by yanfapersagy on 2017/7/23.
+ */
+
+public class NetUtil {
+
+    /**
+     * 没有连接网络
+     */
+    private static final int NETWORK_NONE = -1;
+    /**
+     * 移动网络
+     */
+    private static final int NETWORK_MOBILE = 0;
+    /**
+     * 无线网络
+     */
+    private static final int NETWORK_WIFI = 1;
+
+    public static int getNetWorkState(Context context) {
+        // 得到连接管理器对象
+        ConnectivityManager connectivityManager = (ConnectivityManager) context
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+
+        NetworkInfo activeNetworkInfo = connectivityManager
+                .getActiveNetworkInfo();
+        if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) {
+
+            if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_WIFI)) {
+                return NETWORK_WIFI;
+            } else if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_MOBILE)) {
+                return NETWORK_MOBILE;
+            }
+        } else {
+            return NETWORK_NONE;
+        }
+        return NETWORK_NONE;
+    }
+
+}

+ 69 - 0
demo/src/main/java/com/sybotan/android/demo/tools/OfflineSendRequest.java

@@ -0,0 +1,69 @@
+package com.sybotan.android.demo.tools;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.google.gson.Gson;
+import com.sybotan.android.demo.retrofit.Constant;
+import com.yuyh.library.imgsel.utils.LogUtils;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+
+/**
+ * @author wangjie
+ * @date 2020.06.13
+ * 不基于任何Activity的网络请求发送
+ * 本地离线缓存的各种数据发送至服务器
+ */
+public class OfflineSendRequest {
+    private Gson mGson;
+
+    public OfflineSendRequest() {
+        mGson = new Gson();
+    }
+
+
+    /**
+     * 新的离线图片存储上传
+     *
+     * @param context
+     */
+    public void uploadImageNew(Context context) {
+        String imageFilePath = LocalDataOperation.getInstance().getOfflineImageFilePath(context);
+        File imageFile = new File(imageFilePath);
+        if (imageFile.exists()) {
+            File[] files = imageFile.listFiles();
+            if (files != null && files.length > 0) {
+                for (File file : files) {
+                    if (!TextUtils.isEmpty(file.getName()) && !file.getName().startsWith("uploaded")) {
+                        final String url = Constant.ServerImgIPUp + file.getName();
+                        Log.e("uploadImageNew url", url);
+                        String result = UploadImage.uploadFiles(file, url);
+                        if (!TextUtils.isEmpty(result)) {
+                            String resultString = null;
+                            try {
+                                JSONObject object = new JSONObject(result);
+                                resultString = object.getString("Result");
+                                Log.e("resultString", resultString);
+                            } catch (JSONException e) {
+                                e.printStackTrace();
+                            }
+                            if ("success".equals(resultString)) {
+                                //不管是那种情况,只要成功都需要从本地删除该图片
+                                boolean b = file.renameTo(new File(imageFilePath + File.separator + "uploaded" + file.getName()));
+                                //file.delete();
+                                LogUtils.e("uploadImageNew", "上传成功" + b + file.getName());
+                            } else {
+                                LogUtils.e("uploadImageNew", "上传失败");
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 924 - 0
demo/src/main/java/com/sybotan/android/demo/tools/PhotoViewAttacher.java

@@ -0,0 +1,924 @@
+package com.sybotan.android.demo.tools;
+
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by Fernflower decompiler)
+//
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.graphics.Matrix.ScaleToFit;
+import android.graphics.drawable.Drawable;
+import androidx.core.view.MotionEventCompat;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewParent;
+import android.view.ViewTreeObserver;
+import android.view.GestureDetector.OnDoubleTapListener;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.View.OnLongClickListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+
+import java.lang.ref.WeakReference;
+
+import uk.co.senab.photoview.Compat;
+import uk.co.senab.photoview.gestures.OnGestureListener;
+import uk.co.senab.photoview.gestures.VersionedGestureDetector;
+import uk.co.senab.photoview.log.LogManager;
+import uk.co.senab.photoview.scrollerproxy.ScrollerProxy;
+
+
+public class PhotoViewAttacher implements IPhotoView, OnTouchListener, OnGestureListener, OnGlobalLayoutListener {
+    private static final String LOG_TAG = "PhotoViewAttacher";
+    private static final boolean DEBUG = Log.isLoggable("PhotoViewAttacher", Log.ERROR);
+    static final Interpolator sInterpolator = new AccelerateDecelerateInterpolator();
+    int ZOOM_DURATION;
+    static final int EDGE_NONE = -1;
+    static final int EDGE_LEFT = 0;
+    static final int EDGE_RIGHT = 1;
+    static final int EDGE_BOTH = 2;
+    static int SINGLE_TOUCH = 1;
+    public float mMinScale;
+    public float mMidScale;
+    public float mMaxScale;
+    private boolean mAllowParentInterceptOnEdge;
+    private boolean mBlockParentIntercept;
+    private WeakReference<ImageView> mImageView;
+    private GestureDetector mGestureDetector;
+    private uk.co.senab.photoview.gestures.GestureDetector mScaleDragDetector;
+    private final Matrix mBaseMatrix;
+    private final Matrix mDrawMatrix;
+    private final Matrix mSuppMatrix;
+    private final RectF mDisplayRect;
+    private final float[] mMatrixValues;
+    private OnMatrixChangedListener mMatrixChangeListener;
+    private OnPhotoTapListener mPhotoTapListener;
+    private OnViewTapListener mViewTapListener;
+    private OnLongClickListener mLongClickListener;
+    private OnScaleChangeListener mScaleChangeListener;
+    private OnSingleFlingListener mSingleFlingListener;
+    private int mIvTop;
+    private int mIvRight;
+    private int mIvBottom;
+    private int mIvLeft;
+    private FlingRunnable mCurrentFlingRunnable;
+    private int mScrollEdge;
+    private float mBaseRotation;
+    private boolean mZoomEnabled;
+    private ScaleType mScaleType;
+
+    private static void checkZoomLevels(float minZoom, float midZoom, float maxZoom) {
+        if (minZoom >= midZoom) {
+            throw new IllegalArgumentException("MinZoom has to be less than MidZoom");
+        } else if (midZoom >= maxZoom) {
+            throw new IllegalArgumentException("MidZoom has to be less than MaxZoom");
+        }
+    }
+
+    private static boolean hasDrawable(ImageView imageView) {
+        return null != imageView && null != imageView.getDrawable();
+    }
+
+    private static boolean isSupportedScaleType(ScaleType scaleType) {
+        if (null == scaleType) {
+            return false;
+        } else {
+            switch (scaleType) {
+                case MATRIX:
+                    throw new IllegalArgumentException(scaleType.name() + " is not supported in PhotoView");
+                default:
+                    return true;
+            }
+        }
+    }
+
+    private static void setImageViewScaleTypeMatrix(ImageView imageView) {
+        if (null != imageView && !(imageView instanceof IPhotoView) && !ScaleType.MATRIX.equals(imageView.getScaleType())) {
+            imageView.setScaleType(ScaleType.MATRIX);
+        }
+
+    }
+
+    public PhotoViewAttacher(ImageView imageView) {
+        this(imageView, true);
+    }
+
+    public PhotoViewAttacher(ImageView imageView, boolean zoomable) {
+        this.ZOOM_DURATION = 200;
+        this.mMinScale = 1.0F;
+        this.mMidScale = 5.0F;
+        this.mMaxScale = 50.0F;
+        this.mAllowParentInterceptOnEdge = true;
+        this.mBlockParentIntercept = false;
+        this.mBaseMatrix = new Matrix();
+        this.mDrawMatrix = new Matrix();
+        this.mSuppMatrix = new Matrix();
+        this.mDisplayRect = new RectF();
+        this.mMatrixValues = new float[9];
+        this.mScrollEdge = 2;
+        this.mScaleType = ScaleType.FIT_CENTER;
+        this.mImageView = new WeakReference(imageView);
+        imageView.setDrawingCacheEnabled(true);
+        imageView.setOnTouchListener(this);
+        ViewTreeObserver observer = imageView.getViewTreeObserver();
+        if (null != observer) {
+            observer.addOnGlobalLayoutListener(this);
+        }
+
+        setImageViewScaleTypeMatrix(imageView);
+        if (!imageView.isInEditMode()) {
+            this.mScaleDragDetector = VersionedGestureDetector.newInstance(imageView.getContext(), this);
+            this.mGestureDetector = new GestureDetector(imageView.getContext(), new SimpleOnGestureListener() {
+                public void onLongPress(MotionEvent e) {
+                    if (null != PhotoViewAttacher.this.mLongClickListener) {
+                        PhotoViewAttacher.this.mLongClickListener.onLongClick(PhotoViewAttacher.this.getImageView());
+                    }
+
+                }
+
+                public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+                    if (PhotoViewAttacher.this.mSingleFlingListener != null) {
+                        if (PhotoViewAttacher.this.getScale() > 1.0F) {
+                            return false;
+                        } else {
+                            return MotionEventCompat.getPointerCount(e1) <= PhotoViewAttacher.SINGLE_TOUCH && MotionEventCompat.getPointerCount(e2)
+                                    <= PhotoViewAttacher.SINGLE_TOUCH ? PhotoViewAttacher.this.mSingleFlingListener.onFling(e1, e2, velocityX,
+                                    velocityY) : false;
+                        }
+                    } else {
+                        return false;
+                    }
+                }
+            });
+            this.mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
+            this.mBaseRotation = 0.0F;
+            this.setZoomable(zoomable);
+        }
+    }
+
+    public void setOnDoubleTapListener(OnDoubleTapListener newOnDoubleTapListener) {
+        if (newOnDoubleTapListener != null) {
+            this.mGestureDetector.setOnDoubleTapListener(newOnDoubleTapListener);
+        } else {
+            this.mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
+        }
+
+    }
+
+    public void setOnScaleChangeListener(OnScaleChangeListener onScaleChangeListener) {
+        this.mScaleChangeListener = onScaleChangeListener;
+    }
+
+    public void setOnSingleFlingListener(OnSingleFlingListener onSingleFlingListener) {
+        this.mSingleFlingListener = onSingleFlingListener;
+    }
+
+    public boolean canZoom() {
+        return this.mZoomEnabled;
+    }
+
+    public void cleanup() {
+        if (null != this.mImageView) {
+            ImageView imageView = (ImageView) this.mImageView.get();
+            if (null != imageView) {
+                ViewTreeObserver observer = imageView.getViewTreeObserver();
+                if (null != observer && observer.isAlive()) {
+                    observer.removeGlobalOnLayoutListener(this);
+                }
+
+                imageView.setOnTouchListener((OnTouchListener) null);
+                this.cancelFling();
+            }
+
+            if (null != this.mGestureDetector) {
+                this.mGestureDetector.setOnDoubleTapListener((OnDoubleTapListener) null);
+            }
+
+            this.mMatrixChangeListener = null;
+            this.mPhotoTapListener = null;
+            this.mViewTapListener = null;
+            this.mImageView = null;
+        }
+    }
+
+    public RectF getDisplayRect() {
+        this.checkMatrixBounds();
+        return this.getDisplayRect(this.getDrawMatrix());
+    }
+
+    public boolean setDisplayMatrix(Matrix finalMatrix) {
+        if (finalMatrix == null) {
+            throw new IllegalArgumentException("Matrix cannot be null");
+        } else {
+            ImageView imageView = this.getImageView();
+            if (null == imageView) {
+                return false;
+            } else if (null == imageView.getDrawable()) {
+                return false;
+            } else {
+                this.mSuppMatrix.set(finalMatrix);
+                this.setImageViewMatrix(this.getDrawMatrix());
+                this.checkMatrixBounds();
+                return true;
+            }
+        }
+    }
+
+    public void setBaseRotation(float degrees) {
+        this.mBaseRotation = degrees % 360.0F;
+        this.update();
+        this.setRotationBy(this.mBaseRotation);
+        this.checkAndDisplayMatrix();
+    }
+
+    /**
+     * @deprecated
+     */
+    public void setPhotoViewRotation(float degrees) {
+        this.mSuppMatrix.setRotate(degrees % 360.0F);
+        this.checkAndDisplayMatrix();
+    }
+
+    public void setRotationTo(float degrees) {
+        this.mSuppMatrix.setRotate(degrees % 360.0F);
+        this.checkAndDisplayMatrix();
+    }
+
+    public void setRotationBy(float degrees) {
+        this.mSuppMatrix.postRotate(degrees % 360.0F);
+        this.checkAndDisplayMatrix();
+    }
+
+    public ImageView getImageView() {
+        ImageView imageView = null;
+        if (null != this.mImageView) {
+            imageView = (ImageView) this.mImageView.get();
+        }
+
+        if (null == imageView) {
+            this.cleanup();
+            LogManager.getLogger().i("PhotoViewAttacher", "ImageView no longer exists. You should not use this PhotoViewAttacher any more.");
+        }
+
+        return imageView;
+    }
+
+    /**
+     * @deprecated
+     */
+    @Deprecated
+    public float getMinScale() {
+        return this.getMinimumScale();
+    }
+
+    public float getMinimumScale() {
+        return this.mMinScale;
+    }
+
+    /**
+     * @deprecated
+     */
+    @Deprecated
+    public float getMidScale() {
+        return this.getMediumScale();
+    }
+
+    public float getMediumScale() {
+        return this.mMidScale;
+    }
+
+    /**
+     * @deprecated
+     */
+    @Deprecated
+    public float getMaxScale() {
+        return this.getMaximumScale();
+    }
+
+    public float getMaximumScale() {
+        return this.mMaxScale;
+    }
+
+    public float getScale() {
+        return (float) Math.sqrt((double) ((float) Math.pow((double) this.getValue(this.mSuppMatrix, 0), 2.0D) + (float) Math.pow((double) this
+                .getValue(this.mSuppMatrix, 3), 2.0D)));
+    }
+
+    public ScaleType getScaleType() {
+        return this.mScaleType;
+    }
+
+    public void onDrag(float dx, float dy) {
+        if (!this.mScaleDragDetector.isScaling()) {
+            if (DEBUG) {
+                LogManager.getLogger().d("PhotoViewAttacher", String.format("onDrag: dx: %.2f. dy: %.2f", dx, dy));
+            }
+
+            ImageView imageView = this.getImageView();
+            this.mSuppMatrix.postTranslate(dx, dy);
+            this.checkAndDisplayMatrix();
+            ViewParent parent = imageView.getParent();
+            if (this.mAllowParentInterceptOnEdge && !this.mScaleDragDetector.isScaling() && !this.mBlockParentIntercept) {
+                if ((this.mScrollEdge == 2 || this.mScrollEdge == 0 && dx >= 1.0F || this.mScrollEdge == 1 && dx <= -1.0F) && null != parent) {
+                    parent.requestDisallowInterceptTouchEvent(false);
+                }
+            } else if (null != parent) {
+                parent.requestDisallowInterceptTouchEvent(true);
+            }
+
+        }
+    }
+
+    public void onFling(float startX, float startY, float velocityX, float velocityY) {
+        if (DEBUG) {
+            LogManager.getLogger().d("PhotoViewAttacher", "onFling. sX: " + startX + " sY: " + startY + " Vx: " + velocityX + " Vy: " + velocityY);
+        }
+
+        ImageView imageView = this.getImageView();
+        this.mCurrentFlingRunnable = new FlingRunnable(imageView.getContext());
+        this.mCurrentFlingRunnable.fling(this.getImageViewWidth(imageView), this.getImageViewHeight(imageView), (int) velocityX, (int) velocityY);
+        imageView.post(this.mCurrentFlingRunnable);
+    }
+
+    public void onGlobalLayout() {
+        ImageView imageView = this.getImageView();
+        if (null != imageView) {
+            if (this.mZoomEnabled) {
+                int top = imageView.getTop();
+                int right = imageView.getRight();
+                int bottom = imageView.getBottom();
+                int left = imageView.getLeft();
+                if (top != this.mIvTop || bottom != this.mIvBottom || left != this.mIvLeft || right != this.mIvRight) {
+                    this.updateBaseMatrix(imageView.getDrawable());
+                    this.mIvTop = top;
+                    this.mIvRight = right;
+                    this.mIvBottom = bottom;
+                    this.mIvLeft = left;
+                }
+            } else {
+                this.updateBaseMatrix(imageView.getDrawable());
+            }
+        }
+
+    }
+
+    public void onScale(float scaleFactor, float focusX, float focusY) {
+        if (DEBUG) {
+            LogManager.getLogger().d("PhotoViewAttacher", String.format("onScale: scale: %.2f. fX: %.2f. fY: %.2f", scaleFactor, focusX, focusY));
+        }
+
+        if ((this.getScale() < this.mMaxScale || scaleFactor < 1.0F) && (this.getScale() > this.mMinScale || scaleFactor > 1.0F)) {
+            if (null != this.mScaleChangeListener) {
+                this.mScaleChangeListener.onScaleChange(scaleFactor, focusX, focusY);
+            }
+
+            this.mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
+            this.checkAndDisplayMatrix();
+        }
+
+    }
+
+    @SuppressLint({"ClickableViewAccessibility"})
+    public boolean onTouch(View v, MotionEvent ev) {
+        boolean handled = false;
+        if (this.mZoomEnabled && hasDrawable((ImageView) v)) {
+            ViewParent parent = v.getParent();
+            switch (ev.getAction()) {
+                case 0:
+                    if (null != parent) {
+                        parent.requestDisallowInterceptTouchEvent(true);
+                    } else {
+                        LogManager.getLogger().i("PhotoViewAttacher", "onTouch getParent() returned null");
+                    }
+
+                    this.cancelFling();
+                    break;
+                case 1:
+                case 3:
+                    if (this.getScale() < this.mMinScale) {
+                        RectF rect = this.getDisplayRect();
+                        if (null != rect) {
+                            v.post(new AnimatedZoomRunnable(this.getScale(), this.mMinScale, rect.centerX(), rect.centerY()));
+                            handled = true;
+                        }
+                    }
+                case 2:
+            }
+
+            if (null != this.mScaleDragDetector) {
+                boolean wasScaling = this.mScaleDragDetector.isScaling();
+                boolean wasDragging = this.mScaleDragDetector.isDragging();
+                handled = this.mScaleDragDetector.onTouchEvent(ev);
+                boolean didntScale = !wasScaling && !this.mScaleDragDetector.isScaling();
+                boolean didntDrag = !wasDragging && !this.mScaleDragDetector.isDragging();
+                this.mBlockParentIntercept = didntScale && didntDrag;
+            }
+
+            if (null != this.mGestureDetector && this.mGestureDetector.onTouchEvent(ev)) {
+                handled = true;
+            }
+        }
+
+        return handled;
+    }
+
+    public void setAllowParentInterceptOnEdge(boolean allow) {
+        this.mAllowParentInterceptOnEdge = allow;
+    }
+
+    /**
+     * @deprecated
+     */
+    @Deprecated
+    public void setMinScale(float minScale) {
+        this.setMinimumScale(minScale);
+    }
+
+    public void setMinimumScale(float minimumScale) {
+        checkZoomLevels(minimumScale, this.mMidScale, this.mMaxScale);
+        this.mMinScale = minimumScale;
+    }
+
+    /**
+     * @deprecated
+     */
+    @Deprecated
+    public void setMidScale(float midScale) {
+        this.setMediumScale(midScale);
+    }
+
+    public void setMediumScale(float mediumScale) {
+        checkZoomLevels(this.mMinScale, mediumScale, this.mMaxScale);
+        this.mMidScale = mediumScale;
+    }
+
+    /**
+     * @deprecated
+     */
+    @Deprecated
+    public void setMaxScale(float maxScale) {
+        this.setMaximumScale(maxScale);
+    }
+
+    public void setMaximumScale(float maximumScale) {
+        checkZoomLevels(this.mMinScale, this.mMidScale, maximumScale);
+        this.mMaxScale = maximumScale;
+    }
+
+    public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {
+        checkZoomLevels(minimumScale, mediumScale, maximumScale);
+        this.mMinScale = minimumScale;
+        this.mMidScale = mediumScale;
+        this.mMaxScale = maximumScale;
+    }
+
+    public void setOnLongClickListener(OnLongClickListener listener) {
+        this.mLongClickListener = listener;
+    }
+
+    public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
+        this.mMatrixChangeListener = listener;
+    }
+
+    public void setOnPhotoTapListener(OnPhotoTapListener listener) {
+        this.mPhotoTapListener = listener;
+    }
+
+    /**
+     * @deprecated
+     */
+    @Deprecated
+    public OnPhotoTapListener getOnPhotoTapListener() {
+        return this.mPhotoTapListener;
+    }
+
+    public void setOnViewTapListener(OnViewTapListener listener) {
+        this.mViewTapListener = listener;
+    }
+
+    /**
+     * @deprecated
+     */
+    @Deprecated
+    public OnViewTapListener getOnViewTapListener() {
+        return this.mViewTapListener;
+    }
+
+    public void setScale(float scale) {
+        this.setScale(scale, false);
+    }
+
+    public void setScale(float scale, boolean animate) {
+        ImageView imageView = this.getImageView();
+        if (null != imageView) {
+            this.setScale(scale, (float) (imageView.getRight() / 2), (float) (imageView.getBottom() / 2), animate);
+        }
+
+    }
+
+    public void setScale(float scale, float focalX, float focalY, boolean animate) {
+        ImageView imageView = this.getImageView();
+        if (null != imageView) {
+            if (scale < this.mMinScale || scale > this.mMaxScale) {
+                LogManager.getLogger().i("PhotoViewAttacher", "Scale must be within the range of minScale and maxScale");
+                return;
+            }
+
+            if (animate) {
+                imageView.post(new AnimatedZoomRunnable(this.getScale(), scale, focalX, focalY));
+            } else {
+                this.mSuppMatrix.setScale(scale, scale, focalX, focalY);
+                this.checkAndDisplayMatrix();
+            }
+        }
+
+    }
+
+    public void setScaleType(ScaleType scaleType) {
+        if (isSupportedScaleType(scaleType) && scaleType != this.mScaleType) {
+            this.mScaleType = scaleType;
+            this.update();
+        }
+
+    }
+
+    public void setZoomable(boolean zoomable) {
+        this.mZoomEnabled = zoomable;
+        this.update();
+    }
+
+    public void update() {
+        ImageView imageView = this.getImageView();
+        if (null != imageView) {
+            if (this.mZoomEnabled) {
+                setImageViewScaleTypeMatrix(imageView);
+                this.updateBaseMatrix(imageView.getDrawable());
+            } else {
+                this.resetMatrix();
+            }
+        }
+
+    }
+
+    public Matrix getDisplayMatrix() {
+        return new Matrix(this.getDrawMatrix());
+    }
+
+    public void getDisplayMatrix(Matrix matrix) {
+        matrix.set(this.getDrawMatrix());
+    }
+
+    public Matrix getDrawMatrix() {
+        this.mDrawMatrix.set(this.mBaseMatrix);
+        this.mDrawMatrix.postConcat(this.mSuppMatrix);
+        return this.mDrawMatrix;
+    }
+
+    private void cancelFling() {
+        if (null != this.mCurrentFlingRunnable) {
+            this.mCurrentFlingRunnable.cancelFling();
+            this.mCurrentFlingRunnable = null;
+        }
+
+    }
+
+    private void checkAndDisplayMatrix() {
+        if (this.checkMatrixBounds()) {
+            this.setImageViewMatrix(this.getDrawMatrix());
+        }
+
+    }
+
+    private void checkImageViewScaleType() {
+        ImageView imageView = this.getImageView();
+        if (null != imageView && !(imageView instanceof IPhotoView) && !ScaleType.MATRIX.equals(imageView.getScaleType())) {
+            throw new IllegalStateException("The ImageView's ScaleType has been changed since attaching a PhotoViewAttacher");
+        }
+    }
+
+    private boolean checkMatrixBounds() {
+        ImageView imageView = this.getImageView();
+        if (null == imageView) {
+            return false;
+        } else {
+            RectF rect = this.getDisplayRect(this.getDrawMatrix());
+            if (null == rect) {
+                return false;
+            } else {
+                float height = rect.height();
+                float width = rect.width();
+                float deltaX = 0.0F;
+                float deltaY = 0.0F;
+                int viewHeight = this.getImageViewHeight(imageView);
+                if (height <= (float) viewHeight) {
+                    switch (this.mScaleType) {
+                        case FIT_START:
+                            deltaY = -rect.top;
+                            break;
+                        case FIT_END:
+                            deltaY = (float) viewHeight - height - rect.top;
+                            break;
+                        default:
+                            deltaY = ((float) viewHeight - height) / 2.0F - rect.top;
+                    }
+                } else if (rect.top > 0.0F) {
+                    deltaY = -rect.top;
+                } else if (rect.bottom < (float) viewHeight) {
+                    deltaY = (float) viewHeight - rect.bottom;
+                }
+
+                int viewWidth = this.getImageViewWidth(imageView);
+                if (width <= (float) viewWidth) {
+                    switch (this.mScaleType) {
+                        case FIT_START:
+                            deltaX = -rect.left;
+                            break;
+                        case FIT_END:
+                            deltaX = (float) viewWidth - width - rect.left;
+                            break;
+                        default:
+                            deltaX = ((float) viewWidth - width) / 2.0F - rect.left;
+                    }
+
+                    this.mScrollEdge = 2;
+                } else if (rect.left > 0.0F) {
+                    this.mScrollEdge = 0;
+                    deltaX = -rect.left;
+                } else if (rect.right < (float) viewWidth) {
+                    deltaX = (float) viewWidth - rect.right;
+                    this.mScrollEdge = 1;
+                } else {
+                    this.mScrollEdge = -1;
+                }
+
+                this.mSuppMatrix.postTranslate(deltaX, deltaY);
+                return true;
+            }
+        }
+    }
+
+    private RectF getDisplayRect(Matrix matrix) {
+        ImageView imageView = this.getImageView();
+        if (null != imageView) {
+            Drawable d = imageView.getDrawable();
+            if (null != d) {
+                this.mDisplayRect.set(0.0F, 0.0F, (float) d.getIntrinsicWidth(), (float) d.getIntrinsicHeight());
+                matrix.mapRect(this.mDisplayRect);
+                return this.mDisplayRect;
+            }
+        }
+
+        return null;
+    }
+
+    public Bitmap getVisibleRectangleBitmap() {
+        ImageView imageView = this.getImageView();
+        return imageView == null ? null : imageView.getDrawingCache();
+    }
+
+    public void setZoomTransitionDuration(int milliseconds) {
+        if (milliseconds < 0) {
+            milliseconds = 200;
+        }
+
+        this.ZOOM_DURATION = milliseconds;
+    }
+
+    public IPhotoView getIPhotoViewImplementation() {
+        return this;
+    }
+
+    private float getValue(Matrix matrix, int whichValue) {
+        matrix.getValues(this.mMatrixValues);
+        return this.mMatrixValues[whichValue];
+    }
+
+    private void resetMatrix() {
+        this.mSuppMatrix.reset();
+        this.setRotationBy(this.mBaseRotation);
+        this.setImageViewMatrix(this.getDrawMatrix());
+        this.checkMatrixBounds();
+    }
+
+    private void setImageViewMatrix(Matrix matrix) {
+        ImageView imageView = this.getImageView();
+        if (null != imageView) {
+            this.checkImageViewScaleType();
+            imageView.setImageMatrix(matrix);
+            if (null != this.mMatrixChangeListener) {
+                RectF displayRect = this.getDisplayRect(matrix);
+                if (null != displayRect) {
+                    this.mMatrixChangeListener.onMatrixChanged(displayRect);
+                }
+            }
+        }
+
+    }
+
+    private void updateBaseMatrix(Drawable d) {
+        ImageView imageView = this.getImageView();
+        if (null != imageView && null != d) {
+            float viewWidth = (float) this.getImageViewWidth(imageView);
+            float viewHeight = (float) this.getImageViewHeight(imageView);
+            int drawableWidth = d.getIntrinsicWidth();
+            int drawableHeight = d.getIntrinsicHeight();
+            this.mBaseMatrix.reset();
+            float widthScale = viewWidth / (float) drawableWidth;
+            float heightScale = viewHeight / (float) drawableHeight;
+            if (this.mScaleType == ScaleType.CENTER) {
+                this.mBaseMatrix.postTranslate((viewWidth - (float) drawableWidth) / 2.0F, (viewHeight - (float) drawableHeight) / 2.0F);
+            } else {
+                float scale;
+                if (this.mScaleType == ScaleType.CENTER_CROP) {
+                    scale = Math.max(widthScale, heightScale);
+                    this.mBaseMatrix.postScale(scale, scale);
+                    this.mBaseMatrix.postTranslate((viewWidth - (float) drawableWidth * scale) / 2.0F, (viewHeight - (float) drawableHeight *
+                            scale) / 2.0F);
+                } else if (this.mScaleType == ScaleType.CENTER_INSIDE) {
+                    scale = Math.min(1.0F, Math.min(widthScale, heightScale));
+                    this.mBaseMatrix.postScale(scale, scale);
+                    this.mBaseMatrix.postTranslate((viewWidth - (float) drawableWidth * scale) / 2.0F, (viewHeight - (float) drawableHeight *
+                            scale) / 2.0F);
+                } else {
+                    RectF mTempSrc = new RectF(0.0F, 0.0F, (float) drawableWidth, (float) drawableHeight);
+                    RectF mTempDst = new RectF(0.0F, 0.0F, viewWidth, viewHeight);
+                    if ((int) this.mBaseRotation % 180 != 0) {
+                        mTempSrc = new RectF(0.0F, 0.0F, (float) drawableHeight, (float) drawableWidth);
+                    }
+
+                    switch (this.mScaleType) {
+                        case FIT_START:
+                            this.mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START);
+                            break;
+                        case FIT_END:
+                            this.mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END);
+                            break;
+                        case FIT_CENTER:
+                            this.mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER);
+                            break;
+                        case FIT_XY:
+                            this.mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL);
+                    }
+                }
+            }
+
+            this.resetMatrix();
+        }
+    }
+
+    private int getImageViewWidth(ImageView imageView) {
+        return null == imageView ? 0 : imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight();
+    }
+
+    private int getImageViewHeight(ImageView imageView) {
+        return null == imageView ? 0 : imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom();
+    }
+
+    private class FlingRunnable implements Runnable {
+        private final ScrollerProxy mScroller;
+        private int mCurrentX;
+        private int mCurrentY;
+
+        public FlingRunnable(Context context) {
+            this.mScroller = ScrollerProxy.getScroller(context);
+        }
+
+        public void cancelFling() {
+            if (PhotoViewAttacher.DEBUG) {
+                LogManager.getLogger().d("PhotoViewAttacher", "Cancel Fling");
+            }
+
+            this.mScroller.forceFinished(true);
+        }
+
+        public void fling(int viewWidth, int viewHeight, int velocityX, int velocityY) {
+            RectF rect = PhotoViewAttacher.this.getDisplayRect();
+            if (null != rect) {
+                int startX = Math.round(-rect.left);
+                int minX;
+                int maxX;
+                if ((float) viewWidth < rect.width()) {
+                    minX = 0;
+                    maxX = Math.round(rect.width() - (float) viewWidth);
+                } else {
+                    maxX = startX;
+                    minX = startX;
+                }
+
+                int startY = Math.round(-rect.top);
+                int minY;
+                int maxY;
+                if ((float) viewHeight < rect.height()) {
+                    minY = 0;
+                    maxY = Math.round(rect.height() - (float) viewHeight);
+                } else {
+                    maxY = startY;
+                    minY = startY;
+                }
+
+                this.mCurrentX = startX;
+                this.mCurrentY = startY;
+                if (PhotoViewAttacher.DEBUG) {
+                    LogManager.getLogger().d("PhotoViewAttacher", "fling. StartX:" + startX + " StartY:" + startY + " MaxX:" + maxX + " MaxY:" +
+                            maxY);
+                }
+
+                if (startX != maxX || startY != maxY) {
+                    this.mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
+                }
+
+            }
+        }
+
+        public void run() {
+            if (!this.mScroller.isFinished()) {
+                ImageView imageView = PhotoViewAttacher.this.getImageView();
+                if (null != imageView && this.mScroller.computeScrollOffset()) {
+                    int newX = this.mScroller.getCurrX();
+                    int newY = this.mScroller.getCurrY();
+                    if (PhotoViewAttacher.DEBUG) {
+                        LogManager.getLogger().d("PhotoViewAttacher", "fling run(). CurrentX:" + this.mCurrentX + " CurrentY:" + this.mCurrentY + "" +
+                                " NewX:" + newX + " NewY:" + newY);
+                    }
+
+                    PhotoViewAttacher.this.mSuppMatrix.postTranslate((float) (this.mCurrentX - newX), (float) (this.mCurrentY - newY));
+                    PhotoViewAttacher.this.setImageViewMatrix(PhotoViewAttacher.this.getDrawMatrix());
+                    this.mCurrentX = newX;
+                    this.mCurrentY = newY;
+                    Compat.postOnAnimation(imageView, this);
+                }
+
+            }
+        }
+    }
+
+    private class AnimatedZoomRunnable implements Runnable {
+        private final float mFocalX;
+        private final float mFocalY;
+        private final long mStartTime;
+        private final float mZoomStart;
+        private final float mZoomEnd;
+
+        public AnimatedZoomRunnable(float currentZoom, float targetZoom, float focalX, float focalY) {
+            this.mFocalX = focalX;
+            this.mFocalY = focalY;
+            this.mStartTime = System.currentTimeMillis();
+            this.mZoomStart = currentZoom;
+            this.mZoomEnd = targetZoom;
+        }
+
+        public void run() {
+            ImageView imageView = PhotoViewAttacher.this.getImageView();
+            if (imageView != null) {
+                float t = this.interpolate();
+                float scale = this.mZoomStart + t * (this.mZoomEnd - this.mZoomStart);
+                float deltaScale = scale / PhotoViewAttacher.this.getScale();
+                PhotoViewAttacher.this.onScale(deltaScale, this.mFocalX, this.mFocalY);
+                if (t < 1.0F) {
+                    Compat.postOnAnimation(imageView, this);
+                }
+
+            }
+        }
+
+        private float interpolate() {
+            float t = 1.0F * (float) (System.currentTimeMillis() - this.mStartTime) / (float) PhotoViewAttacher.this.ZOOM_DURATION;
+            t = Math.min(1.0F, t);
+            t = PhotoViewAttacher.sInterpolator.getInterpolation(t);
+            return t;
+        }
+    }
+
+    public interface OnSingleFlingListener {
+        boolean onFling(MotionEvent var1, MotionEvent var2, float var3, float var4);
+    }
+
+    public interface OnViewTapListener {
+        void onViewTap(View var1, float var2, float var3);
+    }
+
+    public interface OnPhotoTapListener {
+        void onPhotoTap(View var1, float var2, float var3);
+
+        void onOutsidePhotoTap();
+    }
+
+    public interface OnScaleChangeListener {
+        void onScaleChange(float var1, float var2, float var3);
+    }
+
+    public interface OnMatrixChangedListener {
+        void onMatrixChanged(RectF var1);
+    }
+}
+

+ 115 - 0
demo/src/main/java/com/sybotan/android/demo/tools/PictureUtils.java

@@ -0,0 +1,115 @@
+package com.sybotan.android.demo.tools;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
+import android.media.ExifInterface;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Created by mengxy on 2017/10/26.
+ */
+
+public class PictureUtils {
+
+    public static String compressImage(String filePath, String targetPath, int quality) {
+        Bitmap bm = getSmallBitmap(filePath);//获取一定尺寸的图片
+        int degree = readPictureDegree(filePath);//获取相片拍摄角度
+        if (degree != 0) {//旋转照片角度,防止头像横着显示
+            bm = rotateBitmap(bm, degree);
+        }
+        File outputFile = new File(targetPath);
+        try {
+            if (!outputFile.exists()) {
+                outputFile.getParentFile().mkdirs();
+                //outputFile.createNewFile();
+            } else {
+                outputFile.delete();
+            }
+            FileOutputStream out = new FileOutputStream(outputFile);
+            bm.compress(Bitmap.CompressFormat.PNG, quality, out);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return outputFile.getPath();
+    }
+
+    /**
+     * 根据路径获得图片信息并按比例压缩,返回bitmap
+     */
+    public static Bitmap getSmallBitmap(String filePath) {
+        final BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inJustDecodeBounds = true;//只解析图片边沿,获取宽高
+        BitmapFactory.decodeFile(filePath, options);
+        // 计算缩放比
+        options.inSampleSize = calculateInSampleSize(options, 480, 800);
+        // 完整解析图片返回bitmap
+        options.inJustDecodeBounds = false;
+        return BitmapFactory.decodeFile(filePath, options);
+    }
+
+
+    /**
+     * 获取照片角度
+     *
+     * @param path
+     * @return
+     */
+    public static int readPictureDegree(String path) {
+        int degree = 0;
+        try {
+            ExifInterface exifInterface = new ExifInterface(path);
+            int orientation = exifInterface.getAttributeInt(
+                    ExifInterface.TAG_ORIENTATION,
+                    ExifInterface.ORIENTATION_NORMAL);
+            switch (orientation) {
+                case ExifInterface.ORIENTATION_ROTATE_90:
+                    degree = 90;
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_180:
+                    degree = 180;
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_270:
+                    degree = 270;
+                    break;
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return degree;
+    }
+
+    /**
+     * 旋转照片
+     *
+     * @param bitmap
+     * @param degress
+     * @return
+     */
+    public static Bitmap rotateBitmap(Bitmap bitmap, int degress) {
+        if (bitmap != null) {
+            Matrix m = new Matrix();
+            m.postRotate(degress);
+            bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
+                    bitmap.getHeight(), m, true);
+            return bitmap;
+        }
+        return bitmap;
+    }
+
+    public static int calculateInSampleSize(BitmapFactory.Options options,
+                                            int reqWidth, int reqHeight) {
+        final int height = options.outHeight;
+        final int width = options.outWidth;
+        int inSampleSize = 1;
+        if (height > reqHeight || width > reqWidth) {
+            final int heightRatio = Math.round((float) height / (float) reqHeight);
+            final int widthRatio = Math.round((float) width / (float) reqWidth);
+            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
+        }
+        return inSampleSize;
+    }
+}

+ 393 - 0
demo/src/main/java/com/sybotan/android/demo/tools/SDCardHelper.java

@@ -0,0 +1,393 @@
+package com.sybotan.android.demo.tools;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Environment;
+import android.os.StatFs;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+public class SDCardHelper {
+
+    // 判断SD卡是否被挂载
+    public static boolean isSDCardMounted() {
+        // return Environment.getExternalStorageState().equals("mounted");
+        return Environment.getExternalStorageState().equals(
+                Environment.MEDIA_MOUNTED);
+    }
+
+    // 获取SD卡的根目录
+    public static String getSDCardBaseDir() {
+        if (isSDCardMounted()) {
+            return Environment.getExternalStorageDirectory().getAbsolutePath();
+        }
+        return "";
+    }
+
+    // 获取SD卡的完整空间大小,返回MB
+    public static long getSDCardSize() {
+        if (isSDCardMounted()) {
+            StatFs fs = new StatFs(getSDCardBaseDir());
+            int count = fs.getBlockCount();
+            int size = fs.getBlockSize();
+            return count * size / 1024 / 1024;
+        }
+        return 0;
+    }
+
+    // 获取SD卡的剩余空间大小
+    public static long getSDCardFreeSize() {
+        if (isSDCardMounted()) {
+            StatFs fs = new StatFs(getSDCardBaseDir());
+            int count = fs.getFreeBlocks();
+            int size = fs.getBlockSize();
+            return count * size / 1024 / 1024;
+        }
+        return 0;
+    }
+
+    // 获取SD卡的可用空间大小
+    public static long getSDCardAvailableSize() {
+        if (isSDCardMounted()) {
+            StatFs fs = new StatFs(getSDCardBaseDir());
+            int count = fs.getAvailableBlocks();
+            int size = fs.getBlockSize();
+            return count * size / 1024 / 1024;
+        }
+        return 0;
+    }
+
+    // 往SD卡的公有目录下保存文件
+    public static boolean saveFileToSDCardPublicDir(byte[] data, String type,
+                                                    String fileName) {
+        BufferedOutputStream bos = null;
+        if (isSDCardMounted()) {
+            File file = Environment.getExternalStoragePublicDirectory(type);
+            try {
+                bos = new BufferedOutputStream(new FileOutputStream(new File(
+                        file, fileName)));
+                bos.write(data);
+                bos.flush();
+                return true;
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                try {
+                    bos.close();
+                } catch (IOException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+            }
+        }
+        return false;
+    }
+
+    // 往SD卡的自定义目录下保存文件
+    public static boolean saveFileToSDCardCustomDir(byte[] data, String dir, String fileName) {
+        BufferedOutputStream bos = null;
+        if (isSDCardMounted()) {
+            File file = new File(getSDCardBaseDir() + File.separator + dir);
+            if (!file.exists()) {
+                file.mkdirs();// 递归创建自定义目录
+            }
+            try {
+                bos = new BufferedOutputStream(new FileOutputStream(new File(file, fileName)));
+                bos.write(data);
+                bos.flush();
+                return true;
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                try {
+                    if (null != bos) {
+                        bos.close();
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return false;
+    }
+
+    public static boolean saveBitmap(String dirPath, String fileName, Bitmap bitmap) {
+        if (isSDCardMounted()) {
+            File file = new File(getSDCardBaseDir() + File.separator + dirPath);
+            if (!file.exists()) {
+                file.mkdirs();
+            }
+            File destFile = new File(file,fileName);
+            FileOutputStream out = null;
+            try {
+                out = new FileOutputStream(destFile);
+                bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+                out.flush();
+                return true;
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                try {
+                    if (out != null) {
+                        out.close();
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return false;
+    }
+
+    public static boolean saveFileToSDCardCustomDir(InputStream is, String fileDir, String fileName, long startsPoint, long contentLength) {
+        FileChannel channelOut = null;
+        // 随机访问文件,可以指定断点续传的起始位置
+        RandomAccessFile randomAccessFile = null;
+        if (isSDCardMounted()) {
+            File file = new File(getSDCardBaseDir() + File.separator + fileDir);
+            if (!file.exists()) {
+                file.mkdirs();// 递归创建自定义目录
+            }
+            File destination = new File(file, fileName);
+            try {
+                randomAccessFile = new RandomAccessFile(destination, "rwd");
+                //Chanel NIO中的用法,由于RandomAccessFile没有使用缓存策略,直接使用会使得下载速度变慢,亲测缓存下载3.3秒的文件,用普通的RandomAccessFile需要20多秒。
+                channelOut = randomAccessFile.getChannel();
+                // 内存映射,直接使用RandomAccessFile,是用其seek方法指定下载的起始位置,使用缓存下载,在这里指定下载位置。
+                MappedByteBuffer mappedBuffer = channelOut.map(FileChannel.MapMode.READ_WRITE, startsPoint, contentLength);
+                byte[] buffer = new byte[1024];
+                int len;
+                while ((len = is.read(buffer)) != -1) {
+                    mappedBuffer.put(buffer, 0, len);
+                }
+                return true;
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                try {
+                    is.close();
+                    if (channelOut != null) {
+                        channelOut.close();
+                    }
+                    if (randomAccessFile != null) {
+                        randomAccessFile.close();
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return false;
+    }
+
+    // 往SD卡的私有Files目录下保存文件
+    public static boolean saveFileToSDCardPrivateFilesDir(byte[] data,
+                                                          String type, String fileName, Context context) {
+        BufferedOutputStream bos = null;
+        if (isSDCardMounted()) {
+            File file = context.getExternalFilesDir(type);
+            try {
+                bos = new BufferedOutputStream(new FileOutputStream(new File(
+                        file, fileName)));
+                bos.write(data);
+                bos.flush();
+                return true;
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                try {
+                    bos.close();
+                } catch (IOException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+            }
+        }
+        return false;
+    }
+
+    // 往SD卡的私有Cache目录下保存文件
+    public static boolean saveFileToSDCardPrivateCacheDir(byte[] data,
+                                                          String fileName, Context context) {
+        BufferedOutputStream bos = null;
+        if (isSDCardMounted()) {
+            File file = context.getExternalCacheDir();
+            try {
+                bos = new BufferedOutputStream(new FileOutputStream(new File(
+                        file, fileName)));
+                bos.write(data);
+                bos.flush();
+                return true;
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                try {
+                    bos.close();
+                } catch (IOException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+            }
+        }
+        return false;
+    }
+
+    // 保存bitmap图片到SDCard的私有Cache目录
+    public static boolean saveFileToSDCardPrivateCacheDir(Bitmap bitmap,
+                                                          String fileName, Context context) {
+        if (isSDCardMounted()) {
+            BufferedOutputStream bos = null;
+            // 获取私有的Cache缓存目录
+            File file = context.getExternalCacheDir();
+            try {
+                bos = new BufferedOutputStream(new FileOutputStream(new File(
+                        file, fileName)));
+                if (fileName != null
+                        && (fileName.contains(".png") || fileName
+                        .contains(".PNG"))) {
+                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
+                } else {
+                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
+                }
+                bos.flush();
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                if (bos != null) {
+                    try {
+                        bos.close();
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    // 从SD卡获取文件
+    public static byte[] loadFileFromSDCard(String fileDir) {
+        BufferedInputStream bis = null;
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+        try {
+            bis = new BufferedInputStream(
+                    new FileInputStream(new File(fileDir)));
+            byte[] buffer = new byte[8 * 1024];
+            int c = 0;
+            while ((c = bis.read(buffer)) != -1) {
+                baos.write(buffer, 0, c);
+                baos.flush();
+            }
+            return baos.toByteArray();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                baos.close();
+                bis.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    // 从SDCard中寻找指定目录下的文件,返回Bitmap
+    public Bitmap loadBitmapFromSDCard(String filePath) {
+        byte[] data = loadFileFromSDCard(filePath);
+        if (data != null) {
+            Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length);
+            if (bm != null) {
+                return bm;
+            }
+        }
+        return null;
+    }
+
+    // 获取SD卡公有目录的路径
+    public static String getSDCardPublicDir(String type) {
+        return Environment.getExternalStoragePublicDirectory(type).toString();
+    }
+
+    // 获取SD卡私有Cache目录的路径
+    public static String getSDCardPrivateCacheDir(Context context) {
+        return context.getExternalCacheDir().getAbsolutePath();
+    }
+
+    // 获取SD卡私有Files目录的路径
+    public static String getSDCardPrivateFilesDir(Context context, String type) {
+        return context.getExternalFilesDir(type).getAbsolutePath();
+    }
+
+    public static boolean isFileExist(String filePath) {
+        File file = new File(filePath);
+        return file.isFile();
+    }
+
+    // 从sdcard中删除文件
+    public static boolean removeFileFromSDCard(String filePath) {
+        File file = new File(filePath);
+        if (file.exists()) {
+            try {
+                file.delete();
+                return true;
+            } catch (Exception e) {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    public static File getFile(String dir, String fileName) {
+        if (isSDCardMounted()) {
+            File file = new File(getSDCardBaseDir() + File.separator + dir, fileName);
+            return file;
+        }
+        return null;
+    }
+
+    /**
+     * 文件转byte
+     * @param file
+     * @return
+     */
+    public static byte[] readFileToByte(File file) {
+        if (!file.exists()) {
+            return null;
+        }
+        RandomAccessFile rf = null;
+        byte[] data;
+        try {
+            rf = new RandomAccessFile(file, "r");
+            data = new byte[(int) rf.length()];
+            rf.readFully(data);
+        } catch (Exception exception) {
+            exception.printStackTrace();
+            return null;
+        } finally {
+            try {
+                if (rf != null) {
+                    rf.close();
+                }
+            } catch (Exception exception) {
+                exception.printStackTrace();
+            }
+        }
+        return data;
+    }
+}

+ 107 - 0
demo/src/main/java/com/sybotan/android/demo/tools/ScreenUtils.java

@@ -0,0 +1,107 @@
+package com.sybotan.android.demo.tools;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Build;
+import android.view.ViewConfiguration;
+
+import java.lang.reflect.Method;
+
+/**
+ * Created by popfisher on 2016/8/19.
+ */
+
+public class ScreenUtils {
+    /**
+     * 获取屏幕高度(px)
+     */
+    public static int getScreenHeight(Context context) {
+        return context.getResources().getDisplayMetrics().heightPixels;
+    }
+    /**
+     * 获取屏幕宽度(px)
+     */
+    public static int getScreenWidth(Context context) {
+        return context.getResources().getDisplayMetrics().widthPixels;
+    }
+    /**
+     * 获得状态栏的高度
+     *
+     * @param context
+     * @return
+     */
+    public static int getStatusHeight(Context context) {
+        int statusHeight = -1;
+        try {
+            Class<?> clazz = Class.forName("com.android.internal.R$dimen");
+            Object object = clazz.newInstance();
+            int height = Integer.parseInt(clazz.getField("status_bar_height").get(object).toString());
+            statusHeight = context.getResources().getDimensionPixelSize(height);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return statusHeight;
+    }
+    /**
+     * Desc: 获取虚拟按键高度
+     * Created by mengxy
+     * Mail: mengxiangyong@persagy.com
+     * 2017/8/31 16:36
+    */
+    public static int getNavigationBarHeight(Context context) {
+        int result = 0;
+        if (hasNavBar(context)) {
+            Resources res = context.getResources();
+            int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android");
+            if (resourceId > 0) {
+                result = res.getDimensionPixelSize(resourceId);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 检查是否存在虚拟按键栏
+     *
+     * @param context
+     * @return
+     */
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+    public static boolean hasNavBar(Context context) {
+        Resources res = context.getResources();
+        int resourceId = res.getIdentifier("config_showNavigationBar", "bool", "android");
+        if (resourceId != 0) {
+            boolean hasNav = res.getBoolean(resourceId);
+            // check override flag
+            String sNavBarOverride = getNavBarOverride();
+            if ("1".equals(sNavBarOverride)) {
+                hasNav = false;
+            } else if ("0".equals(sNavBarOverride)) {
+                hasNav = true;
+            }
+            return hasNav;
+        } else { // fallback
+            return !ViewConfiguration.get(context).hasPermanentMenuKey();
+        }
+    }
+
+    /**
+     * 判断虚拟按键栏是否重写
+     *
+     * @return
+     */
+    private static String getNavBarOverride() {
+        String sNavBarOverride = null;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            try {
+                Class c = Class.forName("android.os.SystemProperties");
+                Method m = c.getDeclaredMethod("get", String.class);
+                m.setAccessible(true);
+                sNavBarOverride = (String) m.invoke(null, "qemu.hw.mainkeys");
+            } catch (Throwable e) {
+            }
+        }
+        return sNavBarOverride;
+    }
+}

+ 153 - 0
demo/src/main/java/com/sybotan/android/demo/tools/ToastUtils.java

@@ -0,0 +1,153 @@
+package com.sybotan.android.demo.tools;
+
+import android.app.Activity;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.sybotan.android.demo.DemoApp;
+import com.sybotan.android.demo.R;
+
+/**
+ * Toast工具类
+ * Created by mengxy on 2017/8/17.
+ */
+
+public class ToastUtils {
+    private static Toast myToast;
+
+    /**
+     * 显示自定义的Toast
+     * showToast
+     *
+     * @Title: showToast
+     * @Description: TODO
+     */
+    public static void showMyToast(String msg) {
+        if (!isFastDoubleToast()) {
+//        MyErrorToast toast = new MyErrorToast(context);
+//        toast.showError(msg);
+            View toast_view = LayoutInflater.from(DemoApp.Companion.getContext()).inflate(R.layout.layout_toast, null);
+            TextView tv_load = (TextView) toast_view.findViewById(R.id.tv_tips);
+            tv_load.setText(msg);
+            //避免Toast连续显示
+            if (myToast == null) {
+//            myToast = new Toast(MyApplication.getContext());
+                myToast = Toast.makeText(DemoApp.Companion.getContext(), "", Toast.LENGTH_LONG);
+            } else {
+                //设置toast显示时间
+                myToast.setDuration(Toast.LENGTH_LONG);
+            }
+            //设置toast显示位置
+            myToast.setGravity(Gravity.CENTER, 0, 0);
+            //设置toast显示的view
+            myToast.setView(toast_view);
+            myToast.show();
+        }
+    }
+
+    //显示操作状态Toast
+    public static void showToast(String msg, int type) {
+        if (!isFastDoubleToast()) {
+            View toast_view = LayoutInflater.from(DemoApp.Companion.getContext()).inflate(R.layout.layout_toast, null);
+            TextView tv_load = (TextView) toast_view.findViewById(R.id.tv_tips);
+            tv_load.setText(msg);
+            //避免Toast连续显示
+            if (myToast == null) {
+//            myToast = new Toast(MyApplication.getContext());
+                myToast = Toast.makeText(DemoApp.Companion.getContext(), msg, Toast.LENGTH_LONG);
+            } else {
+                //设置toast显示时间
+                myToast.setDuration(Toast.LENGTH_LONG);
+            }
+            //设置toast显示位置
+            myToast.setGravity(Gravity.CENTER, 0, 0);
+            //设置toast显示的view
+            myToast.setView(toast_view);
+            myToast.show();
+        }
+    }
+
+    /**
+     * 提示
+     */
+    public static void showNotice(Activity activity, String msg, int type) {
+        //if (!isFastDoubleToast()) {
+            View toast_view = LayoutInflater.from(DemoApp.Companion.getContext()).inflate(R.layout.layout_notice, null);
+            TextView notice_tv = toast_view.findViewById(R.id.notice_tv);
+            notice_tv.setText(msg);
+            //避免Toast连续显示
+            if (myToast == null) {
+//            myToast = new Toast(MyApplication.getContext());
+                myToast = Toast.makeText(DemoApp.Companion.getContext(), msg, Toast.LENGTH_LONG);
+            } else {
+                //设置toast显示时间
+                myToast.cancel();
+                myToast = Toast.makeText(DemoApp.Companion.getContext(), msg, Toast.LENGTH_LONG);
+                //myToast.setDuration(Toast.LENGTH_LONG);
+            }
+            LinearLayout linearLayout = toast_view.findViewById(R.id.notice_linear);
+            switch (type) {
+                //Success
+                case 0:
+                    linearLayout.setBackgroundColor(activity.getResources().getColor(R.color.green_D1F4F6));
+                    notice_tv.setTextColor(activity.getResources().getColor(R.color.green_36A4AA));
+                    break;
+                //Faild
+                case 1:
+                    linearLayout.setBackgroundColor(activity.getResources().getColor(R.color.yellow_FEE8D8));
+                    notice_tv.setTextColor(activity.getResources().getColor(R.color.yellow_E88946));
+                    break;
+                //Error
+                case 2:
+                    linearLayout.setBackgroundColor(activity.getResources().getColor(R.color.red_FFDCD9));
+                    notice_tv.setTextColor(activity.getResources().getColor(R.color.red_D44D44));
+                    break;
+            }
+            WindowManager windowManager = activity.getWindowManager();
+            Display display = windowManager.getDefaultDisplay();
+            linearLayout.getLayoutParams().width = display.getWidth();
+            //设置toast显示位置
+//        myToast.setGravity(Gravity.TOP, 0, DensityUtils.dp2px(activity, 44));
+            myToast.setGravity(Gravity.TOP, 0, 0);
+            //设置toast显示的view
+            myToast.setView(toast_view);
+            myToast.show();
+        //}
+    }
+
+    private static long lastClickTime;
+
+    private static boolean isFastDoubleToast() {
+        long time = System.currentTimeMillis();
+        long timeD = time - lastClickTime;
+        if (0 < timeD && timeD < 4000) {
+            return true;
+        }
+        lastClickTime = time;
+        return false;
+    }
+
+    public static void showNoticeSuccess(Activity activity, String msg) {
+        showNotice(activity, msg, 0);
+    }
+
+    public static void showNoticeFaild(Activity activity, String msg) {
+        showNotice(activity, msg, 1);
+    }
+
+    public static void showNoticeError(Activity activity, String msg) {
+        showNotice(activity, msg, 2);
+    }
+
+    public static void cancel() {
+        if (myToast != null)
+            myToast.cancel();
+    }
+
+}

+ 267 - 0
demo/src/main/java/com/sybotan/android/demo/tools/UploadImage.java

@@ -0,0 +1,267 @@
+package com.sybotan.android.demo.tools;
+
+import android.util.Log;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.util.EntityUtils;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.UUID;
+
+/**
+ * Created by 何鑫欣 on 2017/8/8.
+ */
+
+public class UploadImage {
+    private static final String TAG = "uploadFile";
+    private static final int TIME_OUT = 10 * 10000000; // 超时时间
+    private static final String CHARSET = "utf-8"; // 设置编码
+    public static final String SUCCESS = "1";
+    public static final String FAILURE = "0";
+
+    public static final int UPLOAD_RECORD_SUCCESS = 0x122;
+    public static final int UPLOAD_SUCCESS = 0x123;
+    public static final int UPLOAD_FAIL = 0x124;
+    public static final int UPlOAD_RECORD_FAIL = 0X125;
+
+    public static final int DOWNLOAD_SUCCESS = 0x126;
+    public static final int DOWNLOAD_FAIL = 0x127;
+
+    public static final int READ_FROM_CACHE = 0x128;
+    public static final int READ_FROM_INTERNET = 0x129;
+    private static String result;
+
+    public static String uploadFile(File file, String imageUrl) {
+        String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成
+        String PREFIX = "--", LINE_END = "\r\n";
+        String CONTENT_TYPE = "multipart/form-data"; // 内容类型
+        String RequestURL = imageUrl;
+        try {
+            URL url = new URL(RequestURL);
+            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+            conn.setReadTimeout(TIME_OUT);
+            conn.setConnectTimeout(TIME_OUT);
+            conn.setDoInput(true); // 允许输入流
+            conn.setDoOutput(true); // 允许输出流
+            conn.setUseCaches(false); // 不允许使用缓存
+            conn.setRequestMethod("POST"); // 请求方式
+            conn.setRequestProperty("Charset", CHARSET); // 设置编码
+            conn.setRequestProperty("connection", "keep-alive");
+            conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary="
+                    + BOUNDARY);
+            if (file != null) {
+                /**
+                 * 当文件不为空,把文件包装并且上传
+                 */
+                OutputStream outputSteam = conn.getOutputStream();
+
+                DataOutputStream dos = new DataOutputStream(outputSteam);
+                StringBuffer sb = new StringBuffer();
+                sb.append(PREFIX);
+                sb.append(BOUNDARY);
+                sb.append(LINE_END);
+                /**
+                 * 这里重点注意: name里面的值为服务器端需要key 只有这个key 才可以得到对应的文件
+                 * filename是文件的名字,包含后缀名的 比如:abc.png
+                 */
+
+                sb.append("Content-Disposition: form-data; name=\"img\"; filename=\""
+                        + file.getName() + "\"" + LINE_END);
+                sb.append("Content-Type: application/octet-stream; charset="
+                        + CHARSET + LINE_END);
+                sb.append(LINE_END);
+                dos.write(sb.toString().getBytes());
+                InputStream is = new FileInputStream(file);
+                byte[] bytes = new byte[1024];
+                int len = 0;
+                while ((len = is.read(bytes)) != -1) {
+                    dos.write(bytes, 0, len);
+                }
+                is.close();
+                dos.write(LINE_END.getBytes());
+                byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINE_END)
+                        .getBytes();
+                dos.write(end_data);
+                dos.flush();
+                /**
+                 * 获取响应码 200=成功 当响应成功,获取响应的流
+                 */
+                int res = conn.getResponseCode();
+                if (res == 200) {
+                    return SUCCESS;
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return FAILURE;
+    }
+
+    public static String uploadFile1(File file, String imageUrl) {
+        String TAG = "uploadFile";
+        int TIME_OUT = 10 * 10000000; // 超时时间
+        String CHARSET = "utf-8"; // 设置编码
+        String SUCCESS = "1";
+        String FAILURE = "0";
+        String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成
+        String PREFIX = "--", LINE_END = "\r\n";
+        String CONTENT_TYPE = "multipart/form-data"; // 内容类型
+        String RequestURL = imageUrl;
+        try {
+            URL url = new URL(RequestURL);
+            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+            conn.setReadTimeout(TIME_OUT);
+            conn.setConnectTimeout(TIME_OUT);
+            conn.setDoInput(true); // 允许输入流
+            conn.setDoOutput(true); // 允许输出流
+            conn.setUseCaches(false); // 不允许使用缓存
+            conn.setRequestMethod("POST"); // 请求方式
+            conn.setRequestProperty("Charset", CHARSET); // 设置编码
+            conn.setRequestProperty("connection", "keep-alive");
+            conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary=" + BOUNDARY);
+            /**
+             * 当文件不为空,把文件包装并且上传
+             *  何鑫欣  13:58:50
+             */
+
+            if (file != null) {
+                DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
+                StringBuffer sb = new StringBuffer();
+                sb.append(PREFIX);
+                sb.append(BOUNDARY);
+                sb.append(LINE_END);
+                /**
+                 * 这里重点注意: name里面的值为服务端需要key 只有这个key 才可以得到对应的文件
+                 * filename是文件的名字,包含后缀名的 比如:abc.png
+                 */
+                sb.append("Content-Disposition: form-data; name=\"" + "ht" + "\"; filename=\""
+                        + file.getName() + "\"" + LINE_END);
+                //"Content-Type: application/octet-stream; charset=" + CHARSET
+                sb.append("Content-Type:imagepeg" + LINE_END);
+                sb.append(LINE_END);
+                dos.write(sb.toString().getBytes());
+                InputStream is = new FileInputStream(file);
+                byte[] bytes = new byte[1024];
+                int len = 0;
+                while ((len = is.read(bytes)) != -1) {
+                    dos.write(bytes, 0, len);
+                }
+                is.close();
+                dos.write(LINE_END.getBytes());
+                byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINE_END)
+                        .getBytes();
+                dos.write(end_data);
+                dos.flush();
+                Log.e("======", "====flush===");
+
+                /**
+                 * 获取响应码 200=成功 当响应成功,获取响应的流
+                 */
+                int res = conn.getResponseCode();
+                Log.e("========", "response code:" + res);
+                if (res == 200) {
+                    Log.e(TAG, "request success");
+                    InputStream input = conn.getInputStream();
+                    StringBuffer sb1 = new StringBuffer();
+                    int ss;
+                    while ((ss = input.read()) != -1) {
+                        sb1.append((char) ss);
+                    }
+                    result = sb1.toString();
+                    Log.e(TAG, "result : " + result);
+                    return result;
+                } else {
+                    Log.e(TAG, "request error");
+                }
+
+            }
+
+        } catch (Exception e) {
+
+        }
+        return result;
+    }
+
+
+    /**
+     * @param file 要上传的文件
+     * @return true if upload success else false
+     * @throws IOException
+     */
+    public static String uploadFiles(File file, String url) {
+        String result = "";
+        Log.e("url", "--------" + url);
+        try {
+//            HttpClient client = new DefaultHttpClient();// 开启一个客户端 HTTP 请求
+//            HttpPost post = new HttpPost(url);//创建 HTTP POST 请求
+//
+//            //MultipartEntity对象,需要httpmime-4.1.1.jar文件。
+//            MultipartEntity multipartEntity = new MultipartEntity();
+//            //filesPath为List<String>对象,里面存放的是需要上传的文件的地址
+//            ContentBody fi = new FileBody(file);
+//            multipartEntity.addPart("file", fi);
+//
+//            post.setEntity(multipartEntity);//设置请求参数
+
+            // 将文件将其转换为io流
+            InputStream inputStream = new FileInputStream(file);
+            // 定义一个字节数组封装io流
+            byte[] b = new byte[inputStream.available()];
+            // 将io读入到数组中
+            inputStream.read(b);
+            ByteArrayEntity mEntitys = new ByteArrayEntity(b);
+
+            HttpClient httpclient = HttpClientBuilder.create().build();// 开启一个客户端 HTTP 请求
+            url = url.replaceAll(" ", "%20");
+            HttpPost httpPost = new HttpPost(url);
+            // 配置信息
+            RequestConfig requestConfig = RequestConfig.custom()
+                    // 设置连接超时时间(单位毫秒)
+                    .setConnectTimeout(5000)
+                    // 设置请求超时时间(单位毫秒)
+                    .setConnectionRequestTimeout(5000)
+                    // socket读写超时时间(单位毫秒)
+                    .setSocketTimeout(5000)
+                    // 设置是否允许重定向(默认为true)
+                    .setRedirectsEnabled(true).build();
+            httpPost.setConfig(requestConfig);
+            httpPost.addHeader("Content-Type", "application/json;charset=utf-8");
+            // 设置请求的的参数
+            httpPost.setEntity(mEntitys);
+            HttpResponse response = httpclient.execute(httpPost);// 发起请求 并返回请求的响应
+            Log.e("state_Http", String.valueOf(response.getStatusLine().getStatusCode()));
+
+            if (response.getStatusLine().getStatusCode() == 200) {
+                HttpEntity entit11 = response.getEntity();
+
+                if (entit11 != null) {
+                    result = EntityUtils.toString(entit11, "utf-8");
+                    Log.e(TAG, "uploadFiles: " + result);
+                }
+                return result;
+            } else {
+                result = "网络异常,请检查网络后重试";
+            }
+        } catch (Exception e) {
+            result = "网络异常,请检查网络后重试";
+            e.printStackTrace();
+
+        }
+        return result;
+    }
+}

+ 294 - 0
demo/src/main/java/com/sybotan/android/demo/view/CommonNumberControl.java

@@ -0,0 +1,294 @@
+package com.sybotan.android.demo.view;
+
+import android.content.Context;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+import com.sybotan.android.demo.R;
+
+
+/**
+ * created by lihao on 2019/3/6
+ * 数字控制器
+ */
+public class CommonNumberControl extends RelativeLayout {
+    private Context context;
+    private EditText mNum;
+    private ImageView mMinus;
+    private ImageView mAdd;
+    //可选最小数字,默认0,包含0
+    public int minNum = 0;
+    //可选最大数字,默认9999,包含9999
+    private long maxNum = 99;
+    private int defaltNum = 0;
+    private InputFilter[] fArray = new InputFilter[1];
+    private boolean canEdit = true;
+    //是否点击EditText不弹出键盘,弹出可编辑的Dialog
+    private boolean isShowPop = false;
+    private TextWatcher textWatcher;
+
+    public CommonNumberControl(Context context) {
+        this(context, null);
+        this.context = context;
+    }
+
+    public CommonNumberControl(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+        this.context = context;
+    }
+
+    public CommonNumberControl(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        this.context = context;
+        initView(context);
+    }
+
+    private void initView(Context context) {
+        View inflate = LayoutInflater.from(context).inflate(R.layout.common_number_control, this, true);
+        mMinus = inflate.findViewById(R.id.number_control_minus);
+        mNum = inflate.findViewById(R.id.number_control_num);
+        mAdd = inflate.findViewById(R.id.number_control_add);
+        mNum.setText(defaltNum + "");
+        mNum.setSelection(mNum.getText().length());
+        //减
+        mMinus.setOnClickListener(v -> {
+            long num = 0;
+            if (!TextUtils.isEmpty(mNum.getText().toString())) {
+                num = Long.parseLong(mNum.getText().toString());
+            }
+            if (num > minNum) {
+                num--;
+                mNum.setText(num + "");
+                //mNum.setSelection(mNum.getText().length());
+            } else {
+//                ToastUtils.showMyToast("不能小于" + minNum);
+            }
+        });
+        //加
+        mAdd.setOnClickListener(v -> {
+            long num = 0;
+            if (!TextUtils.isEmpty(mNum.getText().toString())) {
+                num = Long.parseLong(mNum.getText().toString());
+            }
+            if (num < maxNum) {
+                num++;
+                mNum.setText(num + "");
+                //mNum.setSelection(mNum.getText().length());
+            } else {
+//                ToastUtils.showMyToast("不能大于" + maxNum);
+            }
+        });
+        mNum.setOnFocusChangeListener((v, hasFocus) -> {
+            if (!hasFocus) {
+                if (TextUtils.isEmpty(mNum.getText().toString())) {
+                    mNum.setText(minNum + "");
+                }
+                if (Double.parseDouble(mNum.getText().toString()) < minNum) {
+                    mNum.setText(minNum + "");
+                }
+            }
+        });
+        textWatcher = new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                String text = s.toString();
+                if (canEdit && !TextUtils.isEmpty(text)) {
+                    //不包含小数点,输入的长度大于1,且以0打头,将0截取
+                    if (!text.contains(".")) {
+                        if (text.length() > 1 && text.startsWith("0")) {
+                            text = String.valueOf(Long.parseLong(text));
+                            mNum.removeTextChangedListener(this);
+                            mNum.setText(text);
+                            mNum.addTextChangedListener(textWatcher);
+                        }
+                    }
+                    try {
+                        if (Long.parseLong(text) > maxNum) {
+                            mNum.setText("" + maxNum);
+                        }
+                        mNum.setSelection(mNum.getText().length());
+                        if (null != mListener) {
+                            mListener.setCurrentNum(mNum.getText().toString());
+                        }
+                        if (Long.parseLong(mNum.getText().toString()) >= maxNum) {
+                            mAdd.setImageResource(R.mipmap.plus_unclick);
+                            mAdd.setEnabled(false);
+                        } else {
+                            mAdd.setImageResource(R.mipmap.plus);
+                            mAdd.setEnabled(true);
+                        }
+                        if (Long.parseLong(mNum.getText().toString()) <= minNum) {
+                            mMinus.setImageResource(R.mipmap.minus_unclick);
+                            mMinus.setEnabled(false);
+                        } else {
+                            mMinus.setImageResource(R.mipmap.minus);
+                            mMinus.setEnabled(true);
+                        }
+                    } catch (Exception e) {
+                        mNum.setText(minNum + "");
+                    }
+
+                } else {
+                    if (null != mListener) {
+                        mListener.setCurrentNum(text);
+                    }
+                }
+            }
+        };
+        mNum.addTextChangedListener(textWatcher);
+    }
+
+    /**
+     * 设置最小数字
+     *
+     * @param minNum 最小数字
+     * @return this
+     */
+    public CommonNumberControl setMinNum(int minNum) {
+        this.minNum = minNum;
+        mNum.setText(minNum + "");
+        return this;
+    }
+
+    /**
+     * 设置最大数字
+     *
+     * @param maxNum 最大数字
+     * @return this
+     */
+    public CommonNumberControl setMaxNum(long maxNum) {
+        this.maxNum = maxNum;
+        return this;
+    }
+
+    public CommonNumberControl setMaxLength(int maxLength) {
+        fArray[0] = new InputFilter.LengthFilter(maxLength);
+        mNum.setFilters(fArray);
+        return this;
+    }
+
+    public CommonNumberControl setText(String text) {
+        mNum.setText(text);
+        return this;
+    }
+
+    public String getText() {
+        return mNum.getText().toString();
+    }
+
+    /**
+     * 设置默认数字
+     *
+     * @param defaltNum 默认数字
+     * @return this
+     */
+    public CommonNumberControl setDefaultNum(int defaltNum) {
+        this.defaltNum = defaltNum;
+        return this;
+    }
+
+    public String getCurrentNum() {
+        return mNum.getText().toString();
+    }
+
+    private onCommonNumberControlListener mListener;
+
+    public CommonNumberControl setEditTextListener(onCommonNumberControlListener listener) {
+        mListener = listener;
+        return this;
+    }
+
+    public EditText getEditText() {
+        return mNum;
+    }
+
+    public CommonNumberControl setCurrentNum(String num) {
+        mNum.setText(num);
+        return this;
+    }
+
+    public void setSelection(int length) {
+        mNum.setSelection(length);
+    }
+
+    public void canEdit(boolean canEdit) {
+        this.canEdit = canEdit;
+        mNum.setEnabled(canEdit);
+        if (canEdit) {
+            mNum.setBackground(getResources().getDrawable(R.drawable.bg_rect_stroke_gray_top_bottom));
+            mAdd.setVisibility(VISIBLE);
+            mMinus.setVisibility(VISIBLE);
+        } else {
+            mNum.setBackground(getResources().getDrawable(R.color.white_ffffff));
+            mAdd.setVisibility(GONE);
+            mMinus.setVisibility(GONE);
+        }
+    }
+
+    public void setShowPop(boolean showPop) {
+        isShowPop = showPop;
+        setListener();
+    }
+
+    public interface onCommonNumberControlListener {
+        void setCurrentNum(String num);
+    }
+
+    /**
+     * 点击显示数字控制器的弹窗
+     */
+    public interface OnShowDialogListener {
+        void onShowDialog();
+    }
+
+    private OnShowDialogListener onShowDialogListener;
+
+    public CommonNumberControl setOnShowDialogListener(OnShowDialogListener onShowDialogListener) {
+        this.onShowDialogListener = onShowDialogListener;
+        return this;
+    }
+
+    /**
+     * 设置点击中间的editeText是处于编辑状态 还是 弹dialog再编辑
+     */
+    private void setListener() {
+        if (isShowPop) {
+            mNum.setFocusable(false);
+            mNum.setOnClickListener(v -> {
+                if (null != onShowDialogListener) {
+                    onShowDialogListener.onShowDialog();
+                }
+            });
+        } else {
+            mNum.setFocusable(true);
+            mNum.setOnFocusChangeListener((v, hasFocus) -> {
+                if (!hasFocus) {
+                    if (TextUtils.isEmpty(mNum.getText().toString())) {
+                        mNum.setText(minNum + "");
+                    }
+                    if (Double.parseDouble(mNum.getText().toString()) < minNum) {
+                        mNum.setText(minNum + "");
+                    }
+                }
+            });
+        }
+    }
+}

+ 86 - 0
demo/src/main/java/com/sybotan/android/demo/view/EquipListDialog.java

@@ -0,0 +1,86 @@
+package com.sybotan.android.demo.view;
+
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.sybotan.android.demo.R;
+
+/**
+ * 选择设备列表
+ */
+
+public class EquipListDialog extends Dialog {
+
+    private Submit submit;
+    private EditText searchEdt;
+
+    public EquipListDialog(@NonNull Context context) {
+        super(context, R.style.style_dialog);
+        setDisplay(context);
+        init(context);
+    }
+
+    private void init(Context context) {
+        findViewById(R.id.search_delete).setOnClickListener(v -> {
+            searchEdt.setText("");
+        });
+        searchEdt = findViewById(R.id.search_edt);
+        searchEdt.setOnEditorActionListener((textView, actionId, keyEvent) -> {
+            if (actionId == EditorInfo.IME_ACTION_SEARCH) {//修改回车键功能
+                if (!TextUtils.isEmpty(searchEdt.getText().toString().trim())) {
+
+                }
+            }
+            return false;
+        });
+        RecyclerView recycler = findViewById(R.id.recycler);
+        recycler.setLayoutManager(new LinearLayoutManager(context));
+    }
+
+    public void setDisplay(Context context) {
+        setContentView(R.layout.dialog_equip_list);
+        setProperty();
+        setCancelable(true);
+        setCanceledOnTouchOutside(true);
+        setOnCancelListener(new OnCancelListener() {
+            @Override
+            public void onCancel(DialogInterface dialog) {
+                dialog.dismiss();
+            }
+        });
+    }
+
+    public void setProperty() {
+        Window window = getWindow();//得到对话框的窗口.
+        WindowManager.LayoutParams wl = window.getAttributes();
+        wl.x = 0;// 设置对话框的位置.0为中间
+        wl.y = 0;
+        wl.alpha = 1;// 设置对话框的透明度
+        wl.gravity = Gravity.CENTER;
+        window.setAttributes(wl);
+    }
+
+
+    public interface Submit {
+        void onSubmit();
+    }
+
+    public void setOnSubmit(Submit submit) {
+        this.submit = submit;
+    }
+
+}

+ 366 - 0
demo/src/main/java/com/sybotan/android/demo/view/ListItemView.java

@@ -0,0 +1,366 @@
+package com.sybotan.android.demo.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.sybotan.android.demo.R;
+import com.sybotan.android.demo.tools.CommonUtils;
+import com.sybotan.android.demo.tools.DensityUtils;
+import com.sybotan.android.demo.tools.ScreenUtils;
+
+
+/**
+ * function="单个itemview封装:务必指定type,样式详情看layout"
+ * 0-左侧tv+右侧tv+右侧箭头
+ * 1-左侧tv+右侧tv+右侧editText+箭头
+ * 2-左侧tv
+ * 3-左侧tv,右侧两个et
+ * 4-左侧tv,右侧tv
+ * Created by wangmaobo on 2018/4/24.
+ */
+public class ListItemView extends RelativeLayout {
+    private View mContentView;
+    /**
+     * xml属性
+     */
+    private int mType;
+    private String leftText;
+    private String rightText, rightHint;
+    private int rightTextColor;
+    private OnEditTextListener mListener_et;
+    /**
+     * EditText设置最大输入值
+     */
+    private int et_max = 1;
+    private ImageView mIvMust;
+
+    public ListItemView(Context context) {
+        this(context, null, -1);
+    }
+
+    public ListItemView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        if (context instanceof OnEditTextListener) {
+            mListener_et = (OnEditTextListener) context;
+        }
+        initView(context, attrs);
+    }
+
+    public ListItemView(Context context, AttributeSet attrs) {
+        this(context, attrs, -1);
+    }
+
+    private void initView(Context context, AttributeSet attrs) {
+        //setBackground(context.getResources().getDrawable(R.drawable.common_line_ffffff_single_all));
+        if (null != attrs) {
+            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ListItemView);
+            mType = array.getInt(R.styleable.ListItemView_Type, 0);
+            leftText = array.getString(R.styleable.ListItemView_LeftText);
+            rightText = array.getString(R.styleable.ListItemView_RightText);
+            rightHint = array.getString(R.styleable.ListItemView_RightHint);
+            rightTextColor = array.getInteger(R.styleable.ListItemView_RightTextColor, R.color.gray_f7f7f7);
+            array.recycle();
+        }
+        switch (mType) {
+            case 0:
+            case 2:
+            case 4:
+            case 5:
+                mContentView = LayoutInflater.from(context).inflate(R.layout.layout_common_list_item, this, true);
+                TextView tv_left = mContentView.findViewById(R.id.tv_left);
+                tv_left.setMaxWidth(ScreenUtils.getScreenWidth(context) / 2);
+                TextView tv_right = mContentView.findViewById(R.id.tv_right);
+                mIvMust = mContentView.findViewById(R.id.iv_must);
+                tv_right.setHint(rightHint);
+                ImageView iv_right = mContentView.findViewById(R.id.iv_right);
+                if (mType == 2) {
+                    iv_right.getLayoutParams().width = 0;
+                }
+                if (mType == 4) {
+                    iv_right.setVisibility(View.INVISIBLE);
+                    CommonUtils.setRightMargin(tv_right, DensityUtils.dp2px(context, 16));
+                }
+                if (mType == 5) {
+                    TextView tv_line = mContentView.findViewById(R.id.tv_line);
+                    CommonUtils.setMarginsRl(tv_line, 16, 0, 16, 0);
+                }
+                break;
+            case 1:
+                mContentView = LayoutInflater.from(context).inflate(R.layout.layout_common_list_item1, this, true);
+                final EditText et = (EditText) getView(R.id.et_right);
+                mIvMust = mContentView.findViewById(R.id.iv_must);
+                setEditTextListener(et, et_max);
+                break;
+            case 3:
+                mContentView = LayoutInflater.from(context).inflate(R.layout.layout_common_list_item2, this, true);
+                EditText et_left = (EditText) getView(R.id.et_right1);
+                EditText et_right = (EditText) getView(R.id.et_right);
+                mIvMust = mContentView.findViewById(R.id.iv_must);
+                setEditTextListener(et_left, 2);
+                setEditTextListener(et_right, 2);
+                break;
+            case 6:
+                mContentView = LayoutInflater.from(context).inflate(R.layout.layout_common_list_item3, this, true);
+                break;
+            case 7://左边有上下两行
+                mContentView = LayoutInflater.from(context).inflate(R.layout.layout_common_list_item4, this, true);
+                break;
+            case 8://新建工单,选择建议执行人数
+                mContentView = LayoutInflater.from(context).inflate(R.layout.layout_common_list_item5, this, true);
+                final CommonNumberControl numberControl = (CommonNumberControl) getView(R.id.et_right);
+                mIvMust = mContentView.findViewById(R.id.iv_must);
+                setEditTextListener(numberControl, et_max);
+                break;
+//            case 9:
+//                mContentView = LayoutInflater.from(context).inflate(R.layout.layout_common_list_item9, this, true);
+//                final CommonNumberControl num_control = (CommonNumberControl) getView(R.id.num_control);
+//                setEditTextListener(num_control.getEditText(), et_max);
+//                break;
+        }
+        if (mType != 6 && mType != 7) {
+            setTvLeft(leftText);
+            setTvRight(rightText);
+            setTvRightColor(rightTextColor);
+        }
+    }
+
+    private void setEditTextListener(EditText et, int et_max) {
+        et.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                int end = et.getText().toString().trim().length();
+                if (s.length() > et_max) {
+                    s.delete(et_max, end);
+                    et.setText(s);
+                    et.setSelection(et_max);
+                }
+                if (null != mListener_et) {
+                    mListener_et.getData(et, s.toString());
+                }
+            }
+        });
+    }
+
+    private void setEditTextListener(CommonNumberControl et, int et_max) {
+        et.getEditText().addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                int end = et.getEditText().getText().toString().trim().length();
+                if (s.length() > et_max) {
+                    s.delete(et_max, end);
+                    et.getEditText().setText(s);
+                    et.getEditText().setSelection(et_max);
+                }
+                if (null != mListener_et) {
+                    mListener_et.getData(et, s.toString());
+                }
+            }
+        });
+    }
+
+    public ListItemView setETRight1(String text) {
+        if (mType == 3) {
+            ((EditText) getView(R.id.et_right)).setText(text);
+        }
+        return this;
+    }
+
+    public ListItemView setETRight2(String text) {
+        if (mType == 3) {
+            EditText et = (EditText) getView(R.id.et_right1);
+            et.setText(text);
+            et.requestFocus();
+            et.setSelection(et.getText().length());
+        }
+        return this;
+    }
+
+    public ListItemView setIvRight(int width) {
+        ((ImageView) getView(R.id.iv_right)).getLayoutParams().width = width;
+        return this;
+    }
+
+    public ListItemView setTvLeft(String text) {
+        ((TextView) getView(R.id.tv_left)).setText(text);
+        return this;
+    }
+
+    public View getView(int viewId) {
+        return mContentView.findViewById(viewId);
+    }
+
+    public ListItemView setTvRight(String text) {
+        ((TextView) getView(R.id.tv_right)).setText(text);
+        return this;
+    }
+
+    public ListItemView setTvRightSize(float text) {
+        ((TextView) getView(R.id.tv_right)).setTextSize(text);
+        return this;
+    }
+
+    public ListItemView setTvRightColor(int color) {
+        ((TextView) getView(R.id.tv_right)).setTextColor(color);
+        return this;
+    }
+
+    public ListItemView setEtRight(String text) {
+        if (TextUtils.isEmpty(text)) {
+            return this;
+        }
+        if (getView(R.id.et_right) instanceof EditText) {
+            ((EditText) getView(R.id.et_right)).setText(text);
+            ((EditText) getView(R.id.et_right)).setSelection(((EditText) getView(R.id.et_right)).getText().length());
+        } else if (getView(R.id.et_right) instanceof CommonNumberControl) {
+            CommonNumberControl numberControl = (CommonNumberControl) getView(R.id.et_right);
+            numberControl.setCurrentNum(text);
+        }
+        return this;
+    }
+
+    public ListItemView setControlRight(String text) {
+        if (TextUtils.isEmpty(text)) {
+            return this;
+        }
+        ((CommonNumberControl) getView(R.id.et_right)).setCurrentNum(text);
+        ((CommonNumberControl) getView(R.id.et_right)).setSelection(((CommonNumberControl) getView(R.id.et_right)).getEditText().length());
+        return this;
+    }
+
+    public ListItemView setTvRightVisible(boolean flag) {
+        if (flag) {
+            ((TextView) getView(R.id.tv_right)).setVisibility(VISIBLE);
+        } else {
+            ((TextView) getView(R.id.tv_right)).setVisibility(INVISIBLE);
+        }
+        return this;
+    }
+
+    public void setRightIconGone(boolean isGone) {
+        if (isGone) {
+            getView(R.id.iv_right).getLayoutParams().width = 0;
+        }
+    }
+
+    public ListItemView setIcon(int resId) {
+        ((ImageView) getView(R.id.iv_right)).setImageResource(resId);
+        return this;
+    }
+    //=============================type1===================================
+
+    public ListItemView setIconVisible(boolean flag) {
+        if (flag) {
+            ((ImageView) getView(R.id.iv_right)).setVisibility(VISIBLE);
+        } else {
+            ((ImageView) getView(R.id.iv_right)).setVisibility(INVISIBLE);
+        }
+        return this;
+    }
+
+    /* *//**
+     * 将EditText设置为可编辑状态
+     *
+     * @return
+     *//*
+    public ListItemView setEditTextFocus() {
+        EditText et = (EditText) getView(R.id.et_right);
+        et.setFocusable(true);
+        et.setFocusableInTouchMode(true);
+        et.requestFocus();
+        return this;
+    }*/
+
+    /**
+     * @param max
+     * @return
+     */
+    public ListItemView setEditTextMax(int max) {
+        et_max = max;
+        return this;
+    }
+
+    public void setErrorCount(int errorCount) {
+        if (errorCount == 0) {
+            getView(R.id.error_state_linear).setVisibility(GONE);
+        } else {
+            getView(R.id.error_state_linear).setVisibility(VISIBLE);
+        }
+        TextView stateTv = (TextView) getView(R.id.error_state_tv);
+        stateTv.setText("异常点" + errorCount + "个");
+    }
+
+    /**
+     * 右侧箭头隐藏
+     *
+     * @return
+     */
+
+    public ListItemView setEditTextFocus() {
+        if (getView(R.id.et_right) instanceof EditText) {
+            EditText et = (EditText) getView(R.id.et_right);
+            et.setFocusable(true);
+            et.setFocusableInTouchMode(true);
+            et.requestFocus();
+        }
+        return this;
+    }
+
+    /**
+     * 设置白色背景
+     *
+     * @return
+     */
+
+//    public ListItemView setBackGroundWhite() {
+//        setBackground(getResources().getDrawable(R.drawable.common_line_ffffff_single_16));
+//        return this;
+//    }
+    public ListItemView setBackGroundGray() {
+        setBackground(getResources().getDrawable(R.color.gray_F2F3F6));
+        return this;
+    }
+
+
+    /**
+     * EditText的文本监听,如果有必要则实现此接口,务必:type=1
+     */
+    public interface OnEditTextListener {
+        void getData(View view, String data);
+    }
+
+    public void setMustVisibility(int visibility) {
+        if (mIvMust != null) {
+            mIvMust.setVisibility(visibility);
+        }
+    }
+}

+ 46 - 0
demo/src/main/java/com/sybotan/android/demo/view/MaxHeightRecyclerView.java

@@ -0,0 +1,46 @@
+package com.sybotan.android.demo.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.sybotan.android.demo.R;
+
+
+/**
+ * created by lihao on 2019/3/6
+ * 可以设置最大高度的RecyclerView
+ */
+public class MaxHeightRecyclerView extends RecyclerView {
+    private int mMaxHeight;
+
+    public MaxHeightRecyclerView(Context context) {
+        super(context);
+    }
+
+    public MaxHeightRecyclerView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initialize(context, attrs);
+    }
+
+    public MaxHeightRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initialize(context, attrs);
+    }
+
+    private void initialize(Context context, AttributeSet attrs) {
+        TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.MaxHeightRecyclerView);
+        mMaxHeight = arr.getLayoutDimension(R.styleable.MaxHeightRecyclerView_maxHeight, mMaxHeight);
+        arr.recycle();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mMaxHeight > 0) {
+            heightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxHeight, MeasureSpec.AT_MOST);
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+}

+ 96 - 0
demo/src/main/java/com/sybotan/android/demo/view/MyDialogViewSingle.java

@@ -0,0 +1,96 @@
+package com.sybotan.android.demo.view;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.sybotan.android.demo.R;
+import com.sybotan.android.demo.retrofit.Constant;
+
+
+/**
+ * Created by 何鑫欣 on 2017/8/16.
+ */
+
+public abstract class MyDialogViewSingle extends Dialog {
+    private TextView contentview, bottomview, tv_info;
+    private CheckBox checkBox;
+
+    public MyDialogViewSingle(@NonNull Context context) {
+        super(context);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        super.onCreate(savedInstanceState);
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        setContentView(R.layout.activity_newcreate_dialog);
+        contentview = (TextView) findViewById(R.id.contentTV);
+        tv_info = findViewById(R.id.tv_info);
+        bottomview = (TextView) findViewById(R.id.IKnowTV);
+        checkBox = (CheckBox) findViewById(R.id.check_single);
+        checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+
+            }
+        });
+        View.OnClickListener listener = new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                switch (v.getId()) {
+                    case R.id.IKnowTV:
+                        try {
+                            onBottomClick();
+                        } catch (InterruptedException e) {
+                            e.printStackTrace();
+                        }
+                        dismiss();
+                        break;
+                }
+            }
+        };
+        bottomview.setOnClickListener(listener);
+    }
+
+    public void setContent(String info) {
+        contentview.setText(info);
+    }
+
+    public void setInfo(String info) {
+        if (null == tv_info) {
+            tv_info = findViewById(R.id.tv_info);
+        }
+        tv_info.setVisibility(View.VISIBLE);
+        tv_info.setText(info);
+    }
+
+    public void setButtonNotice(String info) {
+        bottomview.setText(info);
+    }
+
+    public void setCancel(boolean isCancel) {
+        setCancelable(isCancel);
+    }
+
+    protected abstract void onBottomClick() throws InterruptedException;
+
+    public MyDialogViewSingle setButtonviewText(String leftText) {
+        bottomview.setText(leftText);
+        return this;
+    }
+
+    public MyDialogViewSingle setContentviewText(String rightText) {
+        contentview.setText(rightText);
+        return this;
+    }
+}

+ 96 - 0
demo/src/main/java/com/sybotan/android/demo/view/ObjectRelationDialog.java

@@ -0,0 +1,96 @@
+package com.sybotan.android.demo.view;
+
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.Gravity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.sybotan.android.demo.R;
+
+
+/**
+ * 设备关系
+ */
+
+public class ObjectRelationDialog extends Dialog implements View.OnClickListener {
+
+    private TextView relationTv;
+    private Submit submit;
+
+    public ObjectRelationDialog(@NonNull Context context) {
+        super(context, R.style.style_dialog);
+        setDisplay(context);
+        init();
+    }
+
+    private void init() {
+        relationTv = findViewById(R.id.relationTv);
+        findViewById(R.id.toleft).setOnClickListener(this);
+        findViewById(R.id.toright).setOnClickListener(this);
+        findViewById(R.id.connect).setOnClickListener(this);
+        findViewById(R.id.btn_submit).setOnClickListener(this);
+    }
+
+    public void setDisplay(Context context) {
+        setContentView(R.layout.dialog_object_relation);
+        setProperty();
+        setCancelable(true);
+        setCanceledOnTouchOutside(true);
+        setOnCancelListener(new OnCancelListener() {
+            @Override
+            public void onCancel(DialogInterface dialog) {
+                dialog.dismiss();
+            }
+        });
+    }
+
+    public void setProperty() {
+        Window window = getWindow();//得到对话框的窗口.
+        WindowManager.LayoutParams wl = window.getAttributes();
+        wl.x = 0;// 设置对话框的位置.0为中间
+        wl.y = 0;
+        wl.alpha = 1;// 设置对话框的透明度
+        wl.gravity = Gravity.CENTER;
+        window.setAttributes(wl);
+    }
+
+    @SuppressLint("NonConstantResourceId")
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.toleft:
+                relationTv.setText("->");
+                break;
+            case R.id.toright:
+                relationTv.setText("<-");
+                break;
+            case R.id.connect:
+                relationTv.setText("--");
+                break;
+            case R.id.btn_submit:
+                if (submit != null) {
+                    submit.onSubmit();
+                }
+                dismiss();
+                break;
+            default:
+                break;
+        }
+    }
+
+    public interface Submit {
+        void onSubmit();
+    }
+
+    public void setOnSubmit(Submit submit) {
+        this.submit = submit;
+    }
+
+}

+ 244 - 0
demo/src/main/java/com/sybotan/android/demo/view/SelectListPopupWindow.java

@@ -0,0 +1,244 @@
+package com.sybotan.android.demo.view;
+
+/**
+ * Created by cunwen on 16/3/25.
+ */
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup.LayoutParams;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.Button;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.sybotan.android.demo.R;
+import com.sybotan.android.demo.adapter.SelectPopListAdapter;
+import com.sybotan.android.demo.tools.AnimateUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class SelectListPopupWindow extends PopupWindow {
+    DisplayMetrics dm;
+
+    private TextView title;
+    private Button btn1, btn_cancel;
+    private RecyclerView recyclerview;
+    private View mMenuView;
+
+
+    private Context context;
+    private static final long DISMISSDELAYED = 2000;
+
+    private Animation inAnim;
+    private Animation outAnim;
+
+    SelectPopListAdapter adapter;
+    public ArrayList<String> list = new ArrayList<String>();
+    public int position = -1;
+
+    public SelectListPopupWindow(Activity context, OnClickListener itemsOnClick) {
+        super(context);
+        this.context = context;
+
+        dm = new DisplayMetrics();
+        context.getWindowManager().getDefaultDisplay().getMetrics(dm);
+        initAnimation();
+
+        LayoutInflater inflater = (LayoutInflater) context
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        mMenuView = inflater.inflate(R.layout.dialog_list_choice, null);
+        title = (TextView) mMenuView.findViewById(R.id.title);
+        btn1 = (Button) mMenuView.findViewById(R.id.btn1);
+        btn_cancel = (Button) mMenuView.findViewById(R.id.btn_cancel);
+        //取消按钮
+        btn_cancel.setOnClickListener(new OnClickListener() {
+
+            public void onClick(View v) {
+                //销毁弹出框
+                dismiss();
+                mMenuView.startAnimation(outAnim);
+            }
+        });
+        //设置按钮监听
+        btn1.setOnClickListener(itemsOnClick);
+        btn1.setTag(0);
+
+        recyclerview = (RecyclerView) mMenuView.findViewById(R.id.recyclerview);
+
+        recyclerview.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
+        recyclerview.setFocusable(false);
+        recyclerview.setVerticalScrollBarEnabled(true);
+        recyclerview.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
+        adapter = new SelectPopListAdapter();
+        adapter.setOnItemClickListener(new SelectPopListAdapter.OnRecyclerViewItemClickListener() {
+            @Override
+            public void onItemClick(View view, int position) {
+                if (mOnItemClickListener != null) {
+                    mOnItemClickListener.onItemClick(position);
+                }
+                dismiss();
+            }
+        });
+        recyclerview.setAdapter(adapter);
+
+
+        //设置SelectPicPopupWindow的View
+        this.setContentView(mMenuView);
+        //设置SelectPicPopupWindow弹出窗体的宽
+        this.setWidth(LayoutParams.MATCH_PARENT);
+        //设置SelectPicPopupWindow弹出窗体的高
+        this.setHeight(LayoutParams.WRAP_CONTENT);
+        //设置SelectPicPopupWindow弹出窗体可点击
+        this.setFocusable(true);
+        //设置SelectPicPopupWindow弹出窗体动画效果
+//        this.setAnimationStyle(R.style.AnimBottom);
+        //实例化一个ColorDrawable颜色为半透明
+//        ColorDrawable dw = new ColorDrawable(0xb0000000);
+        //设置SelectPicPopupWindow弹出窗体的背景
+
+        this.setBackgroundDrawable(new ColorDrawable(0xb0000000));
+        //mMenuView添加OnTouchListener监听判断获取触屏位置如果在选择框外面则销毁弹出框
+        mMenuView.setOnTouchListener(new OnTouchListener() {
+
+            public boolean onTouch(View v, MotionEvent event) {
+
+                int height = mMenuView.findViewById(R.id.pop_layout).getTop();
+                int y = (int) event.getY();
+                if (event.getAction() == MotionEvent.ACTION_UP) {
+                    if (y < height) {
+                        dismiss();
+                    }
+                }
+                return true;
+            }
+        });
+
+
+        mMenuView.startAnimation(inAnim);
+
+    }
+
+    public void setBtn(Boolean flag, String textBtn1) {
+        if (flag) {
+            btn1.setVisibility(View.VISIBLE);
+            btn1.setText(textBtn1);
+        } else {
+            btn1.setVisibility(View.GONE);
+        }
+    }
+
+    public void setBtnbottom(Boolean flag, String textBtn1) {
+        if (flag) {
+            btn_cancel.setVisibility(View.VISIBLE);
+            btn_cancel.setText(textBtn1);
+        } else {
+            btn_cancel.setVisibility(View.GONE);
+        }
+    }
+
+    public void setTitle(Boolean flag, String text) {
+        adapter.setFlag(flag);
+        if (flag) {
+            title.setVisibility(View.VISIBLE);
+            title.setText(text);
+            btn1.setGravity(Gravity.START);
+        } else {
+            title.setVisibility(View.GONE);
+            btn1.setGravity(Gravity.CENTER);
+        }
+    }
+
+    public void setSelect(int position) {
+        this.position = position;
+        if (position == 0) {
+            btn1.setTextColor(context.getResources().getColor(R.color.blue_2f48b7));
+        } else {
+            btn1.setTextColor(context.getResources().getColor(R.color.black_272727));
+        }
+    }
+
+    public void setList(List<?> list, String type) {
+        adapter.setItems(list, context, position, type);
+        LayoutParams mParams1 = recyclerview.getLayoutParams();
+        mParams1.height = (int) (55 * (Math.min(list.size(), 6)) * dm.density);
+        recyclerview.setLayoutParams(mParams1);
+        recyclerview.setVerticalScrollBarEnabled(true);
+        recyclerview.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
+        adapter.notifyDataSetChanged();
+    }
+
+
+    private int gravity = Gravity.BOTTOM;
+
+
+    protected void initAnimation() {
+        if (inAnim == null)
+            inAnim = getInAnimation();
+        if (outAnim == null)
+            outAnim = getOutAnimation();
+    }
+
+
+    public Animation getInAnimation() {
+        int res = AnimateUtil.getAnimationResource(this.gravity, true);
+        return AnimationUtils.loadAnimation(context, res);
+    }
+
+    public Animation getOutAnimation() {
+        int res = AnimateUtil.getAnimationResource(this.gravity, false);
+        return AnimationUtils.loadAnimation(context, res);
+    }
+
+    Animation.AnimationListener outAnimListener = new Animation.AnimationListener() {
+        @Override
+        public void onAnimationStart(Animation animation) {
+
+        }
+
+        @Override
+        public void onAnimationEnd(Animation animation) {
+            dismissImmediately();
+        }
+
+        @Override
+        public void onAnimationRepeat(Animation animation) {
+
+        }
+    };
+
+    public void dismissImmediately() {
+        dismiss();
+        context = null;
+    }
+
+
+    private OnRecyclerViewItemClickListener mOnItemClickListener = null;
+
+    public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
+        this.mOnItemClickListener = listener;
+    }
+
+    public void setRedText(int i) {
+        adapter.setRedText(i, true);
+    }
+
+    public interface OnRecyclerViewItemClickListener {
+        void onItemClick(int position);
+    }
+
+}

+ 21 - 11
demo/src/main/java/com/sybotan/android/demo/viewmodel/GraphyVM.kt

@@ -18,9 +18,11 @@ import kotlin.collections.ArrayList
  * Created by lihao.
  * Date: 2021/6/9
  */
-class GraphyVM(b: BaseViewModelInterface?, activity: BaseActivity?, //建筑id
-               private val buildingId: String, //楼层id
-               private val floorId: String) : BaseViewModel(b, activity) {
+class GraphyVM(
+    b: BaseViewModelInterface?, activity: BaseActivity?, //建筑id
+    private val buildingId: String, //楼层id
+    private val floorId: String
+) : BaseViewModel(b, activity) {
 
     /**
      * 查询空间和任务
@@ -152,13 +154,12 @@ class GraphyVM(b: BaseViewModelInterface?, activity: BaseActivity?, //建筑id
      * @param choseSpace 选中的空间
      * @param toString   数字化交付编码
      */
-    fun setJob(choseSpace: TunableSpaceItem?, toString: String, jobStatus: String) {
+    fun setJob(choseSpace: TunableSpaceItem?, jobStatus: String) {
         val bean = ReqSetJob()
         bean.buildingId = buildingId
         bean.floorId = floorId
         bean.spaceId.add(choseSpace!!.data.id)
         bean.jobStatus = jobStatus
-        bean.spaceCode = toString
         val observable = RetrofitFactory.getInstance().setJob(getRequestBody(bean))
         sendRequest(observable, object : BaseObserver<SetJobModel>(mActivity, this) {
             override fun onSuccess(model: SetJobModel) {
@@ -204,7 +205,12 @@ class GraphyVM(b: BaseViewModelInterface?, activity: BaseActivity?, //建筑id
      * @param remark        描述
      * @param choseSpace    被选中的空间
      */
-    fun addPipe(model: PipeListModel, pipeLineList: java.util.ArrayList<PointF>, remark: String, choseSpace: TunableSpaceItem?) {
+    fun addPipe(
+        model: PipeListModel,
+        pipeLineList: java.util.ArrayList<PointF>,
+        remark: String,
+        choseSpace: TunableSpaceItem?
+    ) {
         val bean = ReqAddPipe()
         bean.spaceId = choseSpace!!.data.id
         bean.sys = model.sysCode
@@ -227,11 +233,15 @@ class GraphyVM(b: BaseViewModelInterface?, activity: BaseActivity?, //建筑id
         val bean = ReqFloorPipe()
         bean.floorId = floorId
         val observable = RetrofitFactory.getInstance().floorPipe(getRequestBody(bean))
-        sendRequest(observable, object : BaseObserver<java.util.ArrayList<SpacePipeModel>>(mActivity, this) {
-            override fun onSuccess(models: java.util.ArrayList<SpacePipeModel>) {
-                mEmitter.SendDircetive(SPACE_PIPE, models)
-            }
-        }, true)
+        sendRequest(
+            observable,
+            object : BaseObserver<java.util.ArrayList<SpacePipeModel>>(mActivity, this) {
+                override fun onSuccess(models: java.util.ArrayList<SpacePipeModel>) {
+                    mEmitter.SendDircetive(SPACE_PIPE, models)
+                }
+            },
+            true
+        )
     }
 
     companion object {

+ 18 - 0
demo/src/main/res/anim/svfade_in_center.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false"
+     android:duration="300">
+
+  <scale
+      android:fromXScale="120%"
+      android:toXScale="100%"
+      android:fromYScale="120%"
+      android:toYScale="100%"
+      android:pivotX="50%"
+      android:pivotY="50%"/>
+
+  <alpha
+      android:fromAlpha="0"
+      android:toAlpha="1"/>
+
+</set>

+ 18 - 0
demo/src/main/res/anim/svfade_out_center.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false"
+     android:duration="300">
+
+  <scale
+      android:fromXScale="100%"
+      android:toXScale="80%"
+      android:fromYScale="100%"
+      android:toYScale="80%"
+      android:pivotX="50%"
+      android:pivotY="50%"/>
+
+  <alpha
+      android:fromAlpha="1"
+      android:toAlpha="0"/>
+
+</set>

+ 11 - 0
demo/src/main/res/anim/svslide_in_bottom.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false">
+
+  <translate
+      android:duration="300"
+      android:fromXDelta="0%"
+      android:toXDelta="0%"
+      android:fromYDelta="100%"
+      android:toYDelta="0%"/>
+</set>

+ 11 - 0
demo/src/main/res/anim/svslide_in_top.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false">
+
+  <translate
+      android:duration="300"
+      android:fromXDelta="0%"
+      android:toXDelta="0%"
+      android:fromYDelta="-100%"
+      android:toYDelta="0%"/>
+</set>

+ 11 - 0
demo/src/main/res/anim/svslide_out_bottom.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false">
+
+  <translate
+      android:duration="300"
+      android:fromXDelta="0%"
+      android:toXDelta="0%"
+      android:fromYDelta="0%"
+      android:toYDelta="100%"/>
+</set>

+ 11 - 0
demo/src/main/res/anim/svslide_out_top.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false">
+
+  <translate
+      android:duration="300"
+      android:fromXDelta="0%"
+      android:toXDelta="0%"
+      android:fromYDelta="0%"
+      android:toYDelta="-100%"/>
+</set>

+ 6 - 0
demo/src/main/res/drawable/bg_button_green.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/BgBlue_3BACB2" />
+    <corners android:radius="5dp" />
+</shape>

+ 6 - 0
demo/src/main/res/drawable/bg_gray_f2f3f6.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/gray_F2F3F6" />
+    <corners android:radius="2dp" />
+</shape>

+ 8 - 0
demo/src/main/res/drawable/bg_rect_stroke_gray_2.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <stroke
+        android:width="1dp"
+        android:color="@color/gray_e6e6e6" />
+    <corners android:radius="2dp" />
+</shape>

+ 12 - 0
demo/src/main/res/drawable/bg_rect_stroke_gray_top_bottom.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:tools="http://schemas.android.com/tools"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    tools:ignore="MissingDefaultResource">
+    <item
+        android:drawable="@color/gray_e6e6e6"
+        />
+    <item
+        android:top="1dp"
+        android:bottom="1dp"
+        android:drawable="@android:color/white"/>
+</layer-list>

+ 6 - 0
demo/src/main/res/drawable/bg_round_search.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/gray_f2f2f2" />
+    <corners android:radius="50dp" />
+</shape>

+ 10 - 0
demo/src/main/res/drawable/bg_toast.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <!-- 填充的颜色 -->
+    <solid android:color="#75000000"></solid>
+    <!-- 设置按钮的四个角为弧形 -->
+    <!-- android:radius 弧形的半径 -->
+    <corners android:radius="5dip" />
+
+</shape>

+ 7 - 0
demo/src/main/res/drawable/bg_white.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/white_ffffff" />
+    <corners android:radius="5dp" />
+    <!--<stroke android:width="0.5dp" android:color="@color/LineGray_e6e6e6"/>-->
+    <padding android:top="0.5dp" android:bottom="0.5dp" android:left="0.5dp" android:right="0.5dp"/>
+</shape>

+ 7 - 0
demo/src/main/res/drawable/bg_yellow_700.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <stroke android:color="@color/yellow_700" android:width="2dp"/>
+    <solid android:color="@color/yellow_700" />
+    <corners android:radius="50dp" />
+</shape>

BIN
demo/src/main/res/drawable/checkbox_selected.png


+ 7 - 0
demo/src/main/res/drawable/checkbox_style.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/checkbox_selected" android:state_checked="true" />  
+    <item android:drawable="@drawable/checkbox_unselected" android:state_checked="false" />  
+
+    <item android:drawable="@drawable/checkbox_unselected" />
+</selector>

BIN
demo/src/main/res/drawable/checkbox_unselected.png


+ 8 - 0
demo/src/main/res/drawable/common_bg_divider.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <size android:height="5dp"/>
+    <size android:width="5dp"/>
+    <solid android:color="@color/white_ffffff"/>
+</shape>

+ 8 - 0
demo/src/main/res/drawable/common_bg_divider_f7f7f7_10.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <size android:height="10dp"/>
+    <size android:width="10dp"/>
+    <solid android:color="@color/gray_f7f7f7"/>
+</shape>

+ 13 - 0
demo/src/main/res/drawable/common_line_ffffff_single_16.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:tools="http://schemas.android.com/tools"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    tools:ignore="MissingDefaultResource">
+    <item
+        android:drawable="@color/gray_F0F0F0"
+        android:left="16dp"
+        android:right="16dp"
+        />
+    <item
+        android:bottom="1dp"
+        android:drawable="@android:color/white"/>
+</layer-list>

+ 9 - 0
demo/src/main/res/drawable/common_line_ffffff_single_all.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:tools="http://schemas.android.com/tools"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    tools:ignore="MissingDefaultResource">
+    <item android:drawable="@color/gray_e6e6e6"/>
+    <item
+        android:drawable="@android:color/white"
+        android:bottom="1dp"/>
+</layer-list>

+ 11 - 0
demo/src/main/res/drawable/common_line_ffffff_top_8.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:tools="http://schemas.android.com/tools"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    tools:ignore="MissingDefaultResource">
+    <item
+        android:drawable="@color/gray_F7F7F7"
+        />
+    <item
+        android:top="8dp"
+        android:drawable="@android:color/white"/>
+</layer-list>

+ 16 - 0
demo/src/main/res/drawable/corners_line_black.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <corners
+        android:bottomLeftRadius="4dip"
+        android:bottomRightRadius="4dip"
+        android:topLeftRadius="4dip"
+        android:topRightRadius="4dip" />
+
+    <stroke
+        android:color="@color/gray_e6e6e6"
+        android:width="0.8dp" />
+
+    <solid android:color="@color/white_ffffff" />
+
+</shape>

+ 12 - 0
demo/src/main/res/drawable/shape_rectangle_green_3.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners
+        android:radius="3dp"/>
+    <solid
+        android:color="@color/BgBlue_3BACB2"/>
+    <stroke
+        android:width="1dp"
+        android:color="@color/BgBlue_3BACB2"/>
+</shape>

+ 115 - 0
demo/src/main/res/layout/activity_equip_detail.xml

@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/white_ffffff"
+    android:orientation="vertical">
+
+    <androidx.appcompat.widget.LinearLayoutCompat
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="48dp"
+            android:background="@color/white_ffffff">
+
+            <ImageView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_marginLeft="16dp"
+                android:src="@drawable/ic_round_arrow_back_24" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_centerInParent="true"
+                android:gravity="center"
+                android:text="设备详情"
+                android:textColor="@color/black_272727"
+                android:textSize="20sp" />
+        </RelativeLayout>
+
+        <com.sybotan.android.demo.view.ListItemView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:LeftText="设备类"
+            app:Type="0" />
+
+        <com.sybotan.android.demo.view.ListItemView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:LeftText="现场记录"
+            app:Type="4" />
+
+        <include layout="@layout/view_mtext" />
+
+        <include layout="@layout/view_picture" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="55dp"
+            android:gravity="center_vertical"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="16sp"
+                android:layout_weight="1"
+                android:text="核查完成"
+                android:textColor="@color/gray_272727"
+                android:textSize="15sp" />
+
+            <androidx.appcompat.widget.SwitchCompat
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:paddingLeft="10dp"
+                android:paddingRight="16dp" />
+        </LinearLayout>
+
+        <include layout="@layout/static_info" />
+
+        <include layout="@layout/geometry_info" />
+
+        <com.sybotan.android.demo.view.ListItemView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:LeftText="管道"
+            app:Type="4" />
+
+        <ExpandableListView
+            android:id="@+id/expandLvPipe"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+        <com.sybotan.android.demo.view.ListItemView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:LeftText="对象关系"
+            app:Type="4" />
+
+        <ExpandableListView
+            android:id="@+id/expandLvRelation"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/btn_submit"
+            android:layout_width="match_parent"
+            android:layout_height="40dp"
+            android:layout_gravity="center"
+            android:layout_marginLeft="16dp"
+            android:layout_marginTop="20dp"
+            android:layout_marginRight="16dp"
+            android:background="@drawable/shape_rectangle_green_3"
+            android:gravity="center"
+            android:text="提交"
+            android:textColor="@color/white_ffffff"
+            android:textSize="17dp" />
+
+    </androidx.appcompat.widget.LinearLayoutCompat>
+
+</androidx.core.widget.NestedScrollView>

+ 221 - 10
demo/src/main/res/layout/activity_graphy.xml

@@ -22,6 +22,7 @@
   -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -56,10 +57,10 @@
             android:layout_marginBottom="5dp" />
 
         <com.sybotan.android.graphy.SGraphyView
-            android:visibility="gone"
             android:id="@+id/eagleView"
             android:layout_width="120dp"
-            android:layout_height="120dp" />
+            android:layout_height="120dp"
+            android:visibility="gone" />
     </RelativeLayout>
 
     <WebView
@@ -155,6 +156,20 @@
                 android:layout_marginTop="22dp"
                 android:text="此空间核查完毕,模型和现场无区别" />
 
+            <TextView
+                android:id="@+id/addSpaceDetail"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:background="@drawable/bg_yellow_700"
+                android:paddingLeft="90dp"
+                android:paddingTop="10dp"
+                android:paddingRight="90dp"
+                android:paddingBottom="10dp"
+                android:text="记录空间信息"
+                android:textColor="@color/black_272727"
+                android:textSize="15sp" />
+
             <EditText
                 android:id="@+id/codeEdit"
                 android:layout_width="match_parent"
@@ -165,7 +180,8 @@
                 android:layout_marginRight="65dp"
                 android:hint="输入数字化交付编码"
                 android:textColorHint="@color/grey_500"
-                android:textSize="16sp" />
+                android:textSize="16sp"
+                android:visibility="gone" />
 
             <TextView
                 android:id="@+id/modelSubmit"
@@ -185,6 +201,56 @@
                 android:textSize="15sp" />
         </LinearLayout>
 
+        <LinearLayout
+            android:id="@+id/doneSpaceStructure"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:visibility="gone">
+
+            <TextView
+                android:id="@+id/spaceName4"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginBottom="5dp"
+                android:paddingLeft="90dp"
+                android:paddingTop="5dp"
+                android:paddingRight="90dp"
+                android:textColor="@color/grey_900"
+                android:textSize="12sp" />
+
+            <TextView
+                android:id="@+id/addSpaceDetail2"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:background="@drawable/bg_yellow_700"
+                android:paddingLeft="90dp"
+                android:paddingTop="10dp"
+                android:paddingRight="90dp"
+                android:paddingBottom="10dp"
+                android:text="记录空间信息"
+                android:textColor="@color/black_272727"
+                android:textSize="15sp" />
+
+            <TextView
+                android:id="@+id/resetSpaceState"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginTop="15dp"
+                android:layout_marginBottom="10dp"
+                android:background="@drawable/bg_yellow_700"
+                android:paddingLeft="90dp"
+                android:paddingTop="10dp"
+                android:paddingRight="90dp"
+                android:paddingBottom="10dp"
+                android:text="重置空间状态"
+                android:textColor="@color/black_272727"
+                android:textSize="15sp" />
+        </LinearLayout>
+
         <RelativeLayout
             android:id="@+id/quesDetail"
             android:layout_width="match_parent"
@@ -358,7 +424,9 @@
                     android:layout_width="0dp"
                     android:layout_height="wrap_content"
                     android:layout_gravity="center_horizontal"
+                    android:layout_marginLeft="90dp"
                     android:layout_marginTop="5dp"
+                    android:layout_marginRight="90dp"
                     android:layout_weight="1"
                     android:background="@drawable/bg_blue_3bacb2"
                     android:gravity="center"
@@ -386,7 +454,8 @@
                     android:paddingBottom="5dp"
                     android:text="绘制管道桥架"
                     android:textColor="@color/white"
-                    android:textSize="15sp" />
+                    android:textSize="15sp"
+                    android:visibility="gone" />
             </LinearLayout>
 
             <LinearLayout
@@ -400,7 +469,8 @@
                     android:layout_height="wrap_content"
                     android:text="设备服务当前空间"
                     android:textColor="@color/grey_900"
-                    android:textSize="12sp" />
+                    android:textSize="12sp"
+                    android:visibility="gone" />
 
                 <TextView
                     android:id="@+id/eqSpServe"
@@ -415,7 +485,8 @@
                     android:paddingBottom="2dp"
                     android:text="服务"
                     android:textColor="@color/white"
-                    android:textSize="14sp" />
+                    android:textSize="14sp"
+                    android:visibility="gone" />
 
                 <TextView
                     android:layout_width="wrap_content"
@@ -423,14 +494,16 @@
                     android:layout_marginTop="5dp"
                     android:text="当前空间内设备与设备"
                     android:textColor="@color/grey_900"
-                    android:textSize="12sp" />
+                    android:textSize="12sp"
+                    android:visibility="gone" />
 
                 <LinearLayout
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:layout_marginTop="5dp"
                     android:layout_marginRight="20dp"
-                    android:orientation="horizontal">
+                    android:orientation="horizontal"
+                    android:visibility="gone">
 
                     <TextView
                         android:id="@+id/eqEqCtrl"
@@ -494,7 +567,8 @@
                     android:layout_marginTop="5dp"
                     android:text="当前空间内设备与管道"
                     android:textColor="@color/grey_900"
-                    android:textSize="12sp" />
+                    android:textSize="12sp"
+                    android:visibility="gone" />
 
                 <TextView
                     android:id="@+id/eqPipeCnct"
@@ -509,9 +583,42 @@
                     android:paddingBottom="2dp"
                     android:text="连接"
                     android:textColor="@color/white"
-                    android:textSize="14sp" />
+                    android:textSize="14sp"
+                    android:visibility="gone" />
             </LinearLayout>
 
+            <TextView
+                android:id="@+id/equipDetail"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginTop="5dp"
+                android:alpha="0.5"
+                android:background="@drawable/bg_blue_3bacb2"
+                android:paddingLeft="34dp"
+                android:paddingTop="2dp"
+                android:paddingRight="34dp"
+                android:paddingBottom="2dp"
+                android:text="记录设备信息"
+                android:textColor="@color/white_ffffff"
+                android:textSize="14sp" />
+
+            <TextView
+                android:id="@+id/equipDelete"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginTop="5dp"
+                android:alpha="0.5"
+                android:background="@drawable/bg_blue_3bacb2"
+                android:paddingLeft="16dp"
+                android:paddingTop="2dp"
+                android:paddingRight="16dp"
+                android:paddingBottom="2dp"
+                android:text="删除设备/标记多余"
+                android:textColor="@color/white_ffffff"
+                android:textSize="14sp" />
+
             <CheckBox
                 android:id="@+id/sceneCheckBox"
                 android:layout_width="wrap_content"
@@ -756,5 +863,109 @@
                 android:textSize="15sp" />
         </LinearLayout>
 
+        <androidx.appcompat.widget.LinearLayoutCompat
+            android:id="@+id/problemLl"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@color/white_ffffff"
+            android:orientation="vertical"
+            android:padding="10dp"
+            android:visibility="gone">
+
+            <TextView
+                android:id="@+id/spaceName3"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginBottom="5dp"
+                android:paddingLeft="90dp"
+                android:paddingTop="5dp"
+                android:paddingRight="90dp"
+                android:textColor="@color/grey_900"
+                android:textSize="12sp"
+                tools:text="12" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="问题描述"
+                android:textColor="@color/black_272727"
+                android:textSize="13sp" />
+
+            <androidx.appcompat.widget.AppCompatEditText
+                android:layout_width="match_parent"
+                android:layout_height="60dp"
+                android:layout_marginTop="8dp"
+                android:background="@drawable/bg_gray_f2f3f6"
+                android:gravity="start"
+                android:hint="请输入问题描述"
+                android:padding="5dp"
+                android:textSize="13sp" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:text="照片补充"
+                android:textColor="@color/black_272727"
+                android:textSize="13sp" />
+
+            <com.sybotan.android.demo.view.MaxHeightRecyclerView
+                android:id="@+id/problemPicRecycler"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:minHeight="20dp"
+                app:maxHeight="120dp" />
+
+            <androidx.appcompat.widget.LinearLayoutCompat
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="16dp"
+                android:layout_marginTop="8dp"
+                android:layout_marginRight="16dp"
+                android:orientation="horizontal">
+
+                <TextView
+                    android:id="@+id/problemClose"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:background="@drawable/bg_grey_e6e6e6"
+                    android:gravity="center"
+                    android:paddingTop="5dp"
+                    android:paddingBottom="5dp"
+                    android:text="关闭"
+                    android:textColor="@color/black_272727"
+                    android:textSize="13sp" />
+
+                <TextView
+                    android:id="@+id/problemDone"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginLeft="16dp"
+                    android:layout_weight="1"
+                    android:background="@drawable/bg_grey_e6e6e6"
+                    android:gravity="center"
+                    android:paddingTop="5dp"
+                    android:paddingBottom="5dp"
+                    android:text="已解决"
+                    android:textColor="@color/black_272727"
+                    android:textSize="13sp" />
+
+                <TextView
+                    android:id="@+id/problemSave"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginLeft="16dp"
+                    android:layout_weight="1"
+                    android:background="@drawable/bg_yellow_700"
+                    android:gravity="center"
+                    android:paddingTop="5dp"
+                    android:paddingBottom="5dp"
+                    android:text="保存"
+                    android:textColor="@color/black_272727"
+                    android:textSize="13sp" />
+            </androidx.appcompat.widget.LinearLayoutCompat>
+        </androidx.appcompat.widget.LinearLayoutCompat>
     </RelativeLayout>
 </LinearLayout>

+ 19 - 0
demo/src/main/res/layout/activity_look_big_pic.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/gray_f5f6f7">
+
+
+    <com.bm.library.PhotoView
+        android:id="@+id/look_big_pic_iv"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alignParentBottom="true"
+        android:layout_centerInParent="true"
+        android:scaleType="fitXY" />
+
+
+</RelativeLayout>

+ 74 - 0
demo/src/main/res/layout/activity_newcreate_dialog.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="290dp"
+    android:layout_height="wrap_content"
+    android:background="@drawable/corners_line_black"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/contentTV"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="25dp"
+        android:layout_marginTop="20dp"
+        android:layout_marginRight="25dp"
+        android:gravity="bottom|center_horizontal"
+        android:lineSpacingExtra="8dp"
+        android:text="提示"
+        android:textColor="@color/black_272727"
+        android:textSize="17sp"
+        android:textStyle="bold" />
+
+    <TextView
+        android:id="@+id/tv_info"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="25dp"
+        android:layout_marginTop="20dp"
+        android:layout_marginRight="25dp"
+        android:gravity="bottom|center_horizontal"
+        android:lineSpacingExtra="8dp"
+        android:text="提示"
+        android:textColor="@color/gray_666666"
+        android:textSize="14sp"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="5dp"
+        android:layout_marginBottom="5dp"
+        android:orientation="horizontal"
+        android:visibility="gone">
+
+        <CheckBox
+            android:id="@+id/check_single"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:button="@drawable/checkbox_style" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="12dp"
+            android:text="不再提示"
+            android:textColor="@color/black_272727"
+            android:textSize="16sp" />
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/IKnowTV"
+        android:layout_width="match_parent"
+        android:layout_height="40dp"
+        android:layout_marginLeft="10dp"
+        android:layout_marginTop="20dp"
+        android:layout_marginRight="10dp"
+        android:layout_marginBottom="10dp"
+        android:background="@drawable/shape_rectangle_green_3"
+        android:gravity="center"
+        android:text="我知道了"
+        android:textColor="@color/white_ffffff"
+        android:textSize="15sp" />
+</LinearLayout>

+ 79 - 0
demo/src/main/res/layout/activity_space_detail.xml

@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/white_ffffff">
+
+    <androidx.appcompat.widget.LinearLayoutCompat
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="48dp"
+            android:background="@color/white_ffffff">
+
+            <ImageView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_marginLeft="16dp"
+                android:src="@drawable/ic_round_arrow_back_24" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_centerInParent="true"
+                android:gravity="center"
+                android:text="空间详情"
+                android:textColor="@color/black_272727"
+                android:textSize="20sp" />
+        </RelativeLayout>
+
+        <com.sybotan.android.demo.view.ListItemView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:LeftText="物业分区"
+            app:Type="4" />
+
+        <com.sybotan.android.demo.view.ListItemView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:LeftText="静态属性信息"
+            app:Type="4" />
+
+        <com.sybotan.android.demo.view.ListItemView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:LeftText="空间功能类型"
+            app:Type="0" />
+
+        <include layout="@layout/static_info" />
+
+        <com.sybotan.android.demo.view.ListItemView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:LeftText="对象关系"
+            app:Type="4" />
+
+        <ExpandableListView
+            android:id="@+id/expandLvRelation"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/btn_submit"
+            android:layout_width="match_parent"
+            android:layout_height="40dp"
+            android:layout_gravity="center"
+            android:layout_marginLeft="16dp"
+            android:layout_marginTop="20dp"
+            android:layout_marginRight="16dp"
+            android:background="@drawable/shape_rectangle_green_3"
+            android:gravity="center"
+            android:text="提交"
+            android:textColor="@color/white_ffffff"
+            android:textSize="17dp" />
+    </androidx.appcompat.widget.LinearLayoutCompat>
+</androidx.core.widget.NestedScrollView>

+ 29 - 0
demo/src/main/res/layout/child_expand_relation.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/add"
+        android:layout_width="50dp"
+        android:layout_height="40dp"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="16dp"
+        android:alpha="0.5"
+        android:paddingLeft="17dp"
+        android:paddingRight="17dp"
+        android:src="@mipmap/delete" />
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
+        android:text=""
+        android:textColor="@color/gray_808080"
+        android:textSize="16sp" />
+
+</androidx.appcompat.widget.LinearLayoutCompat>

+ 68 - 0
demo/src/main/res/layout/common_fragment_img_sel.xml

@@ -0,0 +1,68 @@
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/rvImageList"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginBottom="?android:attr/actionBarSize"/>
+
+        <RelativeLayout
+            android:id="@+id/rlBottom"
+            android:layout_width="match_parent"
+            android:layout_height="?android:attr/actionBarSize"
+            android:layout_alignParentBottom="true"
+            android:background="@color/bottom_bg"
+            android:clickable="true">
+
+            <Button
+                android:id="@+id/btnAlbumSelected"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_centerVertical="true"
+                android:background="@null"
+                android:drawablePadding="5dp"
+                android:drawableRight="@drawable/ic_album"
+                android:ellipsize="end"
+                android:gravity="center_vertical"
+                android:paddingLeft="16dp"
+                android:paddingRight="16dp"
+                android:singleLine="true"
+                android:textAllCaps="false"
+                android:textColor="@android:color/white"
+                android:textSize="16sp"/>
+            <Button
+                android:id="@+id/btn_certain"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_alignParentRight="true"
+                android:layout_centerVertical="true"
+                android:layout_marginBottom="5dp"
+                android:layout_marginRight="16dp"
+                android:layout_marginTop="5dp"
+                android:background="@drawable/bg_button_green"
+                android:paddingLeft="16dp"
+                android:paddingRight="16dp"
+                android:singleLine="true"
+                android:text="添加 (3)"
+                android:textAllCaps="false"
+                android:textColor="@android:color/white"
+                android:textSize="15sp"
+                android:visibility="gone"/>
+        </RelativeLayout>
+
+    </RelativeLayout>
+
+    <com.yuyh.library.imgsel.widget.CustomViewPager
+        android:id="@+id/viewPager"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/black"
+        android:visibility="gone"/>
+</FrameLayout>

+ 28 - 0
demo/src/main/res/layout/common_item_img_sel.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.yuyh.library.imgsel.widget.SquareRelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/ivImage"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_centerInParent="true"
+        android:scaleType="centerCrop"
+        android:src="@drawable/ic_default_image"/>
+
+    <ImageView
+        android:id="@+id/ivPhotoCheaked"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentTop="true"
+        android:layout_marginRight="1dp"
+        android:layout_marginTop="1dp"
+        android:contentDescription="@null"
+        android:paddingBottom="20dp"
+        android:paddingLeft="20dp"
+        android:src="@mipmap/choseicon_gray"/>
+
+</com.yuyh.library.imgsel.widget.SquareRelativeLayout>

+ 34 - 0
demo/src/main/res/layout/common_number_control.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/number_control_minus"
+        android:layout_width="30dp"
+        android:layout_height="30dp"
+        android:background="@drawable/bg_rect_stroke_gray_2"
+        android:padding="9dp"
+        android:src="@mipmap/minus" />
+
+    <EditText
+        android:id="@+id/number_control_num"
+        android:layout_width="wrap_content"
+        android:layout_height="30dp"
+        android:background="@drawable/bg_rect_stroke_gray_top_bottom"
+        android:gravity="center"
+        android:inputType="number"
+        android:minWidth="40dp"
+        android:text="1"
+        android:textColor="@color/gray_262626"
+        android:textSize="15sp" />
+
+    <ImageView
+        android:id="@+id/number_control_add"
+        android:layout_width="30dp"
+        android:layout_height="30dp"
+        android:background="@drawable/bg_rect_stroke_gray_2"
+        android:padding="9dp"
+        android:src="@mipmap/plus" />
+</LinearLayout>

+ 57 - 0
demo/src/main/res/layout/dialog_equip_list.xml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="350dp"
+    android:layout_height="wrap_content"
+    android:background="@drawable/bg_white"
+    android:orientation="vertical"
+    android:padding="16dp">
+
+    <LinearLayout
+        android:id="@+id/search_linear"
+        android:layout_width="match_parent"
+        android:layout_height="32dp"
+        android:layout_marginLeft="16dp"
+        android:layout_toLeftOf="@+id/common_search_cancel"
+        android:background="@drawable/bg_round_search"
+        android:orientation="horizontal">
+
+        <ImageView
+            android:id="@+id/search_iv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginLeft="11dp"
+            android:src="@mipmap/icon_search" />
+
+        <EditText
+            android:id="@+id/search_edt"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_marginLeft="10dp"
+            android:layout_weight="1"
+            android:background="@null"
+            android:hint="搜索设备"
+            android:imeOptions="actionSearch"
+            android:singleLine="true"
+            android:textColor="@color/gray_272727"
+            android:textColorHint="@color/gray_a6a6a6"
+            android:textCursorDrawable="@null"
+            android:textSize="15sp" />
+
+        <ImageView
+            android:id="@+id/search_delete"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center_vertical"
+            android:paddingLeft="9dp"
+            android:paddingRight="9dp"
+            android:src="@mipmap/delete_default" />
+    </LinearLayout>
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/recycler"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp" />
+</androidx.appcompat.widget.LinearLayoutCompat>

+ 65 - 0
demo/src/main/res/layout/dialog_list_choice.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_horizontal"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/pop_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentBottom="true"
+        android:background="@color/white_ffffff"
+        android:gravity="center_horizontal"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="55dp"
+            android:background="@drawable/common_line_ffffff_single_all"
+            android:gravity="center"
+            android:paddingLeft="10dp"
+            android:text="请选择项目"
+            android:textColor="@color/gray_a6a6a6"
+            android:textSize="15sp" />
+
+
+        <Button
+            android:id="@+id/btn1"
+            style="?android:attr/borderlessButtonStyle"
+            android:layout_width="match_parent"
+            android:layout_height="55dp"
+            android:layout_marginLeft="16dp"
+            android:layout_marginRight="16dp"
+            android:background="@color/white_ffffff"
+            android:gravity="center"
+            android:text="全部"
+            android:textColor="@color/gray_272727"
+            android:textSize="16sp" />
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/recyclerview"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:background="@color/white_ffffff"
+            android:overScrollMode="never"
+            android:scrollbarThumbVertical="@color/gray_d8d8d8"
+            android:scrollbars="vertical" />
+
+        <Button
+            android:id="@+id/btn_cancel"
+            style="?android:attr/borderlessButtonStyle"
+            android:layout_width="match_parent"
+            android:layout_height="55dp"
+            android:background="@drawable/common_line_ffffff_top_8"
+            android:gravity="center"
+            android:text="取消"
+            android:textColor="@color/gray_272727"
+            android:textSize="16sp"
+            android:visibility="gone" />
+    </LinearLayout>
+</RelativeLayout>

+ 133 - 0
demo/src/main/res/layout/dialog_object_relation.xml

@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="350dp"
+    android:layout_height="wrap_content"
+    android:background="@drawable/bg_white"
+    android:orientation="vertical"
+    android:padding="16dp">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="对象关系"
+        android:textColor="@color/gray_272727"
+        android:textSize="16sp" />
+
+    <TextView
+        android:id="@+id/relationTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="26dp"
+        android:textColor="@color/gray_272727"
+        android:textSize="16sp"
+        tools:text="冷冻水供水管" />
+
+    <androidx.appcompat.widget.LinearLayoutCompat
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="14dp"
+        android:gravity="center_vertical">
+
+        <TextView
+            android:id="@+id/leftName"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="@color/gray_808080"
+            android:textSize="16sp"
+            tools:text="供热燃煤锅炉" />
+
+        <TextView
+            android:id="@+id/relationTv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="40dp"
+            android:background="@color/gray_f5f6f7"
+            android:paddingLeft="10dp"
+            android:paddingTop="7dp"
+            android:paddingRight="10dp"
+            android:paddingBottom="7dp"
+            android:textColor="@color/gray_272727"
+            android:textSize="16sp"
+            tools:text="->" />
+
+        <TextView
+            android:id="@+id/rightName"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="40dp"
+            android:textColor="@color/gray_808080"
+            android:textSize="16sp"
+            tools:text="某某某设备" />
+    </androidx.appcompat.widget.LinearLayoutCompat>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="30dp"
+        android:text="选择方向"
+        android:textColor="@color/gray_272727"
+        android:textSize="16sp" />
+
+    <androidx.appcompat.widget.LinearLayoutCompat
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="19dp"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/toright"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:background="@color/gray_f5f6f7"
+            android:paddingLeft="20dp"
+            android:paddingTop="7dp"
+            android:paddingRight="20dp"
+            android:paddingBottom="7dp"
+            android:textColor="@color/gray_272727"
+            android:textSize="16sp"
+            tools:text="@string/toright" />
+
+        <TextView
+            android:id="@+id/toleft"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="50dp"
+            android:background="@color/gray_f5f6f7"
+            android:paddingLeft="20dp"
+            android:paddingTop="7dp"
+            android:paddingRight="20dp"
+            android:paddingBottom="7dp"
+            android:textColor="@color/gray_272727"
+            android:textSize="16sp"
+            tools:text="@string/toleft" />
+
+        <TextView
+            android:id="@+id/connect"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="50dp"
+            android:background="@color/gray_f5f6f7"
+            android:paddingLeft="20dp"
+            android:paddingTop="7dp"
+            android:paddingRight="20dp"
+            android:paddingBottom="7dp"
+            android:textColor="@color/gray_272727"
+            android:textSize="16sp"
+            tools:text="--" />
+    </androidx.appcompat.widget.LinearLayoutCompat>
+
+    <TextView
+        android:id="@+id/btn_submit"
+        android:layout_width="match_parent"
+        android:layout_height="40dp"
+        android:layout_gravity="center"
+        android:layout_marginLeft="16dp"
+        android:layout_marginTop="50dp"
+        android:layout_marginRight="16dp"
+        android:background="@drawable/shape_rectangle_green_3"
+        android:gravity="center"
+        android:text="确定"
+        android:textColor="@color/white_ffffff"
+        android:textSize="17dp" />
+</androidx.appcompat.widget.LinearLayoutCompat>

+ 35 - 0
demo/src/main/res/layout/geometry_info.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="55dp"
+        android:layout_marginLeft="16dp"
+        android:gravity="center_vertical"
+        android:text="模型几何信息"
+        android:textColor="@color/gray_272727"
+        android:textSize="15sp"
+        android:textStyle="bold" />
+
+    <com.sybotan.android.demo.view.ListItemView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:LeftText="中心位置点"
+        app:Type="4" />
+
+    <com.sybotan.android.demo.view.ListItemView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:LeftText="高度"
+        app:Type="4" />
+
+    <com.sybotan.android.demo.view.ListItemView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:LeftText="位置"
+        app:Type="4" />
+</androidx.appcompat.widget.LinearLayoutCompat>

+ 28 - 0
demo/src/main/res/layout/group_expand_relation.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="36dp"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
+        android:layout_weight="1"
+        android:text=""
+        android:textColor="@color/gray_272727"
+        android:textSize="16sp" />
+
+    <ImageView
+        android:id="@+id/add"
+        android:layout_width="50dp"
+        android:layout_height="40dp"
+        android:layout_gravity="center_vertical"
+        android:layout_marginRight="16dp"
+        android:paddingLeft="17dp"
+        android:paddingRight="17dp"
+        android:src="@mipmap/add_blue" />
+</androidx.appcompat.widget.LinearLayoutCompat>

+ 34 - 0
demo/src/main/res/layout/item_node_nine_pic.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="20dp"
+    android:layout_marginRight="15dp"
+    android:layout_marginBottom="20dp">
+
+    <ImageView
+        android:id="@+id/node_nine_pic_iv"
+        android:layout_width="104dp"
+        android:layout_height="104dp"
+        android:scaleType="centerCrop"
+        android:src="@mipmap/add_pic"/>
+
+    <ImageView
+        android:id="@+id/node_nine_pic_delete"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_alignRight="@+id/node_nine_pic_iv"
+        android:paddingTop="8dp"
+        android:paddingRight="8dp"
+        android:src="@mipmap/icon_sel_image_close"
+        android:visibility="gone" />
+
+    <TextView
+        android:id="@+id/node_nine_pic_position"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="8dp"
+        android:textColor="@color/white_ffffff"
+        android:textSize="18sp" />
+</RelativeLayout>

+ 70 - 0
demo/src/main/res/layout/layout_common_list_item.xml

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/rl_root"
+    android:layout_width="match_parent"
+    android:layout_height="55dp"
+    android:background="@color/white_ffffff">
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginLeft="16dp">
+
+        <ImageView
+            android:id="@+id/iv_must"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginRight="4dp"
+            android:src="@mipmap/icon_must"
+            android:visibility="gone" />
+
+        <TextView
+            android:id="@+id/tv_left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginRight="8dp"
+            android:layout_toRightOf="@+id/iv_must"
+            android:singleLine="true"
+            android:textColor="@color/gray_272727"
+            android:textSize="15sp" />
+    </RelativeLayout>
+
+    <ImageView
+        android:id="@+id/iv_right"
+        android:layout_width="7dp"
+        android:layout_height="12dp"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:layout_marginRight="16dp"
+        android:src="@mipmap/cell_jiantouicon" />
+
+    <TextView
+        android:id="@+id/tv_right"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginRight="4dp"
+        android:layout_toLeftOf="@id/iv_right"
+        android:ellipsize="end"
+        android:gravity="end"
+        android:hint="请选择"
+        android:maxEms="10"
+        android:singleLine="true"
+        android:textColor="@color/black_272727"
+        android:textColorHint="@color/gray_4D272727"
+        android:textSize="15sp"
+
+        />
+
+    <TextView
+        android:id="@+id/tv_line"
+        android:layout_width="match_parent"
+        android:layout_height="1px"
+        android:layout_alignParentBottom="true"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="16dp"
+        android:background="@color/gray_e6e6e6" />
+</RelativeLayout>

+ 72 - 0
demo/src/main/res/layout/layout_common_list_item1.xml

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="60dp">
+    <!--android:background="@drawable/createclickbg"-->
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginLeft="16dp">
+
+        <ImageView
+            android:id="@+id/iv_must"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginRight="4dp"
+            android:src="@mipmap/icon_must"
+            android:visibility="gone" />
+
+        <TextView
+            android:id="@+id/tv_left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginRight="10dp"
+            android:layout_toRightOf="@+id/iv_must"
+            android:singleLine="true"
+            android:textColor="@color/black_272727"
+            android:textSize="16sp" />
+    </RelativeLayout>
+
+    <ImageView
+        android:id="@+id/iv_right"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:layout_marginRight="15dp"
+        android:src="@mipmap/cell_jiantouicon" />
+
+    <TextView
+        android:id="@+id/tv_right"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginRight="20dp"
+        android:layout_toLeftOf="@id/iv_right"
+        android:gravity="right"
+        android:singleLine="true"
+        android:text="人"
+        android:textColor="@color/black_272727"
+        android:textSize="15sp" />
+
+    <EditText
+        android:id="@+id/et_right"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginRight="8dp"
+        android:layout_toLeftOf="@id/tv_right"
+        android:background="@null"
+        android:gravity="right"
+        android:hint="请输入"
+        android:inputType="number"
+        android:singleLine="true"
+        android:textColor="@color/black_272727"
+        android:textColorHint="@color/gray_a6a6a6"
+        android:textSize="15sp" />
+
+</RelativeLayout>

+ 94 - 0
demo/src/main/res/layout/layout_common_list_item2.xml

@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="60dp">
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginLeft="16dp">
+
+        <ImageView
+            android:id="@+id/iv_must"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginRight="4dp"
+            android:src="@mipmap/icon_must"
+            android:visibility="gone" />
+
+        <TextView
+            android:id="@+id/tv_left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginRight="10dp"
+            android:layout_toRightOf="@+id/iv_must"
+            android:singleLine="true"
+            android:text="选择延期时长"
+            android:textColor="@color/black_272727"
+            android:textSize="16sp" />
+    </RelativeLayout>
+
+    <TextView
+        android:id="@+id/tv_right"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:layout_marginRight="16dp"
+        android:gravity="right"
+        android:singleLine="true"
+        android:text="小时"
+        android:textColor="@color/black_272727"
+        android:textSize="15sp" />
+
+    <EditText
+        android:id="@+id/et_right"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginRight="8dp"
+        android:layout_toLeftOf="@id/tv_right"
+        android:background="@null"
+        android:gravity="right"
+        android:hint=""
+        android:inputType="number"
+        android:minWidth="20dp"
+        android:singleLine="true"
+        android:textColor="@color/black_272727"
+        android:textColorHint="@color/gray_a6a6a6"
+        android:textSize="15sp" />
+
+    <TextView
+        android:id="@+id/tv_right1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginRight="16dp"
+        android:layout_toLeftOf="@id/et_right"
+        android:gravity="right"
+        android:singleLine="true"
+        android:text="天"
+        android:textColor="@color/black_272727"
+        android:textSize="15sp" />
+
+    <EditText
+        android:id="@+id/et_right1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginRight="8dp"
+        android:layout_toLeftOf="@id/tv_right1"
+        android:background="@null"
+        android:gravity="right"
+        android:hint=""
+        android:inputType="number"
+        android:minWidth="32dp"
+        android:singleLine="true"
+        android:textColor="@color/black_272727"
+        android:textColorHint="@color/gray_a6a6a6"
+        android:textSize="15sp" />
+
+</RelativeLayout>

+ 83 - 0
demo/src/main/res/layout/layout_common_list_item3.xml

@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="57dp"
+    android:background="@color/white_ffffff"
+    android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/iv_left"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginLeft="19dp"
+        android:src="@mipmap/icon_green" />
+
+    <TextView
+        android:id="@+id/tv_left_top"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="15dp"
+        android:layout_marginTop="10dp"
+        android:layout_marginRight="8dp"
+        android:layout_toRightOf="@id/iv_left"
+        android:singleLine="true"
+        android:text="4F-四楼"
+        android:textColor="@color/blue_2F3775"
+        android:textSize="16sp" />
+
+    <TextView
+        android:id="@+id/tv_left_bottom"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/tv_left_top"
+        android:layout_marginLeft="15dp"
+        android:layout_marginRight="8dp"
+        android:layout_toRightOf="@id/iv_left"
+        android:singleLine="true"
+        android:text="sdlfksjdl"
+        android:textColor="@color/gray_a6a6a6"
+        android:textSize="12sp" />
+
+    <ImageView
+        android:layout_width="12dp"
+        android:layout_height="12dp"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:layout_gravity="center_vertical|right"
+        android:layout_marginRight="16dp"
+        android:src="@mipmap/cell_jiantouicon"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:id="@+id/error_state_linear"
+        android:layout_width="wrap_content"
+        android:layout_height="18dp"
+        android:layout_alignParentRight="true"
+        android:layout_marginTop="15dp"
+        android:layout_marginRight="16dp"
+        android:background="@drawable/bg_gray_f2f3f6"
+        android:orientation="horizontal"
+        android:paddingLeft="6dp"
+        android:paddingRight="6dp"
+        android:visibility="gone">
+
+        <ImageView
+            android:id="@+id/error_state_iv"
+            android:layout_width="12dp"
+            android:layout_height="12dp"
+            android:layout_gravity="center_vertical"
+            android:src="@mipmap/icon_error" />
+
+        <TextView
+            android:id="@+id/error_state_tv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginLeft="4dp"
+            android:text="异常点2个"
+            android:textColor="@color/gray_666666"
+            android:textSize="12sp" />
+
+    </LinearLayout>
+</RelativeLayout>

+ 0 - 0
demo/src/main/res/layout/layout_common_list_item4.xml


Some files were not shown because too many files changed in this diff