Need a solution to add a Shake View with a progress bar. This view is used as a Toast View in Android, I got a solution such that the View grows from the center and needs help in transforming into the expected result.
The current state is zipped in the drive with the actual video & expected video.
Video and Current state source code
I tried this growFromCenter Extension function, I even tried using Object animator but got blocked while combining it with the progress bar.
private fun View.growFromCenter(
duration: Long,
#FloatRange(from = 0.0, to = 1.0)
startScaleRatio: Float,
endAnimCallback: () -> Unit = {},
) {
val growFromCenter = ScaleAnimation(
startScaleRatio,
1f,
startScaleRatio,
1f,
Animation.RELATIVE_TO_SELF,
0.5f,
Animation.RELATIVE_TO_SELF,
0.5f
)
growFromCenter.duration = duration
growFromCenter.fillAfter = true
growFromCenter.interpolator = FastOutSlowInInterpolator()
growFromCenter.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(p0: Animation?) {
}
override fun onAnimationEnd(p0: Animation?) {
endAnimCallback.invoke()
}
override fun onAnimationRepeat(p0: Animation?) {
}
})
startAnimation(growFromCenter)
}
Use SnackProgressBar instead, I think it is better and easier to manage.
Shake view with a progress bar
Step 1:
In res/anim create file animation.xml
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="350"
android:fromXScale="0.0"
android:fromYScale="0.5"
android:pivotX="100%"
android:pivotY="50%"
android:toXScale="1.0"
android:toYScale="1.0" />
<translate
android:duration="175"
android:fromXDelta="0%"
android:repeatCount="5"
android:repeatMode="reverse"
android:toXDelta="7%" />
</set>
Step 2: In your require Activity
class MainActivity : AppCompatActivity() {
private val viewModel:MainActivityViewModel by viewModels()
private lateinit var mBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding=ActivityMainBinding.inflate(layoutInflater)
setContentView(mBinding.root)
viewModel.currentTime.observe(this) {
val seconds = TimeUnit.MILLISECONDS.toSeconds(it*100)- TimeUnit.MINUTES.toSeconds(
TimeUnit.MILLISECONDS.toMinutes(it*100)
)
val minutes = TimeUnit.MILLISECONDS.toMinutes(it*100)
Log.v("timmer", "$minutes : ${seconds}")
mBinding.progressBar.progress = it.toInt()
}
mBinding.btn.setOnClickListener {
mBinding.mtrlCardChecked.visibility= View.VISIBLE
mBinding.progressBar.visibility=View.VISIBLE
mBinding.mtrlCardChecked.startAnimation(AnimationUtils.loadAnimation(this, R.anim.animation))
viewModel.timer(15000L)
mBinding.progressBar.max=15*10
}
}}
Step 3: In ViewModel of your require activity
package com.example.toasty
import android.os.CountDownTimer
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class MainActivityViewModel : ViewModel() {
private var ONE_SEC = 100L
lateinit var mCountDownTimer: CountDownTimer
private val _currentTime: MutableLiveData<Long> = MutableLiveData<Long>(0)
val currentTime: LiveData<Long> = _currentTime
fun timer(countTimer: Long) {
mCountDownTimer = object : CountDownTimer(countTimer, ONE_SEC) {
override fun onTick(p0: Long) {
_currentTime.value = (p0 / ONE_SEC)
}
override fun onFinish() {
}
}
mCountDownTimer.start()
}
}
Step 4: In your activity_main.xml file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/ctl"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.button.MaterialButton
android:id="#+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello Toasty!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/mtrl_card_checked"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_margin="10dp"
android:visibility="gone"
app:cardCornerRadius="3dp"
app:cardElevation="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<View
android:id="#+id/v_bar"
android:layout_width="5dp"
android:layout_height="90dp"
android:background="#android:color/holo_green_light"
app:layout_constraintBottom_toTopOf="#id/progressBar"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/iv_message_icon"
android:layout_width="56dp"
android:layout_height="56dp"
android:background="#android:color/holo_green_light"
android:src="#drawable/ic_launcher_foreground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#id/v_bar"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/iv_message_icon_type"
android:layout_width="20dp"
android:layout_height="20dp"
android:padding="4dp"
app:layout_constraintBottom_toBottomOf="#id/iv_message_icon"
app:layout_constraintEnd_toEndOf="#id/iv_message_icon" />
<TextView
android:id="#+id/tv_message"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:text="You have too many players in the same position. To pick another, transfer one out. (2/2)"
app:layout_constraintBottom_toBottomOf="#id/iv_message_icon"
app:layout_constraintEnd_toStartOf="#id/iBtn_message_popup_close"
app:layout_constraintStart_toEndOf="#id/iv_message_icon"
app:layout_constraintTop_toTopOf="#id/iv_message_icon" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/iBtn_message_popup_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:background="?actionBarItemBackground"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="#+id/progressBar"
android:layout_width="0dp"
android:layout_height="50dp"
android:progress="10"
android:secondaryProgressTintMode="screen"
app:indicatorColor="#color/teal_200"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/v_bar"
app:trackColor="#color/purple_200"
app:trackThickness="10dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
You can directly download the sample project from Github repo
After making the Custom View, You can Use below code to show that with animation
val snackBarView = Snackbar.make(view, "SnackBar Message" , Snackbar.LENGTH_LONG)
val view = snackBarView.view
val params = view.layoutParams as FrameLayout.LayoutParams
params.gravity = Gravity.TOP
view.layoutParams = params
view.background = ContextCompat.getDrawable(context,R.drawable.custom_drawable) // for custom background
snackBarView.animationMode = BaseTransientBottomBar.ANIMATION_MODE_FADE
snackBarView.show()
below line will resolve the animation issue.
snackBarView.animationMode = BaseTransientBottomBar.ANIMATION_MODE_FADE
Alternate solution-
snackBarView.anchorView = mention viewId above whom you want to show SnackBar
Related
I want to animate TabLayout dot indicator to stretch it on selected state. I used default animation to do that but its bugged and it cant take default shape by animating width only. After animation its stretched to rectangle and I don't understand why.
Code:
val vg = tabLayout.getChildAt(0) as ViewGroup
tabLayout.addOnTabSelectedListener(object: TabLayout.ViewPagerOnTabSelectedListener(viewPager){
override fun onTabReselected(tab: TabLayout.Tab) {}
override fun onTabUnselected(tab: TabLayout.Tab) {
val tabDot = vg.getChildAt(tab.position)
tabDot?.let { v->
animateDotWidthDefault(v)
animateDotColor(v, R.color.selected_blue, R.color.default_grey)
}
}
override fun onTabSelected(tab: TabLayout.Tab) {
val tabDot = vg.getChildAt(tab.position)
tabDot?.let { v->
animateDotWidthStretch(v)
animateDotColor(v, R.color.default_grey, R.color.selected_blue)
}
}
})
private fun animateDotWidthDefault(tabDot: View){
val widthAnimator = ValueAnimator.ofInt(stretchedWidth, defaultWidth)
widthAnimator.duration = 500
widthAnimator.interpolator = DecelerateInterpolator()
widthAnimator.addUpdateListener { animation ->
tabDot.layoutParams.width = animation.animatedValue as Int
tabDot.requestLayout()
}
widthAnimator.start()
}
private fun animateDotWidthStretch(tabDot: View){
val widthAnimator = ValueAnimator.ofInt(defaultWidth, stretchedWidth)
widthAnimator.duration = 500
widthAnimator.interpolator = AccelerateInterpolator()
widthAnimator.apply {
addUpdateListener { animation ->
tabDot.layoutParams.width = animation.animatedValue as Int
tabDot.requestLayout()
}
}
widthAnimator.start()
}
TabIndicator:
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<item android:gravity="center">
<shape
android:shape="oval"
android:useLevel="false">
<solid android:color="#color/selected_blue" />
<size
android:width="8dp"
android:height="8dp"/>
</shape>
</item>
</layer-list>
Goal:
Reality:
The bug could definitely be related to the
android:fillAfter="true"
android:fillEnabled="true"
configurations on the XML. Could you paste the original XML and the container so we can edit our answer and give a more accurate solution to your issue.
Great component by the way, I tested it on my end and it works perfect with this XML config:
<!-- For demonstration / testing purposes -->
<com.google.android.material.tabs.TabLayout
android:id="#+id/indicator3"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:layout_alignParentBottom="true"
android:layout_marginStart="14dp"
android:layout_marginBottom="64dp"
android:clipChildren="false"
android:clipToPadding="false"
app:tabBackground="#drawable/tablayout_unselected"
app:tabGravity="center"
app:tabIndicator="#drawable/tablayout_selected2"
app:tabIndicatorAnimationDuration="250"
app:tabIndicatorAnimationMode="elastic"
app:tabIndicatorColor="#color/xxxRed"
app:tabIndicatorFullWidth="false"
app:tabIndicatorGravity="bottom"
app:tabIndicatorHeight="6dp"
app:tabMode="scrollable"
android:fillAfter="true"
android:fillEnabled="true"
app:tabRippleColor="#color/carbon_red_a700"
app:tabSelectedTextColor="#color/black"
app:tabTextColor="#android:color/black"
app:tabUnboundedRipple="true">
<com.google.android.material.tabs.TabItem
android:id="#+id/firstTab"
android:layout_width="12dp"
android:layout_height="wrap_content" />
<com.google.android.material.tabs.TabItem
android:id="#+id/secondTab"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.google.android.material.tabs.TabItem
android:id="#+id/thirdTab"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.google.android.material.tabs.TabItem
android:id="#+id/fourthTab"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.google.android.material.tabs.TabItem
android:id="#+id/fifthTab"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</com.google.android.material.tabs.TabLayout>
edit1:
I just figured out that even though you didn't provide the animateDotColor() function, ChatGPT took a guess and provided this missing function.
It turns out it is exactly this function that's causing the trouble for you, removing the rounded corners.
private fun animateDotColor(tabDot: View, fromColor: Int, toColor: Int) {
val colorAnimator = ValueAnimator.ofObject(
ArgbEvaluator(),
ContextCompat.getColor(tabDot.context, fromColor),
ContextCompat.getColor(tabDot.context, toColor)
)
colorAnimator.duration = 500
colorAnimator.interpolator = DecelerateInterpolator()
colorAnimator.addUpdateListener { animation ->
tabDot.setBackgroundColor(animation.animatedValue as Int)
}
colorAnimator.start()
}
Because it's replacing the background of the TabItem().
Solution is instead of setting the color, setting the same background with a different color, so you can preserve the edges and corners. I am going through the same work right now and I will edit a second time and provide you this code as well.
edit2:
Here's the working animateDotColor function for you, this one doesn't remove the corners. Enjoy it buddy.
private fun animateDotColor2(tab: TabView, color: Int) {
tab.background = AppCompatResources.getDrawable(tab.context, R.drawable.tablayout_selected_final)
}
I have a problem:
I have a TextView whose content is constantly changing via LiveData. This looks all right when the animation isn't executing, but when the MotionLayout starts executing, my text gets blocked a little bit.
And the constraints of my TextView and Button are packed.
activity:
class ErrorActivity : AppCompatActivity() {
private val mViewModel by viewModels<ErrorViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DataBindingUtil.setContentView<ActivityErrorBinding>(this, R.layout.activity_error).apply {
lifecycleOwner = this#ErrorActivity
viewModel = mViewModel
}
}
fun startStopAnim(v: View) {
mViewModel.anim()
}
}
activity layout:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:binding="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.test.test.ErrorViewModel" />
</data>
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="#xml/scene"
binding:anim="#{viewModel.mAnim}">
<TextView
android:id="#+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{#string/test(viewModel.mCountDown)}"
android:textSize="32sp"
app:layout_constraintEnd_toStartOf="#+id/btn"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="#string/test" />
<Button
android:id="#+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/tv"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/anim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="startStopAnim"
android:text="startOrStop"
android:textAllCaps="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.motion.widget.MotionLayout>
</layout>
viewModel:
class ErrorViewModel : ViewModel() {
private val _countDown: MutableLiveData<String> = MutableLiveData()
val mCountDown: LiveData<String> = _countDown
private val _anim: MutableLiveData<Boolean> = MutableLiveData()
val mAnim: LiveData<Boolean> = _anim
private var count = 0
private var state = false
init {
viewModelScope.launch(Dispatchers.IO) {
while (true) {
delay(1000)
_countDown.postValue(count++.toString())
}
}
}
fun anim() {
state = !state
_anim.value = state
}
}
#BindingAdapter("anim")
fun anim(layout: MotionLayout, play: Boolean?) {
play?.let {
if (it) {
layout.transitionToEnd()
} else {
layout.transitionToStart()
}
}
}
motionScene:
For simplicity, Scene has only one duration
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition
app:constraintSetEnd="#+id/end"
app:constraintSetStart="#+id/start"
app:duration="2000" />
</MotionScene>
gif
As you can see in the gif, when there is no click, everything works fine for the numbers, but when I click the button, a 2 second animation starts, during which time my text doesn't display properly.
Of course, this is just a demo example. In a real scene, not only the text is not displayed completely, but even the TextView And ImageView are misplaced, and once the animation is executed, it cannot be recovered.
Can someone help me...
Actually in a real scene, the parent layout (B) of the problematic TextView (A) would perform a displacement animation. As long as this animation is executed once, the constraint relationship of TextView A will definitely have problems and cannot be restored (onResume can be restored after onPause is currently found)
By design during animation MotionLayout does not honor requestLayout.
Because in the majority of applications it is not needed and would have a significant impact on performance.
To enable it in the transition add
<Transition ... motion:layoutDuringTransition="honorRequest" \>
I found out that sometimes setOnClickListener doesn't work when I click the view. When I run the application and click the view, it has no response, there is no error message it just doesn't work.
In the other part of the project setOnClickListener works well and I can't find any difference. I have no idea why it is not working even though the code is same.
Here is my activity file.
class SplashActivity : AppCompatActivity() {
// 애니메이션을 처리하기 위한 runnable 객체
private val mRunnable : Runnable = Runnable {
if (!isFinishing) { // 끝나지 않았을 때
// 슬라이드업 애니메이션 실행
slideUp(illo_logo, 500)
fadeIn(illo_copy, 500)
slideUp(illo_copy, 500)
fadeIn(btn_login, 500)
slideUp(btn_login, 500)
fadeIn(btn_sign, 500)
slideUp(btn_sign, 500)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
// 핸들러 이용해 0.5초 딜레이 후 mRunnable 실행
Handler().postDelayed(mRunnable, 500)
// 로그인 버튼
btn_login.setOnClickListener {
Toast.makeText(this#SplashActivity, "로그인 버튼 클릭", Toast.LENGTH_SHORT).show()
val intent = Intent(this#SplashActivity, LoginActivity::class.java)
startActivity(intent)
finish()
}
// 가입 버튼
btn_sign.setOnClickListener {
Toast.makeText(this#SplashActivity, "가입 버튼 클릭", Toast.LENGTH_SHORT).show()
val intent = Intent(this#SplashActivity, SignActivity0::class.java)
startActivity(intent)
finish()
}
}
// 위로 올리기
fun slideUp(view : View, time : Int) {
val animation = TranslateAnimation(0f, 0f, 0f, -300f ) // 애니메이션 인스턴스화
animation.duration = time.toLong() // 애니메이션 지속시간 설정
animation.fillAfter = true // 애니메이션 종료 후 상태 유지
view.startAnimation(animation) // 애니메이션 실행
}
// 페이드 인
fun fadeIn(view: View, time: Int) {
val anim = AnimationUtils.loadAnimation(this#SplashActivity, R.anim.fade_in) // 애니메이션 로드
anim.duration = time.toLong() // 애니메이션 지속시간
anim.fillAfter = true // 애니메이션 종료 후 상태 유지
view.startAnimation(anim) // 애니메이션 실행
}
}
Here is my XML code.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/illo_base_color"
tools:context=".SplashActivity">
<ImageView
android:id="#+id/illo_logo"
android:layout_width="140dp"
android:layout_height="140dp"
android:layout_marginBottom="250dp"
android:src="#drawable/app_icon_default"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/illo_copy"
android:layout_width="141dp"
android:layout_height="26dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="206dp"
android:fontFamily="#font/tmoneyroundwind_extrabold"
android:letterSpacing="0"
android:text="우리는 illo 모임"
android:textColor="#color/illo_light_purple"
android:textSize="20sp"
android:textStyle="normal"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/illo_logo"
app:layout_constraintStart_toStartOf="#+id/illo_logo"
app:layout_constraintTop_toBottomOf="#+id/illo_logo"
app:layout_constraintVertical_bias="0.0" />
<RelativeLayout
android:id="#+id/btn_login"
android:layout_width="312dp"
android:layout_height="48dp"
android:layout_marginStart="24dp"
android:layout_marginTop="150dp"
android:layout_marginEnd="24dp"
android:background="#drawable/button_long"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/illo_copy">
<TextView
style="#style/IlloKrTitle1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="로그인하기"
android:textColor="#color/illo_white100"
android:textSize="16dp" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/btn_sign"
android:layout_width="312dp"
android:layout_height="48dp"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:background="#drawable/button_long"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btn_login">
<TextView
style="#style/IlloKrTitle1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="가입하기"
android:textColor="#color/illo_white100"
android:textSize="16dp" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Add android:clickable = "true" and android:focusable = "true" on your widgets like:
<RelativeLayout
android:id="#+id/btn_login"
android:layout_width="312dp"
android:layout_height="48dp"
android:layout_marginStart="24dp"
android:layout_marginTop="150dp"
android:layout_marginEnd="24dp"
android:clickable="true"
android:focusable="true"
android:background="#drawable/button_long"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/illo_copy">
Extra: add this property android:foreground="?attr/selectableItemBackground" That code add selectable animation on RelativeLayout but work better if you added on android:background property. Work with any Widget try and play nice!! ;)
Did you forget to connect those variables? If you have added the whole activity's code than I don't think that doesn't return any error, it must return errors. Did you check logcat?
val btn_login = findViewById<Button>(R.id.btn_login)
If I am missing something than try by making btn_login clickable. Cause your btn_login isn't actually a button. It's a relativeLayout. And for some views default clicking expression is false by default that's why you must enable them.
android:clickable = "true"
I don't remember if it's the accurate one since I am not using Android Studio right now.
I guess you are using Kotlin Synthetic.
If you are using it, try to look at the imported files.
If you copied the same codes that have views in Kotlin Synthetic, it's also copied the same imported files.
Just delete the imported files and change them to the right ones.
Instead of:
import kotlinx.android.synthetic.main."Your layout name with same codes".*
Change it to:
import kotlinx.android.synthetic.main.activity_splash.*
It's better to discard Kotlin Synthetic and move on to ViewBinding
I've created a basic layout in Android in my activity_main.xml for what will eventually be a custom view for a button. It is a relative view with three circles stacked on top of eachother inside it aligned in the centre. The circles change size when the user interacts with them.
I now want to replicate the whole thing purely in Kotlin code, as it will be loaded using data in a database.
I can't figure it out. Heres the manifest...
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<RelativeLayout
android:id="#+id/myContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="#+id/button3"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_centerInParent="true"
app:srcCompat="#drawable/circle"
app:tint="#E60505" />
<ImageView
android:id="#+id/button2"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_centerInParent="true"
app:srcCompat="#drawable/circle"
app:tint="#FFB700" />
<ImageView
android:id="#+id/button1"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentRight="false"
android:layout_centerInParent="true"
app:srcCompat="#drawable/circle" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Then in Kotlin, all I can find about is LayoutParams for width and height, but where do I set all this stuff for the Relative Layout?
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
which I presume I want to do something like:
class MyContainer : RelativeLayout () {
var button1 = MyButtonClass(someArgs)
var button2 = MyButtonClass(someArgs)
var button3 = MyButtonClass(someArgs)
init {
/// can't find out how to do this bit in Kotlin...
///something like....
this.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
this.layoutConstraintBottomToBottomOf = PARENT
this.layoutConstraintHorizontalBias = 0.5
///etc...
//then add the circle buttons
this.addView(button3)
this.addView(button2)
this.addView(button1)
}
}
Am I even vagueley on the right track? Any pointers would be really appreciated.
EDIT: Following first answer, I can set layout attributes, but my app is just blank when i try to reproduce my original manifest tree in code. Have I applied this correctly?
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val appContainer = findViewById<ConstraintLayout>(R.id.appContainer)
var buttonContainer = ButtonContainer(this.applicationContext)
appContainer.addView(buttonContainer)
}
}
class ButtonContainer (context: Context) : RelativeLayout(context) {
var button1 = CustomButton(context)
val buttonLayoutParams by lazy { ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT).apply {
bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID
startToStart = ConstraintLayout.LayoutParams.PARENT_ID
topToTop = ConstraintLayout.LayoutParams.PARENT_ID
endToEnd = ConstraintLayout.LayoutParams.PARENT_ID
horizontalBias = 0.5F
}
}
override fun onFinishInflate() {
super.onFinishInflate()
this.layoutParams = buttonLayoutParams
this.addView(button1)
}
}
class CustomButton(context: Context) : ImageView(context) {
val layoutParams by lazy { RelativeLayout.LayoutParams(60, 60).apply {
addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE)
}
}
override fun onFinishInflate() {
super.onFinishInflate()
setLayoutParams(layoutParams)
setBackgroundResource(squibcircle)
}
}
You need to cast LayoutParams as ConstraintLayout.LayoutParams to set those properties like below
class ButtonContainer(context: Context) : RelativeLayout(context) {
var button1 = CustomButton(context)
val buttonLayoutParams by lazy { ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT).apply {
bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID
startToStart = ConstraintLayout.LayoutParams.PARENT_ID
topToTop = ConstraintLayout.LayoutParams.PARENT_ID
endToEnd = ConstraintLayout.LayoutParams.PARENT_ID
horizontalBias = 0.5F
}
}
override fun onFinishInflate() {
this.layoutParams = buttonLayoutParams
this.addView(button1)
}
}
I have a BottomSheet which houses a product detail card. The problem is, when I click on the + or - button on the product detail while the bottom sheet is in it's Expanded state, it jumps down.
When it is down and I click on the buttons it doesn't jump, it only happens when it is in it's Expanded (completely up) state
I have attached a GIF to show what is exactly happening
Here is the code
scan_sheet.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:animateLayoutChanges="false"
android:background="#drawable/bottom_sheet_dialog_fragment"
android:orientation="vertical"
app:behavior_hideable="true"
app:behavior_peekHeight="100dp"
app:layout_behavior="studio.monotype.storedemo.BottomSheetBehavior">
<include
layout="#layout/hero_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="#+id/divider_view"
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_marginStart="24dp"
android:layout_marginTop="44dp"
android:layout_marginEnd="24dp"
android:background="#color/colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/hero_item" />
<include
layout="#layout/related_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="#+id/divider_view"
tools:layout_editor_absoluteX="0dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
ScanActivity.kt (simplified to show only what is essential)
class ScanActivity : AppCompatActivity() {
private lateinit var bottomSheet: BottomSheetBehavior<*>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scan)
setupBottomSheet()
showSheet()
}
private fun setupBottomSheet() {
bottomSheet = BottomSheetBehavior.from(bottom_sheet)
bottomSheet.isHideable = true
bottomSheet.skipCollapsed= true
bottomSheet.isDraggable = true
bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
bottomSheet.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
#SuppressLint("SwitchIntDef")
override fun onStateChanged(sheet: View, newState: Int) {
when (newState) {
BottomSheetBehavior.STATE_HIDDEN -> {
codeScanner.startPreview()
}
}
}
})
plus_btn.setOnClickListener {
var qty= qty_tv.text.toString().toInt()
qty++
qty_tv.text =qty.toString()
}
minus_btn.setOnClickListener {
var qty= qty_tv.text.toString().toInt()
if(qty!=0)
{
qty--
}
qty_tv.text =qty.toString()
}
}
private fun showSheet() {
bottomSheet.state = BottomSheetBehavior.STATE_EXPANDED
}
}
it seems that google engineer gave correct answer
Seems like something is going on because you are setting
android:layout_gravity="bottom" on the view with the
BottomSheetBehavior. You should remove that line.
It helped on my case
Looks to me like that could be a bug in the BottomSheetBehavior? Seems like the height of the sheet isn't getting saved or restored correctly. After the button is pressed, a layout happens again which changes the height. Could you file a bug at https://issuetracker.google.com/issues/new?component=439535