I have been struggling with this issue for quite a while now, so hoping somebody will be able to lend a hand.
Essentially I have an ArrayAdapter that feeds an ArrayList of CourseCardModel objects to a Custom Swipeable View that extends AdapterView.
I can instatiate the adapter and view first time round absolutely fine, with Course Cards showing up and being able to swipe, however when I attempt to change the ArrayList of CourseCardModels associated with the ArrayAdapter, the view does not change in accordance with the new data, it just freezes.
The reason the data attached to the adapter needs to change is due to the fact that I want the user to be able to filter the Course Cards, so for example they can view cards only from Cambridge University.
Any help with this issue would be hugely appreciated, I have scoured SO for similar issues but have yet to find a solution that worked for me.
ArrayAdapter Code
public class CustomCardAdapter extends ArrayAdapter {
private TextView courseName, uniName, entryStandards, courseDuration, studyMode, qualification,
studentSatisfaction, gradProspects, t1, t2, t3, t4, t5, t6;
ArrayList<CourseCardModel> items;
View v;
LayoutInflater vi;
public CustomCardAdapter(Activity context, int resource, ArrayList<CourseCardModel> courses) {
super(context, resource, courses);
this.items = courses;
vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
v = convertView;
if (v == null) {
v = vi.inflate(R.layout.course_card_inner_template, parent , false);
}
CourseCardModel c = (CourseCardModel) getItem(position);
if (c != null) {
courseName = (TextView) v.findViewById(R.id.courseCardCourseName);
uniName = (TextView) v.findViewById(R.id.courseCardUniName);
entryStandards = (TextView) v.findViewById(R.id.courseCardEntryStandards);
courseDuration = (TextView) v.findViewById(R.id.courseCardCourseDuration);
studyMode = (TextView) v.findViewById(R.id.courseCardStudyMode);
qualification = (TextView) v.findViewById(R.id.courseCardQualification);
studentSatisfaction = (TextView) v.findViewById(R.id.courseCardStudentSatisfaction);
gradProspects = (TextView) v.findViewById(R.id.courseCardGraduateProspects);
t1 = (TextView) v.findViewById(R.id.cardTV1);
t2 = (TextView) v.findViewById(R.id.cardTV2);
t3 = (TextView) v.findViewById(R.id.cardTV3);
t4 = (TextView) v.findViewById(R.id.cardTV4);
t5 = (TextView) v.findViewById(R.id.cardTV5);
t6 = (TextView) v.findViewById(R.id.cardTV6);
v.setBackgroundResource(R.drawable.newcard);
courseName.setText(c.getCourse().getCourseName());
uniName.setText(c.getCourse().getUniversity());
entryStandards.setText(c.getCourse().getEntryStandards());
courseDuration.setText(c.getCourse().getCourseDuration());
studyMode.setText(c.getCourse().getStudyMode());
qualification.setText(c.getCourse().getQualification());
gradProspects.setText(c.getCourse().getGradProspects() + " / 100");
studentSatisfaction.setText(c.getCourse().getStudentSatisfaction() + " / 5");
}
if(position ==0)
{
//float alpha = (float) 0.8;
//v.setAlpha(alpha);
courseName.setVisibility(View.VISIBLE);
}
else if (position == 1){
// Prepare the View for the animation
v.setVisibility(View.VISIBLE);
float alpha = (float) 0.8;
float alpha2 = (float) 0.3;
courseName.setAlpha(alpha2);
uniName.setAlpha(alpha2);
entryStandards.setAlpha(alpha2);
courseDuration.setAlpha(alpha2);
studyMode.setAlpha(alpha2);
qualification.setAlpha(alpha2);
studentSatisfaction.setAlpha(alpha2);
gradProspects.setAlpha(alpha2);
t1.setAlpha(alpha2);
t2.setAlpha(alpha2);
t3.setAlpha(alpha2);
t4.setAlpha(alpha2);
t5.setAlpha(alpha2);
t6.setAlpha(alpha2);
v.setAlpha(alpha);
}
else {
v.setVisibility(View.INVISIBLE);
}
return v ;
}
}
Code that attempts to alter the data
// Set up and assign card adapter
ca = new CustomCardAdapter(CardsActivity.this, android.R.layout.simple_list_item_1, generateCourseCards());
flingContainer.init(CardsActivity.this, ca);
// Update the data in the adapter
ca.clear();
ca.addAll(coursesToReturn);
ca.notifyDataSetChanged();
GenerateCourseCards() Method returns the initial list of CourseCardModel objects to populate the view.
there are situations where the update works out of the box and there are situations where it does not. for the non working cases it helped to reassign the adapter to it-s displayelement
in viewPager i used
mAdapter.notifyDataSetChanged();
mViewPager.setAdapter(mAdapter);
in gridview and listview i used
mGridView.setAdapter(mAdapter);
mList.setAdapter(mDataAdapter);
Related
I'm attempting to null check an EditText (which should be simple enough) however each time I enter text into my two ET's - they always return as empty: "" and they should not (if they have text in them).
I've looked over several other SO articles related to this and none of the fixes seem to resolve the issue.
Can anyone spot what I might be doing wrong in this instance?
Source Snippet:
Java class:
...
public void doneClicked(View v) throws JSONException {
LayoutInflater li = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v1 = li.inflate(R.layout.profile_notification_settings_list_edit, null);
final EditText label = (EditText) v1.findViewById(R.id.emaillabel);
final EditText value = (EditText) v1.findViewById(R.id.emailvalue);
String sValue = value.getText().toString();
if (sValue.matches("")) {
Toast.makeText(this, "You did not enter a value", Toast.LENGTH_SHORT).show();
return;
}
String sLabel = label.getText().toString();
if (sLabel.matches("")) {
Toast.makeText(this, "You did not enter a label", Toast.LENGTH_SHORT).show();
return;
}
...
Adapter:
#Override
public View getView(final int i, View view, ViewGroup viewGroup) {
view = inflater.inflate(R.layout.profile_notification_settings_list_edit, null);
EditText value = (EditText) view.findViewById(R.id.emailvalue);
value.setHint("Value");
value.setText(valueList[i]);
EditText label = (EditText) view.findViewById(R.id.emaillabel);
if (labelList != null)
label.setHint("Label");
label.setText(labelList[i]);
Full source:
Java Class:
https://pastebin.com/y1e9J1yE
Adapter:
https://pastebin.com/Zqh3eCAk
Because everytime you click you are creating a new view which has nothing to do with the views currently being displayed on the screen
public void doneClicked(View v) throws JSONException {
// don't inflate new views
LayoutInflater li = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v1 = li.inflate(R.layout.profile_notification_settings_list_edit, null);
final EditText label = (EditText) v1.findViewById(R.id.emaillabel);
final EditText value = (EditText) v1.findViewById(R.id.emailvalue);
// ... code
Note : you cannot directly fetch the data from rowItemVies from ListView , so you need to add method in your adapter class to get the selected position through which you can fetch the rowView using yourListView.getChildAt(position) and further fetch data from children in rowView
use
sValue.trim().equals("") instead of sValue.matches("")
I have a custom ListView and when I display it I get the 3 first element and they are repeated when I scroll. All the others elements are never display.
I have try with setTag and getTag but it doesnt work. The only way I found is to rebuild the view (disable the if (row = null)) each time I scroll on a element but I don't think it is a good thing to do.
This is my getView methode (with setTag and getTag) :
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
Meal meal = getItem(position);
if (row == null) {
holder = new ViewHolder();
row = LayoutInflater.from(getContext()).inflate(R.layout.custom_list, parent, false);
holder.txtTitle = (TextView) row.findViewById(R.id.nameMeal);
holder.img = (ImageView) row.findViewById(R.id.imageMeals);
holder.price = (TextView) row.findViewById(R.id.textView14);
holder.imgQuantity = (RelativeLayout) row.findViewById(R.id.hide);
holder.soldOut = (ImageView) row.findViewById(R.id.sold_out);
holder.descMenu = (TextView) row.findViewById(R.id.textView);
holder.plus = (Button) row.findViewById(R.id.plus);
holder.minus = (Button) row.findViewById(R.id.minus);
if (User.getInstance().imgBitMap.get(meal.nameFr) == null)
new LoadImageFromURL(holder.img, meal).execute(meal.imageRealUrl);
else
holder.img.setImageBitmap(User.getInstance().imgBitMap.get(meal.nameFr));
holder.txtTitle.setText(meal.nameFr);
holder.price.setText(meal.price + "0 €");
setButton(row, meal);
setImage(row, meal);
row.setTag(holder);
} else {
holder = (ViewHolder) row.getTag();
}
holder.img.setTag(position);
holder.plus.setTag(position);
holder.minus.setTag(position);
holder.txtTitle.setTag(position);
holder.price.setTag(position);
return row;
}
When convertView == null you are setting the image and text views. The else part means that the view is recycled so you don't have to inflate. You do, however, have to do all your image and text setting there too. So just take the image/text setting code out of the if block and move it after the if/else block so that it runs in either case.
as in topic, when I use adapter.notifyDataSetChanged() text color in a cell which i have already changed is seting back to default value. I don't know why it happens im putting here me method for changing color:
for(int l=0;l<list.size();l++){
System.out. println("kolorujemy! "+ list.size() );
LinearLayout root = (LinearLayout) getViewByPosition(l,listView);
((TextView) root.findViewById(R.id.wartosc_calosci)).setTextColor(Color.YELLOW);
I would also add that this part of code is in loop in other thread because the vaules of the cells is updating every 30 seconds. Here is method getViewByPosition:
public View getViewByPosition(int pos, ListView listView) {
final int firstListItemPosition = listView.getFirstVisiblePosition();
final int lastListItemPosition = firstListItemPosition + listView.getChildCount();
if (pos < firstListItemPosition || pos > lastListItemPosition ) {
return listView.getAdapter().getView(pos, null, listView);
} else {
final int childIndex = pos - firstListItemPosition+1;
return listView.getChildAt(childIndex);
}
}
getView:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ListViewHolder listViewHolder;
if(convertView == null){
listViewHolder = new ListViewHolder();
convertView = activity.getLayoutInflater().inflate(R.layout.lista_wlasnych_spolek, null);
listViewHolder.txtFirst = (TextView) convertView.findViewById(R.id.nazwa_spolki);
listViewHolder.txtSecond = (TextView) convertView.findViewById(R.id.wartosc_akt);
listViewHolder.txtThird = (TextView) convertView.findViewById(R.id.wartosc_kupna);
listViewHolder.txtFourth = (TextView) convertView.findViewById(R.id.wartosc_calosci);
convertView.setTag(listViewHolder);
} else {
listViewHolder = (ListViewHolder) convertView.getTag();
}
First of all this line
return listView.getAdapter().getView(pos, null, listView);
makes no sense, because with this call by hand you will internally always create and inflate new row for the list view but this view is never used within your ListView. See that you are always passing second parameter convertView null so internally this method will create new view but this view will be never used inside your ListView.
Tip 1. Don't call getView() method yourself
As you may know ListView stores in memory only as many rows/view as they are visible on the screen when you use ViewHolder pattern properly.
So for now you are setting color for every row that is visible and even those not visible that really not exist in ListView.
Tip 2.
Best way to color or change anything about any of your rows, is to do it just inside getView() method implementation depend on your adapter item state. Don't do it from outside because it looks like some kind of a hack or wrong architecture.
I created the following CursorAdapter which shows messages from my SQL database, everything is added well until I scroll the list, I know that the objects are recycled, but in a wrong way. Here is my CursorAdapter class:
public class ChatAdapter extends CursorAdapter {
public ChatAdapter(Context context, Cursor cursor, int flags) {
super(context, cursor, 0);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.chat_item, parent,
false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// Find fields to populate in inflated template
TextView left = (TextView) view.findViewById(R.id.lefttext);
TextView right = (TextView) view.findViewById(R.id.righttext);
LinearLayout rightBubble = (LinearLayout) view
.findViewById(R.id.right_bubble);
LinearLayout leftBubble = (LinearLayout) view
.findViewById(R.id.left_bubble);
TextView leftDate = (TextView) view.findViewById(R.id.leftdate);
TextView rightDate = (TextView) view.findViewById(R.id.rightdate);
// Extract properties from cursor
String from = cursor.getString(cursor.getColumnIndexOrThrow("from"));
String txt = cursor.getString(cursor.getColumnIndexOrThrow("message"));
String date = cursor.getString(cursor.getColumnIndexOrThrow("t"));
String id = cursor.getString(cursor.getColumnIndexOrThrow("id"));
// Parse time
long datevalue = Long.valueOf(date) * 1000;
Date dateformat = new java.util.Date(datevalue);
String convert = new SimpleDateFormat("HH:mm").format(dateformat);
// Populate fields with extracted properties
if (from.equals("me")) {
right.setText(txt);
left.setText("");
rightBubble
.setBackgroundResource(R.drawable.balloon_outgoing_normal);
leftBubble.setBackgroundDrawable(null);
rightDate.setText(convert);
leftDate.setVisibility(View.GONE);
}
else {
left.setText(txt);
right.setText("");
leftBubble
.setBackgroundResource(R.drawable.balloon_incoming_normal);
rightBubble.setBackgroundDrawable(null);
leftDate.setText(convert);
rightDate.setVisibility(View.GONE);
}
}
}
Unfortenately, after scrolling the list, dates from the rightDate and leftDate dissapears after moving back. I think it't due the .setVisibility(View.GONE)
Any suggestions to fix this?
when the view is recycled, it is in the previous state, android did not clear the status for you.
To fix your problem, you have to set the view in question to VISIBLE when needed
Edit:
like this, add the 2 lines
if (from.equals("me")) {
// your original code
rightDate.setVisibility(View.VISIBLE); //add this
}
else {
// your original code
leftDate.setVisibility(View.VISIBLE); //add this
}
I've been evaluating NOSTRA's Universal-Image-Loader library to asynchronously download images and show them in ListView. So far it works fine except for one problem.
Sometimes Bitmaps from memory cache get attached to wrong ImageViews when the list is being scrolled. After scrolling is stopped, correct images are attached. This situation is quite rare and I couldn't find a 100% way to reproduce it. I shot a video last time it happened.
Here is the ArticleAdapter code, both the UIL config and the bindView() method can be found there.
public class ArticleAdapter extends CursorAdapter {
private LayoutInflater inflater;
private ViewHolder holder;
public ArticleAdapter(Context context, Cursor cursor, boolean autoRequery) {
super(context, cursor, autoRequery);
imageLoader = ImageLoader.getInstance();
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.download_progress_thumb)
.cacheInMemory()
.cacheOnDisc()
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)
.build();
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(context)
.threadPriority(Thread.NORM_PRIORITY - 2)
.threadPoolSize(4)
.discCache(new UnlimitedDiscCache(Utils.getCacheDirectory(context)))
.defaultDisplayImageOptions(options)
.build();
imageLoader.init(configuration);
titleIndex = cursor.getColumnIndex(Articles.TITLE);
descriptionIndex = cursor.getColumnIndex(Articles.DESCRIPTION);
isUnreadIndex = cursor.getColumnIndex(Articles.IS_UNREAD);
isNewIndex = cursor.getColumnIndex(Articles.IS_NEW);
urlIndex = cursor.getColumnIndex(Articles.URL);
hostIndex = cursor.getColumnIndex(Articles.HOST);
timeIndex = cursor.getColumnIndex(Articles.PUBLISH_TIME);
bkgUnreadArticle = context.getResources().getColor(R.color.list_bkg_unread_article);
bkgReadArticle = context.getResources().getColor(R.color.list_bkg_read_article);
textUnreadTitle = context.getResources().getColor(R.color.list_text_unread_title);
textReadTitle = context.getResources().getColor(R.color.list_text_read_title);
inflater = LayoutInflater.from(context);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
String date = Utils.format(cursor.getLong(timeIndex), Utils.DATE);
holder = (ViewHolder) view.getTag();
holder.titleView.setText(cursor.getString(titleIndex));
holder.descriptionView.setText(date);
int isNew = cursor.getInt(isNewIndex);
if (isNew == 1)
holder.isNewView.setVisibility(View.VISIBLE);
else
holder.isNewView.setVisibility(View.INVISIBLE);
int isUnread = cursor.getInt(isUnreadIndex);
if (isUnread == 1){
holder.titleView.setTextColor(textUnreadTitle);
holder.rowLayout.setBackgroundColor(bkgUnreadArticle);
} else {
holder.titleView.setTextColor(textReadTitle);
holder.rowLayout.setBackgroundColor(bkgReadArticle);
}
String url = cursor.getString(urlIndex);
String host = cursor.getString(hostIndex);
if (host.equalsIgnoreCase(Consts.HOST_LENTA) || host.equalsIgnoreCase(Consts.HOST_REALTY)) {
holder.thumbView.setVisibility(View.VISIBLE);
imageLoader.displayImage(Utils.makeImageUrl(url, Utils.THUMBNAIL), holder.thumbView);
} else
holder.thumbView.setVisibility(View.GONE);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.articlelist_item, null);
ViewHolder holder = new ViewHolder();
holder.titleView = (TextView) v.findViewById(R.id.list_title);
holder.descriptionView = (TextView) v.findViewById(R.id.list_description);
holder.thumbView = (ImageView) v.findViewById(R.id.list_thumb);
holder.isNewView = (TextView) v.findViewById(R.id.list_read_unread);
holder.rowLayout = (LinearLayout) v.findViewById(R.id.list_row);
v.setTag(holder);
return v;
}
}
I would really appreciate any help on this matter.
For ListViews, GridViews and other lists which are used view re-using in its adapters you should call .resetViewBeforeLoading() in DisplayImageOptions to prevent this effect.
Also documentation says:
Init ImageLoader with configuration only once
Do you do it only once? Adapter's constructor isn't good place for it.
UPD: Sorry, my answer isn't useful. .resetViewBeforeLoading() doesn't help because you use .showStubImage(...). So you should have correct UIL work but you don't. And it's very strange.
I had this problem on a regular basis, even though I was only initiating the ImageLoader once, I wasn't doing it only when I needed it (in the adaptor), after I changed the init() part in Application class it worked brilliantly. I haven't even had to use restartViewOnLoading() or setStubImage(). Here's the code if necessary.
import android.content.Context;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
public class Application extends android.app.Application {
private static Context mContext;
#Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
DisplayImageOptions imgOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.showImageOnLoading(R.drawable.default_picture)
.build();
ImageLoaderConfiguration imgConfig = new ImageLoaderConfiguration.Builder(mContext)
.defaultDisplayImageOptions(imgOptions)
.build();
ImageLoader.getInstance().init(imgConfig);
}
public static Context getAppContext(){
return mContext;
}
}
EDIT: You can check this conversation here for a deeper understanding of the issue.
Basically there are 3 solutions
1) Set android:layout_width and android:layout_height parameters for ImageViews in dips ('wrap_content' and 'match_parent' are not acceptable)
2) Call ImageLoader after ImageView was drawn (in imageView.post(...):
imageView.post(new Runnable() {
#Override
public void run() {
imageLoader.displayImage(imageUri, imageView);
}
});
3) Pass ImageViewAware (instead of ImageView) which doesn't consider actual view size:
Intead of:
imageLoader.displayImage(imageUri, imageView);
do following:
ImageAware imageAware = new ImageViewAware(imageView, false)
imageLoader.displayImage(imageUri, imageAware);
Just see how to set Holders because I think you have written faulty logic inside your Adapter thats why it is repeating views.
There is also Custom Cursor Adapter with Holder and Get View & BindView discussion.
Add this line in your code ::
holder.thumbView.setTag(Utils.makeImageUrl(url, Utils.THUMBNAIL).get(position));
imageLoader.displayImage(Utils.makeImageUrl(url, Utils.THUMBNAIL), view_holder.image);
I have same problem and fixed it. It is not because Universal-Image-Loader library. It is because you use holder in wrong logic to load image.
Try to replace
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.articlelist_item, null);
ViewHolder holder = new ViewHolder();
holder.titleView = (TextView) v.findViewById(R.id.list_title);
holder.descriptionView = (TextView) v.findViewById(R.id.list_description);
holder.thumbView = (ImageView) v.findViewById(R.id.list_thumb);
holder.isNewView = (TextView) v.findViewById(R.id.list_read_unread);
holder.rowLayout = (LinearLayout) v.findViewById(R.id.list_row);
v.setTag(holder);
return v;
}
With
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.articlelist_item, null);
ViewHolder holder = new ViewHolder();
holder.titleView = (TextView) v.findViewById(R.id.list_title);
holder.descriptionView = (TextView) v.findViewById(R.id.list_description);
ImageView thumbView = (ImageView) v.findViewById(R.id.list_thumb);
imageLoader.displayImage("Your image URL", thumbView);
holder.isNewView = (TextView) v.findViewById(R.id.list_read_unread);
holder.rowLayout = (LinearLayout) v.findViewById(R.id.list_row);
v.setTag(holder);
return v;
}
And remember to remove imageloader in your bindView function