Created
November 16, 2017 02:16
-
-
Save JasonHezz/774211340bfc9942ac6721a6ca5a008d to your computer and use it in GitHub Desktop.
animate avatar
This file contains hidden or 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
| package android.support.design.widget | |
| import android.content.Context | |
| import android.graphics.Rect | |
| import android.graphics.RectF | |
| import android.os.Parcel | |
| import android.os.Parcelable | |
| import android.support.v4.view.AbsSavedState | |
| import android.support.v7.widget.Toolbar | |
| import android.util.AttributeSet | |
| import android.view.View | |
| import android.view.ViewGroup | |
| import android.view.animation.Interpolator | |
| import android.widget.ImageView | |
| import com.github.jasonhezz.likesplash.R | |
| import com.github.jasonhezz.likesplash.util.AppBarStateChangeListener | |
| import com.github.jasonhezz.likesplash.util.State | |
| import timber.log.Timber | |
| /** | |
| * Created by JavaCoder on 2017/11/1. | |
| */ | |
| class ProfileAvatarBehavior @JvmOverloads constructor(context: Context, | |
| attrs: AttributeSet? = null) : CoordinatorLayout.Behavior<ImageView>(context, attrs) { | |
| private var expandRec: Rect? = null | |
| private var collapsedRec: Rect = Rect() | |
| private val currentBounds = RectF() | |
| private var fraction = 0f | |
| override fun layoutDependsOn(parent: CoordinatorLayout?, child: ImageView, | |
| dependency: View?): Boolean { | |
| if (dependency is AppBarLayout) { | |
| return true | |
| } | |
| return super.layoutDependsOn(parent, child, dependency) | |
| } | |
| override fun onDependentViewChanged(parent: CoordinatorLayout?, child: ImageView, | |
| dependency: View): Boolean { | |
| //handle appbar first init | |
| interpolateBounds(fraction) | |
| calculateAvatarSize(child) | |
| return true | |
| } | |
| override fun onLayoutChild(parent: CoordinatorLayout, child: ImageView, | |
| layoutDirection: Int): Boolean { | |
| val appbarLayout = parent.findViewById<AppBarLayout?>(R.id.app_bar_layout) | |
| handleBounds(parent, child) | |
| appbarLayout?.addOnOffsetChangedListener(object : AppBarStateChangeListener() { | |
| override fun onStateChanged(appBarLayout: AppBarLayout, state: State) { | |
| } | |
| override fun onOffsetChanged(state: State, fraction: Float) { | |
| this@ProfileAvatarBehavior.fraction = fraction | |
| } | |
| }) | |
| return super.onLayoutChild(parent, child, layoutDirection) | |
| } | |
| private fun handleBounds(parent: CoordinatorLayout, | |
| child: ImageView) { | |
| findToolbarLogoRec(parent) | |
| findAvatarRec(parent, child) | |
| } | |
| private fun findToolbarLogoRec(viewGroup: ViewGroup) { | |
| if (collapsedRec.isEmpty) { | |
| val toolbar = viewGroup.findViewById<Toolbar>(R.id.profile_toolbar) | |
| (0 until toolbar.childCount).map { | |
| val childView = toolbar.getChildAt(it) | |
| if (childView is ImageView) { | |
| ViewGroupUtils.getDescendantRect(viewGroup, childView, collapsedRec) | |
| Timber.d("childView ${childView.x} ${childView.y}") | |
| } | |
| } | |
| } | |
| } | |
| private fun findAvatarRec(viewGroup: ViewGroup, child: ImageView) { | |
| child.addOnLayoutChangeListener(object : View.OnLayoutChangeListener { | |
| override fun onLayoutChange(p0: View?, p1: Int, p2: Int, p3: Int, p4: Int, p5: Int, p6: Int, | |
| p7: Int, p8: Int) { | |
| if (expandRec == null) { | |
| expandRec = Rect() | |
| ViewGroupUtils.getDescendantRect(viewGroup, child, expandRec) | |
| } | |
| p0?.removeOnLayoutChangeListener(this) | |
| } | |
| }) | |
| } | |
| private fun calculateOffsets(target: View) { | |
| target.x = currentBounds.left | |
| target.y = currentBounds.top | |
| } | |
| private fun calculateAvatarSize(target: View) { | |
| if (expandRec != null) { | |
| target.layoutParams.width = currentBounds.width().toInt() | |
| target.layoutParams.height = currentBounds.height().toInt() | |
| target.requestLayout() | |
| target.addOnLayoutChangeListener(object : View.OnLayoutChangeListener { | |
| override fun onLayoutChange(p0: View?, p1: Int, p2: Int, p3: Int, p4: Int, p5: Int, p6: Int, | |
| p7: Int, p8: Int) { | |
| calculateOffsets(target) | |
| p0?.removeOnLayoutChangeListener(this) | |
| } | |
| }) | |
| Timber.d( | |
| "left ${collapsedRec.left} right ${collapsedRec.right} top ${collapsedRec.top} bottom ${collapsedRec.bottom}") | |
| } | |
| } | |
| private fun interpolateBounds(fraction: Float) { | |
| if (expandRec != null) { | |
| currentBounds.left = lerp(expandRec!!.left.toFloat(), collapsedRec.left.toFloat(), | |
| fraction, AnimationUtils.DECELERATE_INTERPOLATOR) | |
| currentBounds.top = lerp(expandRec!!.top.toFloat(), collapsedRec.top.toFloat(), | |
| fraction, AnimationUtils.DECELERATE_INTERPOLATOR) | |
| currentBounds.right = lerp(expandRec!!.right.toFloat(), collapsedRec.right.toFloat(), | |
| fraction, AnimationUtils.DECELERATE_INTERPOLATOR) | |
| currentBounds.bottom = lerp(expandRec!!.bottom.toFloat(), | |
| collapsedRec.bottom.toFloat(), | |
| fraction, AnimationUtils.DECELERATE_INTERPOLATOR) | |
| } | |
| } | |
| private fun lerp(startValue: Float, endValue: Float, fraction: Float, | |
| interpolator: Interpolator?): Float { | |
| var f = fraction | |
| if (interpolator != null) { | |
| f = interpolator.getInterpolation(f) | |
| } | |
| return AnimationUtils.lerp(startValue, endValue, f) | |
| } | |
| override fun onSaveInstanceState(parent: CoordinatorLayout?, child: ImageView?): Parcelable { | |
| val superState = super.onSaveInstanceState(parent, child) | |
| val state = SavedState(superState) | |
| state.fraction = this.fraction | |
| return state | |
| } | |
| override fun onRestoreInstanceState(parent: CoordinatorLayout?, child: ImageView?, | |
| state: Parcelable?) { | |
| super.onRestoreInstanceState(parent, child, state) | |
| if (state is SavedState) { | |
| fraction = state.fraction | |
| } | |
| } | |
| protected class SavedState : AbsSavedState { | |
| var fraction = 0f | |
| constructor(source: Parcel, loader: ClassLoader?) : super(source, loader) { | |
| fraction = source.readFloat() | |
| } | |
| constructor(superState: Parcelable) : super(superState) {} | |
| override fun writeToParcel(dest: Parcel, flags: Int) { | |
| super.writeToParcel(dest, flags) | |
| dest.writeFloat(fraction) | |
| } | |
| companion object { | |
| val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.ClassLoaderCreator<SavedState> { | |
| override fun createFromParcel(source: Parcel, loader: ClassLoader): SavedState { | |
| return SavedState(source, loader) | |
| } | |
| override fun createFromParcel(source: Parcel): SavedState { | |
| return SavedState(source, null) | |
| } | |
| override fun newArray(size: Int): Array<SavedState?> { | |
| return arrayOfNulls(size) | |
| } | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment