current position:Home>Android technology sharing | customize ViewGroup to realize seamless switching between large and small screens in live broadcasting room
Android technology sharing | customize ViewGroup to realize seamless switching between large and small screens in live broadcasting room
2022-01-26 23:50:20 【anyRTC】
Source code : please Click here
demand
Two display modes :
- Anchor full screen , Other tourists hover on the right . Hereinafter referred to as large and small screen mode .
- Everyone split the screen . Hereinafter referred to as bisection mode .
analysis
- most 4 Man and wheat , Making this clear is convenient to customize the coordinate algorithm .
- Self defined ViewGroup It is better to provide margin setting interfaces for equal division mode and large and small screen mode respectively , Easy to modify .
- SDK Manage yourself TextureView Drawing and measurement of , therefore ViewGroup Need to copy onMeasure Method to notify TextureView Measure and draw .
- A calculation 0.0f ~ 1.0f A function of gradual deceleration , Support the animation process .
- A data model for recording coordinates . And one based on existing Child View The quantity is calculated in two layout modes , Every View Function of placement .
Realization
1. Define the coordinate data model
private data class ViewLayoutInfo( var originalLeft: Int = 0,// original The beginning is the starting value before the animation starts var originalTop: Int = 0, var originalRight: Int = 0, var originalBottom: Int = 0, var left: Float = 0.0f,// Prefixed are temporary values during animation var top: Float = 0.0f, var right: Float = 0.0f, var bottom: Float = 0.0f, var toLeft: Int = 0,// to The beginning is the animation target value var toTop: Int = 0, var toRight: Int = 0, var toBottom: Int = 0, var progress: Float = 0.0f,// speed of progress 0.0f ~ 1.0f, Used to control the Alpha Animation var isAlpha: Boolean = false,// Transparent animation , The newly added performs this animation var isConverted: Boolean = false,// control progress Inverted markers var waitingDestroy: Boolean = false,// Destroy when finished View The tag var pos: Int = 0// Record your index , In order to destroy ) {
init {
left = originalLeft.toFloat()
top = originalTop.toFloat()
right = originalRight.toFloat()
bottom = originalBottom.toFloat()
}
}
Copy code
above , The execution animation and destruction are recorded View Data required .( In the source code 352 That's ok )
2. Calculate... Under different display modes View A function of coordinates
if (layoutTopicMode) {
var index = 0
for (i in 1 until childCount) if (i != position) (getChildAt(i).tag as ViewLayoutInfo).run {
toLeft = measuredWidth - maxWidgetPadding - smallViewWidth
toTop = defMultipleVideosTopPadding + index * smallViewHeight + index * maxWidgetPadding
toRight = measuredWidth - maxWidgetPadding
toBottom = toTop + smallViewHeight
index++
}
} else {
var posOffset = 0
var pos = 0
if (childCount == 4) {
posOffset = 2
pos++
(getChildAt(0).tag as ViewLayoutInfo).run {
toLeft = measuredWidth.shr(1) - multiViewWidth.shr(1)
toTop = defMultipleVideosTopPadding
toRight = measuredWidth.shr(1) + multiViewWidth.shr(1)
toBottom = defMultipleVideosTopPadding + multiViewHeight
}
}
for (i in pos until childCount) if (i != position) {
val topFloor = posOffset / 2
val leftFloor = posOffset % 2
(getChildAt(i).tag as ViewLayoutInfo).run {
toLeft = leftFloor * measuredWidth.shr(1) + leftFloor * multipleWidgetPadding
toTop = topFloor * multiViewHeight + topFloor * multipleWidgetPadding + defMultipleVideosTopPadding
toRight = toLeft + multiViewWidth
toBottom = toTop + multiViewHeight
}
posOffset++
}
}
post(AnimThread(
(0 until childCount).map { getChildAt(it).tag as ViewLayoutInfo }.toTypedArray()
))
Copy code
Demo In the source add、remove、toggle Method duplicate code too much , The future needs to be optimized . Only... Is attached here addVideoView The calculation part of ( In the source code 141 That's ok ), It only needs a little modification to apply add、remove and toggle.( Also can reference CDNLiveVM Medium calcPosition Method , For the optimized version )layoutTopicMode = true when , For large and small screen mode .
Because it's a custom algorithm , Only one layout can be applied , So don't write notes . Just be clear , The ultimate goal of this method is to calculate each View Where it should appear at present , Save to the data model defined above and start the animation ( The last line post AnimThread Code for opening animation , I'm here through post A thread updates each frame ).
Different implementations can be written according to different requirements , Finally, it can meet the defined data model .
3. Gradual deceleration algorithm , Make the animation look more natural .
private inner class AnimThread(
private val viewInfoList: Array<ViewLayoutInfo>,
private var duration: Float = 180.0f,
private var processing: Float = 0.0f
) : Runnable {
private val waitingTime = 9L
override fun run() {
var progress = processing / duration
if (progress > 1.0f) {
progress = 1.0f
}
for (viewInfo in viewInfoList) {
if (viewInfo.isAlpha) {
viewInfo.progress = progress
} else viewInfo.run {
val diffLeft = (toLeft - originalLeft) * progress
val diffTop = (toTop - originalTop) * progress
val diffRight = (toRight - originalRight) * progress
val diffBottom = (toBottom - originalBottom) * progress
left = originalLeft + diffLeft
top = originalTop + diffTop
right = originalRight + diffRight
bottom = originalBottom + diffBottom
}
}
requestLayout()
if (progress < 1.0f) {
if (progress > 0.8f) {
var offset = ((progress - 0.7f) / 0.25f)
if (offset > 1.0f)
offset = 1.0f
processing += waitingTime - waitingTime * progress * 0.95f * offset
} else {
processing += waitingTime
}
postDelayed([email protected], waitingTime)
} else {
for (viewInfo in viewInfoList) {
if (viewInfo.waitingDestroy) {
removeViewAt(viewInfo.pos)
} else viewInfo.run {
processing = 0.0f
duration = 0.0f
originalLeft = left.toInt()
originalTop = top.toInt()
originalRight = right.toInt()
originalBottom = bottom.toInt()
isAlpha = false
isConverted = false
}
}
animRunning = false
processing = duration
if (!taskLink.isEmpty()) {
invokeLinkedTask()// This method performs the task that is waiting , You can see from the source code ,remove、add And other functions need to be executed in turn , Performing the next animation before the previous animation is completed may lead to unpredictable errors .
}
}
}
}
Copy code
In addition to providing deceleration algorithm , Also updated the corresponding View The intermediate value of the data model , That is, the definition of the model left, top, right, bottom .
The progress value provided by the deceleration algorithm , Multiply by the distance between the target coordinate and the starting coordinate , Get the middle value .
The key code of the gradual deceleration algorithm is :
if (progress > 0.8f) {
var offset = ((progress - 0.7f) / 0.25f)
if (offset > 1.0f)
offset = 1.0f
processing += waitingTime - waitingTime * progress * 0.95f * offset
} else {
processing += waitingTime
}
Copy code
The implementation of this algorithm is flawed , Because it directly modifies the progress time , The probability will lead to the completion time and the set expected time ( Such as setting 200ms completion of enforcement , May actually exceed 200ms) Not in conformity with . At the end of the paper, I will provide an optimized deceleration algorithm .
Variable waitingTime Indicates how long to wait to execute the next animation . In seconds 1000ms Calculation is enough , If the goal is 60 Refresh rate animation , Set to 1000 / 60 = 16.66667 that will do ( Approximate value ).
Calculate and store each View After the middle value of , call requestLayout() Of the notification system onMeasure and onLayout Method , Rearrange View .
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
if (childCount == 0)
return
for (i in 0 until childCount) {
val child = getChildAt(i)
val layoutInfo = child.tag as ViewLayoutInfo
child.layout(
layoutInfo.left.toInt(),
layoutInfo.top.toInt(),
layoutInfo.right.toInt(),
layoutInfo.bottom.toInt()
)
if (layoutInfo.isAlpha) {
val progress = if (layoutInfo.isConverted)
1.0f - layoutInfo.progress
else
layoutInfo.progress
child.alpha = progress
}
}
}
Copy code
4. Define variables related to margins , For simple customization and modification
/** * @param multipleWidgetPadding : Bisection mode read * @param maxWidgetPadding : Large and small screen layout reading * @param defMultipleVideosTopPadding : Variable distance from the top */
private var multipleWidgetPadding = 0
private var maxWidgetPadding = 0
private var defMultipleVideosTopPadding = 0
init {
viewTreeObserver.addOnGlobalLayoutListener(this)
attrs?.let {
val typedArray = resources.obtainAttributes(it, R.styleable.AnyVideoGroup)
multipleWidgetPadding = typedArray.getDimensionPixelOffset(
R.styleable.AnyVideoGroup_between23viewsPadding, 0
)
maxWidgetPadding = typedArray.getDimensionPixelOffset(
R.styleable.AnyVideoGroup_at4smallViewsPadding, 0
)
defMultipleVideosTopPadding = typedArray.getDimensionPixelOffset(
R.styleable.AnyVideoGroup_defMultipleVideosTopPadding, 0
)
layoutTopicMode = typedArray.getBoolean(
R.styleable.AnyVideoGroup_initTopicMode, layoutTopicMode
)
typedArray.recycle()
}
}
Copy code
The responsibilities for these three variables are defined when naming , It is different from the definition when writing logic , So it's a little vague , Refer to note .
Because this is only a customized variable , Not important , It can be modified according to the business logic .
5. make carbon copies onMeasure Method , This is mainly to inform TextureView Update size .
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
multiViewWidth = widthSize.shr(1)
multiViewHeight = (multiViewWidth.toFloat() * 1.33334f).toInt()
smallViewWidth = (widthSize * 0.3125f).toInt()
smallViewHeight = (smallViewWidth.toFloat() * 1.33334f).toInt()
for (i in 0 until childCount) {
val child = getChildAt(i)
val info = child.tag as ViewLayoutInfo
child.measure(
MeasureSpec.makeMeasureSpec((info.right - info.left).toInt(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec((info.bottom - info.top).toInt(), MeasureSpec.EXACTLY)
)
}
setMeasuredDimension(
MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY)
)
}
Copy code
summary
- Define the data model , In general, record the starting upper, lower, left and right coordinates 、 Up, down, left and right coordinates of the target 、 And progress percentage is enough .
- Define the animation algorithm according to the needs , Here we add the optimized deceleration algorithm :
factor = 1.0
if (factor == 1.0)
(1.0 - (1.0 - x) * (1.0 - x))
else
(1.0 - pow((1.0 - x), 2 * factor))
// x = time.
Copy code
- Update according to the value calculated by the algorithm layout Just layout .
Such kind ViewGroup The implementation is simple and convenient , Only a few basic systems are involved API. If you don't want to write onMeasure Methods are inheritable FrameLayout When it has been written onMeasure Realized ViewGroup .
copyright notice
author[anyRTC],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201262350186819.html
The sidebar is recommended
- Spring IOC container loading process
- [thinking] the difference between singleton mode and static method - object-oriented programming
- Hadoop environment setup (MySQL environment configuration)
- 10 minutes, using node JS creates a real-time early warning system for bad weather!
- Git tool
- Force deduction algorithm - 92 Reverse linked list II
- What is the sub problem of dynamic programming?
- C / C + +: static keyword summary
- Idea does not have the artifacts option when configuring Tomcat
- Anaconda can't open it
guess what you like
-
I don't know how to start this
-
Matlab simulation of transportation optimization algorithm based on PSO
-
MySQL slow log optimization
-
[Vue] as the window is stretched (larger, smaller, wider and higher), the text will not be displayed
-
Popular Linux distributions for embedded computing
-
Suzhou computer research
-
After installing SSL Certificate in Windows + tomcat, the domain name request is not successful. Please answer!!
-
Implementation time output and greetings of jQuery instance
-
The 72 year old uncle became popular. Wu Jing and Guo fan made his story into a film, which made countless dreamers blush
-
How to save computer research
Random recommended
- Springboot implements excel import and export, which is easy to use, and poi can be thrown away
- The final examination subjects of a class are mathematical programming, and the scores are sorted and output from high to low
- Two pronged approach, Tsinghua Professor Pro code JDK and hotspot source code notes, one-time learning to understand
- C + + recursive knapsack problem
- The use of GIT and GitHub and the latest git tutorial are easy to understand -- Video notes of crazy God speaking
- PostgreSQL statement query
- Ignition database test
- Context didn't understand why he got a high salary?, Nginxfair principle
- Bootstrap switch switch control user's guide, springcloud actual combat video
- A list that contains only strings. What other search methods can be used except sequential search
- [matlab path planning] multi ant colony algorithm grid map path planning [including GUI source code 650]
- [matlab path planning] improved genetic algorithm grid map path planning [including source code phase 525]
- Iinternet network path management system
- Appium settings app is not running after 5000ms
- Reactnative foundation - 07 (background image, status bar, statusbar)
- Reactnative foundation - 04 (custom rpx)
- If you want an embedded database (H2, hsql or Derby), please put it on the classpath
- When using stm32g070 Hal library, if you want to write to flash, you must perform an erase. If you don't let it, you can't write continuously.
- Linux checks where the software is installed and what files are installed
- SQL statement fuzzy query and time interval filtering
- 69. Sqrt (x) (c + + problem solving version with vs runnable source program)
- Fresh students are about to graduate. Do you choose Java development or big data?
- Java project: OA management system (java + SSM + bootstrap + MySQL + JSP)
- Titanic passenger survival prediction
- Vectorization of deep learning formula
- Configuration and use of private image warehouse of microservice architect docker
- Relearn JavaScript events
- For someone, delete return 1 and return 0
- How does Java dynamically obtain what type of data is passed? It is used to judge whether the data is the same, dynamic data type
- How does the database cow optimize SQL?
- [data structure] chain structure of binary tree (pre order traversal) (middle order traversal) (post order traversal) (sequence traversal)
- Webpack packaging optimization solution
- 5. Operation element
- Detailed explanation of red and black trees
- redhat7. 9 install database 19C
- Blue Bridge Cup notes: (the given elements are not repeated) complete arrangement (arrangement cannot be repeated, arrangement can be repeated)
- Detailed explanation of springboot default package scanning mechanism and @ componentscan specified scanning path
- How to solve the run-time exception of test times
- Detailed explanation of k8s management tool kubectl
- Android system view memory command