basic full text expansion #843

This commit is contained in:
Mariotaku Lee 2017-05-21 23:12:53 +08:00
parent 21f5c98642
commit b89d3c198e
No known key found for this signature in database
GPG Key ID: 99505AEA531814F1
7 changed files with 112 additions and 17 deletions

View File

@ -4,6 +4,7 @@ import android.content.Context
import android.content.SharedPreferences
import android.support.v4.text.BidiFormatter
import android.support.v7.widget.RecyclerView
import android.util.SparseBooleanArray
import com.bumptech.glide.RequestManager
import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.R
@ -63,6 +64,7 @@ class DummyItemAdapter(
var showCardActions: Boolean = false
private var showingActionCardPosition = RecyclerView.NO_POSITION
private val showingFullTextStates = SparseBooleanArray()
init {
GeneralComponent.get(context).inject(this)
@ -111,6 +113,17 @@ class DummyItemAdapter(
}
}
override fun isFullTextVisible(position: Int): Boolean {
return showingFullTextStates.get(position)
}
override fun setFullTextVisible(position: Int, visible: Boolean) {
showingFullTextStates.put(position, visible)
if (position != RecyclerView.NO_POSITION && adapter != null) {
adapter.notifyItemChanged(position)
}
}
override fun getUser(position: Int): ParcelableUser? {
if (adapter is ParcelableUsersAdapter) {
return adapter.getUser(position)

View File

@ -23,9 +23,11 @@ import android.content.Context
import android.database.CursorIndexOutOfBoundsException
import android.support.v4.widget.Space
import android.support.v7.widget.RecyclerView
import android.util.SparseBooleanArray
import android.view.LayoutInflater
import android.view.ViewGroup
import com.bumptech.glide.RequestManager
import org.apache.commons.collections.primitives.ArrayLongList
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.*
import org.mariotaku.library.objectcursor.ObjectCursor
@ -118,6 +120,7 @@ abstract class ParcelableStatusesAdapter(
private var displayPositions: IntArray? = null
private var displayDataCount: Int = 0
private var showingActionCardId = RecyclerView.NO_ID
private val showingFullTextStates = SparseBooleanArray()
private val reuseStatus = ParcelableStatus()
private var infoCache: Array<StatusInfo?>? = null
@ -284,6 +287,17 @@ abstract class ParcelableStatusesAdapter(
}
}
override fun isFullTextVisible(position: Int): Boolean {
return showingFullTextStates.get(position)
}
override fun setFullTextVisible(position: Int, visible: Boolean) {
showingFullTextStates.put(position, visible)
if (position != RecyclerView.NO_POSITION) {
notifyItemChanged(position)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) {
ITEM_VIEW_TYPE_GAP -> {

View File

@ -37,6 +37,10 @@ interface IStatusesAdapter<in Data> : IContentAdapter, IGapSupportedAdapter {
fun showCardActions(position: Int)
fun isFullTextVisible(position: Int): Boolean
fun setFullTextVisible(position: Int, visible: Boolean)
fun setData(data: Data?): Boolean
/**

View File

@ -49,6 +49,7 @@ import android.text.Spanned
import android.text.TextUtils
import android.text.method.LinkMovementMethod
import android.text.style.ForegroundColorSpan
import android.util.SparseBooleanArray
import android.view.*
import android.view.View.OnClickListener
import android.widget.Space
@ -1447,7 +1448,8 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
private var replyError: CharSequence? = null
private var conversationError: CharSequence? = null
private var replyStart: Int = 0
private var showingActionCardPosition: Int = 0
private var showingActionCardPosition = RecyclerView.NO_POSITION
private val showingFullTextStates = SparseBooleanArray()
init {
setHasStableIds(true)
@ -1532,6 +1534,16 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
}
}
override fun isFullTextVisible(position: Int): Boolean {
return showingFullTextStates.get(position)
}
override fun setFullTextVisible(position: Int, visible: Boolean) {
showingFullTextStates.put(position, visible)
if (position != RecyclerView.NO_POSITION) {
notifyItemChanged(position)
}
}
override fun setData(data: List<ParcelableStatus>?): Boolean {
val status = this.status ?: return false
val changed = this.data != data

View File

@ -0,0 +1,22 @@
package org.mariotaku.twidere.text
import android.graphics.Canvas
import android.graphics.Paint
import android.text.TextPaint
import android.text.style.ReplacementSpan
/**
* Created by Mariotaku on 2017/5/21.
*/
class ShowMoreToggleSpan: ReplacementSpan() {
override fun getSize(paint: Paint?, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun draw(canvas: Canvas?, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}

View File

@ -0,0 +1,22 @@
package org.mariotaku.twidere.text
import android.text.TextPaint
import android.text.style.ClickableSpan
import org.mariotaku.ktextension.contains
import org.mariotaku.twidere.constant.SharedPreferenceConstants
/**
* Created by Mariotaku on 2017/5/21.
*/
abstract class TwidereClickableSpan(val highlightStyle: Int): ClickableSpan() {
override fun updateDrawState(ds: TextPaint) {
if (SharedPreferenceConstants.VALUE_LINK_HIGHLIGHT_OPTION_CODE_UNDERLINE in highlightStyle) {
ds.isUnderlineText = true
}
if (SharedPreferenceConstants.VALUE_LINK_HIGHLIGHT_OPTION_CODE_HIGHLIGHT in highlightStyle) {
ds.color = ds.linkColor
}
}
}

View File

@ -8,6 +8,7 @@ import android.support.v7.widget.RecyclerView.ViewHolder
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.ClickableSpan
import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan
import android.view.View
@ -37,6 +38,7 @@ import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.task.CreateFavoriteTask
import org.mariotaku.twidere.task.DestroyFavoriteTask
import org.mariotaku.twidere.task.RetweetStatusTask
import org.mariotaku.twidere.text.TwidereClickableSpan
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText
import org.mariotaku.twidere.util.Utils.getUserTypeIconRes
@ -109,7 +111,7 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
adapter.profileImageStyle, profileImageView.cornerRadius,
profileImageView.cornerRadiusRatio).into(profileImageView)
nameView.name = TWIDERE_PREVIEW_NAME
nameView.screenName = "@" + TWIDERE_PREVIEW_SCREEN_NAME
nameView.screenName = "@$TWIDERE_PREVIEW_SCREEN_NAME"
nameView.updateText(adapter.bidiFormatter)
summaryView.hideIfEmpty()
if (adapter.linkHighlightingStyle == VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) {
@ -154,8 +156,8 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
statusContentLowerSpace.visibility = if (showCardActions) View.GONE else View.VISIBLE
val replyCount = status.reply_count
val retweetCount: Long
val favoriteCount: Long
val retweetCount = status.retweet_count
val favoriteCount = status.favorite_count
if (displayPinned && status.is_pinned_status) {
statusInfoLabel.setText(R.string.pinned_status)
@ -232,13 +234,7 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
} else {
quotedTextView.spannable = quotedText
}
if (quotedTextView.length() == 0) {
// No text
quotedTextView.visibility = View.GONE
} else {
quotedTextView.visibility = View.VISIBLE
}
quotedTextView.hideIfEmpty()
val quoted_user_color = colorNameManager.getUserColor(quoted_user_key)
if (quoted_user_color != 0) {
@ -361,11 +357,13 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
val text: CharSequence
val displayEnd: Int
if (!summaryView.empty) {
if (!summaryView.empty && !isFullTextVisible) {
text = SpannableStringBuilder.valueOf(context.getString(R.string.label_status_show_more)).apply {
val colorSecondary = ThemeUtils.getTextColorSecondary(context)
setSpan(ForegroundColorSpan(colorSecondary), 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
setSpan(StyleSpan(Typeface.ITALIC), 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
setSpan(object : TwidereClickableSpan(adapter.linkHighlightingStyle) {
override fun onClick(widget: View?) {
showFullText()
}
}, 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
displayEnd = -1
} else if (adapter.linkHighlightingStyle != VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) {
@ -415,7 +413,6 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
Utils.isMyRetweet(status.account_key, status.retweeted_by_user_key,
status.my_retweet_id)
}
retweetCount = status.retweet_count
if (retweetCount > 0) {
retweetCountView.spannable = UnitConvertUtils.calculateProperCount(retweetCount)
@ -430,7 +427,6 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
val creatingFavorite = CreateFavoriteTask.isCreatingFavorite(status.account_key, status.id)
favoriteIcon.isActivated = creatingFavorite || status.is_favorite
}
favoriteCount = status.favorite_count
if (favoriteCount > 0) {
favoriteCountView.spannable = UnitConvertUtils.calculateProperCount(favoriteCount)
@ -590,6 +586,9 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
private val isCardActionsShown: Boolean
get() = adapter.isCardActionsShown(layoutPosition)
private val isFullTextVisible: Boolean
get() = adapter.isFullTextVisible(layoutPosition)
private fun showCardActions() {
adapter.showCardActions(layoutPosition)
}
@ -599,6 +598,15 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
return !adapter.isCardActionsShown(RecyclerView.NO_POSITION)
}
private fun showFullText() {
adapter.setFullTextVisible(layoutPosition, true)
}
private fun hideFullText(): Boolean {
adapter.setFullTextVisible(layoutPosition, false)
return !adapter.isFullTextVisible(RecyclerView.NO_POSITION)
}
private fun TextView.displayMediaLabel(cardName: String?, media: Array<ParcelableMedia?>?,
location: ParcelableLocation?, placeFullName: String?,
sensitive: Boolean) {