I am trying to change background color in specific item(s) in a RecycleView.
Because I need to set text too, I have the following code that works fine:
protected void populateViewHolder(RankingViewHolder viewHolder, final Ranking model, int position)
{
final Context mContext = getActivity().getApplicationContext();
viewHolder.txt_name.setText(model.getUserName());
viewHolder.txt_score.setText(String.valueOf(model.getScore()));
viewHolder.txt_class.setText(model.getUser_class());
Picasso.with(mContext).load(model.getAvatarUrl()).error(R.drawable.ic_people_black_24dp).into(viewHolder.personPhoto);
int totalRanking = adapter.getItemCount();
int realRank = totalRanking - viewHolder.getAdapterPosition();
viewHolder.ranknumber.setText("# "+String.valueOf(realRank));
}
This works as I want and realRanktakes the correct values, and the viewHolder.ranknumber.setText("# "+String.valueOf(realRank));
Sets the right text with no problem.
Now I am trying (as I got a number/text result correct, to make an if statement like this:
if(adapter.getItemCount() -viewHolder.getAdapterPosition() == 0)
{
viewHolder.itemView.setBackgroundColor(Color.GREEN);
}
if(adapter.getItemCount() -viewHolder.getAdapterPosition() == 1)
{
viewHolder.itemView.setBackgroundColor(Color.YELLOW);
}
if(adapter.getItemCount() -viewHolder.getAdapterPosition() == 2)
{
viewHolder.itemView.setBackgroundColor(Color.BLUE);
}
(I tried with String.valueOf(realRank)equality, with realRankequality too)
In all cases I have the same result. The color changes as its should at positions 1,2,3 BUT it changes at positions 7,8,9 and 14,15,16 and 21,22,23 etc.
What am I missing here?
public class RankingViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
private ItemClickListener itemClickListener;
public TextView txt_name, txt_score, txt_class, ranknumber;
public ImageView personPhoto;
public RankingViewHolder(View itemView)
{
super(itemView);
txt_name = itemView.findViewById(R.id.txt_name);
txt_score = itemView.findViewById(R.id.txt_score);
personPhoto = itemView.findViewById(R.id.person_photo);
txt_class = itemView.findViewById(R.id.txt_class);
ranknumber = itemView.findViewById(R.id.ranknumber);
itemView.setOnClickListener(this);
}
public void setItemClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
#Override
public void onClick(View view) {
itemClickListener.onClick(view , getAdapterPosition(),false);
}
}
The adapter:
adapter = new FirebaseRecyclerAdapter<Ranking, RankingViewHolder>(
Ranking.class,
R.layout.layout_ranking,
RankingViewHolder.class,
rankingTbl.orderByChild("score").limitToFirst(100)
)
This line of code int realRank = totalRanking - viewHolder.getAdapterPosition();gives a number (1,2,3,4,5,6 etc.) Why i cannot use this number to check equality?
Notice
Keeping this code for NOT working solution, just for future reference:
if(position == 0){
viewHolder.itemView.setBackgroundColor(Color.GREEN);
}
else if(position == 1){
viewHolder.itemView.setBackgroundColor(Color.YELLOW);
}
else if(position == 2){
viewHolder.itemView.setBackgroundColor(Color.BLUE);
}
else{
viewHolder.itemView.setBackgroundColor(Color.WHITE);
}
This changes the color BUT not for only 3 first items. As you scroll down, changes the color for every 3 first viewable items like before, meaning 1,2,3, 7,8,9, etc.
Update:
I dont use a custom adapter, i use FirebaseRecyclerAdapter.
Thanks to #Muhammad Haroon comment i checked that has getItemViewType. So now i m trying with it like
position =adapter.getItemViewType( 0);
if(position == 0){
viewHolder.itemView.setBackgroundColor(Color.GREEN);
}
Not working for now, but i think its the correct direction...
Update 2
With position its not possible as RecycleView recycles the views so i have the same result. The working code is
if (linearLayoutManager.findFirstVisibleItemPosition() > 0) {
viewHolder.itemView.setBackgroundResource(R.drawable.blackframe);
}
else{
viewHolder.itemView.setBackgroundResource(R.drawable.goldframe);
}
Works fine except that after scrolling loosing the change of background.
So as we want and need the perfection, any idea for keeping even after scroll?
hi try add this in your Adapater it may solve your problem.
#Override
public int getItemViewType(int position) {
return position;
}
Please give this a try
override in your custom adapter
#Override
public long getItemId(int position) {
return position;
}
and in in your adapter object:
myAdapter.setHasStableIds(true);
In populateViewHolder add these line of code
if(position == 0){
viewHolder.itemView.setBackgroundColor(Color.GREEN);
}
else if(position == 1){
viewHolder.itemView.setBackgroundColor(Color.YELLOW);
}
else if(position == 2){
viewHolder.itemView.setBackgroundColor(Color.BLUE);
}
else{
viewHolder.itemView.setBackgroundColor(Color.WHITE);
}
position is a parameter in populateViewHolder.
Related
Hii i'm making an app where i have small heart image onclick of that image i've set an onclick listener so when i click on that image it is replaced by another red coloured heart image.
so my problem is when i again click on red heart image it doesnt turn to its normal state which is blank heart vector image as i'm new in android i dont have much knowledge please if someone can guide.
my code for image onclick
#Override
public void onBindViewHolder(FeedViewHolder holder, int position) {
int image_id = images[position];
//holder.background_image_layout.setImageDrawable(null);
//holder.background_image_layout.setImageResource(image_id);
holder.background_image.setBackgroundResource(image_id);
}
#Override
public int getItemCount() {
return images.length;
}
public static class FeedViewHolder extends RecyclerView.ViewHolder {
CustomTextViewMedium first_text,second_text,third_text,fourth_text,fifth_text,sixth_text,
seventh_text;
ImageView favourite_image;
CardView primary_card;
LinearLayout background_image;
ImageView background_image_layout;
CircleImageView profile_image;
public FeedViewHolder(View itemView) {
super(itemView);
background_image = (LinearLayout)itemView.findViewById(R.id.background_image);
primary_card = (CardView)itemView.findViewById(R.id.primary_card);
first_text = (CustomTextViewMedium)itemView.findViewById(R.id.first_text);
second_text = (CustomTextViewMedium)itemView.findViewById(R.id.second_text);
third_text = (CustomTextViewMedium)itemView.findViewById(R.id.third_text);
fourth_text = (CustomTextViewMedium)itemView.findViewById(R.id.fourth_text);
fifth_text = (CustomTextViewMedium)itemView.findViewById(R.id.fifth_text);
sixth_text = (CustomTextViewMedium)itemView.findViewById(R.id.sixth_text);
seventh_text = (CustomTextViewMedium)itemView.findViewById(R.id.seventh_text);
favourite_image = (ImageView)itemView.findViewById(R.id.favourite_image);
background_image_layout = (ImageView) itemView.findViewById(R.id.background_image_layout);
profile_image = (CircleImageView)itemView.findViewById(R.id.profile_image);
favourite_image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(favourite_image.isPressed()){
favourite_image.setImageResource(R.drawable.ic_heart);
favourite_image.setScaleType(ImageView.ScaleType.CENTER_CROP);
}
else {
favourite_image.setImageResource(R.drawable.ic_favorite_border_black_24dp);
favourite_image.setScaleType(ImageView.ScaleType.CENTER_CROP);
}
}
});
}
}
Use if condition like this :-
first globally define like this :-
boolean imageChange = true;
then do this in setOnClickListener like this :-
favourite_image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(imageChange ){
favourite_image.setImageResource(R.drawable.ic_heart);
favourite_image.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageChange = false;
}else {
favourite_image.setImageResource(R.drawable.ic_favorite_border_black_24dp);
favourite_image.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageChange = true;
}
}
});
Problem is that favourite_image.isPressed() is not persistent state of the View. It will be true only while user holds its finger on the view.
To overcome this you have two similar options:
Option #1
Instead of checking isPressed you can check some other state, for example isSelected. Since this is ImageView you probably need to set the state manually on click.
public void onClick(View v) {
if(favourite_image.isSelected()){
favourite_image.setImageResource(R.drawable.ic_heart);
favourite_image.setScaleType(ImageView.ScaleType.CENTER_CROP);
favourite_image.setSelected(false)
}
else {
favourite_image.setImageResource(R.drawable.ic_favorite_border_black_24dp);
favourite_image.setScaleType(ImageView.ScaleType.CENTER_CROP);
favourite_image.setSelected(true)
}
}
Options #2
Use some simple boolean to keep track of the selected state, and set/chec its values similarity to above code.
Got a list (recyclerview) that should show an image for certain types of Class X, everything is working perfectly, the thing is that after I start another activity and finish to go back to it, all of the images are removed, except for the ones that don't have a type 1 after them, so it seems there is a reason that causes if type 1 doesn't make its image only gone, but all the previous
private class XHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView mImageView;
private X mX;
private XHolder(View v) {
super(v);
v.setOnClickListener(this);
mImageView = (ImageView) v.findViewById(R.id.imageview);
}
public void bindX(X x){
mX = x;
if(mX.getType() == 1) {
mImageView.setVisibility(View.GONE);
}
}
#Override
public void onClick(View v) {
xClickEvent(mX);
}
}
Add else condition too, like:
if (mX.getType() == 1) {
mImageView.setVisibility(View.GONE);
} else {
mImageView.setVisibility(View.VISIBLE);
}
RecyclerView does not inflate your View every time, it rather uses the View going out of the screen in the onBindViewHolder() for the next item to appear. So you need to handle if and else condition both each time.
I want to create a list of items using RecyclerView and want to expand particular item when clicked (Like in phone call list ). I want to achieve this without using any library. Can anyone help ?
Get child data list as a Member of Parent data in dataset.
And, at click event of RecyclerView row, use them like this..
here
mdataSet is main dataset for RecyclerView
final TitleHolder holder = (TitleHolder) h;
final Model model = (Model) mdataSet.get(position);
holder.txt_title.setText(model.getTitle());
holder.childItem = model;
holder.txt_title.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (model.getChildList() == null) {
//collapse
((Model) mdataSet.get(mdataSet.indexOf(holder.childItem))).isExpanded = false;
holder.arrow.startAnimation(AnimationUtils.loadAnimation(context, R.anim.arrow_reverse));
model.childList = new ArrayList<ModelData>();
int count = 0;
int pos = mdataSet.indexOf(holder.childItem);
while (mdataSet.size() > pos + 1 && mdataSet.get(pos + 1).type == Model.VIEW_CHILD) {
model.childList.add((ModelData) mdataSet.remove(pos + 1));
count++;
}
notifyItemRangeRemoved(pos + 1, count);
} else {
//expand
((Model) mdataSet.get(mdataSet.indexOf(holder.childItem))).isExpanded = true;
holder.arrow.startAnimation(AnimationUtils.loadAnimation(context, R.anim.arrow));
int pos = mdataSet.indexOf(holder.childItem);
int index = pos + 1;
for (ModelData i : model.getChildList()) {
mdataSet.add(index, i);
index++;
}
notifyItemRangeInserted(pos + 1, index - pos - 1);
model.childList = null;
}
}
});
if (((Model) mdataSet.get(mdataSet.indexOf(holder.childItem))).isExpanded) {
holder.arrow.startAnimation(AnimationUtils.loadAnimation(context, R.anim.arrow));
}
Here, I will add child data to Main dataset at click event on txt_title
Again, use Title(parent) and data(child) as two different ViewTypes like this
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TITLE) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_expand_title, parent, false);
return new TitleHolder(itemView);
} else {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_wallet_history, parent, false);
return new DataHolder(itemView);
}
}
OR
If your child view is fix (which you want to expand/collapse) then wrap them inside layout and, make that Layout visible/Gone with animation in order to achieve expand collapse effect
Refere this link to make them animated
Is it possible to check if a ScrollView is scrolled all its way in the top?
I want to check this so I can enable a SwipeRefreshLayout, otherwise keeping it disabled.
With a ListView it could be done like this, but there's no setOnScrollListener for ScrollViews
listView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
boolean enable = false;
if(listView != null && listView.getChildCount() > 0){
// check if the first item of the list is visible
boolean firstItemVisible = listView.getFirstVisiblePosition() == 0;
// check if the top of the first item is visible
boolean topOfFirstItemVisible = listView.getChildAt(0).getTop() == 0;
// enabling or disabling the refresh layout
enable = firstItemVisible && topOfFirstItemVisible;
}
swipeRefreshLayout.setEnabled(enable);
}
});
This link might be helpful to You. It shows, how to set scroll listener for ScrollView. Then, refer to #antonio answer.
For your case it would be:
mScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int scrollY = mScrollView.getScrollY(); //for verticalScrollView
if (scrollY == 0)
swipeRefresh.setEnabled(true);
else
swipeRefresh.setEnabled(false);
}
});
You can use the getScrollY() method (from the View class)
In Kotlin:
scrollView.viewTreeObserver.addOnScrollChangedListener {
if (!scrollView.canScrollVertically(1)) {
// Bottom of scroll view.
}
if (!scrollView.canScrollVertically(-1)) {
// Top of scroll view.
}
}
I have been reading different posts on here about baseadapters and trying to learn so that I can fix my issue but I haven't been able to resolve it. On my BaseAdapter I have a String called post that is used in a column in the listview. If the post is longer than 13 characters then it is shortened automatically when the user Clicks on the shortened post then it displays it's full length,however the issue is that once you scroll down the listview and come back up to that same post it's still shortened even though the user clicked before to show the full post. I think this is an issue of the Listview or Baseadapter recycling or cache mechanism is there anyway I can fix this? This image will clear things up .. This post is more than 13 characters so it shows the shortened version
if a user wants to read it in full then they will click on the Read More which will then show all of the content which looks like this
and when the user scrolls down or up that same long post will return to this without the user clicking it again, which I want to avoid
I know that the Listview recycles but how can I update it? This is my code below
public class LocalFeed_CustomView extends BaseAdapter {
JSONObject names;
Context ctx;
Activity m;
// More is the default value
String result="More";
#Override
public int getCount() {
try {
JSONArray jaLocalstreams = names.getJSONArray("localstreams");
return jaLocalstreams.length();
} catch (Exception e) {
Toast.makeText(ctx, "Error: Please try again", Toast.LENGTH_LONG).show();
return names.length();
}
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row=convertView;
MyViewHolder holder=null;
try {
if (row == null) {
LayoutInflater li = (LayoutInflater) m.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = li.inflate(R.layout.customadapter, null);
holder = new MyViewHolder(row);
row.setTag(holder);
} else {
holder = (MyViewHolder) row.getTag();
}
final MyViewHolder finalHolder1=holder;
// Json data has been read
JSONArray jaLocalstreams = names.getJSONArray("localstreams");
final JSONObject jsonObject = jaLocalstreams.getJSONObject(position);
// if post length is more than 14 then shorten it
if (jsonObject.getString("post").length() > 14) {
holder.post.setText(jsonObject.getString("post").substring(0, 13) + "...Read More");
holder.post.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
// if Result is More then show full post
if (result.equals("More")) {
finalHolder1.post.setText(jsonObject.getString("post") + "... Read Less");
result = "Less";
}
else
{
//Result is Less so shorten it again
finalHolder1.post.setText(jsonObject.getString("post").substring(0, 13) + "... Read More");
result = "More";
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
} else{
// This Post is already less than 14 characters so no Onclick here
holder.post.setText(jsonObject.getString("post"));
}
return row;
} catch (Exception e) {
e.printStackTrace();
}
return row;
}
class MyViewHolder{
TextView post;
MyViewHolder(View v)
{
post = (TextView)v.findViewById(R.id.post);
}
}
}
The adapter represents the model of the list at any given moment in time.
What this means to you is that if a user clicks a TextView to expand it with the idea that the view is going to stay expanded, then that expanded TextView is state information that will have to be captured in the adapter.
Adapters should always be thought of in two phases:
Event (like onClick()) will update state in the adapter and call notifyDataSetChanged().
getView() uses the current state to create the view.
So let's say in the adapter constructor we create an array of flags
boolean expanded[] = new boolean[size];
where size is the length of your list.
Then you can do this:
// use the current state to create the view
String text;
if (expanded[position]) {
text = jsonObject.getString("post") + "... Read Less";
} else {
text = jsonObject.getString("post").substring(0, 13) + "...Read More";
}
holder.post.setText(text);
holder.post.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// update the current state and request list refresh
expanded[position] = ! expanded[position]; // toggle
notifyDataSetChanged();
}
});
This code doesn't do exactly what yours does, I just wanted to give you the basic idea.