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. I want it to have a width large enough hold all the children that goes past the device width.
Here if the Carousel layout 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:
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:
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
<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. Also changed the width of the container and priceTicker to the desired width.
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()
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.