简体   繁体   English

宽度大于屏幕文本的 RecyclerView 中的 Android 动画轮播 TextView 被剪掉。 即使动画进入视野

[英]Android Animated Carousel TextView within RecyclerView width wider than screen text gets clipped off. Even when animated into view

I am building a stock ticker type carousel that has market prices running along the screen as shown.我正在构建一个股票行情类型轮播,其市场价格沿屏幕运行,如图所示。 The 'CRC' Ticker has only the partial view showing, it clips off at the edge of the parent which is the width of the device even when it is animated into view. 'CRC' Ticker 仅显示部分视图,它在父设备的边缘被剪掉,即使它被动画化到视图中也是设备的宽度。 I want it to have a width large enough hold all the children that goes past the device width.我希望它有一个足够大的宽度来容纳所有超过设备宽度的孩子。 股票代码未显示完整的 CBC 指数 Textview

Here if the Carousel layout xml:这里如果轮播布局 xml:

<?xml version="1.0" encoding="utf-8"?>
<com.android.forexwatch.views.timepanel.CarouselView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/carouselLinear"
    android:layout_width="match_parent"
    android:layout_height="30dp"
    android:orientation="horizontal"
    android:singleLine="true"
    android:animateLayoutChanges="true"
    />

Here is the class:这是 class:

package com.android.forexwatch.views.timepanel

import android.content.Context
import android.util.AttributeSet
import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.widget.RelativeLayout

import androidx.appcompat.widget.LinearLayoutCompat
import com.android.forexwatch.model.Index
import com.android.forexwatch.R
import com.android.forexwatch.model.CityInfoDetailed
import com.github.ybq.android.spinkit.SpinKitView

class CarouselView(context: Context, attrs: AttributeSet): LinearLayoutCompat(context, attrs) {

    companion object {
        private const val IndexWidth = 600F
    }

    private var contextThemeWrapper: ContextThemeWrapper? = null

    init {
        contextThemeWrapper = ContextThemeWrapper(context.applicationContext, R.style.Theme_ForexWatch)
    }

    fun setupView ( cityInfo: CityInfoDetailed?, isMirror: Boolean = false) {
        createPriceTickerViews(cityInfo, isMirror)
    }

    private fun createPriceTickerViews(cityInfo: CityInfoDetailed?, isMirror: Boolean = false) {
        destroy()
        removeAllViews()
        var calculatedWidth = 0
        cityInfo?.indexes?.forEachIndexed {
            index, element ->
            if (isMirror) index.plus(cityInfo?.indexes.count())
            val loader: SpinKitView = LayoutInflater.from(contextThemeWrapper).inflate(R.layout.preloader, null) as SpinKitView
            val priceTicker: PriceTicker = LayoutInflater.from(contextThemeWrapper).inflate(R.layout.price_ticker, null) as PriceTicker
            addView(priceTicker)
            addView(loader)
            // priceTicker.x = IndexWidth * index
            calculatedWidth.plus(IndexWidth)
            priceTicker.initialize(element, cityInfo, loader)
        }

       layoutParams.width = calculatedWidth
    }

    private fun destroy() {
        for (idx in 0 .. this.childCount) {
            val view = getChildAt(idx)
            if (view is PriceTicker) {
                view.destroy()
            }
        }
    }
}

Here is the parent class:这是父 class:

package com.android.forexwatch.views.timepanel

import android.animation.ValueAnimator
import android.graphics.Color
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.android.forexwatch.R
import com.android.forexwatch.adapters.TimePanelRecyclerViewAdapter
import com.android.forexwatch.events.TimeEvent
import com.android.forexwatch.model.CityInfoDetailed
import com.android.forexwatch.model.TimeObject
import com.android.forexwatch.utils.TimeKeeper
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe


class TimePanelView(view: View, container: ViewGroup, timePanelAdapter: TimePanelRecyclerViewAdapter) : RecyclerView.ViewHolder(view) {

    companion object {
        private const val OffsetTime: Long = 15 * 60
        private const val FrameRate: Long = 32
    }

    private var fxTime: TextView? = null
    private var stockTime: TextView? = null
    private var city: TextView? = null
    private var currentTime: TextView? = null
    private var fxTimeLabel: TextView? = null
    private var stockTimeLabel: TextView?  = null
    private var carouselView: CarouselView? = null
    private var carouselViewMirror: CarouselView? = null
    private var parentContainer: ViewGroup? = null

    private var adapter: TimePanelRecyclerViewAdapter? = null
    private var cityInfo: CityInfoDetailed? = null
    private var view: View? = null

    init {
        adapter = timePanelAdapter
        this.view = view
        this.parentContainer = container
        EventBus.getDefault().register(this)
    }

