Skip to content

Instantly share code, notes, and snippets.

View dadouf's full-sized avatar

David Ferrand dadouf

View GitHub Profile
@dadouf
dadouf / MockitoKotlinHelpers.kt
Last active February 25, 2020 10:36
Mockito+Kotlin: set mocks to throw unchecked exceptions
import org.mockito.BDDMockito
// Use just like a normal .willThrow:
// ```
// given(api.fetch()).willThrowUnchecked(Exception("First error"), Exception("Second error"))
// ```
fun <T> BDDMockito.BDDMyOngoingStubbing<T>.willThrowUnchecked(vararg throwables: Throwable) {
var invocationNumber = 0
this.willAnswer {
@dadouf
dadouf / HeightWrappingViewPager.kt
Created June 2, 2020 08:45
ViewPager that wraps its content's height
import android.content.Context
import android.support.v4.view.ViewPager
import android.util.AttributeSet
internal class HeightWrappingViewPager @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null)
: ViewPager(context, attrs) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var heightMeasureSpecModif = heightMeasureSpec
private var hasInitParentDimensions = false
private var maxImageWidth: Int = 0
private var maxImageHeight: Int = 0
private var maxImageAspectRatio: Float = 1f
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
if (!hasInitParentDimensions) {
maxImageWidth = parent.width
maxImageHeight = parent.height
maxImageAspectRatio = maxImageWidth.toFloat() / maxImageHeight.toFloat()
override fun onBindViewHolder(vh: VH, position: Int) {
val image = images[position]
// Resize view to respect aspect ratio
val imageAspectRatio = image.aspectRatio
val targetImageWidth: Int =
if (imageAspectRatio < maxImageAspectRatio) {
// Tall image: height = max, width adjusts
(maxImageHeight * imageAspectRatio).roundToInt()
class BoundsOffsetDecoration : ItemDecoration() {
override fun getItemOffsets(outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
val itemPosition = parent.getChildAdapterPosition(view)
// It is crucial to refer to layoutParams.width
private fun initRecyclerViewPosition(position: Int) {
// This initial scroll will be slightly off because it doesn't
// respect the SnapHelper. Do it anyway so that the target view
// is laid out, then adjust onPreDraw.
layoutManager.scrollToPosition(position)
recyclerView.doOnPreDraw {
val targetView = layoutManager.findViewByPosition(position)
?: return@doOnPreDraw
internal class ProminentLayoutManager(
context: Context,
private val minScaleDistanceFactor: Float = 1.5f,
private val scaleDownBy: Float = 0.5f
) : LinearLayoutManager(context, HORIZONTAL, false) {
override fun onLayoutCompleted(state: RecyclerView.State?) =
super.onLayoutCompleted(state).also { scaleChildren() }
override fun scrollHorizontallyBy(
val translationDirection = if (childCenter > containerCenter) -1 else 1
val translationXFromScale = translationDirection * child.width * (1 - scale) / 2f
child.translationX = translationXFromScale
var translationXForward = 0f
for (i in 0 until childCount) {
val translationXFromScale = ... // like before
child.translationX = translationXForward + translationXFromScale
translationXForward = 0f
if (translationXFromScale > 0 && i >= 1) {
// Edit previous child
vh.imageView.setOnClickListener {
val rv = vh.imageView.parent as RecyclerView
rv.smoothScrollToCenteredPosition(position)
}
fun RecyclerView.smoothScrollToCenteredPosition(position: Int) {
val smoothScroller = object : LinearSmoothScroller(context) {
override fun calculateDxToMakeVisible(view: View?,
snapPref: Int): Int {