mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-16 19:50:53 +01:00
finished conversation info app bar animation
This commit is contained in:
parent
a8c5fca745
commit
81bf409cca
1
twidere/proguard-rules.pro
vendored
1
twidere/proguard-rules.pro
vendored
@ -69,6 +69,7 @@
|
||||
-keep class * extends org.mariotaku.twidere.util.TwitterCardFragmentFactory
|
||||
-keep class * extends org.mariotaku.twidere.util.Analyzer
|
||||
-keep class * extends org.mariotaku.twidere.util.premium.ExtraFeaturesService
|
||||
-keep class * extends org.mariotaku.twidere.util.view.AppBarChildBehavior.ChildTransformation
|
||||
|
||||
-keepclassmembers class * {
|
||||
private <fields>;
|
||||
|
@ -27,7 +27,7 @@ import org.mariotaku.twidere.model.ParcelableUser
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.view.holder.SimpleUserViewHolder
|
||||
|
||||
class SimpleParcelableUsersAdapter @JvmOverloads constructor(
|
||||
class SimpleParcelableUsersAdapter(
|
||||
context: Context,
|
||||
layoutRes: Int = R.layout.list_item_simple_user
|
||||
) : BaseArrayAdapter<ParcelableUser>(context, layoutRes) {
|
||||
@ -54,7 +54,7 @@ class SimpleParcelableUsersAdapter @JvmOverloads constructor(
|
||||
return view
|
||||
}
|
||||
|
||||
@JvmOverloads fun setData(data: List<ParcelableUser>?, clearOld: Boolean = false) {
|
||||
fun setData(data: List<ParcelableUser>?, clearOld: Boolean = false) {
|
||||
if (clearOld) {
|
||||
clear()
|
||||
}
|
||||
|
@ -21,9 +21,58 @@ package org.mariotaku.twidere.extension
|
||||
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.support.v4.view.ViewCompat
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/20.
|
||||
*/
|
||||
|
||||
val Rect.origin: Point get() = Point(left, top)
|
||||
|
||||
fun Rect.offsetTopTo(y: Int) {
|
||||
offsetTo(left, y)
|
||||
}
|
||||
|
||||
fun Rect.offsetBottomTo(y: Int) {
|
||||
offsetTo(left, y - height())
|
||||
}
|
||||
|
||||
fun Rect.offsetLeftTo(x: Int) {
|
||||
offsetTo(x, top)
|
||||
}
|
||||
|
||||
fun Rect.offsetRightTo(x: Int) {
|
||||
offsetTo(x - width(), top)
|
||||
}
|
||||
|
||||
fun Rect.offsetStartTo(x: Int, layoutDirection: Int) {
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
offsetRightTo(x)
|
||||
} else {
|
||||
offsetLeftTo(x)
|
||||
}
|
||||
}
|
||||
|
||||
fun Rect.offsetEndTo(x: Int, layoutDirection: Int) {
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
offsetLeftTo(x)
|
||||
} else {
|
||||
offsetRightTo(x)
|
||||
}
|
||||
}
|
||||
|
||||
fun Rect.getStart(layoutDirection: Int): Int {
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
return this.right
|
||||
} else {
|
||||
return this.left
|
||||
}
|
||||
}
|
||||
|
||||
fun Rect.getEnd(layoutDirection: Int): Int {
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
return this.left
|
||||
} else {
|
||||
return this.right
|
||||
}
|
||||
}
|
@ -21,31 +21,33 @@ package org.mariotaku.twidere.extension
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.support.annotation.MainThread
|
||||
import android.support.annotation.UiThread
|
||||
import android.view.View
|
||||
|
||||
private val tempLocation = IntArray(2)
|
||||
private val tempRect = Rect()
|
||||
|
||||
@MainThread
|
||||
@UiThread
|
||||
fun View.getBounds(rect: RectF) {
|
||||
rect.set(x, y, x + width, y + height)
|
||||
}
|
||||
|
||||
@MainThread
|
||||
@UiThread
|
||||
fun View.getFrame(rect: Rect) {
|
||||
rect.set(left, top, right, bottom)
|
||||
}
|
||||
|
||||
@MainThread
|
||||
fun View.getFrameRelatedTo(rect: Rect, view: View? = null) {
|
||||
if (view != null) {
|
||||
view.getFrame(tempRect)
|
||||
offsetToRoot(view, tempRect)
|
||||
}
|
||||
@UiThread
|
||||
fun View.getFrameRelatedTo(rect: Rect, other: View? = null) {
|
||||
this.getFrame(rect)
|
||||
offsetToRoot(this, rect)
|
||||
if (view != null) {
|
||||
if (other == null) {
|
||||
offsetToRoot(this, rect)
|
||||
} else if (other === this) {
|
||||
rect.offsetTo(0, 0)
|
||||
} else if (other !== parent) {
|
||||
offsetToRoot(this, rect)
|
||||
other.getFrame(tempRect)
|
||||
offsetToRoot(other, tempRect)
|
||||
rect.offset(-tempRect.left, -tempRect.top)
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,9 @@ import android.support.v4.app.LoaderManager
|
||||
import android.support.v4.content.AsyncTaskLoader
|
||||
import android.support.v4.content.Loader
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.FixedLinearLayoutManager
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@ -39,6 +42,8 @@ import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.useCursor
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter
|
||||
import org.mariotaku.twidere.adapter.iface.IItemCountsAdapter
|
||||
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_KEY
|
||||
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_CONVERSATION_ID
|
||||
import org.mariotaku.twidere.constant.nameFirstKey
|
||||
@ -47,10 +52,12 @@ import org.mariotaku.twidere.extension.model.displayAvatarTo
|
||||
import org.mariotaku.twidere.extension.model.getConversationName
|
||||
import org.mariotaku.twidere.fragment.BaseFragment
|
||||
import org.mariotaku.twidere.fragment.iface.IToolBarSupportFragment
|
||||
import org.mariotaku.twidere.model.ItemCounts
|
||||
import org.mariotaku.twidere.model.ParcelableMessageConversation
|
||||
import org.mariotaku.twidere.model.ParcelableMessageConversationCursorIndices
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
|
||||
import org.mariotaku.twidere.view.holder.SimpleUserViewHolder
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/15.
|
||||
@ -62,6 +69,8 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
||||
private val accountKey: UserKey get() = arguments.getParcelable(EXTRA_ACCOUNT_KEY)
|
||||
private val conversationId: String get() = arguments.getString(EXTRA_CONVERSATION_ID)
|
||||
|
||||
private lateinit var adapter: ConversationInfoAdapter
|
||||
|
||||
override val controlBarHeight: Int get() = toolbar.measuredHeight
|
||||
override var controlBarOffset: Float = 0f
|
||||
|
||||
@ -76,20 +85,29 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
||||
activity.supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||
}
|
||||
|
||||
adapter = ConversationInfoAdapter(context)
|
||||
recyclerView.adapter = adapter
|
||||
recyclerView.layoutManager = FixedLinearLayoutManager(context, LinearLayoutManager.VERTICAL,
|
||||
false)
|
||||
|
||||
val theme = Chameleon.getOverrideTheme(context, activity)
|
||||
|
||||
val profileImageStyle = preferences[profileImageStyleKey]
|
||||
appBarConversationAvatar.style = profileImageStyle
|
||||
appBarIcon.style = profileImageStyle
|
||||
conversationAvatar.style = profileImageStyle
|
||||
|
||||
val avatarBackground = ChameleonUtils.getColorDependent(theme.colorToolbar)
|
||||
appBarConversationAvatar.setBackgroundColor(avatarBackground)
|
||||
appBarConversationName.setTextColor(ChameleonUtils.getColorDependent(theme.colorToolbar))
|
||||
|
||||
appBarIcon.setBackgroundColor(avatarBackground)
|
||||
appBarTitle.setTextColor(ChameleonUtils.getColorDependent(theme.colorToolbar))
|
||||
appBarSubtitle.setTextColor(ChameleonUtils.getColorDependent(theme.colorToolbar))
|
||||
|
||||
conversationAvatar.setBackgroundColor(avatarBackground)
|
||||
conversationName.setTextColor(ChameleonUtils.getColorDependent(theme.colorToolbar))
|
||||
conversationSummary.setTextColor(ChameleonUtils.getColorDependent(theme.colorToolbar))
|
||||
|
||||
loaderManager.initLoader(0, null, this)
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
@ -112,11 +130,17 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
||||
activity?.finish()
|
||||
return
|
||||
}
|
||||
adapter.conversation = data
|
||||
|
||||
val name = data.getConversationName(context, userColorNameManager, preferences[nameFirstKey]).first
|
||||
val summary = resources.getQuantityString(R.plurals.N_users, data.participants.size, data.participants.size)
|
||||
|
||||
data.displayAvatarTo(mediaLoader, conversationAvatar)
|
||||
data.displayAvatarTo(mediaLoader, appBarConversationAvatar)
|
||||
appBarConversationName.text = name
|
||||
data.displayAvatarTo(mediaLoader, appBarIcon)
|
||||
appBarTitle.text = name
|
||||
appBarSubtitle.text = summary
|
||||
conversationName.text = name
|
||||
conversationSummary.text = summary
|
||||
}
|
||||
|
||||
class ConversationInfoLoader(
|
||||
@ -140,4 +164,67 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
||||
forceLoad()
|
||||
}
|
||||
}
|
||||
|
||||
class ConversationInfoAdapter(context: Context) : BaseRecyclerViewAdapter<RecyclerView.ViewHolder>(context),
|
||||
IItemCountsAdapter {
|
||||
private val inflater = LayoutInflater.from(context)
|
||||
override val itemCounts: ItemCounts = ItemCounts(2)
|
||||
var conversation: ParcelableMessageConversation? = null
|
||||
set(value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
val conversation = this.conversation ?: return 0
|
||||
itemCounts[ITEM_INDEX_ITEM] = conversation.participants.size
|
||||
return itemCounts.itemCount
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder.itemViewType) {
|
||||
VIEW_TYPE_USER -> {
|
||||
val user = this.conversation!!.participants[position - itemCounts.getItemStartPosition(position)]
|
||||
(holder as SimpleUserViewHolder).displayUser(user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
when (viewType) {
|
||||
VIEW_TYPE_HEADER -> {
|
||||
val view = inflater.inflate(HeaderViewHolder.layoutResource, parent, false)
|
||||
return HeaderViewHolder(view)
|
||||
}
|
||||
VIEW_TYPE_USER -> {
|
||||
val view = inflater.inflate(SimpleUserViewHolder.layoutResource, parent, false)
|
||||
return SimpleUserViewHolder(view, this)
|
||||
}
|
||||
}
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
when (itemCounts.getItemCountIndex(position)) {
|
||||
ITEM_INDEX_HEADER -> return VIEW_TYPE_HEADER
|
||||
ITEM_INDEX_ITEM -> return VIEW_TYPE_USER
|
||||
}
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ITEM_INDEX_HEADER = 0
|
||||
private const val ITEM_INDEX_ITEM = 1
|
||||
|
||||
private const val VIEW_TYPE_HEADER = 1
|
||||
private const val VIEW_TYPE_USER = 2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
companion object {
|
||||
const val layoutResource = R.layout.header_message_conversation_info
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* 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.util.view
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.graphics.Rect
|
||||
import android.support.annotation.StyleableRes
|
||||
import android.support.design.widget.AppBarLayout
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import org.mariotaku.microblog.library.annotation.NoObfuscate
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.extension.*
|
||||
import org.mariotaku.twidere.util.DebugLog
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/20.
|
||||
*/
|
||||
class AppBarChildBehavior(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : CoordinatorLayout.Behavior<View>(context, attrs) {
|
||||
private val appBarId: Int
|
||||
private val toolbarId: Int
|
||||
private val dependencyViewId: Int
|
||||
private val targetViewId: Int
|
||||
private val alignmentRule: Int
|
||||
|
||||
private val marginTop: Int
|
||||
private val marginBottom: Int
|
||||
private val marginLeft: Int
|
||||
private val marginRight: Int
|
||||
private val marginStart: Int
|
||||
private val marginEnd: Int
|
||||
|
||||
private val transformation: ChildTransformation
|
||||
|
||||
private val dependencyRect = Rect()
|
||||
|
||||
private val thisRect = Rect()
|
||||
private val thisInitRect = Rect()
|
||||
private val targetRect = Rect()
|
||||
|
||||
private val tempRect = Rect()
|
||||
|
||||
|
||||
init {
|
||||
val a = context.obtainStyledAttributes(attrs, R.styleable.AppBarChildBehavior)
|
||||
appBarId = a.getResourceIdOrThrow(R.styleable.AppBarChildBehavior_behavior_appBarId, "appBarId")
|
||||
toolbarId = a.getResourceIdOrThrow(R.styleable.AppBarChildBehavior_behavior_toolbarId, "toolbarId")
|
||||
dependencyViewId = a.getResourceIdOrThrow(R.styleable.AppBarChildBehavior_behavior_dependencyViewId, "dependencyViewId")
|
||||
targetViewId = a.getResourceIdOrThrow(R.styleable.AppBarChildBehavior_behavior_targetViewId, "targetViewId")
|
||||
alignmentRule = a.getIntegerOrThrow(R.styleable.AppBarChildBehavior_behavior_alignmentRule, "alignmentRule")
|
||||
|
||||
marginTop = a.getDimensionPixelSize(R.styleable.AppBarChildBehavior_behavior_marginTop, 0)
|
||||
marginBottom = a.getDimensionPixelSize(R.styleable.AppBarChildBehavior_behavior_marginBottom, 0)
|
||||
marginLeft = a.getDimensionPixelSize(R.styleable.AppBarChildBehavior_behavior_marginLeft, 0)
|
||||
marginRight = a.getDimensionPixelSize(R.styleable.AppBarChildBehavior_behavior_marginRight, 0)
|
||||
marginStart = a.getDimensionPixelSize(R.styleable.AppBarChildBehavior_behavior_marginStart, 0)
|
||||
marginEnd = a.getDimensionPixelSize(R.styleable.AppBarChildBehavior_behavior_marginEnd, 0)
|
||||
|
||||
transformation = a.getTransformation(R.styleable.AppBarChildBehavior_behavior_childTransformation)
|
||||
|
||||
a.recycle()
|
||||
}
|
||||
|
||||
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||
return dependency.id == dependencyViewId
|
||||
}
|
||||
|
||||
override fun onLayoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int): Boolean {
|
||||
val target = parent.findViewById(targetViewId)
|
||||
val dependency = parent.getDependencies(child).first()
|
||||
|
||||
dependency.getFrameRelatedTo(dependencyRect, parent)
|
||||
child.layoutRelatedTo(dependencyRect, layoutDirection)
|
||||
|
||||
child.getFrameRelatedTo(thisRect, parent)
|
||||
target.getFrameRelatedTo(targetRect, parent)
|
||||
|
||||
transformation.onChildLayoutChanged(child, dependency, target)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||
val appBar = parent.findViewById(appBarId)
|
||||
val target = parent.findViewById(targetViewId)
|
||||
val toolbar = parent.findViewById(toolbarId)
|
||||
val behavior = (appBar.layoutParams as CoordinatorLayout.LayoutParams).behavior as AppBarLayout.Behavior
|
||||
toolbar.getLocationOnScreen(tempRect)
|
||||
val offset = behavior.topAndBottomOffset
|
||||
val percent = offset / (tempRect.bottom - appBar.height).toFloat()
|
||||
transformation.onTargetChanged(child, thisRect, target, targetRect, percent, offset)
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun View.layoutRelatedTo(frame: Rect, layoutDirection: Int) {
|
||||
val verticalRule = alignmentRule and VERTICAL_MASK
|
||||
val horizontalRule = alignmentRule and HORIZONTAL_MASK
|
||||
tempRect.set(0, 0, measuredWidth, measuredHeight)
|
||||
when (verticalRule) {
|
||||
ALIGNMENT_CENTER_VERTICAL -> {
|
||||
tempRect.offsetTopTo(frame.centerY() - measuredHeight / 2 + marginTop - marginBottom)
|
||||
}
|
||||
0, ALIGNMENT_TOP -> {
|
||||
tempRect.offsetTopTo(frame.top + marginTop)
|
||||
}
|
||||
ALIGNMENT_BOTTOM -> {
|
||||
tempRect.offsetBottomTo(frame.bottom - marginBottom)
|
||||
}
|
||||
ALIGNMENT_ABOVE -> {
|
||||
tempRect.offsetBottomTo(frame.top + marginTop - marginBottom)
|
||||
}
|
||||
ALIGNMENT_BELOW -> {
|
||||
tempRect.offsetTopTo(frame.bottom + marginTop - marginBottom)
|
||||
}
|
||||
ALIGNMENT_ABOVE_CENTER -> {
|
||||
tempRect.offsetBottomTo(frame.centerY() + marginTop - marginBottom)
|
||||
}
|
||||
ALIGNMENT_BELOW_CENTER -> {
|
||||
tempRect.offsetTopTo(frame.centerY() + marginTop - marginBottom)
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Illegal alignment flag ${Integer.toHexString(alignmentRule)}")
|
||||
}
|
||||
}
|
||||
when (horizontalRule) {
|
||||
ALIGNMENT_CENTER_HORIZONTAL -> {
|
||||
tempRect.offsetLeftTo(frame.centerX() - measuredWidth / 2
|
||||
+ absoluteMarginLeft(layoutDirection) - absoluteMarginRight(layoutDirection))
|
||||
}
|
||||
0, ALIGNMENT_LEFT -> {
|
||||
tempRect.offsetLeftTo(frame.left + absoluteMarginLeft(layoutDirection))
|
||||
}
|
||||
ALIGNMENT_RIGHT -> {
|
||||
tempRect.offsetRightTo(frame.right - absoluteMarginRight(layoutDirection))
|
||||
}
|
||||
ALIGNMENT_TO_LEFT_OF -> {
|
||||
tempRect.offsetRightTo(frame.left + absoluteMarginLeft(layoutDirection)
|
||||
- absoluteMarginRight(layoutDirection))
|
||||
}
|
||||
ALIGNMENT_TO_RIGHT_OF -> {
|
||||
tempRect.offsetLeftTo(frame.right + absoluteMarginLeft(layoutDirection)
|
||||
- absoluteMarginRight(layoutDirection))
|
||||
}
|
||||
ALIGNMENT_TO_LEFT_OF_CENTER -> {
|
||||
tempRect.offsetRightTo(frame.centerX() + absoluteMarginLeft(layoutDirection)
|
||||
- absoluteMarginRight(layoutDirection))
|
||||
}
|
||||
ALIGNMENT_TO_RIGHT_OF_CENTER -> {
|
||||
tempRect.offsetLeftTo(frame.centerX() + absoluteMarginLeft(layoutDirection)
|
||||
- absoluteMarginRight(layoutDirection))
|
||||
}
|
||||
ALIGNMENT_START -> {
|
||||
tempRect.offsetStartTo(frame.getStart(layoutDirection)
|
||||
+ relativeMarginStart(layoutDirection), layoutDirection)
|
||||
}
|
||||
ALIGNMENT_END -> {
|
||||
tempRect.offsetEndTo(frame.getEnd(layoutDirection)
|
||||
- relativeMarginEnd(layoutDirection), layoutDirection)
|
||||
}
|
||||
ALIGNMENT_TO_START_OF -> {
|
||||
tempRect.offsetEndTo(frame.getStart(layoutDirection)
|
||||
+ relativeMarginStart(layoutDirection) - relativeMarginEnd(layoutDirection),
|
||||
layoutDirection)
|
||||
}
|
||||
ALIGNMENT_TO_END_OF -> {
|
||||
tempRect.offsetStartTo(frame.getEnd(layoutDirection)
|
||||
+ relativeMarginStart(layoutDirection) - relativeMarginEnd(layoutDirection),
|
||||
layoutDirection)
|
||||
}
|
||||
ALIGNMENT_TO_START_OF_CENTER -> {
|
||||
tempRect.offsetEndTo(frame.centerX() + relativeMarginStart(layoutDirection)
|
||||
- relativeMarginEnd(layoutDirection), layoutDirection)
|
||||
}
|
||||
ALIGNMENT_TO_END_OF_CENTER -> {
|
||||
tempRect.offsetStartTo(frame.centerX() + relativeMarginStart(layoutDirection)
|
||||
- relativeMarginEnd(layoutDirection), layoutDirection)
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Illegal alignment flag ${Integer.toHexString(alignmentRule)}")
|
||||
}
|
||||
}
|
||||
this.layout(tempRect.left, tempRect.top, tempRect.right, tempRect.bottom)
|
||||
}
|
||||
|
||||
private fun absoluteMarginLeft(layoutDirection: Int): Int {
|
||||
if (marginStart == 0 && marginEnd == 0) return marginLeft
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
return marginEnd
|
||||
} else {
|
||||
return marginStart
|
||||
}
|
||||
}
|
||||
|
||||
private fun absoluteMarginRight(layoutDirection: Int): Int {
|
||||
if (marginStart == 0 && marginEnd == 0) return marginRight
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
return marginStart
|
||||
} else {
|
||||
return marginEnd
|
||||
}
|
||||
}
|
||||
|
||||
private fun relativeMarginStart(layoutDirection: Int): Int {
|
||||
if (marginStart != 0) {
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
return -marginStart
|
||||
} else {
|
||||
return marginStart
|
||||
}
|
||||
} else {
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
return marginRight
|
||||
} else {
|
||||
return marginLeft
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun relativeMarginEnd(layoutDirection: Int): Int {
|
||||
if (marginEnd != 0) {
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
return -marginEnd
|
||||
} else {
|
||||
return marginEnd
|
||||
}
|
||||
} else {
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
return marginLeft
|
||||
} else {
|
||||
return marginRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ChildTransformation {
|
||||
fun onChildLayoutChanged(child: View, dependency: View, target: View) {}
|
||||
fun onTargetChanged(child: View, frame: Rect, target: View, targetFrame: Rect, percent: Float, offset: Int)
|
||||
}
|
||||
|
||||
open class ScaleTransformation : ChildTransformation {
|
||||
|
||||
override fun onTargetChanged(child: View, frame: Rect, target: View, targetFrame: Rect, percent: Float, offset: Int) {
|
||||
child.pivotX = child.width.toFloat()
|
||||
child.pivotY = child.height.toFloat()
|
||||
child.scaleX = 1 - (frame.width() - targetFrame.width()) * percent / frame.width()
|
||||
child.scaleY = 1 - (frame.height() - targetFrame.height()) * percent / frame.height()
|
||||
child.translationX = (targetFrame.right - frame.right) * percent
|
||||
child.translationY = -offset - (frame.bottom - offset - targetFrame.bottom) * percent
|
||||
DebugLog.d(msg = "bot:${frame.bottom}")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@NoObfuscate
|
||||
open class TextViewTransformation : ChildTransformation {
|
||||
|
||||
private var sourceSize: Float = Float.NaN
|
||||
private var destSize: Float = Float.NaN
|
||||
|
||||
private var viewLaidOut: Boolean = false
|
||||
|
||||
override fun onChildLayoutChanged(child: View, dependency: View, target: View) {
|
||||
if (viewLaidOut) return
|
||||
child as TextView
|
||||
target as TextView
|
||||
sourceSize = child.textSize
|
||||
destSize = target.textSize
|
||||
viewLaidOut = true
|
||||
}
|
||||
|
||||
override fun onTargetChanged(child: View, frame: Rect, target: View, targetFrame: Rect, percent: Float, offset: Int) {
|
||||
child as TextView
|
||||
child.pivotX = child.width.toFloat()
|
||||
child.pivotY = child.height.toFloat()
|
||||
child.translationX = (targetFrame.left - frame.left) * percent
|
||||
child.translationY = -offset - (frame.bottom - offset - targetFrame.bottom) * percent
|
||||
child.setTextSize(TypedValue.COMPLEX_UNIT_PX, sourceSize + (destSize - sourceSize) * percent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val HORIZONTAL_MASK: Int = 0x0000FFFF
|
||||
private const val VERTICAL_MASK: Int = 0xFFFF0000.toInt()
|
||||
private const val HORIZONTAL_RELATIVE_FLAG: Int = 0x00001000
|
||||
const val ALIGNMENT_LEFT: Int = 0x00000001
|
||||
const val ALIGNMENT_RIGHT: Int = 0x00000002
|
||||
const val ALIGNMENT_TO_LEFT_OF: Int = 0x00000004
|
||||
const val ALIGNMENT_TO_RIGHT_OF: Int = 0x00000008
|
||||
const val ALIGNMENT_TO_LEFT_OF_CENTER: Int = 0x00000010
|
||||
const val ALIGNMENT_TO_RIGHT_OF_CENTER: Int = 0x00000020
|
||||
const val ALIGNMENT_CENTER_HORIZONTAL: Int = ALIGNMENT_LEFT or ALIGNMENT_RIGHT
|
||||
const val ALIGNMENT_START: Int = ALIGNMENT_LEFT or HORIZONTAL_RELATIVE_FLAG
|
||||
const val ALIGNMENT_END: Int = ALIGNMENT_RIGHT or HORIZONTAL_RELATIVE_FLAG
|
||||
const val ALIGNMENT_TO_START_OF: Int = ALIGNMENT_TO_LEFT_OF or HORIZONTAL_RELATIVE_FLAG
|
||||
const val ALIGNMENT_TO_END_OF: Int = ALIGNMENT_TO_RIGHT_OF or HORIZONTAL_RELATIVE_FLAG
|
||||
const val ALIGNMENT_TO_START_OF_CENTER: Int = ALIGNMENT_TO_LEFT_OF_CENTER or HORIZONTAL_RELATIVE_FLAG
|
||||
const val ALIGNMENT_TO_END_OF_CENTER: Int = ALIGNMENT_TO_RIGHT_OF_CENTER or HORIZONTAL_RELATIVE_FLAG
|
||||
const val ALIGNMENT_TOP: Int = 0x00010000
|
||||
const val ALIGNMENT_BOTTOM: Int = 0x00020000
|
||||
const val ALIGNMENT_ABOVE: Int = 0x00040000
|
||||
const val ALIGNMENT_BELOW: Int = 0x00080000
|
||||
const val ALIGNMENT_ABOVE_CENTER: Int = 0x00100000
|
||||
const val ALIGNMENT_BELOW_CENTER: Int = 0x00200000
|
||||
const val ALIGNMENT_CENTER_VERTICAL: Int = ALIGNMENT_TOP or ALIGNMENT_BOTTOM
|
||||
|
||||
private fun TypedArray.getIntegerOrThrow(@StyleableRes index: Int, name: String): Int {
|
||||
if (!hasValue(index)) {
|
||||
throw IllegalArgumentException("$name required")
|
||||
}
|
||||
return getInteger(index, 0)
|
||||
}
|
||||
|
||||
private fun TypedArray.getResourceIdOrThrow(@StyleableRes index: Int, name: String): Int {
|
||||
if (!hasValue(index)) {
|
||||
throw IllegalArgumentException("$name required")
|
||||
}
|
||||
return getResourceId(index, 0)
|
||||
}
|
||||
|
||||
private fun TypedArray.getTransformation(@StyleableRes index: Int): ChildTransformation {
|
||||
val className = getString(index) ?: return ScaleTransformation()
|
||||
return Class.forName(className).newInstance() as ChildTransformation
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.util.view
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/21.
|
||||
*/
|
||||
|
||||
class ConversationAvatarTransformation : AppBarChildBehavior.ScaleTransformation() {
|
||||
override fun onTargetChanged(child: View, frame: Rect, target: View, targetFrame: Rect, percent: Float, offset: Int) {
|
||||
super.onTargetChanged(child, frame, target, targetFrame, percent, offset)
|
||||
if (percent < 1) {
|
||||
target.visibility = View.INVISIBLE
|
||||
} else {
|
||||
target.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* 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.util.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.support.design.widget.AppBarLayout
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import kotlinx.android.synthetic.main.fragment_messages_conversation_info.view.*
|
||||
import kotlinx.android.synthetic.main.layout_toolbar_message_conversation_title.view.*
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.extension.getFrame
|
||||
import org.mariotaku.twidere.extension.getFrameRelatedTo
|
||||
import org.mariotaku.twidere.extension.getLocationOnScreen
|
||||
import org.mariotaku.twidere.extension.origin
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/20.
|
||||
*/
|
||||
|
||||
class ConversationInfoAvatarBehavior(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : CoordinatorLayout.Behavior<View>(context, attrs) {
|
||||
private val marginBottom: Float
|
||||
private val marginStart: Float
|
||||
|
||||
private val appBarRect = Rect()
|
||||
|
||||
private val sourceRect = Rect()
|
||||
private val destRect = Rect()
|
||||
private val tempRect = Rect()
|
||||
|
||||
private var toolbarLayoutHeight = 0f
|
||||
|
||||
var dependentPercent: Float = 0f
|
||||
private set
|
||||
|
||||
init {
|
||||
val a = context.obtainStyledAttributes(attrs, R.styleable.ConversationInfoAvatarBehavior)
|
||||
marginBottom = a.getDimension(R.styleable.ConversationInfoAvatarBehavior_behavior_avatarMarginBottom, 0f)
|
||||
marginStart = a.getDimension(R.styleable.ConversationInfoAvatarBehavior_behavior_avatarMarginStart, 0f)
|
||||
a.recycle()
|
||||
}
|
||||
|
||||
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||
return dependency is AppBarLayout
|
||||
}
|
||||
|
||||
override fun onLayoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int): Boolean {
|
||||
val appBar = parent.getDependencies(child).first()
|
||||
// Get AppBar frame (and reset origin to 0, 0)
|
||||
appBar.getFrame(appBarRect)
|
||||
val dependencyOrigin = appBarRect.origin
|
||||
appBarRect.offsetTo(0, 0)
|
||||
|
||||
// Layout avatar
|
||||
val left: Int
|
||||
val right: Int
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
right = appBarRect.right - Math.round(marginStart)
|
||||
left = right - child.measuredWidth
|
||||
} else {
|
||||
left = Math.round(marginStart)
|
||||
right = left + child.measuredWidth
|
||||
}
|
||||
val bottom = Math.round(appBarRect.bottom - marginBottom)
|
||||
val top = bottom - child.measuredHeight
|
||||
child.layout(left, top, right, bottom)
|
||||
|
||||
|
||||
val toolbarLayout = appBar.toolbarLayout
|
||||
toolbarLayoutHeight = toolbarLayout.height.toFloat()
|
||||
|
||||
child.getFrameRelatedTo(sourceRect)
|
||||
appBar.conversationAvatar.getFrameRelatedTo(destRect)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||
val behavior = (dependency.layoutParams as CoordinatorLayout.LayoutParams).behavior as AppBarLayout.Behavior
|
||||
val toolbar = dependency.toolbar
|
||||
toolbar.getLocationOnScreen(tempRect)
|
||||
val percent = behavior.topAndBottomOffset / (tempRect.bottom - toolbarLayoutHeight)
|
||||
child.pivotX = child.width.toFloat()
|
||||
child.pivotY = child.height.toFloat()
|
||||
child.scaleX = 1 - (sourceRect.width() - destRect.width()) * percent / sourceRect.width()
|
||||
child.scaleY = 1 - (sourceRect.height() - destRect.height()) * percent / sourceRect.height()
|
||||
child.translationX = (destRect.right - sourceRect.right) * percent
|
||||
child.translationY = (destRect.bottom - sourceRect.bottom) * percent
|
||||
|
||||
if (percent >= 1) {
|
||||
child.visibility = View.INVISIBLE
|
||||
dependency.conversationAvatar.visibility = View.VISIBLE
|
||||
} else {
|
||||
child.visibility = View.VISIBLE
|
||||
dependency.conversationAvatar.visibility = View.INVISIBLE
|
||||
}
|
||||
dependentPercent = percent
|
||||
return true
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.util.view
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/21.
|
||||
*/
|
||||
|
||||
class ConversationTitlesTransformation : AppBarChildBehavior.TextViewTransformation() {
|
||||
override fun onTargetChanged(child: View, frame: Rect, target: View, targetFrame: Rect, percent: Float, offset: Int) {
|
||||
super.onTargetChanged(child, frame, target, targetFrame, percent, offset)
|
||||
if (percent < 1) {
|
||||
target.visibility = View.INVISIBLE
|
||||
} else {
|
||||
target.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import android.view.View
|
||||
import android.widget.CheckBox
|
||||
import android.widget.TextView
|
||||
import kotlinx.android.synthetic.main.list_item_simple_user.view.*
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.adapter.iface.IContentAdapter
|
||||
import org.mariotaku.twidere.model.ParcelableUser
|
||||
import org.mariotaku.twidere.view.ProfileImageView
|
||||
@ -35,4 +36,8 @@ open class SimpleUserViewHolder(itemView: View, val adapter: IContentAdapter) :
|
||||
profileImageView.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val layoutResource = R.layout.list_item_simple_user
|
||||
}
|
||||
}
|
||||
|
@ -40,20 +40,14 @@
|
||||
</android.support.design.widget.CollapsingToolbarLayout>
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<android.support.v4.widget.NestedScrollView
|
||||
<org.mariotaku.twidere.view.ExtendedRecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/text_margin"
|
||||
android:text="@string/large_text"/>
|
||||
</android.support.v4.widget.NestedScrollView>
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:id="@+id/editButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/element_spacing_large"
|
||||
@ -64,19 +58,25 @@
|
||||
tools:tint="?android:colorForeground"/>
|
||||
|
||||
<org.mariotaku.twidere.view.ProfileImageView
|
||||
android:id="@+id/appBarConversationAvatar"
|
||||
android:id="@+id/appBarIcon"
|
||||
android:layout_width="@dimen/icon_size_conversation_info"
|
||||
android:layout_height="@dimen/icon_size_conversation_info"
|
||||
app:behavior_avatarMarginBottom="@dimen/element_spacing_large"
|
||||
app:behavior_avatarMarginStart="@dimen/element_spacing_large"
|
||||
app:layout_behavior="org.mariotaku.twidere.util.view.ConversationInfoAvatarBehavior"
|
||||
app:behavior_alignmentRule="bottom|start"
|
||||
app:behavior_appBarId="@+id/appBar"
|
||||
app:behavior_childTransformation="org.mariotaku.twidere.util.view.ConversationAvatarTransformation"
|
||||
app:behavior_dependencyViewId="@+id/appBar"
|
||||
app:behavior_marginBottom="@dimen/element_spacing_xlarge"
|
||||
app:behavior_marginStart="@dimen/element_spacing_xlarge"
|
||||
app:behavior_targetViewId="@+id/conversationAvatar"
|
||||
app:behavior_toolbarId="@+id/toolbar"
|
||||
app:layout_behavior="org.mariotaku.twidere.util.view.AppBarChildBehavior"
|
||||
app:sivDrawShadow="false"
|
||||
app:sivElevation="8dp"
|
||||
app:sivShape="circle"
|
||||
tools:src="@drawable/ic_profile_image_default_group"/>
|
||||
|
||||
<org.mariotaku.twidere.view.FixedTextView
|
||||
android:id="@+id/appBarConversationName"
|
||||
android:id="@+id/appBarTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="8dp"
|
||||
@ -85,15 +85,35 @@
|
||||
android:textAppearance="?android:textAppearanceLarge"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textStyle="normal"
|
||||
app:behavior_titleMarginStart="@dimen/element_spacing_normal"
|
||||
app:layout_behavior="org.mariotaku.twidere.util.view.ConversationInfoTitleBehavior"
|
||||
app:behavior_alignmentRule="toEndOf|aboveCenter"
|
||||
app:behavior_appBarId="@+id/appBar"
|
||||
app:behavior_childTransformation="org.mariotaku.twidere.util.view.ConversationTitlesTransformation"
|
||||
app:behavior_dependencyViewId="@+id/appBarIcon"
|
||||
app:behavior_marginStart="@dimen/element_spacing_normal"
|
||||
app:behavior_targetViewId="@+id/conversationName"
|
||||
app:behavior_toolbarId="@+id/toolbar"
|
||||
app:layout_behavior="org.mariotaku.twidere.util.view.AppBarChildBehavior"
|
||||
tools:text="Title"/>
|
||||
|
||||
<org.mariotaku.twidere.view.FixedTextView
|
||||
android:id="@+id/appBarConversationSummary"
|
||||
android:id="@+id/appBarSubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="8dp"
|
||||
android:ellipsize="end"
|
||||
android:minLines="1"
|
||||
android:textAppearance="?android:textAppearanceSmall"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textStyle="normal"
|
||||
app:behavior_alignmentRule="toEndOf|belowCenter"
|
||||
app:behavior_appBarId="@+id/appBar"
|
||||
app:behavior_childTransformation="org.mariotaku.twidere.util.view.ConversationTitlesTransformation"
|
||||
app:behavior_dependencyViewId="@+id/appBarIcon"
|
||||
app:behavior_marginStart="@dimen/element_spacing_normal"
|
||||
app:behavior_marginTop="@dimen/element_spacing_small"
|
||||
app:behavior_targetViewId="@+id/conversationSummary"
|
||||
app:behavior_toolbarId="@+id/toolbar"
|
||||
app:layout_behavior="org.mariotaku.twidere.util.view.AppBarChildBehavior"
|
||||
tools:text="Subtitle"/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="@dimen/element_size_normal"
|
||||
android:padding="@dimen/element_spacing_normal"
|
||||
android:text="@string/action_mute_notifications"/>
|
||||
</LinearLayout>
|
@ -145,14 +145,46 @@
|
||||
<declare-styleable name="ProfileBannerImageView">
|
||||
<attr name="bannerAspectRatio" format="fraction"/>
|
||||
</declare-styleable>
|
||||
<declare-styleable name="ConversationInfoAvatarBehavior">
|
||||
<attr name="behavior_avatarMarginBottom" format="dimension"/>
|
||||
<attr name="behavior_avatarMarginStart" format="dimension"/>
|
||||
</declare-styleable>
|
||||
<declare-styleable name="ConversationInfoTitleBehavior">
|
||||
<attr name="behavior_titleMarginBottom" format="dimension"/>
|
||||
<attr name="behavior_titleMarginStart" format="dimension"/>
|
||||
<declare-styleable name="AppBarChildBehavior">
|
||||
<attr name="behavior_appBarId" format="reference"/>
|
||||
<attr name="behavior_toolbarId" format="reference"/>
|
||||
<attr name="behavior_dependencyViewId" format="reference"/>
|
||||
<attr name="behavior_targetViewId" format="reference"/>
|
||||
|
||||
<attr name="behavior_marginLeft" format="dimension"/>
|
||||
<attr name="behavior_marginRight" format="dimension"/>
|
||||
<attr name="behavior_marginStart" format="dimension"/>
|
||||
<attr name="behavior_marginEnd" format="dimension"/>
|
||||
<attr name="behavior_marginTop" format="dimension"/>
|
||||
<attr name="behavior_marginBottom" format="dimension"/>
|
||||
|
||||
<attr name="behavior_alignmentRule"/>
|
||||
|
||||
<attr name="behavior_childTransformation" format="string"/>
|
||||
</declare-styleable>
|
||||
<attr name="behavior_alignmentRule">
|
||||
<flag name="left" value="0x00000001"/>
|
||||
<flag name="right" value="0x00000002"/>
|
||||
<flag name="toLeftOf" value="0x00000004"/>
|
||||
<flag name="toRightOf" value="0x00000008"/>
|
||||
<flag name="toLeftOfCenter" value="0x00000010"/>
|
||||
<flag name="toRightOfCenter" value="0x00000020"/>
|
||||
<flag name="centerHorizontal" value="0x00000003"/>
|
||||
<flag name="start" value="0x00001001"/>
|
||||
<flag name="end" value="0x00001002"/>
|
||||
<flag name="toStartOf" value="0x00001004"/>
|
||||
<flag name="toEndOf" value="0x00001008"/>
|
||||
<flag name="toStartOfCenter" value="0x00001010"/>
|
||||
<flag name="toEndOfCenter" value="0x00001020"/>
|
||||
<flag name="top" value="0x00010000"/>
|
||||
<flag name="bottom" value="0x00020000"/>
|
||||
<flag name="above" value="0x00040000"/>
|
||||
<flag name="below" value="0x00080000"/>
|
||||
<flag name="aboveCenter" value="0x00100000"/>
|
||||
<flag name="belowCenter" value="0x00200000"/>
|
||||
<flag name="centerVertical" value="0x00030000"/>
|
||||
<flag name="center" value="0x00030003"/>
|
||||
</attr>
|
||||
<attr name="notificationType">
|
||||
<flag name="none" value="0"/>
|
||||
<flag name="ringtone" value="1"/>
|
||||
|
@ -111,5 +111,4 @@
|
||||
<dimen name="toolbar_elevation">8dp</dimen>
|
||||
|
||||
<dimen name="unread_indicator_size">16dp</dimen>
|
||||
<dimen name="text_margin">16dp</dimen>
|
||||
</resources>
|
||||
|
@ -65,6 +65,7 @@
|
||||
<string name="action_liking">liking</string>
|
||||
<string name="action_modifying_lists">modifying lists</string>
|
||||
<string name="action_mute">Mute</string>
|
||||
<string name="action_mute_notifications">Mute notifications</string>
|
||||
<string name="action_muting">muting</string>
|
||||
<string name="action_name_saved_at_time"><xliff:g id="action">%1$s</xliff:g>, saved at <xliff:g id="time">%2$s</xliff:g></string>
|
||||
<string name="action_open_in_browser">Open in browser</string>
|
||||
@ -1220,94 +1221,4 @@
|
||||
<string name="users_statuses">User\'s tweets</string>
|
||||
|
||||
<string name="vibration">Vibration</string>
|
||||
<string name="title_activity_scrolling">ScrollingActivity</string>
|
||||
<string name="large_text">
|
||||
"Material is the metaphor.\n\n"
|
||||
|
||||
"A material metaphor is the unifying theory of a rationalized space and a system of motion."
|
||||
"The material is grounded in tactile reality, inspired by the study of paper and ink, yet "
|
||||
"technologically advanced and open to imagination and magic.\n"
|
||||
"Surfaces and edges of the material provide visual cues that are grounded in reality. The "
|
||||
"use of familiar tactile attributes helps users quickly understand affordances. Yet the "
|
||||
"flexibility of the material creates new affordances that supercede those in the physical "
|
||||
"world, without breaking the rules of physics.\n"
|
||||
"The fundamentals of light, surface, and movement are key to conveying how objects move, "
|
||||
"interact, and exist in space and in relation to each other. Realistic lighting shows "
|
||||
"seams, divides space, and indicates moving parts.\n\n"
|
||||
|
||||
"Bold, graphic, intentional.\n\n"
|
||||
|
||||
"The foundational elements of print based design typography, grids, space, scale, color, "
|
||||
"and use of imagery guide visual treatments. These elements do far more than please the "
|
||||
"eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge "
|
||||
"imagery, large scale typography, and intentional white space create a bold and graphic "
|
||||
"interface that immerse the user in the experience.\n"
|
||||
"An emphasis on user actions makes core functionality immediately apparent and provides "
|
||||
"waypoints for the user.\n\n"
|
||||
|
||||
"Motion provides meaning.\n\n"
|
||||
|
||||
"Motion respects and reinforces the user as the prime mover. Primary user actions are "
|
||||
"inflection points that initiate motion, transforming the whole design.\n"
|
||||
"All action takes place in a single environment. Objects are presented to the user without "
|
||||
"breaking the continuity of experience even as they transform and reorganize.\n"
|
||||
"Motion is meaningful and appropriate, serving to focus attention and maintain continuity. "
|
||||
"Feedback is subtle yet clear. Transitions are efficient yet coherent.\n\n"
|
||||
|
||||
"3D world.\n\n"
|
||||
|
||||
"The material environment is a 3D space, which means all objects have x, y, and z "
|
||||
"dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the "
|
||||
"positive z-axis extending towards the viewer. Every sheet of material occupies a single "
|
||||
"position along the z-axis and has a standard 1dp thickness.\n"
|
||||
"On the web, the z-axis is used for layering and not for perspective. The 3D world is "
|
||||
"emulated by manipulating the y-axis.\n\n"
|
||||
|
||||
"Light and shadow.\n\n"
|
||||
|
||||
"Within the material environment, virtual lights illuminate the scene. Key lights create "
|
||||
"directional shadows, while ambient light creates soft shadows from all angles.\n"
|
||||
"Shadows in the material environment are cast by these two light sources. In Android "
|
||||
"development, shadows occur when light sources are blocked by sheets of material at "
|
||||
"various positions along the z-axis. On the web, shadows are depicted by manipulating the "
|
||||
"y-axis only. The following example shows the card with a height of 6dp.\n\n"
|
||||
|
||||
"Resting elevation.\n\n"
|
||||
|
||||
"All material objects, regardless of size, have a resting elevation, or default elevation "
|
||||
"that does not change. If an object changes elevation, it should return to its resting "
|
||||
"elevation as soon as possible.\n\n"
|
||||
|
||||
"Component elevations.\n\n"
|
||||
|
||||
"The resting elevation for a component type is consistent across apps (e.g., FAB elevation "
|
||||
"does not vary from 6dp in one app to 16dp in another app).\n"
|
||||
"Components may have different resting elevations across platforms, depending on the depth "
|
||||
"of the environment (e.g., TV has a greater depth than mobile or desktop).\n\n"
|
||||
|
||||
"Responsive elevation and dynamic elevation offsets.\n\n"
|
||||
|
||||
"Some component types have responsive elevation, meaning they change elevation in response "
|
||||
"to user input (e.g., normal, focused, and pressed) or system events. These elevation "
|
||||
"changes are consistently implemented using dynamic elevation offsets.\n"
|
||||
"Dynamic elevation offsets are the goal elevation that a component moves towards, relative "
|
||||
"to the component’s resting state. They ensure that elevation changes are consistent "
|
||||
"across actions and component types. For example, all components that lift on press have "
|
||||
"the same elevation change relative to their resting elevation.\n"
|
||||
"Once the input event is completed or cancelled, the component will return to its resting "
|
||||
"elevation.\n\n"
|
||||
|
||||
"Avoiding elevation interference.\n\n"
|
||||
|
||||
"Components with responsive elevations may encounter other components as they move between "
|
||||
"their resting elevations and dynamic elevation offsets. Because material cannot pass "
|
||||
"through other material, components avoid interfering with one another any number of ways, "
|
||||
"whether on a per component basis or using the entire app layout.\n"
|
||||
"On a component level, components can move or be removed before they cause interference. "
|
||||
"For example, a floating action button (FAB) can disappear or move off screen before a "
|
||||
"user picks up a card, or it can move if a snackbar appears.\n"
|
||||
"On the layout level, design your app layout to minimize opportunities for interference. "
|
||||
"For example, position the FAB to one side of stream of a cards so the FAB won’t interfere "
|
||||
"when a user tries to pick up one of cards.\n\n"
|
||||
</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user