Multiple cards opening in RecyclerView when clicked on only Single card - android

I am trying to make a periodic table app.
RecyclerView was used to display items in listed fashion.
When an item is clicked then the hidden layout which contains element details changes from View.GONE to View.VISIBLE.
Problem is that when I click 1st item and then scroll then every 11th item is opened (11,21,31..) and when closed every 11th item is closed.
Code for hiding and unhiding the details layout:
holder.element_single_card.setOnClickListener {
if(holder.hidden_elements.visibility == View.GONE) {
holder.hidden_elements.visibility = View.VISIBLE
}
else{
holder.hidden_elements.visibility = View.GONE
}
}
Code of Parent Layout containing all text views for showing details of element
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/element_hidden_information"
android:visibility="gone"
android:layout_below="#id/element_shown_information"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/element_symbol"
android:layout_centerHorizontal="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/element_period"
android:layout_below="#id/element_symbol"
android:layout_centerHorizontal="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/element_mass"
android:layout_below="#id/element_period"
android:layout_centerHorizontal="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/element_density"
android:layout_below="#id/element_mass"
android:layout_centerHorizontal="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/element_boiling_point"
android:layout_below="#id/element_density"
android:layout_centerHorizontal="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/element_melting_point"
android:layout_below="#id/element_boiling_point"
android:layout_centerHorizontal="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/element_electron_affinity"
android:layout_below="#id/element_melting_point"
android:layout_centerHorizontal="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/element_summary"
android:layout_below="#id/element_electron_affinity"
android:layout_centerHorizontal="true"
/>
</RelativeLayout>
How can I stop every 11th item from opening and what is the problem with my code?

Ideally, you should have a Boolean type isExpanded variable in your model along with getter/setter methods.
This variable will be responsible for storing the expanded state corresponding to each item.
From onClick call -
myModel.setIsExpanded(true);
and write below code in onBindViewHolder()
if(myModel.getIsExpanded())
{
holder.hidden_elements.visibility = View.VISIBLE
}
else
{
holder.hidden_elements.visibility = View.GONE
}
Since RecyclerView reuses it's viewHolder objects, you can't rely on the earlier state of the View.
Let me know if you need any more clarification :)

This is because when you scroll down the 11th element is reusing the same view as the 1st element(this is a desired android feature due to memory constraints) you need to reset the layout to View.GONE if the element you are trying to display is not in open state.
To do this you cannot rely on the view's current state as you are doing in OnClickListener and should instead maintain a state of selected or clicked in the list item itself that you are using to display the data.
Further documentation about view recycling can be found in the official Android docs.

Related

Android recycler view animation bug

I'm animating my recycler view so that it slides side-to-side, exposing a checkbox on the left. The initial animation is great, but when the list is longer than the device height, we get issues with where the two elements (the date and the horizontal rule) are placed. I've recorded two videos to demonstrate the bug in more detail.
Goal: How do I prevent the dates from zig-zagging like that?
http://dai.ly/x6j70qz
http://dai.ly/x6j70yr
Here's the parent recycler view:
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview_vins"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingStart="-46dp"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
And here's the view holder for each list item:
<android.support.constraint.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/front_layout_read_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="#+id/is_selected_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:button="#drawable/checkbox_selector"
android:checked="false"
android:scaleX="0.5"
android:scaleY="0.5"
app:layout_constraintEnd_toStartOf="#+id/year_make_model_text_view"
app:layout_constraintTop_toTopOf="#+id/year_make_model_text_view" />
<TextView
android:id="#+id/year_make_model_text_view"
style="#style/VinListYearMakeModel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="64dp"
android:layout_marginTop="24dp"
app:layout_constraintEnd_toStartOf="#+id/date_text_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="1998 Honda Fit blahblahblahblahblahblahblahblahblahblahblah" />
<TextView
android:id="#+id/vin_text_view"
style="#style/VinListVin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
app:layout_constraintStart_toStartOf="#+id/year_make_model_text_view"
app:layout_constraintTop_toBottomOf="#+id/year_make_model_text_view"
tools:text="4JHG2J43HJHG34" />
<TextView
android:id="#+id/date_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/year_make_model_text_view"
tools:text="03/14/18" />
<TextView
android:id="#+id/horizontal_rule"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginTop="24dp"
android:background="#E1E1E1"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/vin_text_view" />
</android.support.constraint.ConstraintLayout>
And here's the gist of the animation that slides the recycler view side-to-side:
val aul = AnimatorUpdateListener {
Log.i("Animated Value", it.animatedValue.toString())
recyclerview_vins.setPadding(it.animatedValue as Int, 0 ,0 ,0)
}
val varl = ValueAnimator.ofInt(0, -137) // These values reversed for opposite animation
varl.duration = 400
varl.addUpdateListener(aul)
varl.start()
I think it may be related to other bugs having to do with the last item in a recycler view list behaving weirdly:
Weird Animation Comes For RecyclerView Last Item
When you using recyclerviews you have to keep in mind that the VIEWS are recycled. this means as you scroll the same view is brought onto screen that previously left the screen.
So if you just scroll without ever sliding anything, it will probably be fine, but the second you animate a view into a different position, that view will stay there for the next active data item that will populate it with it's content.
You need to make sure that as onBindView happens that you are clearing the animations and cleaning up your previous movement of the view. Otherwise, it will stay selected, slide, or whatever else you did to the view.
If you are using animations, you can simply use UIElementThatYouUsed.clearAnimations() or something like that, and it should remove whatever you animated over.
I've had very similar issues in the past when animating recycler view rows, they do remain where you move them unless you clear it.

