use the MyScalableRecyclerView from the commons lib
This commit is contained in:
parent
c6066bcdc4
commit
e8168db2f7
7 changed files with 5 additions and 243 deletions
|
@ -32,7 +32,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'com.simplemobiletools:commons:2.21.4'
|
compile 'com.simplemobiletools:commons:2.21.7'
|
||||||
compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.6.0'
|
compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.6.0'
|
||||||
compile 'com.theartofdev.edmodo:android-image-cropper:2.4.0'
|
compile 'com.theartofdev.edmodo:android-image-cropper:2.4.0'
|
||||||
compile 'com.bignerdranch.android:recyclerview-multiselect:0.2'
|
compile 'com.bignerdranch.android:recyclerview-multiselect:0.2'
|
||||||
|
|
|
@ -19,6 +19,7 @@ import android.widget.FrameLayout
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.simplemobiletools.commons.extensions.*
|
import com.simplemobiletools.commons.extensions.*
|
||||||
import com.simplemobiletools.commons.models.Release
|
import com.simplemobiletools.commons.models.Release
|
||||||
|
import com.simplemobiletools.commons.views.MyScalableRecyclerView
|
||||||
import com.simplemobiletools.gallery.BuildConfig
|
import com.simplemobiletools.gallery.BuildConfig
|
||||||
import com.simplemobiletools.gallery.R
|
import com.simplemobiletools.gallery.R
|
||||||
import com.simplemobiletools.gallery.adapters.DirectoryAdapter
|
import com.simplemobiletools.gallery.adapters.DirectoryAdapter
|
||||||
|
@ -27,7 +28,6 @@ import com.simplemobiletools.gallery.dialogs.ChangeSortingDialog
|
||||||
import com.simplemobiletools.gallery.extensions.*
|
import com.simplemobiletools.gallery.extensions.*
|
||||||
import com.simplemobiletools.gallery.helpers.*
|
import com.simplemobiletools.gallery.helpers.*
|
||||||
import com.simplemobiletools.gallery.models.Directory
|
import com.simplemobiletools.gallery.models.Directory
|
||||||
import com.simplemobiletools.gallery.views.MyScalableRecyclerView
|
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
||||||
import com.simplemobiletools.commons.extensions.*
|
import com.simplemobiletools.commons.extensions.*
|
||||||
|
import com.simplemobiletools.commons.views.MyScalableRecyclerView
|
||||||
import com.simplemobiletools.gallery.R
|
import com.simplemobiletools.gallery.R
|
||||||
import com.simplemobiletools.gallery.adapters.MediaAdapter
|
import com.simplemobiletools.gallery.adapters.MediaAdapter
|
||||||
import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
|
import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
|
||||||
|
@ -29,7 +30,6 @@ import com.simplemobiletools.gallery.dialogs.ExcludeFolderDialog
|
||||||
import com.simplemobiletools.gallery.extensions.*
|
import com.simplemobiletools.gallery.extensions.*
|
||||||
import com.simplemobiletools.gallery.helpers.*
|
import com.simplemobiletools.gallery.helpers.*
|
||||||
import com.simplemobiletools.gallery.models.Medium
|
import com.simplemobiletools.gallery.models.Medium
|
||||||
import com.simplemobiletools.gallery.views.MyScalableRecyclerView
|
|
||||||
import kotlinx.android.synthetic.main.activity_media.*
|
import kotlinx.android.synthetic.main.activity_media.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
|
@ -1,236 +0,0 @@
|
||||||
package com.simplemobiletools.gallery.views
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Handler
|
|
||||||
import android.support.v7.widget.RecyclerView
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.ScaleGestureDetector
|
|
||||||
import com.simplemobiletools.gallery.R
|
|
||||||
|
|
||||||
// drag selection is based on https://github.com/afollestad/drag-select-recyclerview
|
|
||||||
class MyScalableRecyclerView : RecyclerView {
|
|
||||||
private val AUTO_SCROLL_DELAY = 25L
|
|
||||||
var isZoomingEnabled = false
|
|
||||||
var isDragSelectionEnabled = false
|
|
||||||
var listener: MyScalableRecyclerViewListener? = null
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
private var currScaleFactor = 1.0f
|
|
||||||
private var lastUp = 0L // allow only pinch zoom, not double tap
|
|
||||||
|
|
||||||
constructor(context: Context) : super(context)
|
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
|
||||||
|
|
||||||
init {
|
|
||||||
hotspotHeight = context.resources.getDimensionPixelSize(R.dimen.dragselect_hotspot_height)
|
|
||||||
|
|
||||||
val gestureListener = object : MyGestureListenerInterface {
|
|
||||||
override fun getLastUp() = lastUp
|
|
||||||
|
|
||||||
override fun getScaleFactor() = currScaleFactor
|
|
||||||
|
|
||||||
override fun setScaleFactor(value: Float) {
|
|
||||||
currScaleFactor = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getMainListener() = listener
|
|
||||||
}
|
|
||||||
|
|
||||||
mScaleDetector = ScaleGestureDetector(context, GestureListener(gestureListener))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMeasure(widthSpec: Int, heightSpec: Int) {
|
|
||||||
super.onMeasure(widthSpec, heightSpec)
|
|
||||||
if (hotspotHeight > -1) {
|
|
||||||
hotspotTopBoundStart = hotspotOffsetTop
|
|
||||||
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
|
|
||||||
inTopHotspot = false
|
|
||||||
inBottomHotspot = false
|
|
||||||
autoScrollHandler.removeCallbacks(autoScrollRunnable)
|
|
||||||
currScaleFactor = 1.0f
|
|
||||||
lastUp = 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
|
|
||||||
}
|
|
||||||
|
|
||||||
listener?.selectRange(initialSelection, lastDraggedIndex, minReached, maxReached)
|
|
||||||
|
|
||||||
if (initialSelection == lastDraggedIndex) {
|
|
||||||
minReached = lastDraggedIndex
|
|
||||||
maxReached = lastDraggedIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return if (isZoomingEnabled)
|
|
||||||
mScaleDetector.onTouchEvent(ev)
|
|
||||||
else
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setDragSelectActive(initialSelection: Int) {
|
|
||||||
if (dragSelectActive || !isDragSelectionEnabled)
|
|
||||||
return
|
|
||||||
|
|
||||||
lastDraggedIndex = -1
|
|
||||||
minReached = -1
|
|
||||||
maxReached = -1
|
|
||||||
this.initialSelection = initialSelection
|
|
||||||
dragSelectActive = true
|
|
||||||
listener?.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(val gestureListener: MyGestureListenerInterface) : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
|
||||||
private val ZOOM_IN_THRESHOLD = -0.4f
|
|
||||||
private val ZOOM_OUT_THRESHOLD = 0.15f
|
|
||||||
|
|
||||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
|
||||||
gestureListener.apply {
|
|
||||||
if (System.currentTimeMillis() - getLastUp() < 1000)
|
|
||||||
return false
|
|
||||||
|
|
||||||
val diff = getScaleFactor() - detector.scaleFactor
|
|
||||||
if (diff < ZOOM_IN_THRESHOLD && getScaleFactor() == 1.0f) {
|
|
||||||
getMainListener()?.zoomIn()
|
|
||||||
setScaleFactor(detector.scaleFactor)
|
|
||||||
} else if (diff > ZOOM_OUT_THRESHOLD && getScaleFactor() == 1.0f) {
|
|
||||||
getMainListener()?.zoomOut()
|
|
||||||
setScaleFactor(detector.scaleFactor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MyScalableRecyclerViewListener {
|
|
||||||
fun zoomOut()
|
|
||||||
|
|
||||||
fun zoomIn()
|
|
||||||
|
|
||||||
fun selectItem(position: Int)
|
|
||||||
|
|
||||||
fun selectRange(initialSelection: Int, lastDraggedIndex: Int, minReached: Int, maxReached: Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MyGestureListenerInterface {
|
|
||||||
fun getLastUp(): Long
|
|
||||||
|
|
||||||
fun getScaleFactor(): Float
|
|
||||||
|
|
||||||
fun setScaleFactor(value: Float)
|
|
||||||
|
|
||||||
fun getMainListener(): MyScalableRecyclerViewListener?
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<com.simplemobiletools.gallery.views.MyScalableRecyclerView
|
<com.simplemobiletools.commons.views.MyScalableRecyclerView
|
||||||
android:id="@+id/directories_grid"
|
android:id="@+id/directories_grid"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<com.simplemobiletools.gallery.views.MyScalableRecyclerView
|
<com.simplemobiletools.commons.views.MyScalableRecyclerView
|
||||||
android:id="@+id/media_grid"
|
android:id="@+id/media_grid"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -7,6 +7,4 @@
|
||||||
<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>
|
||||||
|
|
Loading…
Reference in a new issue