How to make selector from a recyclerview? - android

I need to make day selector like in the screenshot below.
The problem is in coloring numbers. I need white numbers while they are in the circle.
I decided to do it with with 2 recyclerviews that are synchronized with each other. One recyclerview has black colored items and the other has white ones.
But there is no way to combine them together to get the desired result. Please, give me an idea of the correct implementation of this.
If it was possible to give the recyclerview view a shape of a circle, it would resolve my problem. But it seems that recyclerview doesn't have this feature.

Yes you can give recycler view items's shape circle by these steps
1: Create a framelayout
2: Place cardview inside it.
3: Add app:cardCornerRadius="150dp" in card view.
4: Add textview inside
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="4dp"
>
<androidx.cardview.widget.CardView
android:padding="10dp"
android:layout_margin="1dp"
android:id="#+id/crd"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardCornerRadius="150dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:text="4"
android:textColor="#000"
android:gravity="center"
android:layout_centerVertical="true"
android:id="#+id/colorBox"
/>
</androidx.cardview.widget.CardView>
</FrameLayout>
output would b like :
Output

use Model to RecyclerView and handle state inside it. make one boolean into Model and If date gets selected make it true else make it false and notify adapter. Inside adapter, if the flag is true then make date TextBox colour white else black
class Model {
String date;
boolean isSelected;
//....
public boolean isSelected(){
return isSelected;
}
public void setSelected(boolean isSelected){
this.isSelected = isSelected;
}
}
inside onBindViewHolder() of adapter
if (modelList.get(position).isSelected())
holder.textBoxDate.setTextColor(ContextCompat.getColor(context, R.color.white));
else
holder.textBoxDate.setTextColor(ContextCompat.getColor(context, R.color.black));
for more google it "how to manage selected state in recyclerview android"

Related

android recyclerview - TextView is not expanding properly with setmaxlines method