OnItemLongClick event of listView Android

I have a list view with custom cell layout. Actually it shows data from a table, there are two button one for editing and the other is for deleting the record. these two buttons are hidden, when long click on the row then these two buttons shows up .
Here is the cell_layout :
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Customer Code and Name "
android:textSize="16sp"
android:textColor="#ff000000" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginRight="25dp">
<TextView
android:id="#+id/txtCusCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cus code"
android:textSize="16sp"
android:textColor="#ff000000" />
<TextView
android:id="#+id/txtCusName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="cus Name"
android:textSize="16sp"
android:textColor="#ff000000"
android:layout_marginLeft="10dp" />
</LinearLayout>
<ImageView
android:id="#+id/imgbtnOrderActions"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="#drawable/down"
android:layout_alignParentEnd="false"
android:clickable="true"
android:layout_alignParentRight="true"
android:background="#drawable/test"/>
</RelativeLayout>
<TableLayout
android:id="#+id/tblLayoutOrderAction"
android:layout_width="fill_parent"
android:layout_height="0dp">
<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<ImageView
android:id="#+id/lmgbtnOrderEdit"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:src="#drawable/edit"
android:layout_weight="1"
android:layout_column="1"
android:clickable="true"
android:background="#ff00b4df" />
<ImageView
android:id="#+id/ImgbtnOrderDelete"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:src="#drawable/delete"
android:layout_weight="1"
android:layout_column="2"
android:background="#ffff625a"
android:clickable="true" />
</TableRow>
</TableLayout>
</LinearLayout>
those two buttons are in Table Layout i give them 0dp height fro hiding them.
And this is OnLongItemClick event of listView :
lstviewOrders.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener()
{
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l)
{
final TableLayout tblLay = (TableLayout) view.findViewById(R.id.tblLayoutOrderAction);
TableLayout.LayoutParams lay = new TableLayout.LayoutParams(30, ViewGroup.LayoutParams.MATCH_PARENT);
tblLay.setLayoutParams(lay);
return false ;
}
});
Here comes the problem . When an item in listview is long clicked then it shows the edit and delete button of that items but it also shows those button in the item which is at next 7th position . For example if i click item on position 3 then button of 3,10,17,.... are also showed ...
how to get ride of this problem ???
This sounds to me like you're dealing with the ListView's view recycling feature. This answer provides a great explanation.
Basic example: if a ListView has a total of 20 items but only enough room to show 4 on screen at the same time, then the ListView will only use 4 view objects but recycle them for each item in the list. So if you change something on view 2, then scroll down you'll find that this change also applied to view 6. This is what makes dynamic views difficult with ListViews.
In the above example, if your adapter is loading view 6, in your adapter's getView method, the convertView object is the view from 2, which is then repurposed for data element 6. I would play around with storing whether the buttons are shown in your data and resetting the convertView in this method and then showing/hiding the buttons based on the underlying data.
ListViews handle focus on displaying underlying data, but not necessarily editing that data in a view.
You could try skipping the part of your getView that tries to use the convertView so that you're always making a new view, but I'm found this can cause some other unexpected UI experiences. Good luck!
The problem is occuring because listview cells are reusing.
And when you are showing the button for a particular cell and scroll the list view then the item which is reusing that cell will also show the buttons.
To avoid the problem what you can do is take a position in adapter by any int variable and update the posion on long press in adapter.
In getView method of adapter put the position check if items are having same postion will show the button else it will set visibilty GONE.
Like in adapter : -
int selectedposition = -1;
public View getView(.........){
// your code
if (position == selectedposition){
button.setVisibility(VISIBLE);
}
else{
button.setVisibility(GONE);
}
return convertedView;
}

ScrollView makes content disappear

I am trying to put a custom listView into a scrollView and when I scroll down and up I lose the top items in the listView which are a TextView and a CheckBox for some reason.
Here's my code:
activity_program.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/tv_program_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.10"
android:text="Program name"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/tv_program_info"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_weight="0.20"
android:text="program info, talk abt sets, reps, weight etc..." />
<ListView
android:id="#+id/android:list"
android:layout_width="match_parent"
android:layout_height="395dp" >
</ListView>
<Button
android:id="#+id/bt_savework"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.10"
android:text="Save Workout" />
</LinearLayout>
My custom listView contains these:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp" >
<TextView
android:id="#+id/tv_exercice_title"
android:layout_width="270dp"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/cb_exercicedone"
android:layout_alignParentTop="true"
android:gravity="center_vertical"
android:text="Exercice title"
android:textAppearance="?android:attr/textAppearanceMedium" />
<LinearLayout
android:id="#+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_below="#+id/tv_exercice_title"
android:orientation="horizontal" >
<CheckBox
android:id="#+id/cb_setdone"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="7dp"
android:text="Set" />
<TextView
android:id="#+id/tv_set_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="3dp"
android:text="1" />
<TextView
android:id="#+id/tv_reps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:text="Reps x" />
<EditText
android:id="#+id/et_reps_number"
android:layout_width="47dp"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number" />
<TextView
android:id="#+id/tv_weight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:text="Weight" />
<EditText
android:id="#+id/et_weight_number"
android:layout_width="65dp"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number" />
<TextView
android:id="#+id/tv_kgorlbs"
android:layout_width="59dp"
android:layout_height="wrap_content"
android:text="kg/lbs"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<CheckBox
android:id="#+id/cb_exercicedone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignRight="#+id/linearLayout1" />
</RelativeLayout>
In my ListActivity I use a custom adapter to fill the ListView with the above template and only edit the tv_set_number (for now) then set the tv_exercice_title's visibility to "gone" to add more sets.
If you think the problem could be in my java code let me know in the comment and I will post it.
Please tell me what's the problem.
Here's some screenshots:
Before I scroll:
image 1
After I scroll:
image 2
Seems to me you are using the ListView to generate different kinds of rows and you're missing some steps. You could also consider to rethink the structure, as maybe this would better fit into a ExpandableListView: from the screenshots you post seems like you'd have a set of rows that hierarchically belong to a group view.
The ExpandableListView would fit this purpose, and also allow to collapse/expand the groups. I'd take a look at it, it's really easy, you'd only have to make the adapter descend from ExpandableListViewAdapter and provide methods to obtain the Group views (Your title & checkbox) and the item views (the sets with reps, weight, etc...)
If, on the contrary, you want to make it with a ListView, there are some issues to care about (I'll call TITLE ROWS to those with the title & checkbox, and regular rows to the regular ones)
What is happening now is, when you create a view and this view is not a TITLE ROW, you are setting the visibility of the title to GONE. But if you scroll, ie., DOWN and a TITLE row has to appear from the upper edge, the View you are given to recycle is the one that just left the screen by the lower edge, that was probably a REGULAR ROW. So in getView() you not only have to set Visibility to GONE for regular rows, but also back to VISIBLE for Title Rows. Google an explanation on how View recycling works for ListView and you'll understand it right away.
ListView provides a mechanism to help in these cases: The View Types:
You declare 2 "row types" overriding getViewTypeCount() and getItemViewType(int position) in your adapter. Type 0 will be TITLE ROWS, and Type 1 would be REGULAR ROWS.
In getView / convertView you'll generate / reuse the 2 different views separately based on the type.
.
#Override
public int getViewTypeCount() {
return 2; // you have 2 different types
}
#Override
public int getItemViewType(int position) {
if (position IS A TITLE ROW) return 0; else return 1;
}
... and then in getView() {
if (getItemViewType(position) == 0) {
// it's a TITLE ROW, create / reuse it accordingly
} else {
// it's a REGULAR ROW, create / reuse it accordingly
}
This has the advantage that ListView does some of the dirty work for you, so you'll be given the correct view type to recycle.

duplicateParentState stops working when ImageButton is added

I've got a ListView with a custom Adapter inheriting from ArrayAdapter. I'm using a custom Checkable subclass of LinearLayout to highlight the last selected item in the list where it basically maps the checked state to selected.
The layout used by the Adapter for list items is as follows:
<com.wordlistdictionary.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="40dp">
<LinearLayout
android:id="#+id/entry_text"
android:layout_width="0dp"
android:layout_weight="7"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:duplicateParentState="true"
/>
<TextView
android:id="#+id/entry_number_of_occurences_1"
android:textSize="9pt"
android:typeface="monospace"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="#color/item_text_color_selector"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:duplicateParentState="true"
/>
<TextView
android:id="#+id/entry_frequency_1"
android:textSize="9pt"
android:typeface="monospace"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="#color/item_text_color_selector"
android:layout_gravity="center_vertical"
android:layout_weight="2"
android:duplicateParentState="true"/>
<ImageButton
android:id="#+id/delete_entry_button_1"
android:src="#drawable/delete_button_selector"
android:layout_height="40dp"
android:layout_width="40dp"
android:layout_gravity="center_vertical"
android:layout_marginRight="15dp"
android:duplicateParentState="true"
/>
</com.wordlistdictionary.CheckableLinearLayout>
It works as I expect it with the text color changing for the last touched item until I'm adding the last ImageButton view and it stops highlighting the text from that point.
Has anybody encountered this and what was your solution?
Currently I'm thinking of a workaround with manually propagating the selected state to all child views from my custom layout as opposed to just changing the selected state of the layout view itself and relying on the duplicateParentState mechanism.
It turns out when added to a list item a Button or ImageButton "swallows" the touched event so it doesn't propagate to the list view so I had to set imageButton.setTouchable(false) in getView() in my custom Adapter.
Note that I first tried to set android:touchable='falase' in my layout file but it didn't work so I had to set it programmatically.
I've found the answer in these 2 SO questions:
ListView setOnItemClickListener not working by adding button
Click is not working on the Listitem Listview android

Is this is OK to use the ListView for inline editing?

EDIT:
I can't write into the EditText, it disappears when i try to write
something, its because the getView() is called when i modify the data
I need to load some data from SQLite & list it in a ListView or Grid. The next thing is to provide the inline editing functionality, i.e the user can edit the data also within that ListView OR grid.
Currently i am using the ListView for this purpose. What i have done is that i have defined layout for the row item, the sample xml is provide below:
rowitem.xml
<TableRow
android:id="#+id/tableRow1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:weightSum="1">
<TextView
android:text=""
android:id="#+id/txtQuestionId"
android:layout_width="100dp"
android:layout_height="50dp"
android:clickable="true"
android:padding="5dip"
android:gravity="left"
android:background="#android:color/transparent"/>
<EditText
android:text=""
android:id="#+id/txtQuestion"
android:layout_width="400dp"
android:layout_height="50dp"
android:clickable="true"
android:padding="3dip"
android:gravity="left"
/>
<TextView
android:text=""
android:id="#+id/txtStandard"
android:layout_width="200dp"
android:layout_height="50dp"
android:padding="5dip"
android:gravity="left"/>
<RadioGroup android:id="#+id/rdbStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" android:weightSum="1">
<RadioButton android:id="#+id/rbSAT"
android:layout_width="wrap_content" android:text="SAT"
android:layout_height="wrap_content"
android:onClick="onStatusClicked"/>
<RadioButton android:id="#+id/rbUNSAT"
android:layout_width="wrap_content" android:text="UNSAT"
android:layout_height="wrap_content"
android:onClick="onStatusClicked"/>
<RadioButton android:id="#+id/rbNA"
android:layout_width="wrap_content" android:text="NA"
android:layout_height="wrap_content"
android:onClick="onStatusClicked"/>
</RadioGroup>
</TableRow>
<!-- just draw a red line -->
<View
android:layout_height="2dip"
android:background="#FF0000" />
</TableLayout>
I am getting the data from SQLite & using a custom DataAdapter class bind the data with ListView.
I have few questions:
1- What are the best practices for inline editing in android?
2- What is the best option for inline Editiing ListView OR Grid?
3- What are pros & coins of using ListView for inline Editing?
Great Thanks.
Ok, Its my personal opinion.. try if you like it..
Fill data in your list from database. (No need of EditText in layout).
Make a dialog with Edittext. Now when user click on ListItem open that dialog with pre-populated text from current selected list row textview. Then allow user to change in it. When user click OK on dialog then modify that Textview in list item..
Why not? It would be definitely nice for user, but kind of tricky to develop.
Your list entry layout must provide text edit ( maybe invisible ) and switch to it on click on text view. This text view shall have suitale listener keeping track in parent layout etc.
you have to save data back as soon as textedit loses focus / about to being reclaimed by list view for reuse in cause fo scrolling.
#yaqub use EditorActionListener with imeOptions="actionDone"
like
set the android:imeOptions="actionDone" property for edittext which is there in list.
use editText.setOnEditorActionListener(onEditorActionListener)
track if(actionId == EditorInfo.IME_ACTION_DONE) {//here do your action}

Categories

Resources