    fun setupView(cityInfo: CityInfoDetailed?) {
        this.cityInfo = cityInfo

        city = view?.findViewById(R.id.city)
        stockTime = view?.findViewById(R.id.stockTime)
        fxTime = view?.findViewById(R.id.fxTime)
        fxTimeLabel = view?.findViewById(R.id.fxTimeLabel)
        stockTimeLabel = view?.findViewById(R.id.stockTimeLabel)
        currentTime = view?.findViewById(R.id.currentTime)
        carouselView = view?.findViewById(R.id.carouselLinear)
        carouselViewMirror = view?.findViewById(R.id.carouselLinearMirror)
        city?.text = cityInfo?.cityName

        createCarousel()
    }

    @Subscribe
    fun update(event: TimeEvent.Interval?) {
        updateCurrentTime()
        updateFxTime()
        updateStockTime()
    }

    private fun createCarousel() {
        carouselView?.setupView(cityInfo)
        carouselViewMirror?.setupView(cityInfo, true)
        carouselView?.bringToFront()
        carouselViewMirror?.bringToFront()
        animateCarousel()
    }

    private fun updateCurrentTime() {
        val timezone: String? = cityInfo?.timeZone
        currentTime?.text =  TimeKeeper.getCurrentTimeWithTimezone(timezone, "EEE' 'HH:mm:ss")
    }

    private fun updateFxTime() {
        updateTime(fxTime, fxTimeLabel, cityInfo?.forexOpenTimes, cityInfo?.forexCloseTimes, cityInfo?.timeZone, "FX", Companion.OffsetTime * 8)
    }

    private fun updateStockTime() {
        updateTime(stockTime, stockTimeLabel, cityInfo?.stockOpenTime, cityInfo?.stockCloseTime, cityInfo?.timeZone, cityInfo?.stockExchangeName, Companion.OffsetTime)
    }

    private fun updateTime(timeView: TextView?, timeLabel: TextView?, open: TimeObject?, close: TimeObject?, timezone: String?, type: String?, timeOffset: Long) {

        if (TimeKeeper.isMarketOpen(open, close, timezone)) {
            timeView?.text = TimeKeeper.getTimeDifference(close, close, timezone, true)

            displayMarketColors(timeView, timeOffset, close, timezone, Color.GREEN, true)

            timeLabel?.text = "To " + type + " Close"
        } else {
            timeView?.text = TimeKeeper.getTimeDifference(open, close, timezone, false)

            displayMarketColors(timeView, timeOffset, open, timezone, Color.RED, false)

            timeLabel?.text = "To " + type + " Open"
        }
    }

    private fun displayMarketColors(timeView: TextView?, timeOffset: Long, time: TimeObject?, timezone: String?, outRangeColor: Int, isMarketOpen: Boolean?) {
        val color = if (TimeKeeper.isTimeWithinRange(timeOffset, time, timezone, isMarketOpen)) Color.parseColor("#FF7F00") else outRangeColor
        timeView?.setTextColor(color)
    }

    private fun animateCarousel() {
        if (cityInfo?.indexes?.count() == 1) {
            return
        }

        // carouselView?.x = carouselView?.x?.minus(3.0F)!!
        /* CoroutineScope(Dispatchers.Main).launch {
            delay(FrameRate)
            animateCarousel()
        }*/
        val animator = ValueAnimator.ofFloat(0.0f, 1.0f)
        animator.repeatCount = ValueAnimator.INFINITE
        animator.interpolator = LinearInterpolator()
        animator.duration = 18000L
        animator.addUpdateListener { animation ->
            val progress = animation.animatedValue as Float
            val width: Int? = carouselView?.width
            val translationX = -width?.times(progress)!!
            carouselView?.translationX = translationX!!
            carouselViewMirror?.translationX = translationX!! + width!!
        }
        animator.start()
    }
}

The PriceTicker layout which extends AppCompatTextView扩展AppCompatTextViewPriceTicker布局

<com.android.forexwatch.views.timepanel.PriceTicker
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    class="com.android.forexwatch.views.timepanel.PriceTicker"
    android:layout_width="240dp"
    android:layout_height="match_parent"
    android:textAlignment="center"
    android:textSize="13sp"
    android:singleLine="true"
    android:background="@drawable/rectangle_shape"
    app:layout_constraintBottom_toBottomOf="parent"
    />

I found the solution by invalidating and requestingLayout on the priceTicker TextView and the container.我通过在 priceTicker TextView 和容器上无效和请求布局找到了解决方案。 Also changed the width of the container and priceTicker to the desired width.还将容器的宽度和 priceTicker 更改为所需的宽度。

    calculatedWidth = calculatedWidth.plus(IndexWidth).toInt()

                priceTicker.initialize(index, cityInfo, loader)
                priceTicker.width = IndexWidth.toInt()
                resetLayout(priceTicker)
            }

            layoutParams.width = (calculatedWidth)
            resetLayout(thisContainer)
        }

    }

    private fun destroy() {
        for (idx in 0 .. this.childCount) {
            val view = getChildAt(idx)
            if (view is PriceTicker) {
                view.destroy()
            }
        }
    }

    private fun resetLayout(view: View?) {
        view?.invalidate()
        view?.requestLayout()
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM