This is my first time to ask a question here. I will to explain clearly what problem is.
I am creating an Android app that displays questions with choices/options in a feed. I used RecyclerView and CardView to display them. I was able to display the questions and other stuff but I am having difficulties display the options. I am trying to loop the array of options/choices inside the onBindViewHolder(). I can see in the log that the data is there but I can't/ I do not know how to add the views for the choices dynamically.
I already have the loop but I am not certain how I can I inflate or display the textviews. This is my onBindViewHolder() code:
public void onBindViewHolder(PublicQuestionViewHolder publicQuestionViewHolder, int i) {
publicQuestionViewHolder.questionAuthor.setText(questions.get(i).getQuestionAuthor());
publicQuestionViewHolder.questionText.setText(questions.get(i).getQuestionText());
ParseUser currentUser = ParseUser.getCurrentUser();
if (currentUser == questions.get(i).getQuestionParseUser()) {
publicQuestionViewHolder.questionEditIconImageView.setVisibility(View.VISIBLE);
} else {
publicQuestionViewHolder.questionEditIconImageView.setVisibility(View.INVISIBLE);
}
//Log.d("Options", String.valueOf(questions.get(i).getQuestionOptions().size()));
if (questions.get(i).getQuestionOptions().size() != 0) {
for (QuestionOption option : questions.get(i).getQuestionOptions()) {
if (option.getQuestionOptionType().equals("T")) {
Log.d("Option", option.getQuestionOptionText());
} else if (option.getQuestionOptionType().equals("I")) {
Log.d("Option", option.getQuestionOptionImage().getName());
}
}
}
}
First get the activity context in your adapter constructor and save it.
Then just inflate that view inside onBindViewHolder like this -
View newView = LayoutInflater.from(context).inflate(R.layout.view_name, null);
Add that view to whichever parent you like, for eg. -
publicQuestionViewHolder.parentName.addView(newView);
Then get reference to its children like this -
TextView name = (TextView) newView.findViewById(R.id.name);
Related
I'm using recyclerView to implement ExpandableList and I want when user clicks on parent view, other expanded parents collapse. It works nice with code below:
private void collapseOthers(int position) {
for (int i = 0; i < ol.size(); i++) {
OrderForShow o = ol.get(i);
if (i != position) {
if (!o.isChild()) {
if (o.getChilds() == null) {
RecyclerView.ViewHolder vh = rView.findViewHolderForAdapterPosition(i);
if (vh != null)
vh.itemView.findViewById(R.id.root_layer).performClick();
}
}
}
}
}
but when user scrolls down, upper items will be null and vh goes null and I can't collapse invisible item, Is there any way to handle this issue?
Try assigning ids to the layout (or item within layout) inside onBindViewHolder and make changes on basis of id only.
Another alternative is to maintain a HashMap<Integer,String> where Integer will be the position of the item and String can be used to store it's status like "visible" or "invisible".
This is because while scrolling, very often, internally RecyclerView does make some mess in maintaining the dirty views and maintain their ids. Better, we maintain it and take control of them.
This link explains how RecyclerView works.
In my app I am using recycler view.I want to show and hide view on particular condition.But when I scroll recycler views I am not getting expected behaviour.When I Visible a view it gets visible for other rows as well randomly.
What I understand is when it recycles it reuses view and when previous view when it gets recycled it finds the visibility of that view.How can I hide view on particular condition? here is my adapter code
#Override
public void onBindViewHolder(UrduRhymesViewHolder holder, int position) {
RhymesModel current = mUrduRhymesList.get(position);
AppUtility.setCustomFont(mContext, holder.tvUrduRhymesName, Constants.HANDLEE_REGULAR);
holder.tvUrduRhymesName.setText(current.getRhymeName());
holder.ivUrduRhymesLogo.setImageUrl(current.getThumbnailUrl(), mImageRequest);
int status = AppUtility.getFavouriteStatus(mContext, current.getRhymeName(), new UrduRhymesDb(mContext));
if (status == 0)
holder.btnFavourite.setBackgroundResource(R.mipmap.btn_star_unactive);
else
holder.btnFavourite.setBackgroundResource(R.mipmap.btn_star);
ProgressbarDetails progressbarDetails = ProgressbarDetails.getProgressDetail(current.getRhymeName());
if (progressbarDetails == null) {
progressbarDetails = new ProgressbarDetails();
progressbarDetails.prgProgressBar = holder.pbRhymeDownload;
progressbarDetails.download_btn_settings = holder.downloadButtonLayout;
} else {
progressbarDetails.prgProgressBar = holder.pbRhymeDownload;
progressbarDetails.download_btn_settings = holder.downloadButtonLayout;
holder.pbRhymeDownload.setProgress(progressbarDetails.progress);
}
ProgressbarDetails.addUpdateProgressDetail(current.getRhymeName(), progressbarDetails);
if (progressbarDetails != null && progressbarDetails.isDownloading) {
Log.e("test","downloading foe position "+position );
holder.downloadButtonLayout.setBackgroundResource(R.mipmap.btn_download);
holder.pbRhymeDownload.setVisibility(View.VISIBLE);
holder.pbRhymeDownload.setProgress(progressbarDetails.progress);
} else {
Log.e("test","should not be visible for position "+position);
holder.pbRhymeDownload.setVisibility(View.GONE);
}
}
Here progressbarDetails.isDownloading (having value true) is the criteria when I want to show my view but is else clause it is not hiding my view
Edit: Here ProgressbarDetails (Singleton )is a class keeping reference of every row of adapter's progress bar.
No direct way of hiding and unhiding recylerview childitems.
Solution:
Let us assume that the recyclerview adapter is ArrayList
Now make another arraylist (temp_list)
Scenarios:
Hide: iterate through your adapter items and remove the ones that you want to hide. Put each of these into temp_list. After iteration is over, call notifyDataSetChanged()
Show: iterate through your temp_list items and remove the ones that you want to show. Put each of these into adapter. After iteration is over, call notifyDataSetChanged()
You should add a flag in your viewHolder that indicates if this view should be displayed or not . and check this flag every time in the onBindViewHolder.
because the recyclerView reuses the same views you should make a decision depending on something special for every view in you viewHolder.
do u mean when your data has been changed?
and your layout want to change ?
adapter.notifyDataSetChanged();
I have a list of Items that are "seen" or "not seen" in ArrayList<Item>. If they're not seen I change the background color of the ListView item in my CustomArrayAdapter like this :
if(item.is_seen == null || item.is_seen == 0) {
row.setBackgroundResource(R.color.yellow);
} else {
row.setBackgroundResource(R.color.transparent);
}
Now what I want to do is set all items background to transparent after 3 seconds spent on the page.
I already tried to do something like this:
mScheduledExecutor.schedule(new Runnable() {
#Override
public void run() {
for(int i=0; i<mItems.size(); i++) {
final Item n = mItems.get(i);
if(n.is_seen == null || n.is_seen == 0) {
// update value in db
int isSeen = 1;
updateItem(n._id, isSeen);
// change the color of backgrounds
View view = listViewItem.getChildAt(i);
if(view != null) {
view.setBackgroundResource(R.color.red);
}
}
}
Updating the value in the DB works, but the rest doesn't. But I'd like to avoid to reload the data. I just need the color to change.
I don't have errors, it just does nothing.
I look everywhere for an answer and didn't find one.
Am I wrong since the beginning? Is what I want to achieve even possible?
I thank you in advance for all the help you could give me.
Instead of changing the color of the view like youre doing,
// change the color of backgrounds
View view = listViewItem.getChildAt(i);
if(view != null) {
view.setBackgroundResource(R.color.red);
}
(code above will not work, because the views are recycled in the ListAdapter) update the DATA off which you build your list - add a property to the class you are passing into your ListAdapter, then grab that instance from the list and update that property, you have the position at which it needs to be updated already, so that's easy. Then, call notifyDataSetChanged() on the list. It will not redraw the list if you didn't ADD/REMOVE items from list, but it will update correct view for you. This is the only way to do it - absolutely NO WAY to get to a view corresponding to a specific element in a list after list has been drawn already. Only way is to refresh/redraw the list with notifyDataSetChanged(), followed by refreshDrawableState().
I'm actually creating a ListView by populating items from the database.
When the end of the listview is reached I populate a few more items. Now, I want the dividers of the ListView based on the value returned from the database. If two consecutive values of the database are same I want them to be seperated by a thinline and if not a thick line.
I tried setting them through the adapter like this
if (convertView == null) {
holder = new ViewHolder();
if (eventSource.get(position).equalsIgnoreCase("asdsadas")
&& eventSource.get(position + 1).equalsIgnoreCase(
"fdgdfgfd")
|| eventSource.get(position).equalsIgnoreCase(
"dfgdfgdfg")
&& eventSource.get(position + 1).equalsIgnoreCase(
"jgghjhhgg")) {
convertView = mInflater.inflate(R.layout.list_adapter, null);
} else {
convertView = mInflater.inflate(R.layout.list_adapterthinline,
null);
}
I'm inflating a new layout based on the condition. It works for the first time, but after I scroll down and come up the view changes. It gets all mixed up.
I tried setting the divider height in the Activity too, like this and I call the 'setdivider' method in onCreate and in onScroll listener too.
public void setdivider() {
// TODO Auto-generated method stub
for (int i = 0; i < listSource.size() - 1; i++) {
if (!listSource.get(i).equalsIgnoreCase(
listSource.get(i + 1))) {
Log.v("inside not equals", "become smalllllllllllllllll");
list.setDivider(red);
list.setDividerHeight(5);
} else if (listSource.get(i).equalsIgnoreCase(
listSource.get(i + 1))) {
Log.v("inside equals", "become bigggggggggggg");
list.setDivider(blue);
list.setDividerHeight(10);
}
}
}
But here even though both the log comments are shown on the LogCat, only one divider is set in the list.
Please tell me where I'm going wrong, or do suggest some other approaches, if any.
ListView caches views, so when you scroll around, view are reused. This is why convertView isn't always null. However, since you have two different kinds of views, you need to tell that to the ListView so that the convertView you get back is the kind you want. You do that by implementing Adapter.getItemViewType() and Adapter.getViewTypeCount().
In your example, you would let getViewTypeCount return 2, and let getItemViewType return 1 if it's a divider, and 0 if it's not.
merge the layout R.layout.list_adapter and R.layout.list_adapterthinline
with one single layout.
and set the thinline gone by default. and set it visible when you needs
..... Your layout here.
when you needs the line.
convertView.findViewById(R.id.thin_line).setVisible(View.Visible)
When you don't need it.
convertView.findViewById(R.id.thin_line).setVisible(View.Gone)
I'm facing a very strange phenomenon. I'm using a ViewPager from the compatibility package to display profiles.
Every profile is just a ListView with custom elements in it. Every element has two states:
There is data - display it
There's no data - display place holder
Every time I swipe between profiles I reset the data within the item objects. When I remove the REST-Calls and just let the profile item empty - which should display the placeholder - all items keep blank.
What could be the problem?
EDIT:/
That's how I set the current item in the listview. The PageViewAdapter seems to be OK, if I use static content everything works perfect.
final TextView lblTitle = (TextView)convertView.findViewById(R.id.lblTitle);
final LinearLayout llContent = (LinearLayout)convertView.findViewById(R.id.llContent);
final ProfileItem item = getItem(userPosition);
String title = item.getTitle();
if(title == null || title.equals("")) {
lblTitle.setVisibility(View.GONE);
} else {
lblTitle.setText(item.getTitle());
}
if(item.getContent().getParent() != null) {
((ViewGroup)item.getContent().getParent()).removeAllViews();
}
llContent.removeAllViews();
llContent.addView(item.getContent());
The Items look all very similar like that:
#Override
public View getContent() {
if(isTextEmpty()) {
return noTextTherePlaceholder;
} else {
return view;
}
}
The effect keeps the same even if I return just view or noTextTherePlaceholder.
If I would instate new views like a TextView in the getView method everything works as expected.
With view pager you have to get all your data in your Activity.onCreate and then use that data in your ViewPagers adapter's onInstantiateItem.