I am new to android development. For my learning purpose, I am developing an android application to list the fibonacci numbers in a recycler view. The list gets appended with new numbers as the user scrolls down the recycler view.
The image shows the app displaying the index and respective fibonacci number for the index in the recycler view
This is the layout xml of single item in recycler view.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:clickable="false"
android:focusable="false"
android:padding="5dp">
<TextView
android:id="#+id/info_index"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:background="#CDD6D5"
android:gravity="center"
android:maxLines="1" />
<TextView
android:id="#+id/info_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#CDD6D5"
android:gravity="center"
android:maxLines="1" />
</LinearLayout>
Excerpt from the adaptor class,
public class ViewHolder extends RecyclerView.ViewHolder {
final TextView index;
final TextView value;
ViewHolder(View itemView) {
super(itemView);
index = itemView.findViewById(R.id.info_index);
value = itemView.findViewById(R.id.info_value);
}
}
public void onBindViewHolder(#NonNull ViewHolder holder, final int position) {
final TextView value = holder.value;
final TextView index = holder.index;
value.setOnClickListener(new View.OnClickListener() {
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
#Override
public void onClick(View v) {
if (value.getMaxLines() == 1000) {
value.setMaxLines(1);
notifyItemChanged(position);
} else {
value.setMaxLines(1000);
notifyItemChanged(position);
}
}
});
Log.d(TAG, "position-value:" + String.valueOf(position));
holder.index.setBackgroundColor(Color.WHITE);
holder.index.setText(String.valueOf(position + 1));
holder.value.setBackgroundColor(Color.WHITE);
holder.value.setText(mData.get(position));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
holder.index.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START);
holder.value.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START);
}
}
When I click on the value textview, the textview does not expand on the first click, instead I have to press two times to make it expand. The first time only the flickering happens. I have tried disabling the animator for recyclerview, tried re using the same view in the recycler view, but nothings helps.
My requirement is to expand fibonacci-value textview on click. By default it should be 1 line and when clicked it should show the whole content with multiple lines (as many as required).
currently this happens with two clicks. first time flickering and second time expands.
I believe this is a bug in android code. But just want to confirm here for any solutions that I might have missed.
The reason of the flickering is that, each time an item is clicked, just after setting maxLines, you are calling notifyItemChanged (which is the correct thing to do), but as a result, before redrawing the item onBindViewHolder is called again. So, when it is called again, there should be a way to know current max lines for that item.
Besides, if you try adding lots of items and scroll up and down, you'll see more bugs (since viewholders are reused) Thus, it is important to set/reset maxlines for each item inside onBindViewHolder (but outside click listener)
Secondly, DefaultItemAnimator of RecyclerView uses cross-fade animation when an item changes and by default, it creates two viewholders for that position for cross-fading between the two. So, above, you set a clicklistener on your "value" textview and interfere with the textview inside onClick callback. However, when you later click and inform adapter that the item is changed, it binds the second viewholder instance. So when you click, your click is consumed with the previous "value" instance, and right afterwards a new instance is bound and you set a new clicklistener to this second viewholder instance.
This is one of many reasons that interfering with viewholder items inside the click listener is error-prone. Sometimes people solve this kind of problems with setTag/getTag but I think it is similarly error-prone as well.
I think the easiest solution is to use a POJO (plain old java object) for each item and include the maxLine state in this POJO. Something like FibonacciItem with fields such as int: index, String: fibonacciNumber, boolean : expanded. Then you will provide the list as a list of FibonacciItems. And inside your click listener, you'll update maxlines of the clicked item. Something like this:
public void onClick(View v) {
if (mList.get(position).isExpanded()) {
mList.get(position).setExpanded(false);
notifyItemChanged(position);
} else {
mList.get(position).setExpanded(true);
notifyItemChanged(position);
}
}
And inside your onBindViewHolder, (but outside your click listener) you should set max lines for each item according to this value:
if(mList.get(position).isExpanded(){
holder.value.setMaxLines(1);
} else {
holder.value.setMaxLines(1000);
}
This will solve the issue. Besides, we usually use POJOs (or data classes) for each item in a list. It is easier to manage.
I was able to repeat your issue on my device, the issue you have is that your LinearLayout is consuming your click before it reaches your textview.
Include:
android:clickable="false"
android:focusableInTouchMode="false"
android:focusable="false"
Within your LinearLayout like so:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:clickable="false"
android:focusableInTouchMode="false"
android:focusable="false"
android:padding="5dp">
<TextView
android:id="#+id/info_index"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:background="#CDD6D5"
android:gravity="center"
android:maxLines="1" />
<TextView
android:id="#+id/info_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#CDD6D5"
android:gravity="center"
android:maxLines="1" />
</LinearLayout>
Also include:
android:nestedScrollingEnabled="false"
Within your RecyclerView

Borderless RippleDrawable issue when notifying RecyclerView

I'm having a horizontal RecyclerView.Adapter subclass with a simple LinearLayout row, having ?attr/selectableItemBackgroundBorderless as a background:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="60dp"
android:layout_height="match_parent"
android:gravity="center"
android:background="?attr/selectableItemBackgroundBorderless"
android:orientation="vertical"
>
<TextView/>
<TextView/>
</LinearLayout>
An interface for the view's OnClickListener is set from the RecyclerView - nothing fancy here.
The OnClickListener is handling the "selected" color of the view, and passing some data to another interface:
setAdapter(new CalendarViewAdapter(view -> {
int position = getChildLayoutPosition(view);
if (position == selectedDayPosition) return;
getAdapter().notifyItemChanged(selectedDayPosition);
selectedDayPosition = position;
getAdapter().notifyItemChanged(selectedDayPosition);
//view.setPressed(true);
//view.setPressed(false); <-- That didn't help as well (nor delaying in a handler etc.)
if (listener != null) {
listener.onDayPicked(CalendarPicker.this.days.get(selectedDayPosition).getPaginationInfo());
}
}));
My concern is after the notifyItemChanged() instead of the nice fade out of the ripple I end up with this weird result:
screenshot taken in the moment of the release of the touch event
My only explanation is this is some strange combination between the ?attr/selectableItemBackgroundBorderless and ?attr/selectableItemBackground. But I want it only in the borderless style.
There are a few questions on this subject, I tried all of the answers but unfortunately they are all using ?attr/selectableItemBackground and not the borderless variation.
Any help with my UI dilemma will be highly appreciated!

RecyclerView not clickable

I have a LinearLayout with a nested RecyclerView showing a list of items. I'd like to open a popup when RecyclerView is clicked (either one of the items or the background white area), but the usual setOnClickListener is not working.
Of course I can put a click listener on each of the items, but the white area between them remains unclickable.
Is there a way to make the entire RecyclerView area clickable?
EDIT: I've added some sample code. I'd like to have the entire layout clickable to open a popup, but while the first three views behave properly, the RecyclerView does not.
<LinearLayout
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="#dimen/spacing_half"
android:background="#color/color_item_margin_divider"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/fragment_tags_title"
style="#style/ItemFragmentHeader"/>
<View
android:layout_width="match_parent"
android:layout_height="#dimen/spacing_line"
android:background="#color/color_line_divider"/>
<RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/spacing_half"/>
</LinearLayout>
Add onClickListener in the viewHolder . Below is a snippet of my project where I had implemented Listener
public class MyViewHolder extends RecyclerView.ViewHolder {
public ImageView shotThumbnail;
public MyViewHolder(View view) {
super(view);
shotThumbnail = (ImageView) view.findViewById(R.id.shotThumbnail);
shotThumbnail.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Put here stuff that you want the onclickListener should do
}
});
Check if this link helps:
Detect click on RecyclerView outside of items
You should use padding instead of margin.
<RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_padding="#dimen/spacing_half"/>
Cause whenever you use padding it will crop your RecyclerView. But whenever you use padding it will just shorten your inside functions. hence you need padding. Then you should use an onClickListener in MainActivity.
recyclerView.setOnClickListener(v->{
//Do whatever you want
});

