Last active
January 26, 2021 11:31
-
-
Save DanteAndroid/d9e9f05c5555a54fef9acce00989e73e to your computer and use it in GitHub Desktop.
ConstrainLayout that has ability to infinitely scroll background image vertically or horizontally
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.animation.ValueAnimator | |
import android.content.Context | |
import android.util.AttributeSet | |
import android.view.animation.LinearInterpolator | |
import android.widget.ImageView | |
import androidx.constraintlayout.widget.ConstraintLayout | |
import androidx.constraintlayout.widget.ConstraintSet | |
import androidx.core.content.res.ResourcesCompat | |
/** | |
* 支持横竖方向无限滚动背景图的 ConstraintLayout | |
* @author Dante | |
* 2020/12/22 | |
*/ | |
class InfiniteBackgroundLayout @JvmOverloads constructor( | |
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 | |
) : ConstraintLayout(context, attrs, defStyleAttr) { | |
private var backgroundRes: Int = 0 | |
// 单位s | |
private var scrollDuration: Int = 0 | |
private var orientation: Int = 0 | |
private lateinit var imageStart: ImageView | |
private lateinit var imageEnd: ImageView | |
private var animator: ValueAnimator? = null | |
private val isScrollVertical get() = orientation == 0 | |
init { | |
context.obtainStyledAttributes(attrs, R.styleable.InfiniteBackgroundLayout, defStyleAttr, 0).apply { | |
backgroundRes = | |
getResourceId(R.styleable.InfiniteBackgroundLayout_backgroundResource, 0) | |
scrollDuration = | |
getInteger(R.styleable.InfiniteBackgroundLayout_scrollDuration, 0) | |
orientation = getInteger(R.styleable.InfiniteBackgroundLayout_scrollOrientation, 0) | |
}.recycle() | |
if (scrollDuration == 0) { | |
scrollDuration = if (isScrollVertical) { | |
UIUtils.getScreenHeight() / 74 | |
} else { | |
UIUtils.getScreenWidth() / 74 | |
} | |
} | |
val drawable = ResourcesCompat.getDrawable(resources, backgroundRes, null) | |
if (drawable != null) { | |
imageStart = ImageView(context, attrs, defStyleAttr).apply { | |
id = generateViewId() | |
scaleType = ImageView.ScaleType.CENTER_CROP | |
setImageDrawable(drawable) | |
} | |
imageEnd = ImageView(context, attrs, defStyleAttr).apply { | |
id = generateViewId() | |
scaleType = ImageView.ScaleType.CENTER_CROP | |
setImageDrawable(drawable) | |
} | |
initView(drawable.intrinsicWidth, drawable.intrinsicHeight) | |
} | |
} | |
private fun initView(width: Int, height: Int) { | |
addView(imageStart, LayoutParams(width, height)) | |
addView(imageEnd, LayoutParams(width, height)) | |
ConstraintSet().also { | |
it.clone(this) | |
if (isScrollVertical) { | |
it.connect( | |
imageStart.id, | |
ConstraintSet.TOP, | |
ConstraintSet.PARENT_ID, | |
ConstraintSet.TOP | |
) | |
it.constrainWidth(imageStart.id, LayoutParams.MATCH_PARENT) | |
// it.constrainHeight(imageStart.id, LayoutParams.WRAP_CONTENT) | |
it.connect(imageEnd.id, ConstraintSet.TOP, imageStart.id, ConstraintSet.BOTTOM) | |
it.constrainWidth(imageEnd.id, LayoutParams.MATCH_PARENT) | |
// it.constrainHeight(imageEnd.id, LayoutParams.WRAP_CONTENT) | |
} else { | |
it.connect( | |
imageStart.id, | |
ConstraintSet.START, | |
ConstraintSet.PARENT_ID, | |
ConstraintSet.START | |
) | |
// it.constrainWidth(imageStart.id, LayoutParams.WRAP_CONTENT) | |
it.constrainHeight(imageStart.id, LayoutParams.MATCH_PARENT) | |
it.connect(imageEnd.id, ConstraintSet.START, imageStart.id, ConstraintSet.END) | |
// it.constrainWidth(imageEnd.id, LayoutParams.WRAP_CONTENT) | |
it.constrainHeight(imageEnd.id, LayoutParams.MATCH_PARENT) | |
} | |
it.applyTo(this) | |
} | |
} | |
override fun onWindowFocusChanged(hasWindowFocus: Boolean) { | |
super.onWindowFocusChanged(hasWindowFocus) | |
if (!this::imageStart.isInitialized) return | |
animateBackground(hasWindowFocus) | |
} | |
private fun animateBackground(start: Boolean) { | |
if (start) { | |
if (animator == null) { | |
imageStart.post { | |
val translation = | |
if (isScrollVertical) imageStart.measuredHeight.toFloat() else imageStart.measuredWidth.toFloat() | |
animator = ValueAnimator.ofFloat(0f, -translation) | |
.setDuration(scrollDuration * 1000L) | |
animator!!.interpolator = LinearInterpolator() | |
animator!!.repeatMode = ValueAnimator.RESTART | |
animator!!.repeatCount = ValueAnimator.INFINITE | |
animator!!.addUpdateListener { | |
if (isScrollVertical) { | |
imageStart.translationY = it.animatedValue as Float | |
imageEnd.translationY = it.animatedValue as Float | |
} else { | |
imageStart.translationX = it.animatedValue as Float | |
imageEnd.translationX = it.animatedValue as Float | |
} | |
} | |
animator!!.start() | |
} | |
} else { | |
animator!!.resume() | |
} | |
} else { | |
animator?.pause() | |
} | |
} | |
fun getScreenWidth(): Int { | |
return resources.displayMetrics.widthPixels | |
} | |
fun getScreenHeight(): Int { | |
return resources.displayMetrics.heightPixels | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add following to
attrs.xml
before use: