improved user fragment layout
This commit is contained in:
parent
a3547e82a0
commit
0ec9096a9d
|
@ -345,9 +345,10 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher,
|
|||
this[EXTRA_REQUEST_TOKEN_SECRET] = requestToken.oauthTokenSecret
|
||||
})
|
||||
activity.startActivityForResult(intent, REQUEST_BROWSER_TWITTER_SIGN_IN)
|
||||
}.failUi {
|
||||
}.failUi { exception ->
|
||||
val activity = weakThis.get() ?: return@failUi
|
||||
// TODO show error message
|
||||
Toast.makeText(activity, exception.getErrorMessage(activity), Toast.LENGTH_LONG).show()
|
||||
DebugLog.w(tr = exception)
|
||||
}.alwaysUi {
|
||||
executeAfterFragmentResumed {
|
||||
it.supportFragmentManager.dismissDialogFragment("get_request_token")
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
package org.mariotaku.twidere.extension.model
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.Spannable
|
||||
import android.text.Spanned
|
||||
import android.text.style.URLSpan
|
||||
import android.widget.TextView
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.mariotaku.twidere.model.CustomEmoji
|
||||
import org.mariotaku.twidere.model.SpanItem
|
||||
|
@ -36,7 +36,7 @@ val SpanItem.length: Int get() = end - start
|
|||
|
||||
@SuppressLint("SwitchIntDef")
|
||||
fun Array<SpanItem>.applyTo(spannable: Spannable, emojis: Map<String, CustomEmoji>?,
|
||||
requestManager: RequestManager, callback: Drawable.Callback) {
|
||||
requestManager: RequestManager, textView: TextView) {
|
||||
forEach { span ->
|
||||
when (span.type) {
|
||||
SpanItem.SpanType.HIDE -> {
|
||||
|
@ -54,7 +54,7 @@ fun Array<SpanItem>.applyTo(spannable: Spannable, emojis: Map<String, CustomEmoj
|
|||
SpanItem.SpanType.EMOJI -> {
|
||||
val shortCode = span.link ?: return@forEach
|
||||
val emoji = emojis?.get(shortCode) ?: return@forEach
|
||||
spannable.setSpan(CustomEmojiSpan(emoji.url, requestManager, callback), span.start,
|
||||
spannable.setSpan(CustomEmojiSpan(emoji.url, requestManager, textView), span.start,
|
||||
span.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
else -> {
|
||||
|
|
|
@ -22,24 +22,19 @@ package org.mariotaku.twidere.fragment
|
|||
import android.accounts.AccountManager
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.annotation.SuppressLint
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.Outline
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.net.Uri
|
||||
import android.nfc.NdefMessage
|
||||
import android.nfc.NdefRecord
|
||||
import android.nfc.NfcAdapter.CreateNdefMessageCallback
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.support.annotation.ColorRes
|
||||
|
@ -118,8 +113,8 @@ import org.mariotaku.twidere.fragment.timeline.AbsTimelineFragment
|
|||
import org.mariotaku.twidere.fragment.timeline.FavoritesTimelineFragment
|
||||
import org.mariotaku.twidere.fragment.timeline.UserMediaTimelineFragment
|
||||
import org.mariotaku.twidere.fragment.timeline.UserTimelineFragment
|
||||
import org.mariotaku.twidere.graphic.ActionBarColorDrawable
|
||||
import org.mariotaku.twidere.graphic.ActionIconDrawable
|
||||
import org.mariotaku.twidere.graphic.drawable.userprofile.ActionBarDrawable
|
||||
import org.mariotaku.twidere.loader.ParcelableUserLoader
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.event.FriendshipTaskEvent
|
||||
|
@ -637,7 +632,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
|
|||
profileBanner.onSizeChangedListener = this
|
||||
profileBannerSpace.setOnTouchListener(this)
|
||||
|
||||
profileNameBackground.setBackgroundColor(cardBackgroundColor)
|
||||
profileHeaderBackground.setBackgroundColor(cardBackgroundColor)
|
||||
toolbarTabs.setBackgroundColor(cardBackgroundColor)
|
||||
|
||||
actionBarBackground = ActionBarDrawable(ResourcesCompat.getDrawable(activity.resources,
|
||||
|
@ -1301,8 +1296,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
|
|||
location.setLinkTextColor(optimalAccentColor)
|
||||
url.setLinkTextColor(optimalAccentColor)
|
||||
profileBanner.setBackgroundColor(color)
|
||||
|
||||
toolbarTabs.setBackgroundColor(primaryColor)
|
||||
}
|
||||
|
||||
private fun setupBaseActionBar() {
|
||||
|
@ -1370,11 +1363,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
|
|||
private fun updateScrollOffset(offset: Int) {
|
||||
val spaceHeight = profileBannerSpace.height
|
||||
val factor = (if (spaceHeight == 0) 0f else offset / spaceHeight.toFloat()).coerceIn(0f, 1f)
|
||||
profileBannerContainer.translationY = (-offset).toFloat()
|
||||
profileBanner.translationY = (offset / 2).toFloat()
|
||||
if (profileBirthdayStub == null) {
|
||||
profileBirthdayBanner.translationY = (offset / 2).toFloat()
|
||||
}
|
||||
|
||||
val activity = activity as BaseActivity
|
||||
|
||||
|
@ -1386,7 +1374,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
|
|||
val stackedTabColor = primaryColor
|
||||
|
||||
|
||||
val profileContentHeight = profileNameBackground.height.toFloat()
|
||||
val profileContentHeight = profileHeaderBackground.height.toFloat()
|
||||
val tabOutlineAlphaFactor: Float
|
||||
if (offset - spaceHeight > 0) {
|
||||
tabOutlineAlphaFactor = 1f - ((offset - spaceHeight) / profileContentHeight).coerceIn(0f, 1f)
|
||||
|
@ -1514,70 +1502,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
|
|||
}
|
||||
}
|
||||
|
||||
private class ActionBarDrawable(shadow: Drawable) : LayerDrawable(arrayOf(shadow, ActionBarColorDrawable.create(true))) {
|
||||
|
||||
private val shadowDrawable = getDrawable(0)
|
||||
private val colorDrawable = getDrawable(1) as ColorDrawable
|
||||
private var alphaValue: Int = 0
|
||||
|
||||
var factor: Float = 0f
|
||||
set(value) {
|
||||
field = value
|
||||
updateValue()
|
||||
}
|
||||
|
||||
var color: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
colorDrawable.color = value
|
||||
updateValue()
|
||||
}
|
||||
|
||||
var outlineAlphaFactor: Float = 0f
|
||||
set(value) {
|
||||
field = value
|
||||
updateValue()
|
||||
}
|
||||
|
||||
init {
|
||||
alpha = 0xFF
|
||||
updateValue()
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
alphaValue = alpha
|
||||
updateValue()
|
||||
}
|
||||
|
||||
override fun getAlpha(): Int {
|
||||
return alphaValue
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
override fun getOutline(outline: Outline) {
|
||||
colorDrawable.getOutline(outline)
|
||||
outline.alpha = factor * outlineAlphaFactor * 0.99f
|
||||
}
|
||||
|
||||
override fun getIntrinsicWidth(): Int {
|
||||
return colorDrawable.intrinsicWidth
|
||||
}
|
||||
|
||||
override fun getIntrinsicHeight(): Int {
|
||||
return colorDrawable.intrinsicHeight
|
||||
}
|
||||
|
||||
private fun updateValue() {
|
||||
val shadowAlpha = Math.round(alpha * (1 - factor).coerceIn(0f, 1f))
|
||||
shadowDrawable.alpha = shadowAlpha
|
||||
val hasColor = color != 0
|
||||
val colorAlpha = if (hasColor) Math.round(alpha * factor.coerceIn(0f, 1f)) else 0
|
||||
colorDrawable.alpha = colorAlpha
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal class UserRelationshipLoader(
|
||||
context: Context,
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.graphic.drawable.userprofile
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.graphics.Outline
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Build
|
||||
import org.mariotaku.twidere.graphic.ActionBarColorDrawable
|
||||
|
||||
internal class ActionBarDrawable(shadow: Drawable) : LayerDrawable(arrayOf(shadow, ActionBarColorDrawable.create(true))) {
|
||||
|
||||
private val shadowDrawable = getDrawable(0)
|
||||
private val colorDrawable = getDrawable(1) as ColorDrawable
|
||||
private var alphaValue: Int = 0
|
||||
|
||||
var factor: Float = 0f
|
||||
set(value) {
|
||||
field = value
|
||||
updateValue()
|
||||
}
|
||||
|
||||
var color: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
colorDrawable.color = value
|
||||
updateValue()
|
||||
}
|
||||
|
||||
var outlineAlphaFactor: Float = 0f
|
||||
set(value) {
|
||||
field = value
|
||||
updateValue()
|
||||
}
|
||||
|
||||
init {
|
||||
alpha = 0xFF
|
||||
updateValue()
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
alphaValue = alpha
|
||||
updateValue()
|
||||
}
|
||||
|
||||
override fun getAlpha(): Int {
|
||||
return alphaValue
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
override fun getOutline(outline: Outline) {
|
||||
colorDrawable.getOutline(outline)
|
||||
outline.alpha = factor * outlineAlphaFactor * 0.99f
|
||||
}
|
||||
|
||||
override fun getIntrinsicWidth(): Int {
|
||||
return colorDrawable.intrinsicWidth
|
||||
}
|
||||
|
||||
override fun getIntrinsicHeight(): Int {
|
||||
return colorDrawable.intrinsicHeight
|
||||
}
|
||||
|
||||
private fun updateValue() {
|
||||
val shadowAlpha = Math.round(alpha * (1 - factor).coerceIn(0f, 1f))
|
||||
shadowDrawable.alpha = shadowAlpha
|
||||
val hasColor = color != 0
|
||||
val colorAlpha = if (hasColor) Math.round(alpha * factor.coerceIn(0f, 1f)) else 0
|
||||
colorDrawable.alpha = colorAlpha
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
}
|
|
@ -19,77 +19,89 @@
|
|||
|
||||
package org.mariotaku.twidere.text.style
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.support.v4.widget.CircularProgressDrawable
|
||||
import android.text.style.DynamicDrawableSpan
|
||||
import android.text.style.ReplacementSpan
|
||||
import android.widget.TextView
|
||||
import com.bumptech.glide.RequestManager
|
||||
import com.bumptech.glide.load.resource.drawable.GlideDrawable
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.BaseTarget
|
||||
import com.bumptech.glide.request.target.SizeReadyCallback
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.graphic.DrawableWrapper
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class CustomEmojiSpan(
|
||||
val uri: String,
|
||||
requestManager: RequestManager,
|
||||
callback: Drawable.Callback
|
||||
) : DynamicDrawableSpan(DynamicDrawableSpan.ALIGN_BASELINE) {
|
||||
val textView: TextView,
|
||||
val alignBaseline: Boolean = false
|
||||
) : ReplacementSpan() {
|
||||
|
||||
private val textSize = (callback as TextView).textSize.toInt()
|
||||
private val textSize = textView.textSize.toInt()
|
||||
|
||||
private val emojiDrawable = EmojiDrawable(textSize)
|
||||
private val emojiDirtyBounds = Rect()
|
||||
|
||||
private val target = GlideTarget()
|
||||
private val target = GlideTarget(textSize)
|
||||
|
||||
init {
|
||||
emojiDrawable.callback = callback
|
||||
requestManager.load(uri)
|
||||
.placeholder(CircularProgressDrawable((callback as TextView).context))
|
||||
.asBitmap()
|
||||
.placeholder(R.mipmap.ic_emoji_loading)
|
||||
.error(R.mipmap.ic_emoji_error)
|
||||
.fitCenter()
|
||||
.dontAnimate()
|
||||
.into(target)
|
||||
}
|
||||
|
||||
override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int,
|
||||
fm: Paint.FontMetricsInt?): Int {
|
||||
return textSize
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int,
|
||||
y: Int, bottom: Int, paint: Paint) {
|
||||
val b = target.drawable ?: return
|
||||
canvas.save()
|
||||
|
||||
var transY = bottom - emojiDrawable.bounds.bottom
|
||||
if (verticalAlignment == ALIGN_BASELINE) {
|
||||
var transY = bottom - b.bounds.bottom
|
||||
if (alignBaseline) {
|
||||
transY -= paint.fontMetricsInt.descent
|
||||
}
|
||||
emojiDrawable.copyBounds(emojiDirtyBounds)
|
||||
emojiDirtyBounds.offsetTo(x.toInt(), transY)
|
||||
super.draw(canvas, text, start, end, x, top, y, bottom, paint)
|
||||
|
||||
canvas.translate(x, transY.toFloat())
|
||||
b.setBounds(0, 0, textSize, textSize)
|
||||
b.draw(canvas)
|
||||
canvas.restore()
|
||||
}
|
||||
|
||||
override fun getDrawable(): Drawable = emojiDrawable
|
||||
private inner class GlideTarget(
|
||||
val textSize: Int
|
||||
) : BaseTarget<Bitmap>() {
|
||||
|
||||
fun verify(who: Drawable): Boolean = who === emojiDrawable
|
||||
var drawable: Drawable?
|
||||
get() = drawableRef?.get()
|
||||
private set(value) {
|
||||
drawableRef = if (value != null) WeakReference(value) else null
|
||||
textView.invalidate()
|
||||
}
|
||||
|
||||
private inner class GlideTarget : BaseTarget<GlideDrawable>() {
|
||||
private var drawableRef: WeakReference<Drawable>? = null
|
||||
|
||||
override fun onResourceReady(resource: GlideDrawable, glideAnimation: GlideAnimation<in GlideDrawable>) {
|
||||
resource.setLoopCount(GlideDrawable.LOOP_FOREVER)
|
||||
emojiDrawable.setDrawable(resource)
|
||||
override fun onResourceReady(resource: Bitmap, glideAnimation: GlideAnimation<in Bitmap>) {
|
||||
drawable = BitmapDrawable(textView.resources, resource)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
emojiDrawable.setDrawable(placeholder)
|
||||
drawable = placeholder
|
||||
}
|
||||
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
emojiDrawable.setDrawable(placeholder)
|
||||
drawable = placeholder
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
emojiDrawable.setDrawable(errorDrawable)
|
||||
drawable = errorDrawable
|
||||
}
|
||||
|
||||
override fun getSize(cb: SizeReadyCallback) {
|
||||
|
@ -98,20 +110,4 @@ class CustomEmojiSpan(
|
|||
|
||||
}
|
||||
|
||||
private inner class EmojiDrawable(val textSize: Int) : DrawableWrapper() {
|
||||
|
||||
override fun getDirtyBounds(): Rect {
|
||||
return emojiDirtyBounds
|
||||
}
|
||||
|
||||
fun setDrawable(drawable: Drawable?) {
|
||||
wrapped = drawable
|
||||
drawable?.setBounds(0, 0, textSize, textSize)
|
||||
setBounds(0, 0, textSize, textSize)
|
||||
if (drawable is Animatable) {
|
||||
drawable.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,9 +21,7 @@ package org.mariotaku.twidere.view
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.Spannable
|
||||
import android.text.Spanned
|
||||
import android.text.method.BaseMovementMethod
|
||||
import android.text.method.MovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
|
@ -32,7 +30,6 @@ import android.view.MotionEvent
|
|||
import android.widget.TextView
|
||||
import org.mariotaku.chameleon.view.ChameleonTextView
|
||||
import org.mariotaku.twidere.extension.setupEmojiFactory
|
||||
import org.mariotaku.twidere.text.style.CustomEmojiSpan
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
|
@ -90,16 +87,6 @@ class TimelineContentTextView(
|
|||
}
|
||||
}
|
||||
|
||||
override fun verifyDrawable(who: Drawable): Boolean {
|
||||
val result = super.verifyDrawable(who)
|
||||
if (result) return true
|
||||
val spanned = text as? Spanned ?: return false
|
||||
val spans = spanned.getSpans(0, length(), CustomEmojiSpan::class.java)
|
||||
return spans.any {
|
||||
it.verify(who)
|
||||
}
|
||||
}
|
||||
|
||||
internal class InternalMovementMethod : BaseMovementMethod() {
|
||||
private var targetSpan: WeakReference<ClickableSpan?>? = null
|
||||
|
||||
|
|
|
@ -19,20 +19,40 @@
|
|||
|
||||
package org.mariotaku.twidere.view.behavior.userprofile
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.support.design.widget.AccessorHeaderBehavior
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.support.v4.math.MathUtils
|
||||
import android.support.graphics.drawable.ArgbEvaluator
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import kotlinx.android.synthetic.main.fragment_user.view.*
|
||||
import kotlinx.android.synthetic.main.header_user.view.*
|
||||
import org.mariotaku.chameleon.Chameleon
|
||||
import org.mariotaku.chameleon.ChameleonUtils
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.constant.themeBackgroundAlphaKey
|
||||
import org.mariotaku.twidere.constant.themeBackgroundOptionKey
|
||||
import org.mariotaku.twidere.extension.view.measureChildIgnoringInsets
|
||||
import org.mariotaku.twidere.graphic.drawable.userprofile.ActionBarDrawable
|
||||
import org.mariotaku.twidere.util.ThemeUtils
|
||||
import org.mariotaku.twidere.util.dagger.DependencyHolder
|
||||
|
||||
internal class HeaderBehavior(context: Context, attrs: AttributeSet? = null) : AccessorHeaderBehavior<View>(context, attrs) {
|
||||
|
||||
var offsetDelta: Int = 0
|
||||
private set
|
||||
|
||||
private val cardBackgroundColor: Int
|
||||
private var tabItemIsDark: Int = 0
|
||||
|
||||
init {
|
||||
val preferences = DependencyHolder.get(context).preferences
|
||||
cardBackgroundColor = ThemeUtils.getCardBackgroundColor(context,
|
||||
preferences[themeBackgroundOptionKey], preferences[themeBackgroundAlphaKey])
|
||||
}
|
||||
|
||||
override fun onMeasureChild(parent: CoordinatorLayout, child: View,
|
||||
parentWidthMeasureSpec: Int, widthUsed: Int, parentHeightMeasureSpec: Int,
|
||||
heightUsed: Int): Boolean {
|
||||
|
@ -40,6 +60,12 @@ internal class HeaderBehavior(context: Context, attrs: AttributeSet? = null) : A
|
|||
parentHeightMeasureSpec, heightUsed)
|
||||
}
|
||||
|
||||
override fun onLayoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int): Boolean {
|
||||
val result = super.onLayoutChild(parent, child, layoutDirection)
|
||||
updateTabColor(parent, child, topAndBottomOffset)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun layoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int) {
|
||||
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
|
||||
}
|
||||
|
@ -62,38 +88,19 @@ internal class HeaderBehavior(context: Context, attrs: AttributeSet? = null) : A
|
|||
val curOffset = topBottomOffsetForScrollingSibling
|
||||
var consumed = 0
|
||||
|
||||
var newOffset = newOffset
|
||||
if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
|
||||
// If we have some scrolling range, and we're currently within the min and max
|
||||
// offsets, calculate a new offset
|
||||
newOffset = MathUtils.clamp(newOffset, minOffset, maxOffset)
|
||||
if (curOffset != newOffset) {
|
||||
val interpolatedOffset = if (header.hasChildWithInterpolator)
|
||||
interpolateOffset(header, newOffset)
|
||||
else
|
||||
newOffset
|
||||
|
||||
val offsetChanged = setTopAndBottomOffset(interpolatedOffset)
|
||||
val clampedOffset = newOffset.coerceIn(minOffset, maxOffset)
|
||||
if (curOffset != clampedOffset) {
|
||||
|
||||
topAndBottomOffset = clampedOffset
|
||||
updateTabColor(parent, header, clampedOffset)
|
||||
// Update how much dy we have consumed
|
||||
consumed = curOffset - newOffset
|
||||
consumed = curOffset - clampedOffset
|
||||
// Update the stored sibling offset
|
||||
offsetDelta = newOffset - interpolatedOffset
|
||||
offsetDelta = clampedOffset - clampedOffset
|
||||
|
||||
if (!offsetChanged && header.hasChildWithInterpolator) {
|
||||
// If the offset hasn't changed and we're using an interpolated scroll
|
||||
// then we need to keep any dependent views updated. CoL will do this for
|
||||
// us when we move, but we need to do it manually when we don't (as an
|
||||
// interpolated scroll may finish early).
|
||||
parent.dispatchDependentViewsChanged(header)
|
||||
}
|
||||
|
||||
// Dispatch the updates to any listeners
|
||||
// header.dispatchOffsetUpdates(topAndBottomOffset)
|
||||
|
||||
// Update the AppBarLayout's drawable state (for any elevation changes)
|
||||
// updateAppBarLayoutDrawableState(parent, header, newOffset,
|
||||
// if (newOffset < curOffset) -1 else 1, false)
|
||||
}
|
||||
} else {
|
||||
// Reset the offset delta
|
||||
|
@ -103,13 +110,42 @@ internal class HeaderBehavior(context: Context, attrs: AttributeSet? = null) : A
|
|||
return consumed
|
||||
}
|
||||
|
||||
override fun canDragView(view: View) = true
|
||||
@SuppressLint("RestrictedApi")
|
||||
private fun updateTabColor(parent: CoordinatorLayout, header: View, offset: Int) {
|
||||
val actionBarBackground = parent.toolbar.background as? ActionBarDrawable ?: return
|
||||
val profileHeaderBackground = parent.profileHeaderBackground
|
||||
|
||||
private fun interpolateOffset(header: View, offset: Int): Int {
|
||||
return offset
|
||||
val toolbarBottom = parent.toolbar.bottom
|
||||
val headerBackgroundOffset = offset + profileHeaderBackground.top
|
||||
|
||||
val factor = ((toolbarBottom - headerBackgroundOffset) / profileHeaderBackground.height.toFloat()).coerceIn(0f, 1f)
|
||||
|
||||
val toolbarTabs = header.toolbarTabs
|
||||
|
||||
val colorPrimary = actionBarBackground.color
|
||||
val currentTabColor = ArgbEvaluator.getInstance().evaluate(factor,
|
||||
cardBackgroundColor, colorPrimary) as Int
|
||||
|
||||
toolbarTabs.setBackgroundColor(currentTabColor)
|
||||
|
||||
val tabItemIsDark = if (ThemeUtils.isLightColor(currentTabColor)) 1 else -1
|
||||
if (this.tabItemIsDark != tabItemIsDark) {
|
||||
val context = parent.context
|
||||
val activity = ChameleonUtils.getActivity(context)
|
||||
val tabContrastColor = ThemeUtils.getColorDependent(currentTabColor)
|
||||
toolbarTabs.setIconColor(tabContrastColor)
|
||||
toolbarTabs.setLabelColor(tabContrastColor)
|
||||
val theme = Chameleon.getOverrideTheme(context, activity)
|
||||
if (theme.isToolbarColored) {
|
||||
toolbarTabs.setStripColor(tabContrastColor)
|
||||
} else {
|
||||
toolbarTabs.setStripColor(ThemeUtils.getOptimalAccentColor(colorPrimary, tabContrastColor))
|
||||
}
|
||||
toolbarTabs.updateAppearance()
|
||||
}
|
||||
this.tabItemIsDark = tabItemIsDark
|
||||
}
|
||||
|
||||
private val View.hasChildWithInterpolator: Boolean
|
||||
get() = false
|
||||
override fun canDragView(view: View) = true
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.view.behavior.userprofile
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.support.design.widget.lastWindowInsetsCompat
|
||||
import android.support.graphics.drawable.ArgbEvaluator
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import kotlinx.android.synthetic.main.fragment_user.view.*
|
||||
import org.mariotaku.chameleon.ChameleonUtils
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.graphic.drawable.userprofile.ActionBarDrawable
|
||||
import org.mariotaku.twidere.util.ThemeUtils
|
||||
import org.mariotaku.twidere.util.support.WindowSupport
|
||||
|
||||
internal class StatusBarBehavior(context: Context, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior<View>(context, attrs) {
|
||||
|
||||
private val window: Window = ChameleonUtils.getActivity(context)!!.window
|
||||
private var lightStatusBar: Int = 0
|
||||
|
||||
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||
return dependency.id == R.id.profileHeader
|
||||
}
|
||||
|
||||
override fun onLayoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int): Boolean {
|
||||
val lastInsets = parent.lastWindowInsetsCompat ?: return true
|
||||
val height = lastInsets.systemWindowInsetTop
|
||||
child.layout(0, 0, child.measuredWidth, height)
|
||||
return true
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||
if (child.height == 0) return true
|
||||
val actionBarBackground = parent.toolbar.background as? ActionBarDrawable ?: return true
|
||||
val bannerContainer = parent.profileBannerContainer
|
||||
val bannerBottom = dependency.top + bannerContainer.height
|
||||
val currentOffset = bannerBottom - child.bottom
|
||||
val maxOffset = (bannerContainer.height - child.bottom).toFloat()
|
||||
val factor = (1 - currentOffset / maxOffset).coerceIn(0f, 1f)
|
||||
|
||||
val primaryColorDark = ChameleonUtils.darkenColor(actionBarBackground.color)
|
||||
val statusBarColor = ArgbEvaluator.getInstance().evaluate(factor, 0xA0000000.toInt(),
|
||||
ChameleonUtils.darkenColor(primaryColorDark))
|
||||
child.setBackgroundColor(statusBarColor as Int)
|
||||
val lightStatusBar = if (ThemeUtils.isLightColor(statusBarColor)) 1 else -1
|
||||
if (this.lightStatusBar != lightStatusBar) {
|
||||
WindowSupport.setLightStatusBar(window, lightStatusBar == 1)
|
||||
}
|
||||
this.lightStatusBar = lightStatusBar
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
|
@ -19,11 +19,47 @@
|
|||
|
||||
package org.mariotaku.twidere.view.behavior.userprofile
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.support.graphics.drawable.ArgbEvaluator
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import kotlinx.android.synthetic.main.fragment_user.view.*
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.graphic.drawable.userprofile.ActionBarDrawable
|
||||
import org.mariotaku.twidere.util.ThemeUtils
|
||||
|
||||
internal class ToolbarBehavior(context: Context?, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior<Toolbar>(context, attrs) {
|
||||
|
||||
private val actionBarShadowColor: Int = 0xA0000000.toInt()
|
||||
private var actionItemIsDark: Int = 0
|
||||
|
||||
override fun layoutDependsOn(parent: CoordinatorLayout, child: Toolbar, dependency: View): Boolean {
|
||||
return dependency.id == R.id.profileHeader
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onDependentViewChanged(parent: CoordinatorLayout, child: Toolbar, dependency: View): Boolean {
|
||||
val actionBarBackground = child.background as? ActionBarDrawable ?: return false
|
||||
val bannerContainer = parent.profileBannerContainer
|
||||
val bannerBottom = dependency.top + bannerContainer.height
|
||||
val currentOffset = bannerBottom - child.bottom
|
||||
val maxOffset = (bannerContainer.height - child.bottom).toFloat()
|
||||
val factor = (1 - currentOffset / maxOffset).coerceIn(0f, 1f)
|
||||
actionBarBackground.factor = factor
|
||||
actionBarBackground.outlineAlphaFactor = factor
|
||||
|
||||
val colorPrimary = actionBarBackground.color
|
||||
val currentActionBarColor = ArgbEvaluator.getInstance().evaluate(factor, actionBarShadowColor,
|
||||
colorPrimary) as Int
|
||||
val actionItemIsDark = if (ThemeUtils.isLightColor(currentActionBarColor)) 1 else -1
|
||||
if (this.actionItemIsDark != actionItemIsDark) {
|
||||
ThemeUtils.applyToolbarItemColor(parent.context, child, currentActionBarColor)
|
||||
}
|
||||
this.actionItemIsDark = actionItemIsDark
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -117,7 +117,12 @@
|
|||
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Don't change view order, since this view consumes window insets-->
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="org.mariotaku.twidere.view.behavior.userprofile.StatusBarBehavior"
|
||||
tools:layout_height="25dp"/>
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/profileNameBackground"
|
||||
android:id="@+id/profileHeaderBackground"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/countsContainer"
|
||||
|
|
Loading…
Reference in New Issue