Make a view in recyclerview cell float above a previous cell

I am using a RecyclerView to show a list of videos.
Each item in the list holds Video and SeekBar (and more stuff actually but not relevant here) in a RelativeLayout, as follows:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/performance"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.my.company.VideoView
android:id="#+id/the_video"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:keepScreenOn="true"
android:scaleType="fitCenter" />
<SeekBar
android:id="#+id/the_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:alpha="1.0"
android:maxHeight="#dimen/seekbar_height"
android:minHeight="#dimen/seekbar_height"
android:padding="0dp"
android:progressDrawable="#drawable/my_progressbar"
android:thumb="#drawable/my_progressbar_circle"
android:translationY="-5dp" />
</RelativeLayout>
As you can see I added a android:translationY property that brings the SeekBar up a little so it would be partially positioned on top of the previous cell, i.e. the previous Video.
However it remains partially hidden. I can only see the part that is in the RelativeLayout in which is it declared.
I tried calling bringToFront() on the seekbar and on the RelativeLayout (performance) itself - but that did not help.
Probably the question is not relevant to RecyclerView only. Being somewhat new in android dev I am not sure if I can place a view that is declared inside a RelativeLayout to show up outside of its borders.
Hope I was clear, need your help. Tx.
By default, every view is clipped to its parent size.
You could try to disable this clipping, by adding this in your RelativeLayout XML attributes:
android:clipChildren="false"
android:clipToPadding="false"
or in code
viewGroup.setClipChildren(false);
viewGroup.setClipToPadding(false);
In your case, it seems that either RecyclerView or LinearLayoutManager consider that previous items should be displayed over following ones. One way could be to use RecycleView decoration to overlap :
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
private final static int vertOverlap = -10;// TODO : do not forget to convert dp in pixels
#Override
public void getItemOffsets (Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(0, vertOverlap, 0, 0);
}
});
So, you would not need to use translationY on your SeekBar, but rather to add some paddingTop to your VideoView :
android:paddingTop="5dp"
That way, I think you could hide the SeekBar if needed, and cell overlapping would not be visible.
Follow this answer with same case only the difference is it is overlapping next item of Recycleview https://stackoverflow.com/a/40603773/3839498

Custom InfoWindow -- image showing, text not showing

I want to make an infowindow showing an image, a title, and a subtitle, using the default design for the InfoWindow, and am thereby implementing the InfoWindowAdapter interface in my code. Because I want to keep the default design for the window, I am using the getInfoContents method and setting my values there. However, the only thing that shows when I click on the marker is the image, not the text. I tried commenting out the image to see if the text was just getting buried by the image, but nothing showed up. My Title and subtitle fields are not showing in the callout.
However, when I put the same code inside the getInfoWindow method, all three fields show up as expected.
I'd rather not use the getInfoWindow method since the default callout style suffices for me.
Here's where I call the custom info adapter:
private class CustomWindowAdapter implements GoogleMap.InfoWindowAdapter {
private View infoView;
CustomWindowAdapter() {
infoView = getLayoutInflater().inflate(R.layout.custom_map_infoview, null);
}
#Override
public View getInfoContents(Marker marker)
{
TextView title = (TextView)infoView.findViewById(R.id.popup_title);
TextView subtitle = (TextView)infoView.findViewById(R.id.popup_subtitle);
ImageView image = (ImageView)infoView.findViewById(R.id.popup_image);
title.setText(marker.getTitle());
subtitle.setText(marker.getSnippet());
image.setImageResource(R.drawable.image);
return infoView;
}
#Override
public View getInfoWindow(Marker marker)
{
return null;
}
}
And here's my layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/popup_image"
android:layout_height="match_parent"
android:layout_width="match_parent" />
<TextView
android:id="#+id/popup_title"
android:layout_height="match_parent"
android:layout_width="match_parent" />
<TextView
android:id="#+id/popup_subtitle"
android:layout_height="match_parent"
android:layout_width="match_parent" />
</LinearLayout>
Any ideas of why it might not be showing up?
One of the problems with info windows is that since the View is being rendered by another process, you cannot readily use tools like Hierarchy View to see if the layout is working as expected. Using an IDE's graphical layout editor can help provide a preview that may be useful in diagnosing problems.
Regardless, in this case, you requested that all three children of a vertical LinearLayout have a height of match_parent, and with no weight. As a result, "first one in wins", and the two TextView widgets would wind up with a height of 0.

Categories

Resources