We are having hard times to smoothly resize a here SDK map on Android.
We want to smoothly resize the map to the bottom sheet collapse and hidden state as shown in
But as you can see it does not really resize instead its jumps to the new position while the map keeps its dimensions and does not scale.
And this is what we did:
...
<com.here.sdk.mapview.MapView
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="#dimen/nine_grid_unit" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/menuBottomSheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:clickable="true"
android:elevation="#dimen/four_grid_unit"
android:focusable="true"
app:behavior_hideable="true"
app:behavior_peekHeight="#dimen/thirtytwo_grid_unit"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<View
android:id="#+id/tap_stop"
android:layout_width="#dimen/nine_grid_unit"
android:layout_height="#dimen/one_grid_unit"
android:layout_marginTop="#dimen/one_grid_unit"
android:background="#color/grey_light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<edeka.digital.app.widget.SegmentedControlView
android:id="#+id/tabSwitchSegmentedControl"
android:layout_width="#dimen/thirtyfive_grid_unit"
android:layout_height="wrap_content"
android:paddingStart="#dimen/three_grid_unit"
android:paddingEnd="#dimen/three_grid_unit"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/tap_stop"
app:segmentCount="2"
app:segmentTitles="#array/segment_titles_shop_search" />
</androidx.constraintlayout.widget.ConstraintLayout>
...
And code:
val bottomBehavior = BottomSheetBehavior.from(binding.menuBottomSheet)
bottomBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
val mapView = binding.map
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
bottomSheetBehaviorObservable.onNext(newState)
when (newState) {
BottomSheetBehavior.STATE_COLLAPSED -> {
mapView.bottom = binding.menuBottomSheet.top
mapView.invalidate()
}
BottomSheetBehavior.STATE_HIDDEN -> {
mapView.bottom = binding.menuBottomSheet.top
mapView.invalidate()
}
else -> { /* void */
}
}
}
})
I would have expected some kind of resize() function or that it layouts itself if layout dimensions change.
What we really want is already implemented in HERE WeGo App. The whole maps scales (inc. here logo) if user swipes the bottom sheet:
Can anyone help us out?
The demo shown in 1 can be found here:
https://github.com/edekadigital/heremaps-demo
The best solution that I've found to achieve it is to add a new method:
private fun updateMapView(bottomSheetTop: Int) {
val mapView = binding.map
val principalY = Math.min(bottomSheetTop / 2.0, mapView.height / 2.0)
mapView.camera.principalPoint = Point2D(mapView.width / 2.0, principalY)
val logoMargin = Math.max(0, mapView.bottom - bottomSheetTop)
mapView.setWatermarkPosition(WatermarkPlacement.BOTTOM_CENTER, logoMargin.toLong())
}
and call it in onSlide and onStateChanged like this:
updateMapView(bottomSheet.top)
Note that you need to have the HERE logo at the bottom center position, otherwise it can't use an adjustable margin.
I was also trying to resize the map view, but the results were unsatisfying. Here is the code if you want to give a try:
private fun updateMapView(bottomSheetTop: Int) {
val mapView = binding.map
mapView.layoutParams.height = bottomSheetTop
mapView.requestLayout()
}
It looks like that your map view is covered by the sliding panel and is not redrawn during slide animation. It renders only when the state changes. You can try to add mapView.invalidate() in onSlide method, like this:
override fun onSlide(bottomSheet: View, slideOffset: Float) {
mapView.invalidate()
}
However, to be sure if that's the actual reason, I would need to get and build your code.
I was able to get your code, compile and reproduce the bug. I've found two options to fix that, both tested on an emulator and a real device.
Copy the code from state change handling code into onSlide method:
override fun onSlide(bottomSheet: View, slideOffset: Float) {
mapView.bottom = binding.menuBottomSheet.top
mapView.invalidate()
}
Remove map view resizing and invalidating code at all. It basically makes the whole setupBottomSheet method redundant. Map view works correctly without resizing and it's a preferable way to fix it, as it involves less code and operations.
Related
It's my first question in this incredible community.
I'm developing an android app in kotlin. I need a permanent bottomsheet (not modal). I have developed all the behavior that I needed, but for one detail.
I need to set de STATE_HALF_EXPANDED, by default is 50% of the screen, but I need 70%. I have visited this question:
How i can set Half Expanded state for my BottomSheet
In that question, the user Adauton Heringer explain how to do it in one of the answers. He said:
behavior = BottomSheetBehavior.from(your_bottom_sheet_xml)
behavior.isFitToContents = false
behavior.halfExpandedRatio = 0.6f
I tried the same, because it looks like very easy to do. I did the two first lines, but when I try to used setHalfExpandedRatio() is like it wasn't exist. I have checked the official documentation and it is a public method.
https://developer.android.com/reference/com/google/android/material/bottomsheet/BottomSheetBehavior#sethalfexpandedratio
Am I doing something wrong?
My code is this:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
var bottomSheet: View = view.findViewById(R.id.departures_bottomsheet)
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
bottomSheetBehavior.isFitToContents = false
// this doesn't work for me
// bottomSheetBehavior.halfExpandedRatio = 0.7
bottomSheetBehavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(p0: View, dragPoint: Float) {
val upper = 0.66
val lower = 0.33
if (dragPoint >= upper) {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
}
if (dragPoint < upper && dragPoint >= lower) {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
}
if (dragPoint < lower) {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
}
}
override fun onStateChanged(p0: View, p1: Int) {
}
} )
}
I have this import:
import com.google.android.material.bottomsheet.BottomSheetBehavior
And this implementation in build.gradle of the app:
implementation 'com.google.android.material:material:1.0.0'
In the layout file, the View is a child of a CoordinatorLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/globalMap"
class="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.circularreveal.CircularRevealLinearLayout
android:id="#+id/departures_bottomsheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
app:behavior_peekHeight="80dp"
app:layout_behavior="#string/bottom_sheet_behavior">
I have navigated to BottomSheetBehavior.class in Android Studio and this method doesn't exist.
Any help is welcome and I will be grateful.
If I don't find any other way, I will create a SubClass with this method.
From BottomSheetBehavior.java commit history, the method setHalfExpandedRatio(float ratio) is added from version 1.1.0-alpha05.
You are using vevrsion 1.0.0, that why you cannot see this method.
Solution: Change version code from 1.0.0 to 1.1.0-alpha05 in your gradle file.
// implementation 'com.google.android.material:material:1.0.0'
implementation 'com.google.android.material:material:1.1.0-alpha05'
or using the latest version
// implementation 'com.google.android.material:material:1.0.0'
implementation 'com.google.android.material:material:1.2.0-alpha03'
You can find all available versions here.
I have a ConstraintLayout. For the purposes of this example, we can have three views inside.
<android.support.constraint.ConstraintLayout ...>
<TextView
android:id="#+id/text"
.../>
<TextView
android:id="#+id/value"
.../>
<View
android:id="#+id/bar"
android:layout_width="0dp"
android:layout_height="8dp"
android:background="#color/bar_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="#+id/text"
app:layout_constraintEnd_toStartOf="#id/value"
/>
</android.support.constraint.ConstraintLayout>
At runtime, I want to set the width of bar to some value between the distance between text and value.
I already have a value between 0 and 100 which is a percentage of the distance between the two text views.
I've tried two methods so far but have gotten stuck.
Attempt 1
In order to do this, I feel I need the actual distance between text and value and a method to set the bar's width correctly.
I'm able to set the width of bar by doing the following:
val params: ConstraintLayout.LayoutParams = percentageBar.layoutParams as LayoutParams
params.matchConstraintMaxWidth = 300 // should be actual value rather than 300
percentageBar.layoutParams = params
This sets the width of the bar fine. But I need some way of figuring out what the actual number should be rather than 300. Something like (percent value * text-value-distance / 100).
Attempt 2
From looking at other answers on SO, I found out about the percent constraint.
...
<View
android:id="#+id/bar"
android:layout_width="0dp"
android:layout_height="8dp"
android:background="#color/bar_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#id/value"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="#id/text"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="1.0"/>
...
Then, in the code, I can just do params.matchConstraintPercentWidth = item.percentageOfMaxValue.toFloat()
The problem with this is that when the percent value is set high, it's taking the percentage of the parent and goes past the start of value.
Am I heading in the right direction?
I hope I've described the problem clearly enough.
Thanks in advance.
// Update
I've pushed a simplified version with what resembles the problem I'm having.
BarProblem on GitHub
You can actually see the problem from the design tab of the layout editor.
// Update 2
Problem solved. GitHub repo now contains the code for the solution.
I good solution for your case is guidelines. Those helpers are anchors that won’t be displayed in your app, they are like one line of a grid above your layout and can be used to attach or constraint your widgets to it.
The key was that we need to wait until the ConstraintLayout is laid out. We can do this by using a ViewTreeObserver.OnGlobalLayoutListener.
Example solution:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
text.text = "Text"
value.text = "Value"
val globalLayoutListener = MainActivityGlobalListener(item_view, text, value, percentage_bar)
item_view.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
}
}
class MainActivityGlobalListener(private val itemView: View,
private val text: View,
private val value: View,
private val bar: View) : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
Log.i("yoyo", "in onGlobalLayout")
val width = value.x - (text.x)
Log.i("yoyo", "Width: $width")
val params: ConstraintLayout.LayoutParams = bar.layoutParams as ConstraintLayout.LayoutParams
params.matchConstraintMaxWidth = (1.0 * width).toInt() // using 0.1 here - this value is dynamic in real life scenario
bar.layoutParams = params
// required to prevent infinite loop
itemView.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
Note the need to remove the listener on the last line to prevent an infinite loop from occurring (listen for layout -> set bar width -> triggers re-layout etc)
If using Android KTX, we can simplify to the following:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
text.text = "Text"
value.text = "Value"
item_view.doOnLayout {
Log.i("yoyo", "in onGlobalLayout")
val width = value.x - (text.x)
Log.i("yoyo", "Width: $width")
val params: ConstraintLayout.LayoutParams = percentage_bar.layoutParams as ConstraintLayout.LayoutParams
params.matchConstraintMaxWidth = (1.0 * width).toInt() // using 0.1 here - this value is dynamic in real life scenario
percentage_bar.layoutParams = params
}
}
Thanks to CommonsWare for the answer!
GitHub repo with code
I would like to calculate the navigationBar height. I've seen this presentation : https://chris.banes.me/talks/2017/becoming-a-master-window-fitter-nyc/
So, I tried to use the method View.setOnApplyWindowInsetsListener().
But, for some reason, it's never called.
Does anyone knows why ? Any limitation there ?
I've tried to use it like this :
navBarOverlay.setOnApplyWindowInsetsListener { v, insets ->
Timber.i("BOTTOM = ${insets.systemWindowInsetBottom}")
return#setOnApplyWindowInsetsListener insets
}
Note that my root layout is a ConstraintLayout.
I faced the same issue.
If your root view is ConstraintLayout and contains android:fitsSystemWindows="true" attr, the view consumed onApplyWindowInsets callbacks.
So if you set onApplyWindowInsets on child views, they never get onApplyWindowInsets callbacks.
Or check your parent views consume the callback.
This is what I observed; in other words, your experience might be different.
[Layout for Activity]
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true" <--
tools:context=".MyAppActivity">
...
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Notice android:fitsSystemWindows="true" in the outer most layout. As long as we have it, setOnApplyWindowInsetsListener() does get called.
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
ViewCompat.setOnApplyWindowInsetsListener(fab) { view, insets ->
...
insets
}
}
Alternatively, if you are going for the "full screen", meaning you want your layout to extend to the status bar and the navigation bar, you can do something like the following.
override fun onCreate(savedInstanceState: Bundle?) {
...
WindowCompat.setDecorFitsSystemWindows(window, false) <--
ViewCompat.setOnApplyWindowInsetsListener(fab) { view, insets ->
...
insets
}
}
The same idea is applicable when you are using a Fragment, as long as the Activity (that contains the Fragment) has either fitsSystemWindows in the outer most layout or you set your Activity as full screen.
I have faced with this problem when I've used CollapsingToolbarLayout, problem is that CollapsingToolbarLayout not invoking insets listener, if you have CollapsingToolbarLayout, then right after this component all other view insets wouldn't be triggered. If so, then remove listener from CollapsingToolbarLayout by calling
ViewCompat.setOnApplyWindowInsetsListener(collapsingToolbarLayout, null)
If you don't CollapsingToolbarLayout, then some other view is blocking insets from passing from view to view.
Or you have already consumed them, I guess you didn't do it)
There is also bug with CollapsingToolbarLayout, it prevents siblings to receive insets, you can see it in issues github link. One of the solutions is to putAppbarLayout below in xml other views for them to receive insets.
I've had to (and I think I am expected to) explicitly call requestApplyInsets() at some appropriate time to make the listener get hit.
Check this article for some possible tips: https://medium.com/androiddevelopers/windowinsets-listeners-to-layouts-8f9ccc8fa4d1
My solution is to call it on navBarOverlay.rootView.
putting ViewCompat.setOnApplyWindowInsetsListener into onResume worked for me with constraintLayout.
#Override
public void onResume() {
super.onResume();
ViewCompat.setOnApplyWindowInsetsListener(requireActivity().getWindow().getDecorView(), (v, insets) -> {
boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
return insets;
});
}
I faced similar issue on API 30.
For setOnApplyWindowInsetsListener() to work you have to make sure that your activity is in full-screen mode. You can use below method to do so
WindowCompat.setDecorFitsSystemWindows(activity.window, false) //this is backward compatible version
Also make sure you are not using below method anywhere to set UI flags
View.setSystemUiVisibility(int visibility)
I had this problem on android 7.1. But on android 11 it worked correctly. Just create a class:
import android.content.Context
import android.util.AttributeSet
import androidx.core.view.ViewCompat
import com.google.android.material.appbar.CollapsingToolbarLayout
class InsetsCollapsingToolbarLayout #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : CollapsingToolbarLayout(context, attrs, defStyle) {
init {
ViewCompat.setOnApplyWindowInsetsListener(this, null)
}
}
And use everywhere InsetsCollapsingToolbarLayout instead of CollapsingToolbarLayout
In my app, it gets called once and not every time I wanted to. Therefore, in that one time it gets called, I saved the widnowInsets to a global variable to use it throughout the app.
I used the following solution using this answer:
ViewCompat.setOnApplyWindowInsetsListener(
findViewById(android.R.id.content)
) { _: View?, insets: WindowInsetsCompat ->
navigationBarHeight = insets.systemWindowInsetBottom
insets
}
I used following solution in my project and it's works like a charm.
val decorView: View = requireActivity().window.decorView
val rootView = decorView.findViewById<View>(android.R.id.content) as ViewGroup
ViewCompat.setOnApplyWindowInsetsListener(rootView) { _, insets ->
val isKeyboardVisible = isKeyboardVisible(insets)
Timber.d("isKeyboardVisible: $isKeyboardVisible")
// Do something with isKeyboardVisible
insets
}
private fun isKeyboardVisible(insets: WindowInsetsCompat): Boolean {
val systemWindow = insets.systemWindowInsets
val rootStable = insets.stableInsets
if (systemWindow.bottom > rootStable.bottom) {
// This handles the adjustResize case on < API 30, since
// systemWindow.bottom is probably going to be the IME
return true
}
return false
}
Use setWindowInsetsAnimationCallback instead of setOnApplyWindowInsetsListener in Android API > 30
One problem I had is ViewCompat.setOnApplyWindowInsetsListener was called again in some other place in the code for the same view. So make sure that is only set once.
I'm trying to make a transition with simple animation of shared element between Fragments. In the first fragment I have elements in RecyclerView, in second - exactly the same element (defined in separate xml layout, in the list elements are also of this type) on top and details in the rest of the view. I'm giving various transitionNames for all elements in bindViewHolder and in onCreateView of target fragment I'm reading them and set them to element I want make transition. Anyway animation is not happening and I don't have any other ideas. Here below I'm putting my code snippets from source and target fragments and list adapter:
ListAdapter:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = list[position]
ViewCompat.setTransitionName(holder.view, item.id)
holder.view.setOnClickListener {
listener?.onItemSelected(item, holder.view)
}
...
}
interface interactionListener {
fun onItemSelected(item: ItemData, view: View)
}
ListFragment (Source):
override fun onItemSelected(item: ItemData, view: View) {
val action = ListFragmentDirections.itemDetailAction(item.id)
val extras = FragmentNavigatorExtras(view to view.transitionName)
val data = Bundle()
data.putString("itemId", item.id)
findNavController().navigate(action.actionId, data, null, extras)
}
SourceFragmentLayout:
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="#+id/pullToRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="#layout/item_overview_row" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
DetailFragment (Target):
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.fragment_detail, container, false)
val itemId = ItemDetailFragmentArgs.fromBundle(arguments).itemId
(rootView.findViewById(R.id.includeDetails) as View).transitionName = itemId
sharedElementEnterTransition = ChangeBounds().apply {
duration = 750
}
sharedElementReturnTransition= ChangeBounds().apply {
duration = 750
}
return rootView
}
DetailFragmentLayout:
<include
android:id="#+id/includeDetails"
layout="#layout/item_overview_row"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
ItemOverviewRowLayout (this one included as item in recyclerView and in target fragment as header):
<androidx.cardview.widget.CardView 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="wrap_content"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="vertical" >
I made also another application using Jetpack navigation, shared elements and elements described by the same layout.xml and it's working since I'm not making transition from recyclerView to target fragment. Maybe I'm wrong here, setting the transitionName to found view in target fragment? I don't know how to make it another way, because the IDs of target included layout should be unique because of recyclerView items.
Okay, I found that how should it looks like to have enter animation with shared element:
In DetailFragment (Target) you should run postponeEnterTransition() on start onViewCreated (my code from onCreateView can be moved to onViewCreated). Now you have time to sign target view element with transitionName. After you end with loading data and view, you HAVE TO run startPostponedEnterTransition(). If you don't do it, ui would freeze, so you can't do time consuming operations between postponeEnterTransition and startPostponedEnterTransition.
Anyway, now the problem is with return transition. Because of course it's the same situation - you have to reload recyclerView before you release animation. Of course you can also use postponeEnterTransition (even if it's return transition). In my case, I have list wrapped by LiveData. In source fragment lifecycle observer is checking data. There is another challenge - how to determine if data is loaded. Theoretically with recyclerView you can use helpful inline function:
inline fun <T : View> T.afterMeasure(crossinline f: T.() -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (measuredWidth > 0 && measuredHeight > 0) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
f()
}
}
})
}
...and in code where you are applying your layout manager and adapter you can use it like this:
recyclerView.afterMeasure { startPostponedEnterTransition() }
it should do the work with determine time when return animation should start (you have to be sure if transitionNames are correct in recyclerView items so transition can have target view item)
From the answer that using ViewTreeObserver is quite consume resources a lot. and also have a lot of processes to do. so I do suggest you use doOnPreDraw instead of waiting after recyclerView was measured. the code implement will like this below.
recyclerView.doOnPreDraw {
startPostponedEnterTransition()
}
I am trying to change the transparency of item-views in a RecyclerView according to certain user inputs.
if (quantity>0) {
holder.itemView.setAlpha((float) 1);
} else {
holder.itemView.setAlpha((float) 0.65);
}
Changing alpha from 0.65 to 1 works fine when quantity > 0. But the reverse is not working on the other case. When debugging, it clearly shows going through the line holder.itemView.setAlpha((float) 0.65); However, alpha is not reduced. Any clue about what's going on?
recycler's ItemAnimator changes alpha during update item process
you can try to add
((SimpleItemAnimator) myRecyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
I had this same problem. Instead of changing the alpha of the itemView, give an name to your root layout and change its alpha instead, as the recyclerview animations handle the itemView alpha animation making it not work.
Remove item animator
In Java:
mRecyclerView.setItemAnimator(null);
Or in Kotlin:
recycler_view.itemAnimator = null
Consider this is the HolderView class
class MyViewHolder(val viewHolder: View) : RecyclerView.ViewHolder(view)
And consider this is how your Adapter class looks like from inside
// ...
override fun onBindViewHolder(holder: MyViewHolder, i: Int) {
holder.viewHolder.alpha = 0.65f
}
Sometimes if your code is holder.viewHolder.alpha = 0.65f it doesn't work always!
Rather than that, you may use alpha of the main container just like that
<RelativeLayout
android:id="#+id/viewMain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
... Your other components goes here
</RelativeLayout>
Now, from your adapter use this instead, it should work in all cases
// ...
override fun onBindViewHolder(holder: MyViewHolder, i: Int) {
holder.viewMain.alpha = 0.65f
}