mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
fix: better swipe gesture support for swipe reveal layout
This commit is contained in:
Generated
+2
-2
@@ -4,10 +4,10 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2024-05-15T21:46:48.001145026Z">
|
||||
<DropdownSelection timestamp="2024-05-17T16:06:34.884444646Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.240.112:5555;connection=fd32ca23" />
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.240.112:5555;connection=084079ce" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.itsaky.androidide.activities.editor
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller.SessionCallback
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
@@ -30,6 +31,7 @@ import android.text.TextUtils
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.ActivityResult
|
||||
@@ -41,6 +43,8 @@ import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.collection.MutableIntIntMap
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.core.view.updatePaddingRelative
|
||||
import com.blankj.utilcode.constant.MemoryConstants
|
||||
import com.blankj.utilcode.util.ConvertUtils.byte2MemorySize
|
||||
@@ -54,6 +58,9 @@ import com.github.mikephil.charting.data.LineDataSet
|
||||
import com.github.mikephil.charting.formatter.IAxisValueFormatter
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
||||
import com.google.android.material.shape.CornerFamily
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayout.Tab
|
||||
@@ -84,6 +91,7 @@ import com.itsaky.androidide.projects.ProjectManagerImpl
|
||||
import com.itsaky.androidide.tasks.cancelIfActive
|
||||
import com.itsaky.androidide.ui.CodeEditorView
|
||||
import com.itsaky.androidide.ui.ContentTranslatingDrawerLayout
|
||||
import com.itsaky.androidide.ui.SwipeRevealLayout
|
||||
import com.itsaky.androidide.uidesigner.UIDesignerActivity
|
||||
import com.itsaky.androidide.utils.ActionMenuUtils.createMenu
|
||||
import com.itsaky.androidide.utils.ApkInstallationSessionCallback
|
||||
@@ -104,6 +112,7 @@ import org.greenrobot.eventbus.ThreadMode.MAIN
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.File
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
/**
|
||||
@@ -157,10 +166,11 @@ abstract class BaseEditorActivity : EdgeToEdgeIDEActivity(), TabLayout.OnTabSele
|
||||
private val memoryUsageListener = MemoryUsageWatcher.MemoryUsageListener { memoryUsage ->
|
||||
memoryUsage.forEachValue { proc ->
|
||||
_binding?.memUsageView?.chart?.apply {
|
||||
val dataset = (data.getDataSetByIndex(pidToDatasetIdxMap[proc.pid]) as LineDataSet?) ?: run {
|
||||
log.error("No dataset found for process: {}: {}", proc.pid, proc.pname)
|
||||
return@forEachValue
|
||||
}
|
||||
val dataset = (data.getDataSetByIndex(pidToDatasetIdxMap[proc.pid]) as LineDataSet?)
|
||||
?: run {
|
||||
log.error("No dataset found for process: {}: {}", proc.pid, proc.pname)
|
||||
return@forEachValue
|
||||
}
|
||||
|
||||
dataset.entries.mapIndexed { index, entry ->
|
||||
entry.y = byte2MemorySize(proc.usageHistory[index], MemoryConstants.MB).toFloat()
|
||||
@@ -174,13 +184,21 @@ abstract class BaseEditorActivity : EdgeToEdgeIDEActivity(), TabLayout.OnTabSele
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val editorSurfaceContainerBackground by lazy {
|
||||
resolveAttr(R.attr.colorSurfaceDim)
|
||||
}
|
||||
|
||||
private var optionsMenuInvalidator: Runnable? = null
|
||||
|
||||
companion object {
|
||||
@JvmStatic protected val PROC_IDE = "IDE"
|
||||
@JvmStatic protected val PROC_GRADLE_TOOLING = "Gradle Tooling"
|
||||
@JvmStatic protected val PROC_GRADLE_DAEMON = "Gradle Daemon"
|
||||
|
||||
@JvmStatic
|
||||
protected val PROC_IDE = "IDE"
|
||||
@JvmStatic
|
||||
protected val PROC_GRADLE_TOOLING = "Gradle Tooling"
|
||||
@JvmStatic
|
||||
protected val PROC_GRADLE_DAEMON = "Gradle Daemon"
|
||||
|
||||
@JvmStatic
|
||||
protected val log: Logger = LoggerFactory.getLogger(BaseEditorActivity::class.java)
|
||||
@@ -244,6 +262,9 @@ abstract class BaseEditorActivity : EdgeToEdgeIDEActivity(), TabLayout.OnTabSele
|
||||
drawerSidebar.getFragment<EditorSidebarFragment>()
|
||||
.onApplyWindowInsets(insets)
|
||||
|
||||
editorAppBarLayout.updatePadding(
|
||||
top = insets.top
|
||||
)
|
||||
editorToolbar.updatePaddingRelative(
|
||||
start = editorToolbar.paddingStart + insets.left,
|
||||
end = editorToolbar.paddingEnd + insets.right
|
||||
@@ -302,6 +323,18 @@ abstract class BaseEditorActivity : EdgeToEdgeIDEActivity(), TabLayout.OnTabSele
|
||||
watchMemory()
|
||||
}
|
||||
|
||||
private fun onSwipeRevealDragProgress(progress: Float) {
|
||||
_binding?.apply {
|
||||
val insetsTop = lastWindowInsets?.top ?: 0
|
||||
editorAppBarLayout.updatePadding(
|
||||
top = (insetsTop * (1f - progress)).roundToInt()
|
||||
)
|
||||
memUsageView.chart.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = (insetsTop * progress).roundToInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupMemUsageChart() {
|
||||
binding.memUsageView.chart.apply {
|
||||
val colorAccent = resolveAttr(R.attr.colorAccent)
|
||||
@@ -312,7 +345,7 @@ abstract class BaseEditorActivity : EdgeToEdgeIDEActivity(), TabLayout.OnTabSele
|
||||
axisRight.axisLineColor = colorAccent
|
||||
|
||||
setPinchZoom(false)
|
||||
setBackgroundColor(resolveAttr(R.attr.colorSurfaceContainer))
|
||||
setBackgroundColor(editorSurfaceContainerBackground)
|
||||
setDrawGridBackground(true)
|
||||
setScaleEnabled(true)
|
||||
|
||||
@@ -335,9 +368,13 @@ abstract class BaseEditorActivity : EdgeToEdgeIDEActivity(), TabLayout.OnTabSele
|
||||
protected fun resetMemUsageChart() {
|
||||
val processes = memoryUsageWatcher.getMemoryUsages()
|
||||
val datasets = Array(processes.size) { index ->
|
||||
LineDataSet(List(MemoryUsageWatcher.MAX_USAGE_ENTRIES) { Entry(it.toFloat(), 0f) }, processes[index].pname)
|
||||
LineDataSet(List(MemoryUsageWatcher.MAX_USAGE_ENTRIES) { Entry(it.toFloat(), 0f) },
|
||||
processes[index].pname)
|
||||
}
|
||||
|
||||
val bgColor = editorSurfaceContainerBackground
|
||||
val textColor = resolveAttr(R.attr.colorOnSurface)
|
||||
|
||||
for ((index, proc) in processes.withIndex()) {
|
||||
val dataset = datasets[index]
|
||||
dataset.color = getMemUsageLineColorFor(proc)
|
||||
@@ -351,8 +388,17 @@ abstract class BaseEditorActivity : EdgeToEdgeIDEActivity(), TabLayout.OnTabSele
|
||||
pidToDatasetIdxMap[proc.pid] = index
|
||||
}
|
||||
|
||||
binding.memUsageView.chart.setBackgroundColor(bgColor)
|
||||
|
||||
binding.memUsageView.chart.apply {
|
||||
data = LineData(*datasets)
|
||||
axisRight.textColor = textColor
|
||||
axisLeft.textColor = textColor
|
||||
legend.textColor = textColor
|
||||
|
||||
data.setValueTextColor(textColor)
|
||||
setBackgroundColor(bgColor)
|
||||
setGridBackgroundColor(bgColor)
|
||||
notifyDataSetChanged()
|
||||
invalidate()
|
||||
}
|
||||
@@ -603,6 +649,13 @@ abstract class BaseEditorActivity : EdgeToEdgeIDEActivity(), TabLayout.OnTabSele
|
||||
app.prefManager.putBoolean(KEY_BOTTOM_SHEET_SHOWN, true)
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
binding.swipeReveal.dragListener = object : SwipeRevealLayout.OnDragListener {
|
||||
override fun onDragStateChanged(swipeRevealLayout: SwipeRevealLayout, state: Int) {}
|
||||
override fun onDragProgress(swipeRevealLayout: SwipeRevealLayout, progress: Float) {
|
||||
onSwipeRevealDragProgress(progress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupNoEditorView() {
|
||||
@@ -664,9 +717,10 @@ abstract class BaseEditorActivity : EdgeToEdgeIDEActivity(), TabLayout.OnTabSele
|
||||
|
||||
val observer: OnGlobalLayoutListener = object : OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
binding.let {
|
||||
it.viewContainer.pivotY = 0f
|
||||
it.viewContainer.pivotX = it.viewContainer.width / 2f
|
||||
binding.also {
|
||||
it.realContainer.pivotX = it.realContainer.width.toFloat() / 2f
|
||||
it.realContainer.pivotY = (it.realContainer.height.toFloat() / 2f) + (lastWindowInsets?.height()
|
||||
?: 0)
|
||||
it.viewContainer.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.os.Bundle
|
||||
import android.os.PersistableBundle
|
||||
import android.view.WindowInsets
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.core.view.OnApplyWindowInsetsListener
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.doOnAttach
|
||||
@@ -66,6 +67,11 @@ abstract class EdgeToEdgeIDEActivity : IDEActivity() {
|
||||
*/
|
||||
protected open var decorViewPadding: Rect? = null
|
||||
|
||||
/**
|
||||
* Last window insets.
|
||||
*/
|
||||
protected open var lastWindowInsets: Rect? = null
|
||||
|
||||
override var enableSystemBarTheming: Boolean
|
||||
get() = false
|
||||
set(@Suppress("UNUSED_PARAMETER") value) {
|
||||
@@ -144,5 +150,8 @@ abstract class EdgeToEdgeIDEActivity : IDEActivity() {
|
||||
}
|
||||
|
||||
/** Called whenever insets are updated */
|
||||
protected open fun onInsetsUpdated(insets: Rect) {}
|
||||
@CallSuper
|
||||
protected open fun onInsetsUpdated(insets: Rect) {
|
||||
this.lastWindowInsets = insets
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,24 @@ package com.itsaky.androidide.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.View.OnLayoutChangeListener
|
||||
import android.widget.RelativeLayout
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.IntRange
|
||||
import androidx.customview.widget.ViewDragHelper
|
||||
import com.blankj.utilcode.util.SizeUtils
|
||||
import com.google.android.material.shape.CornerFamily
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
import com.itsaky.androidide.R
|
||||
import com.itsaky.androidide.utils.resolveAttr
|
||||
import org.slf4j.LoggerFactory
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* A layout which can be dragged vertically to reveal a hidden content.
|
||||
@@ -44,7 +48,7 @@ open class SwipeRevealLayout @JvmOverloads constructor(
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
defStyleRes: Int = 0,
|
||||
) : RelativeLayout(context, attrs, defStyleAttr, defStyleRes) {
|
||||
) : ViewGroup(context, attrs, defStyleAttr, defStyleRes) {
|
||||
|
||||
/**
|
||||
* Interface for listening to drag events.
|
||||
@@ -62,40 +66,26 @@ open class SwipeRevealLayout @JvmOverloads constructor(
|
||||
fun onDragProgress(swipeRevealLayout: SwipeRevealLayout, progress: Float)
|
||||
}
|
||||
|
||||
private val onLayoutChangeListener = OnLayoutChangeListener { v, _, _, _, _, oldLeft, oldTop, oldRight, oldBottom ->
|
||||
if (isDragging) {
|
||||
v.left = oldLeft
|
||||
v.top = oldTop
|
||||
v.right = oldRight
|
||||
v.bottom = oldBottom
|
||||
}
|
||||
}
|
||||
|
||||
private val dragHelperCallback = object : ViewDragHelper.Callback() {
|
||||
override fun tryCaptureView(child: View, pointerId: Int): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getOrderedChildIndex(index: Int): Int {
|
||||
val idx = dragHandleContainerView?.let(this@SwipeRevealLayout::indexOfChild) ?: index
|
||||
if (idx > index) {
|
||||
return idx
|
||||
}
|
||||
|
||||
return index
|
||||
return child === overlappingContent
|
||||
}
|
||||
|
||||
override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) {
|
||||
draggingViewTop = top
|
||||
onDragProgress(min(0f, max(1f, top.toFloat() / dragRangeVertical.toFloat())))
|
||||
onDragProgress(min(1f, top.toFloat() / dragHeightMax.toFloat()))
|
||||
}
|
||||
|
||||
override fun getViewVerticalDragRange(child: View): Int {
|
||||
return dragRangeVertical
|
||||
return dragHeightMax
|
||||
}
|
||||
|
||||
override fun getOrderedChildIndex(index: Int): Int {
|
||||
return OVERLAPPING_CONTENT_INDEX
|
||||
}
|
||||
|
||||
override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
|
||||
return min(max(top, paddingTop), dragRangeVertical)
|
||||
return min(max(top, paddingTop), dragHeightMax)
|
||||
}
|
||||
|
||||
override fun onViewDragStateChanged(state: Int) {
|
||||
@@ -103,12 +93,11 @@ open class SwipeRevealLayout @JvmOverloads constructor(
|
||||
return
|
||||
}
|
||||
|
||||
if (isDragging && state == ViewDragHelper.STATE_IDLE && draggingViewTop == dragRangeVertical) {
|
||||
isOpen = false
|
||||
if (isDragging && state == ViewDragHelper.STATE_IDLE) {
|
||||
isOpen = draggingViewTop >= dragHeightMax
|
||||
}
|
||||
|
||||
draggingState = state
|
||||
onDragStateChanged(draggingState)
|
||||
onDragStateChanged(state)
|
||||
}
|
||||
|
||||
override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) {
|
||||
@@ -117,20 +106,19 @@ open class SwipeRevealLayout @JvmOverloads constructor(
|
||||
return
|
||||
}
|
||||
|
||||
if (draggingViewTop == dragRangeVertical) {
|
||||
if (draggingViewTop >= dragHeightMax) {
|
||||
isOpen = true
|
||||
return
|
||||
}
|
||||
|
||||
// whether the view should settle to open or close
|
||||
val settleDestY = if (yvel > AUTO_OPEN_VELOCITY_LIM || draggingViewTop > dragRangeVertical / 2) {
|
||||
dragRangeVertical
|
||||
val settleDestY = if (yvel > AUTO_OPEN_VELOCITY_LIM || draggingViewTop > dragHeightMax / 2) {
|
||||
dragHeightMax
|
||||
} else {
|
||||
dragLowerLimit
|
||||
paddingTop
|
||||
}
|
||||
|
||||
if (dragHelper.settleCapturedViewAt(0, settleDestY)) {
|
||||
left = 0
|
||||
this@SwipeRevealLayout.postInvalidateOnAnimation()
|
||||
}
|
||||
}
|
||||
@@ -144,36 +132,18 @@ open class SwipeRevealLayout @JvmOverloads constructor(
|
||||
return field
|
||||
}
|
||||
|
||||
private var dragHandleContainerView: View? = null
|
||||
get() {
|
||||
if (field == null) {
|
||||
field = findViewById<View?>(dragHandleContainerViewId)?.also {
|
||||
it.removeOnLayoutChangeListener(onLayoutChangeListener)
|
||||
it.addOnLayoutChangeListener(onLayoutChangeListener)
|
||||
}
|
||||
}
|
||||
private val hiddenContent: View
|
||||
get() = getChildAt(HIDDEN_CONTENT_INDEX)!!
|
||||
|
||||
if (field == null) {
|
||||
field = dragHandleView
|
||||
}
|
||||
|
||||
return field
|
||||
}
|
||||
|
||||
private var dragHiddenContent: View? = null
|
||||
get() {
|
||||
if (field == null) {
|
||||
field = findViewById(dragHiddenContentId)
|
||||
}
|
||||
return field
|
||||
}
|
||||
|
||||
private var isOpen = false
|
||||
private val overlappingContent: View
|
||||
get() = getChildAt(OVERLAPPING_CONTENT_INDEX)!!
|
||||
|
||||
private var initialDragX = 0f
|
||||
private var initialDragY = 0f
|
||||
private var draggingState = -1
|
||||
private var draggingViewTop = 0
|
||||
private var dragRangeVertical = 0
|
||||
get() = dragHiddenContent?.height ?: maxDragHeight.takeIf { it >= 0 } ?: field
|
||||
private val dragHeightMax
|
||||
get() = hiddenContent.height
|
||||
|
||||
private lateinit var dragHelper: ViewDragHelper
|
||||
|
||||
@@ -191,52 +161,44 @@ open class SwipeRevealLayout @JvmOverloads constructor(
|
||||
var dragHandleViewId = 0
|
||||
|
||||
/**
|
||||
* The ID of the outermost view containing [dragHandleViewId] which will be dragged.
|
||||
*/
|
||||
@IdRes
|
||||
var dragHandleContainerViewId = 0
|
||||
|
||||
/**
|
||||
* The ID of the view which is shown when the view is open.
|
||||
*/
|
||||
@IdRes
|
||||
var dragHiddenContentId = 0
|
||||
|
||||
/**
|
||||
* The % of this view's height at which the dragged view will be settled after open.
|
||||
* The current drag progress.
|
||||
*/
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
var draggedViewSettleRatio = 0.0
|
||||
|
||||
/**
|
||||
* The maximum height of the drag container view when it is open.
|
||||
*/
|
||||
@IntRange(from = -1)
|
||||
var maxDragHeight = -1
|
||||
|
||||
/**
|
||||
* The minimum height of the drag container view when it is closed.
|
||||
*/
|
||||
@IntRange(from = 0)
|
||||
var dragLowerLimit = 0
|
||||
var dragProgress = 0.0f
|
||||
private set
|
||||
|
||||
/**
|
||||
* Listener for drag events.
|
||||
*/
|
||||
var dragListener: OnDragListener? = null
|
||||
|
||||
/**
|
||||
* Whether the view is open.
|
||||
*/
|
||||
var isOpen = false
|
||||
protected set
|
||||
|
||||
/**
|
||||
* The corner radius of the overlapping content.
|
||||
*/
|
||||
var overlappingContentCorners = SizeUtils.dp2px(28f).toFloat()
|
||||
|
||||
companion object {
|
||||
|
||||
private const val HIDDEN_CONTENT_INDEX = 0
|
||||
private const val OVERLAPPING_CONTENT_INDEX = 1
|
||||
private val log = LoggerFactory.getLogger(SwipeRevealLayout::class.java)
|
||||
|
||||
@Suppress("UNUSED")
|
||||
const val STATE_IDLE = ViewDragHelper.STATE_IDLE
|
||||
|
||||
@Suppress("UNUSED")
|
||||
const val STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING
|
||||
|
||||
@Suppress("UNUSED")
|
||||
const val STATE_SETTLING = ViewDragHelper.STATE_SETTLING
|
||||
|
||||
const val AUTO_OPEN_VELOCITY_LIM = 800.0
|
||||
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
const val DEFAULT_DRAGGED_VIEW_SETTLE_RATIO = 0.5
|
||||
}
|
||||
|
||||
init {
|
||||
@@ -245,10 +207,6 @@ open class SwipeRevealLayout @JvmOverloads constructor(
|
||||
defStyleAttr, defStyleRes)
|
||||
dragHandleViewId = typedArray.getResourceId(R.styleable.SwipeRevealLayout_dragHandle,
|
||||
dragHandleViewId)
|
||||
dragHandleContainerViewId = typedArray.getResourceId(
|
||||
R.styleable.SwipeRevealLayout_dragHandleContainer, dragHandleContainerViewId)
|
||||
dragHiddenContentId = typedArray.getResourceId(
|
||||
R.styleable.SwipeRevealLayout_dragHiddenContentLayout, dragHiddenContentId)
|
||||
typedArray.recycle()
|
||||
}
|
||||
}
|
||||
@@ -258,30 +216,69 @@ open class SwipeRevealLayout @JvmOverloads constructor(
|
||||
this.dragHelper = ViewDragHelper.create(this, dragHelperCallback)
|
||||
this.isOpen = false
|
||||
|
||||
// call the getter to initialize the view and set the listener
|
||||
dragHandleContainerView
|
||||
check(childCount == 2) {
|
||||
"SwipeRevealLayout must have exactly two children; the hidden content and the overlapping content"
|
||||
}
|
||||
|
||||
val shapeAppearanceModel = ShapeAppearanceModel.Builder().apply {
|
||||
setAllCorners(CornerFamily.ROUNDED, overlappingContentCorners)
|
||||
}.build()
|
||||
|
||||
val realBg = MaterialShapeDrawable(shapeAppearanceModel)
|
||||
realBg.fillColor = ColorStateList.valueOf(context.resolveAttr(R.attr.colorSurface))
|
||||
realBg.interpolation = 0f
|
||||
overlappingContent.background = realBg
|
||||
}
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
dragRangeVertical = (h * draggedViewSettleRatio).roundToInt()
|
||||
if (dragRangeVertical <= 0) {
|
||||
dragRangeVertical = (h * DEFAULT_DRAGGED_VIEW_SETTLE_RATIO).roundToInt()
|
||||
}
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
measureChildren(widthMeasureSpec, heightMeasureSpec)
|
||||
|
||||
val maxWidth = MeasureSpec.getSize(widthMeasureSpec)
|
||||
val maxHeight = MeasureSpec.getSize(heightMeasureSpec)
|
||||
|
||||
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
|
||||
resolveSizeAndState(maxHeight, heightMeasureSpec, 0))
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
||||
hiddenContent.layout(0, paddingTop, r, paddingTop + hiddenContent.measuredHeight)
|
||||
|
||||
val olapTop = paddingTop + (hiddenContent.height * dragProgress).toInt()
|
||||
overlappingContent.layout(0, olapTop, r, b)
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
||||
return isInTargetView(ev) && dragHelper.shouldInterceptTouchEvent(ev)
|
||||
return isViewHit(dragHandleView!!, ev.x.toInt(), ev.y.toInt()) && dragHelper.shouldInterceptTouchEvent(ev)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
if (isInTargetView(event) || isDragging) {
|
||||
dragHelper.processTouchEvent(event)
|
||||
return true
|
||||
dragHelper.processTouchEvent(event)
|
||||
|
||||
val x = event.x
|
||||
val xInt = x.toInt()
|
||||
val y = event.y
|
||||
val yInt = y.toInt()
|
||||
val isInHandle = dragHelper.isViewUnder(dragHandleView, xInt, yInt)
|
||||
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
initialDragX = x
|
||||
initialDragY = y
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
val dx = x - initialDragX
|
||||
val dy = y - initialDragY
|
||||
val slop = dragHelper.touchSlop
|
||||
if (dx * dx + dy * dy < slop * slop && isInHandle) {
|
||||
smoothSlideTo(if (dragProgress == 0f) 0f else 1f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.onTouchEvent(event)
|
||||
return isInHandle && isViewHit(hiddenContent, xInt, yInt) || isViewHit(overlappingContent, xInt,
|
||||
yInt)
|
||||
}
|
||||
|
||||
override fun computeScroll() {
|
||||
@@ -295,6 +292,7 @@ open class SwipeRevealLayout @JvmOverloads constructor(
|
||||
*/
|
||||
@CallSuper
|
||||
protected open fun onDragStateChanged(state: Int) {
|
||||
draggingState = state
|
||||
dragListener?.onDragStateChanged(this, state)
|
||||
}
|
||||
|
||||
@@ -303,18 +301,78 @@ open class SwipeRevealLayout @JvmOverloads constructor(
|
||||
*/
|
||||
@CallSuper
|
||||
protected open fun onDragProgress(progress: Float) {
|
||||
if (dragProgress == progress) {
|
||||
return
|
||||
}
|
||||
|
||||
dragProgress = progress
|
||||
applyDragProgress(progress)
|
||||
dragListener?.onDragProgress(this, progress)
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the drag progress to the content.
|
||||
*/
|
||||
protected open fun applyDragProgress(progress: Float) {
|
||||
val min = 0.97f
|
||||
val max = 1f
|
||||
val scale = min + (max - min) * (1 - progress)
|
||||
overlappingContent.scaleX = scale
|
||||
overlappingContent.scaleY = scale
|
||||
(overlappingContent.background as? MaterialShapeDrawable?)?.interpolation = progress
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the state of the view.
|
||||
*/
|
||||
fun toggleState(isOpen: Boolean) {
|
||||
if (isOpen) {
|
||||
open()
|
||||
} else {
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the view.
|
||||
*/
|
||||
fun open() {
|
||||
if (isOpen) {
|
||||
return
|
||||
}
|
||||
smoothSlideTo(1f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the view.
|
||||
*/
|
||||
fun close() {
|
||||
if (!isOpen) {
|
||||
return
|
||||
}
|
||||
|
||||
smoothSlideTo(0f)
|
||||
}
|
||||
|
||||
private fun smoothSlideTo(offset: Float) {
|
||||
val y = paddingTop + offset * dragHeightMax
|
||||
if (dragHelper.smoothSlideViewTo(overlappingContent, overlappingContent.left, y.toInt())) {
|
||||
postInvalidateOnAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given motion event lies within the target view.
|
||||
*/
|
||||
private fun isInTargetView(ev: MotionEvent): Boolean {
|
||||
val queenLocation = IntArray(2)
|
||||
dragHandleView!!.getLocationOnScreen(queenLocation)
|
||||
val upperLimit: Int = queenLocation[1] + dragHandleView!!.measuredHeight
|
||||
val lowerLimit = queenLocation[1]
|
||||
val y = ev.rawY.toInt()
|
||||
return y in (lowerLimit + 1)..<upperLimit
|
||||
private fun isViewHit(view: View, x: Int, y: Int): Boolean {
|
||||
val viewLoc = IntArray(2)
|
||||
view.getLocationOnScreen(viewLoc)
|
||||
val parentLoc = IntArray(2)
|
||||
getLocationOnScreen(parentLoc)
|
||||
|
||||
val scrX = parentLoc[0] + x
|
||||
val scrY = parentLoc[1] + y
|
||||
return scrX >= viewLoc[0] && scrX < viewLoc[0] + view.width &&
|
||||
scrY >= viewLoc[1] && scrY < viewLoc[1] + view.height
|
||||
}
|
||||
}
|
||||
@@ -6,29 +6,29 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:background="?attr/colorSurfaceDim"
|
||||
tools:ignore="DuplicateIncludedIds">
|
||||
|
||||
<com.itsaky.androidide.ui.SwipeRevealLayout
|
||||
android:id="@+id/swipe_reveal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:dragHandle="@id/editor_appBarLayout"
|
||||
app:dragHandleContainer="@id/realContainer"
|
||||
app:dragHiddenContentLayout="@id/mem_usage_view">
|
||||
app:dragHandle="@id/editor_appBarLayout">
|
||||
|
||||
<include android:id="@+id/mem_usage_view" layout="@layout/layout_mem_usage" />
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/realContainer"
|
||||
android:background="?attr/colorSurface"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/editor_appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
android:fitsSystemWindows="false"
|
||||
android:background="@android:color/transparent"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$Behavior">
|
||||
|
||||
<com.itsaky.androidide.ui.ExtendedMenuToolbar
|
||||
|
||||
@@ -35,8 +35,7 @@
|
||||
android:id="@+id/header_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_below="@id/border"
|
||||
android:layout_height="@dimen/editor_sheet_collapsed_height"
|
||||
android:background="?attr/colorSurface">
|
||||
android:layout_height="@dimen/editor_sheet_collapsed_height">
|
||||
|
||||
<include
|
||||
layout="@layout/layout_editor_build_status"
|
||||
@@ -65,7 +64,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="?attr/colorSurface"
|
||||
tools:layout_height="56dp" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
@@ -75,8 +73,7 @@
|
||||
android:layout_above="@id/space_bottom"
|
||||
android:layout_below="@id/tabs"
|
||||
android:clickable="true"
|
||||
android:focusable="false"
|
||||
android:background="?attr/colorSurface" />
|
||||
android:focusable="false" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/clear_fab"
|
||||
|
||||
@@ -18,14 +18,13 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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="200dp"
|
||||
android:background="?attr/colorSurfaceContainerHighest">
|
||||
android:layout_height="@dimen/editor_mem_usage_view_height">
|
||||
|
||||
<!-- Need to wrap this in a layout so that we can apply margins at runtime-->
|
||||
<com.github.mikephil.charting.charts.LineChart
|
||||
android:id="@+id/chart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:padding="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<dimen name="editor_mem_usage_view_height">200dp</dimen>
|
||||
</resources>
|
||||
@@ -18,7 +18,5 @@
|
||||
<resources>
|
||||
<declare-styleable name="SwipeRevealLayout">
|
||||
<attr name="dragHandle" format="reference"/>
|
||||
<attr name="dragHandleContainer" format="reference"/>
|
||||
<attr name="dragHiddenContentLayout" format="reference"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user