add Drag selection to directories

This commit is contained in:
tibbi 2017-05-13 21:10:13 +02:00
parent 44a71c032b
commit c174b7d395
4 changed files with 163 additions and 8 deletions

View file

@ -124,6 +124,10 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
config.temporarilyShowHidden = false config.temporarilyShowHidden = false
}
override fun onDestroy() {
super.onDestroy()
DirectoryAdapter.cleanup() DirectoryAdapter.cleanup()
} }
@ -211,6 +215,10 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
} }
} }
override fun itemLongClicked(position: Int) {
directories_grid.setDragSelectActive(position)
}
private fun handleZooming() { private fun handleZooming() {
val layoutManager = directories_grid.layoutManager as GridLayoutManager val layoutManager = directories_grid.layoutManager as GridLayoutManager
layoutManager.spanCount = config.dirColumnCnt layoutManager.spanCount = config.dirColumnCnt

View file

@ -321,6 +321,10 @@ class DirectoryAdapter(val activity: SimpleActivity, val dirs: MutableList<Direc
override fun getItemCount() = dirs.size override fun getItemCount() = dirs.size
fun selectItem(pos: Int) {
toggleItemSelection(true, pos)
}
fun selectRange(from: Int, to: Int, min: Int, max: Int) { fun selectRange(from: Int, to: Int, min: Int, max: Int) {
if (from == to) { if (from == to) {
(min..max).filter { it != from } (min..max).filter { it != from }

View file

@ -1,13 +1,38 @@
package com.simplemobiletools.gallery.views package com.simplemobiletools.gallery.views
import android.content.Context import android.content.Context
import android.os.Handler
import android.support.v7.widget.RecyclerView import android.support.v7.widget.RecyclerView
import android.util.AttributeSet import android.util.AttributeSet
import android.view.MotionEvent import android.view.MotionEvent
import android.view.ScaleGestureDetector import android.view.ScaleGestureDetector
import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.adapters.DirectoryAdapter
// drag selection is based on https://github.com/afollestad/drag-select-recyclerview
class MyScalableRecyclerView : RecyclerView { class MyScalableRecyclerView : RecyclerView {
var mScaleDetector: ScaleGestureDetector private val AUTO_SCROLL_DELAY = 25L
private var mScaleDetector: ScaleGestureDetector
private var dragSelectActive = false
private var lastDraggedIndex = -1
private var minReached = 0
private var maxReached = 0
private var initialSelection = 0
private var hotspotHeight = 0
private var hotspotOffsetTop = 0
private var hotspotOffsetBottom = 0
private var hotspotTopBoundStart = 0
private var hotspotTopBoundEnd = 0
private var hotspotBottomBoundStart = 0
private var hotspotBottomBoundEnd = 0
private var autoScrollVelocity = 0
private var inTopHotspot = false
private var inBottomHotspot = false
companion object { companion object {
var mListener: ZoomListener? = null var mListener: ZoomListener? = null
@ -20,22 +45,138 @@ class MyScalableRecyclerView : RecyclerView {
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
init { init {
hotspotHeight = context.resources.getDimensionPixelSize(R.dimen.dragselect_hotspot_height)
mScaleDetector = ScaleGestureDetector(context, GestureListener()) mScaleDetector = ScaleGestureDetector(context, GestureListener())
} }
override fun dispatchTouchEvent(ev: MotionEvent): Boolean { override fun onMeasure(widthSpec: Int, heightSpec: Int) {
super.dispatchTouchEvent(ev) super.onMeasure(widthSpec, heightSpec)
if (ev.action == MotionEvent.ACTION_UP) { if (hotspotHeight > -1) {
mCurrScaleFactor = 1.0f hotspotTopBoundStart = hotspotOffsetTop
mLastUp = System.currentTimeMillis() hotspotTopBoundEnd = hotspotOffsetTop + hotspotHeight
hotspotBottomBoundStart = measuredHeight - hotspotHeight - hotspotOffsetBottom
hotspotBottomBoundEnd = measuredHeight - hotspotOffsetBottom
} }
}
private var autoScrollHandler = Handler()
private val autoScrollRunnable = object : Runnable {
override fun run() {
if (inTopHotspot) {
scrollBy(0, -autoScrollVelocity)
autoScrollHandler.postDelayed(this, AUTO_SCROLL_DELAY)
} else if (inBottomHotspot) {
scrollBy(0, autoScrollVelocity)
autoScrollHandler.postDelayed(this, AUTO_SCROLL_DELAY)
}
}
}
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
if (!dragSelectActive)
super.dispatchTouchEvent(ev)
when (ev.action) {
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
dragSelectActive = false
mCurrScaleFactor = 1.0f
mLastUp = System.currentTimeMillis()
return true
}
MotionEvent.ACTION_MOVE -> {
if (dragSelectActive) {
val itemPosition = getItemPosition(ev)
if (hotspotHeight > -1) {
if (ev.y in hotspotTopBoundStart..hotspotTopBoundEnd) {
inBottomHotspot = false
if (!inTopHotspot) {
inTopHotspot = true
autoScrollHandler.removeCallbacks(autoScrollRunnable)
autoScrollHandler.postDelayed(autoScrollRunnable, AUTO_SCROLL_DELAY.toLong())
}
val simulatedFactor = (hotspotTopBoundEnd - hotspotTopBoundStart).toFloat()
val simulatedY = ev.y - hotspotTopBoundStart
autoScrollVelocity = (simulatedFactor - simulatedY).toInt() / 2
} else if (ev.y in hotspotBottomBoundStart..hotspotBottomBoundEnd) {
inTopHotspot = false
if (!inBottomHotspot) {
inBottomHotspot = true
autoScrollHandler.removeCallbacks(autoScrollRunnable)
autoScrollHandler.postDelayed(autoScrollRunnable, AUTO_SCROLL_DELAY.toLong())
}
val simulatedY = ev.y + hotspotBottomBoundEnd
val simulatedFactor = (hotspotBottomBoundStart + hotspotBottomBoundEnd).toFloat()
autoScrollVelocity = (simulatedY - simulatedFactor).toInt() / 2
} else if (inTopHotspot || inBottomHotspot) {
autoScrollHandler.removeCallbacks(autoScrollRunnable)
inTopHotspot = false
inBottomHotspot = false
}
}
if (itemPosition != RecyclerView.NO_POSITION && lastDraggedIndex != itemPosition) {
lastDraggedIndex = itemPosition
if (minReached == -1) {
minReached = lastDraggedIndex
}
if (maxReached == -1) {
maxReached = lastDraggedIndex
}
if (lastDraggedIndex > maxReached) {
maxReached = lastDraggedIndex
}
if (lastDraggedIndex < minReached) {
minReached = lastDraggedIndex
}
(adapter as DirectoryAdapter).selectRange(initialSelection, lastDraggedIndex, minReached, maxReached)
if (initialSelection == lastDraggedIndex) {
minReached = lastDraggedIndex
maxReached = lastDraggedIndex
}
}
return true
}
}
}
return mScaleDetector.onTouchEvent(ev) return mScaleDetector.onTouchEvent(ev)
} }
fun setDragSelectActive(initialSelection: Int) {
if (dragSelectActive)
return
lastDraggedIndex = -1
minReached = -1
maxReached = -1
this.initialSelection = initialSelection
dragSelectActive = true
(adapter as DirectoryAdapter).selectItem(initialSelection)
}
private fun getItemPosition(e: MotionEvent): Int {
val v = findChildViewUnder(e.x, e.y) ?: return RecyclerView.NO_POSITION
if (v.tag == null || v.tag !is RecyclerView.ViewHolder) {
throw IllegalStateException("Make sure your adapter makes a call to super.onBindViewHolder(), and doesn't override itemView tags.")
}
val holder = v.tag as RecyclerView.ViewHolder
return holder.adapterPosition
}
class GestureListener : ScaleGestureDetector.SimpleOnScaleGestureListener() { class GestureListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
val ZOOM_IN_THRESHOLD = -0.6f private val ZOOM_IN_THRESHOLD = -0.4f
val ZOOM_OUT_THRESHOLD = 0.25f private val ZOOM_OUT_THRESHOLD = 0.15f
override fun onScale(detector: ScaleGestureDetector): Boolean { override fun onScale(detector: ScaleGestureDetector): Boolean {
if (System.currentTimeMillis() - mLastUp < 1000) if (System.currentTimeMillis() - mLastUp < 1000)

View file

@ -7,4 +7,6 @@
<dimen name="play_outline_size_big">150dp</dimen> <dimen name="play_outline_size_big">150dp</dimen>
<dimen name="timer_padding">24dp</dimen> <dimen name="timer_padding">24dp</dimen>
<dimen name="tmb_shadow_height">50dp</dimen> <dimen name="tmb_shadow_height">50dp</dimen>
<dimen name="dragselect_hotspot_height">56dp</dimen>
</resources> </resources>