Trying to build a recyclerview where one of the widgets (a TextView holding a date) is gone (View.GONE) depending on a variable. What I have for the item in the recyclerview looks like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/historyItemLayout"
android:weightSum="100"
android:layout_marginTop="3dp"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:id="#+id/historyItemDateTV"
android:background="#color/grey_300"
android:text="09-24-2020"
/>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="100"
android:layout_marginTop="3dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="20"
android:layout_marginStart="15dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/historyItemTeamNameTV"
android:text="#string/team_name"
android:textColor="#color/black"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/historyItemPlayerNameTV"
android:text="#string/player_name"
android:textColor="#color/black"
/>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
There is a list of entries that each holds all of the data that will be pushed to the adapter. So, each entry into that list holds a date field, a player name a team name and some other data (elided here for brevity).
What I want to do is display the date the first time it comes up, but hide (actually, View.GONE) it until the date changes.
Here is an example:
Entry1 2020-09-27 other data
Entry2 2020-09-27 other data
Entry3 2020-09-27 other data
Entry4 2020-08-01 other data
Entry5 2020-05-05 other data
So, for this, I would display a line showing 2020-09-27, then have three lines under it that show the data for the three entries that each have 2020-09-27 as their date. Then, I'd display another line holding just the date 2020-08-01 then the data for that date on the line following, and finally display another line with just 2020-05-05 and another line with the data for that final date.
I've got all of the data sorted correctly using a comparator for insertion into my adapter, but how upon display do I check to see if the date line needs to be hidden or not?
Think I have the answer...will verify.
In the RecyclerView I'll save off the last date I display, and check that in onBindViewHolder. If the dates match, I'll hide that TextView (or perhaps its layout).
Something like this:
//Now handle hiding of extraneous dates
if(lastDate == child.date) {
holder.dateTV.visibility = View.GONE
}
else {
lastDate = child.date
}
Not sure if I understood that correctly (a screenshot probably would have explained it better).
But I think, that you should use View.INVISIBLE instead of View.GONE, in order not to mess up the whole layout. View.INVISIBLE and View.GONE both literally do what they say - and when the parent node (the recycler-item) has wrap_content set, this may result in a mess (uneven heights).
Related
I tried to implement this answer and it worked but only if the both list are prepopulated already, but in my case one list is prepopulated and the other is incremented/decremented dynamically.
Here is my layout structure
Black box represents the recycle list which can be incremented/decremented dynamically, and its main action is to show the users favorite fonts.
Red box represents all the fonts which are available and they are prepopulated.
i want both these list to flow each-other in scroll, such that user can not notice that these are actually two different list.
my xml code :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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:padding="#dimen/home_section_padding"
android:background="#color/background"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:layout_width="match_parent"
android:id="#+id/editText"
android:textColorHint=" #C0C0C0"
android:hint="Paste"
android:textColor="#color/text_color"
android:inputType="text"
android:layout_height="wrap_content"/>
<Button
android:layout_width="match_parent"
android:text="Style"
android:textStyle="bold"
android:textColor="#color/text_color"
android:layout_marginBottom="10dp"
android:fontFamily="serif-monospace"
android:textAppearance="?android:textAppearanceLarge"
android:background="#drawable/round_shape_for_button"
android:onClick="fire"
android:id="#+id/button"
android:layout_height="wrap_content"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyelerViewFav"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</androidx.recyclerview.widget.RecyclerView>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyelerView"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="0dp" />
</LinearLayout>
There is 2 options, You can either use ConcatAdapter to combine both adapters in one adapter and use one RecyclerView (Recommended). Or use NestedScrollView to wrap both your RecyclerViews and make sure to set both RecyclerView heights to wrap_content.
But based on what you are trying to achieve, I would recommend to use ConcatAdapter(adapter1, adapter2) to wrap both your adapters and set that new adapter on one RecyclerView. You will still be able to control each adapter by itself Add, Remove and calling notifyDataSetChanged on the changed adapter will reflect changes on the RecyclerView.
I think you should use two lists in one RecyclerView adapter.And have two different view holders.Whenever you need to make a change just make the change in specific array list (1 or 2) and call notifyDatasetChanged() on the single adapter you have.
Something like this answer
Create 2 different xml file apart from activity_main.xml ,create 2 external java file associated with these 2 XML file for user handling code purposes. Give your desired layout for recycler view in those 2 XML file,map those XML file in MainActivity.java class using some java class which will extend RecyclerView.Adapter class and override some methods as onCreateViewHolder(),onBindViewHolder().To inflate the recycler view data ,create 1 more java file which will use some getter() & setter() inorder to populate the recycler view with data by the help of the class extending RecyclerView.Adapter class.
Problem:
It seems when I have a specific combination of views and setVisibility between Visible and Gone the RecyclerView does not update properly after initial load. I have a RelativeLayout->ConstraintLayout->Constraint Group(with visibility dynamically set). The view within the constraint group is not updating properly.
Example Use Case of Problem:
So the linked code below it will show a search view at the top. The initial state of empty shows the view properly(With the search icon showing). Then if you type "T" then the search icon will disappear(it shouldn't). So if you either delete the T or type the next letter "E" then it shows again. Also if you delete all search text and type T again it will show.
View Code:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="vm"
type="com.example.davidcorrado.myapplication.PlayerVM" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.constraint.ConstraintLayout
android:id="#+id/playerCell_main_layout"
android:layout_width="match_parent"
android:layout_height="84dp"
android:layout_alignParentTop="true">
<android.support.constraint.Group
android:id="#+id/playerCell_status_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="#{vm.showingStatusIcons}"
app:constraint_referenced_ids="playerCell_lineup_status_image" />
<ImageView
android:id="#+id/playerCell_lineup_status_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="#drawable/ic_search" />
</android.support.constraint.ConstraintLayout>
</RelativeLayout>
</layout>
Quirks that might help:
1) If I move the visibility line to the relativeLayout or the ConstraintLayout the things seem to work
2) If I remove the RelativeLayout view things seem to work.
See the below for the full code example:
https://github.com/DavidCorrado/LayoutIssue
As far as I can tell, the reason the search icon disappears is due to the call to ObservableList.clear() then ObservableList.addAll(*)
So the disappear happens on the clear, then the reappear happens on the addAll.
As the callbacks in the adapter kick in on both calls, my hunch is it animates the view to disappear, then animates to show when the addAll is triggered.
I have verified this by not actioning a 'List.clear()' and instead in your textChange listener simply adding more views to the list.
I'm not sure how you would clear and add items to the list without animating the clear, perhaps there are some settings you can toggle in the RecyclerAdapter to ignore the remove of Views or not animate the entry of Views?
My adjusted code in your callback for class PlayersListVM
class PlayersListVM {
val filteredVms: ObservableList<ViewModel> = ObservableArrayList<ViewModel>()
val showing = PlayerVM(isShowing = true)
val missing = PlayerVM()
init {
filteredVms.add(showing)
}
fun onQueryChanged(newText: String) {
if (filteredVms.size % 2 == 1) filteredVms.add(missing)
else filteredVms.add(showing)
}
}
I want a content placeholder for my TextViews in a RecyclerView until the data is fetched & parsed from the server.
By content placeholder, I don't mean default text or a text saying no results. Please look how Facebook displays gray colored animated blocks before displaying the actual data on the web. I want to achieve that.
Basically a kind of loader unless my data is ready to be displayed (please don't suggest me with horizontal or spinning loaders).
e.g.: I display a gray colored block and as soon as my data is ready to be displayed I'll remove the block and then display the text.
All of that needs to be done in a RecyclerView. I hope I am clear with my need.
How do I achieve this?
I can't place dummy views in the RecyclerView because, unless I have the data my getCount will return 0 so no views will be displayed.
I don't want to display a dummy layout and then make it gone when I get the actual data, that doesn't look clean.
Without knowing exactly Facebook's implementation I assume they load an initial data source with "lean" data from which you can tell how many units you will need to display, based on that you could create a placeholder (that would be a type in your RecyclerView), once the full data for that placeholder was loaded you notify data set change and replace the placeholder with the full item (again another type in your RecyclerView).
Basically a kind of loader unless my data is ready to be displayed
If you have absolutely no data, for example you don't have the amount of "blocks" that will be displayed you can't display a placeholder as it might be discarded if you have an empty data set returned from your data source.
this should work,
<TextView android:id="#android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="No Results" />
The id does the trick!
You have to be using a ListViewActivity for it to work!
If not extending ListViewActivity
try,
ListView.setEmptyView() read here
You need to create the skeleton layout and inflate it on the whole screen. Then you can use different libraries to add the shimmer effect.
drawable/skeleton.xml:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<solid android:color="#color/skeleton"/>
<corners android:radius="4dp"/>
</shape>
layout/skeleton_row_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="#dimen/row_layout_height">
<View
android:id="#+id/icon"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_margin="15dp"
android:layout_gravity="center_vertical"
android:background="#drawable/skeleton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<View
android:id="#+id/topText"
android:layout_width="200dp"
app:layout_constraintTop_toTopOf="#id/icon"
app:layout_constraintStart_toEndOf="#id/icon"
android:layout_height="15dp"
android:layout_marginLeft="16dp"
android:background="#drawable/skeleton"/>
<View
android:id="#+id/bottomText"
android:layout_width="250dp"
android:layout_height="15dp"
app:layout_constraintTop_toBottomOf="#id/topText"
android:layout_marginTop="10dp"
app:layout_constraintStart_toEndOf="#id/icon"
android:layout_marginLeft="16dp"
android:background="#drawable/skeleton"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#drawable/skeleton"
app:layout_constraintTop_toBottomOf="#id/icon"
android:layout_marginTop="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</android.support.constraint.ConstraintLayout>
In this tutorial I wrote you will find how to replace the skeleton with the recyclerView: https://medium.com/#sha17187/upgrade-progress-loading-with-a-skeleton-and-shimmer-effect-in-android-863ea4ff5b0b
actually my problem is same as this guy's.But I don't know how to resolve it.Here's my listview xml file.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFFFFF">
<LinearLayout android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:weightSum="3"
android:background="#CCCCCC"
android:id="#+id/tabs">
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#CCCCCC"
android:textSize="20sp"
android:textColor="#000000"
android:text="#string/NewTask"
android:id="#+id/tab1"
/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#CCCCCC"
android:layout_weight="1"
android:textSize="20sp"
android:textColor="#000000"
android:text="#string/Friends"
android:id="#+id/tab2"
/>
<Button
android:layout_width="fill_parent"
android:background="#CCCCCC"
android:layout_height="wrap_content"
android:textColor="#000000"
android:layout_weight="1"
android:text="#string/AddPeople"
android:textSize="20sp"
android:id="#+id/tab3"
/>
</LinearLayout>
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#000000"
android:layout_below="#+id/tabs"
/>
</RelativeLayout>
I am trying to call a function in my listactivity from the onpostExecute of my AsyncTask class.
Here's the function of my listactivity.
public void SetImage(Bitmap bmp,int index)
{
View v = this.ls.getChildAt(index);
ImageView img = (ImageView)v.findViewById(R.id.tasks_userimg);
img.setImageBitmap(bmp);
}
This bmp is the bitmap downloaded in the asynctask class and ls is the listview and the index is the index of the item in the listview.When the postexecute runs I call this function with the arguments to update the listview item image.
The problem is exactly same as I've mentioned in the link i.e when I scroll it gives me error.But he solved the problem by changing the layout width of listview but it is not working here.
Seems that like many others , you have some problems with this special view .
I highly recommend watching the lecture "the world of listView".
They talk about a lot of topics related to listView , including the topic of calling getView multiple times on the same view. In short , the reason why it occurs is because getView is also called for measuring the listView items . You can use (and should) use the convertView in order to avoid un-needed inflating and fetching of data .
About the question itself , you should not use findViewById on the listView items , since they are getting re-used . For example , a view that was set for the 0-th position may be set to the 7-th position in case the user has scrolled down .
if you wish to update a single item from the listView , you can use something like this:
// itemIndex is the index of the item to update
final View v=listView.getChildAt(itemIndex-listView.getFirstVisiblePosition());
//now you update your view according to what you wish .
//you must of course handle the cases that you can't get the view .
Of course , once you do it , you will also have to update your data behind the adapter , so that the next time the user scrolls to this item , it will have the bitmap shown to it.
What you should do depends on the asyncTask you've used .
I assume that you download multiple bitmaps while the listview fills its items . If that's the case , check out this sample , or use my suggestion : have an asyncTask for each viewHolder you have . For each time getView is called , cancel the old one and create a new one for the current item.
1) Define your custom adapter
2) Apply holder pattern
3) Use some image uploader (for example this)
Take a look simple example
I have a listview.
The list items must be populated using code and in the following pattern
The odd numbered list items must be placed aligned with the left side of the screen.
The even numbered list items must be place aligned with the right side of the screen.
The width must depend on the size of the content.
These task must be performed using code.
<LinearLayout android:id="#+id/list_item"
android:paddingLeft="5dip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
<TextView android:id="#+id/text_view"
android:autoLink="all"
android:paddingTop="6dip"
android:paddingBottom="3dip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:linksClickable="false"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#ff000000"
android:textSize="18sp" />
</LinearLayout>
note:i use cursor adapter to bind the data with listview
You must see Google I/O 2010 - The world of ListView to understand how ListView works.
For your situation in getView(...) method of your list adapter you could add something like this:
if(position%2==0){
//even
convertView.setGravity(Gravity.RIGHT);
} else{
//odd
convertView.setGravity(Gravity.LEFT);
}
In order to set width dependency on content change all "layout_width properties" to "wrap_content" in your xml above.
Also it would be useful to read ListView Tips & Tricks post series.
Hope I've got the question correctly.