Skip to content

Commit

Permalink
feat: easier way to swap to views
Browse files Browse the repository at this point in the history
Fixes: #2
BREAKING CHANGE:

SwapperView now uses a generic View identifier. This makes using enums way easier.
* Use `SwapperViewContainer` in XML instead of `SwapperView`
* Call `swapperViewContainer.init(...)` to setup strongly typed `SwapperView`
  • Loading branch information
levibostian committed Nov 17, 2020
1 parent 1437fc4 commit 997f65d
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 16 deletions.
26 changes: 15 additions & 11 deletions app/src/main/java/com/levibostian/swapperexample/MainActivity.kt
Expand Up @@ -4,6 +4,8 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import coil.load
import com.levibostian.swapper.SwapperView
import com.levibostian.swapperexample.MainActivity.SwapperViews.FIRST_VIEW
import com.levibostian.swapperexample.MainActivity.SwapperViews.SECOND_VIEW
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
Expand All @@ -13,6 +15,8 @@ class MainActivity : AppCompatActivity() {
SECOND_VIEW
}

private lateinit var swapperView: SwapperView<SwapperViews>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand All @@ -22,24 +26,24 @@ class MainActivity : AppCompatActivity() {
}

private fun setupViews() {
swapper_view.apply {
viewMap = mapOf(
Pair(SwapperViews.FIRST_VIEW.name, first_view),
Pair(SwapperViews.SECOND_VIEW.name, second_view)
)
swapTo(SwapperViews.FIRST_VIEW.name) {
first_view_imageview.load("https://raw.githubusercontent.com/levibostian/Swapper-iOS/d494bc41894b5e5bc7eeacc162a96ddadca024cc/Example/Swapper/Images.xcassets/little_hill.imageset/little_hill.jpg")
}
}
swapperView = swapper_view.init(
mapOf(
Pair(FIRST_VIEW, first_view),
Pair(SECOND_VIEW, second_view)
),
FIRST_VIEW
)

first_view_imageview.load("https://raw.githubusercontent.com/levibostian/Swapper-iOS/d494bc41894b5e5bc7eeacc162a96ddadca024cc/Example/Swapper/Images.xcassets/little_hill.imageset/little_hill.jpg")

first_view_swap_button.setOnClickListener {
swapper_view.swapTo(SwapperViews.SECOND_VIEW.name) {
swapperView.swapTo(SECOND_VIEW) {
second_view_imageview.load("https://raw.githubusercontent.com/levibostian/Swapper-iOS/d494bc41894b5e5bc7eeacc162a96ddadca024cc/Example/Swapper/Images.xcassets/mt_mckinley.imageset/mt_mckinley.jpg")
}
}

second_view_swap_button.setOnClickListener {
swapper_view.swapTo(SwapperViews.FIRST_VIEW.name) {}
swapperView.swapTo(FIRST_VIEW)
}

animation_duration_100.setOnClickListener { SwapperView.config.animationDuration = 100 }
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/res/layout/activity_main.xml
Expand Up @@ -43,7 +43,7 @@
android:text="5s"/>
</LinearLayout>

<com.levibostian.swapper.SwapperView
<com.levibostian.swapper.SwapperViewContainer
android:id="@+id/swapper_view"
android:layout_width="match_parent"
android:layout_weight="1"
Expand Down Expand Up @@ -88,6 +88,6 @@
android:layout_height="wrap_content"
android:text="Swap to first view"/>
</LinearLayout>
</com.levibostian.swapper.SwapperView>
</com.levibostian.swapper.SwapperViewContainer>

</LinearLayout>
18 changes: 15 additions & 3 deletions swapper/src/main/java/com/levibostian/swapper/SwapperView.kt
Expand Up @@ -6,11 +6,11 @@ import android.os.Build.VERSION_CODES
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import java.lang.ref.WeakReference

typealias SwapperViewId = String
typealias SwapperViewSwapAnimator = (oldView: View, newView: View, duration: Long, onComplete: () -> Unit) -> Unit

class SwapperView : FrameLayout {
class SwapperView<SwapperViewId : Any> : FrameLayout {

companion object {
val config: SwapperConfig = SwapperConfig
Expand Down Expand Up @@ -39,6 +39,8 @@ class SwapperView : FrameLayout {

private var currentlyShownViewId: Pair<SwapperViewId, View>? = null

internal var container: WeakReference<SwapperViewContainer>? = null

private val children: List<View>
get() {
val children: MutableList<View> = mutableListOf()
Expand All @@ -47,10 +49,14 @@ class SwapperView : FrameLayout {
children.add(getChildAt(index))
}

container?.get()?.let { container ->
children.addAll(container.childrenExceptSwapperView)
}

return children
}

@Transient var viewMap: Map<SwapperViewId, View>? = null
private var viewMap: Map<SwapperViewId, View>? = null
set(value) {
field = value

Expand Down Expand Up @@ -84,6 +90,12 @@ class SwapperView : FrameLayout {
hideAllChildren()
}

@Synchronized fun init(views: Map<SwapperViewId, View>, swapTo: SwapperViewId) {
this.viewMap = views

this.swapTo(swapTo)
}

private fun hideAllChildren() {
children.forEach { childView ->
childView.visibility = View.GONE
Expand Down
@@ -0,0 +1,80 @@
package com.levibostian.swapper

import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import java.lang.ref.WeakReference

class SwapperViewContainer : FrameLayout {

private var attrs: AttributeSet? = null
private var defStyleAttr: Int = -1

internal val childrenExceptSwapperView: List<View>
get() {
val children: MutableList<View> = mutableListOf()
val swapperViewIndex = this.swapperViewChildIndex

for (index in 0 until childCount) {
if (index != swapperViewIndex) {
children.add(getChildAt(index))
}
}

return children
}

private val swapperViewChildIndex: Int?
get() {
for (index in 0 until childCount) {
val view = getChildAt(index)

if (view is SwapperView<*>) {
return index
}
}

return null
}

constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
initialize(context, attrs, defStyleAttr)
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes) {
initialize(context, attrs, defStyleAttr)
}

private fun initialize(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)

this.attrs = attrs
this.defStyleAttr = defStyleAttr
}

fun <SwapperViewId : Any> init(views: Map<SwapperViewId, View>, swapTo: SwapperViewId): SwapperView<SwapperViewId> {
swapperViewChildIndex?.let { removeViewAt(it) }

val swapperView = SwapperView<SwapperViewId>(context, attrs, defStyleAttr)
addView(swapperView)
swapperView.container = WeakReference(this)
swapperView.init(views, swapTo)

return swapperView
}
}

0 comments on commit 997f65d

Please sign in to comment.