I am using the code below:
private Runnable returnRes = new Runnable() {
#Override
public void run() {
if(m_orders != null && m_orders.size() > 0){
m_adapter.notifyDataSetChanged();
for(int i=0;i<m_orders.size();i++)
m_adapter.add(m_orders.get(i));
}
m_ProgressDialog.dismiss();
m_adapter.notifyDataSetChanged();
}
};
but the weird thing is, after the list populates, the only item available is the first thing on the list the rows directly below would be empty unless I drag down out of view then back again then it'd show. I'm pretty sure the code above is right as I followed a tutorial. But, I cant expect the user to drag down and back again to see the things involved...
And to add, I just noticed that my datas are not populated properly as this warning would appear 07-19 23:54:49.947: WARN/InputManagerService(58): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy#44eb97c0
and I'm quite sure that my codes are correct and the following is where it stops:
public View getView(int position, View convertView, ViewGroup parent){
View v = convertView;
if(v != null){
return v;
}
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
Log.d("added", "g" + position);
Grade g = grades.get(position);
if(g != null){
TextView name = (TextView) findViewById(R.id.bottomtext);
TextView id = (TextView) findViewById(R.id.toptext);
if(name != null)
name.setText(g.getName());
if(id != null)
id.setText(g.getId());
Log.d("grade", "grade " + g.toString());
}
return v;
}
and from the LogCat trace I would only get to position 3 :( what could be the problem?
someone please help me...
LoginByHttpPost gradeIndex = new LoginByHttpPost();
HttpURLConnection gradePage = gradeIndex.doHttpGet(TARGETURL);
String gradeInd = gradeIndex.readResponse(gradePage);
Document doc = Jsoup.parse(gradeInd);
// do more things here
Log.d("grade now ", grades.get(0).text());
Log.d("gradef now ", gradesF.text());
for(int i = 0; i < grades.size(); i += 5){
Grade grade = new Grade();
grade.setId(grades.get(i).text());
grade.setName(grades.get(i + 1).text());
//gradeList.add(grade);
ga.add(grade); //this is my arrayadapter not sure where to add my object to through :(
}
for(int i = 0; i < gradesF.size(); i++){
gradeList.get(i).setGrade(gradesF.get(i).text());
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.d("prob", e.getMessage());
}
this is called from the asyncatask in the function doInBackground()
Try calling ListView.invalidateViews() on the list view. Worked for me.
Even if you call notifyDataSetChanged() and/or notifyDataSetInvalidated() from the UI thread on the adapter, these only invalidates the data and not the views. Hence.
You should call notifyDataSetChanged() in the UI thread try using runOnUiThread().
The second thing is notifyDataSetChanged() should be called only after add, remove and clear functions.
You could try refreshing the listview by calling listView1.requestLayout() or listView1.setAdapter(adapter). You could also try adapter.notifyDataSetChanged(). If scrolling on listview makes the views visible, you could also try scrolling the listview to the bottom and then scroll back to the original position programmatically.
UPDATE:
I think the problem may be coming from your getView() function. Try changing it to this:
public View getView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
if (v == null)
{
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
Log.d("added", "g" + position);
}
Grade g = grades.get(position);
if(g != null)
{
TextView name = (TextView) findViewById(R.id.bottomtext);
TextView id = (TextView) findViewById(R.id.toptext);
if(name != null)
{
name.setText(g.getName());
}
if(id != null)
{
id.setText(g.getId());
}
Log.d("grade", "grade " + g.toString());
}
return v;
}
Ok, I solved the problem.
There is nothing wrong with the ListAdapter. The problem is from the parent views of the ListView.
onMeasure must be called on the ListView every time the layout is changed. i.e. onMeasure or onLayout is called on one of its parents.
I had a custom view as the parent of the parent of the ListView. In which I precisely refused to measure the children to make the layout process faster.
You want to do something in background then send some change to UI, right? If you are doing this, you should use AsyncTask, a simpler and more effective way for background processing. Whenever your want to change the UI, just call onProgressUpdate() then do what you want there.
I had a similar problem. A simple file manager: if I have an image I've to resize it with a separate thread. So I show a placeholder until the resized image is ready. After that I've to call notifyDataSetChanged() on the adapter. My solution is to use an handler like this on the adapter
public final Handler fileArrayAdapterHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
notifyDataSetChanged();
}
};
On the thread I send an empty message to the handler at the end of it....
With different message you could do many other things ...
i was having the same issue, what i was missing was that the position was not always been sent, for example was skipping (position 0 and 2) and these were no updating until i scrolled.
This fix it for me (See that i used an asynctask) went from this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
...
new AsyncTask<ViewHolder, Void, Bitmap>() {
private ViewHolder v;
#Override
protected Bitmap doInBackground(ViewHolder... params) {
// Code
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (v.position == position) {
// Code
}
}
}.execute(viewHolder);
return convertView;
}
To this (Created an inner class, pass the position in the constructor):
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
....
new DownloadImage(position).execute(viewHolder);
return convertView;
}
private class DownloadImage extends AsyncTask<ViewHolder, Void, Bitmap> {
private ViewHolder v;
private int myPosition;
public DownloadImage(int p) {
myPosition = p;
}
#Override
protected Bitmap doInBackground(ViewHolder... params) {
// Code
return result;
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (v.position == myPosition) {
// Code
}
}
}
As some others have already stated, this problem is caused by the fact that the code is not called on the UI thread since you are executing it from an AsyncTask. Since I cannot comment yet, here's another answer.
I was facing the same issue: I updated the data (which was held in a static context), but the listview did not update after calling notifyDataSetChanged() on the adapter. After dragging/scrolling, the UI is updated and the data automatically refreshed.
The issue is that only the original thread that created a view hierarchy can touch its views. I suppose you are running the Runnable from the callback Thus, you need to call it on the UI thread for the listview to update itself, in a fragment you would do something along the lines of:
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
// Change the data source here,
// eg. some ArrayList<ItemObject> in this case
someDataSource.clear();
someDataSource.add(new ItemObject());
// ...
// And notify the adapter of the changes to the data source
adapter.notifyDataSetChanged();
}
});
If you run adapter.notifyDataSetChanged() outside the UI thread, you will usually also run into a CalledFromWrongThreadException, some try catch block might have masked that.
Same as #ac19 's answer, problem was sloved by adding handler-message.
I use custom adapter and typical ListView, will update data if I get bluetooth callback. When I called "Adapter.notifyDataSetChanged()" in callback function, List didn't updated until I touched screen.
I defiend a message and add following code in callback function (replaced Adapter.notifyDataSetChanged())
Message m = new Message();
m.what = MessageYouDefined;
mHandler.sendMessage(m);
And added handler in onCreate
mHandler=new Handler(){
public void handleMessage(Message msg)
{
switch (msg.what){
case UpdateChargerList:
chargerAdapter.notifyDataSetChanged();
break;
}
super.handleMessage(msg);
}
};
Related
I have a Runnable class that calculates model values for my composite list views, in that runnable there's a UI thread inside of a custom thread. There I have adapter's notifyDataSetChanged() call, but after notifyDataSetChanged() I try updating some TextView value in the main layout. The problem is when running TextView gets updated first and only then ListViews and getting updated. That means notifyDataSetChanged() of the Adapter custom class gets updated last which is not suitable for me. Is there any possibility to synchronize those screen updates?
Here's the sample code:
public class TestRunnable implements Runnable {
private TestAdapter adapter;
#Override
public void run() {
Context.runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
MainActivity.setTextViewValue("Something...");
}
});
}
}
public class TestAdapter extends ArrayAdapter<TestModel> {
#Override
public View getView(final int position, View view, ViewGroup parent) {
View row = view;
TestHolder holder;
Boolean rowIsNew = false;
if (row == null) {
rowIsNew = true;
LayoutInflater layoutInflater = ((Activity) context)
.getLayoutInflater();
row = layoutInflater.inflate(layoutResourceId, parent, false);
holder = new TestHolder();
...
row.setTag(holder);
} else {
holder = (TestHolder) row.getTag();
}
TestModel testModel = data.get(position);
holder.property = testModel.property;
...
if (rowIsNew) {
holder.....setTypeface(...);
holder.....setTypeface(...);
}
return row;
}
}
I have revised the source code of ArrayAdapter and I see no way of executing code after it has called the onChanged() on it's observer so my answer would be:
Implement your own even on onChanged() being called
Call ListView.setAdapter with a brand new adapter with the new dataset
P.S. Number 1 is the optimum solution but number 2 is the easy solution, depending on your time and performance requirement use what you need, but I recommend taking some time and implementing Number 1.
I presume your MainActivity.setTextViewValue("Something..."); line is trying to print some data from the adapter and you're getting the old value, is that so?
I'm not 100% sure of this, perhaps someone else can help confirm this, but I think notifyDataSetChanged() only marks the current data on the adapter as dirty, so the adapter will know that it has to refresh the data, but it doesn't do it immediately when you do the call.
EDIT:
If my first paragraph is correct, you should try to update the text view with data from the data source instead of the adapter, this would be a nicer way to solve the problem.
I trying to create custom Listadapter using BaseAdapter , in this adapter the images are URLs and the adapter must download every thing then update the view ..
every thing done and work ok but my problem is the I cant move the list view when the images still loading I should wait to finish load every thing then I can move ..
I think the problem happen because the list view return to top index every update because I update on every image done download but I'm not sure because the list freeze so I can't move it to confirm that, or there is some thing else ..
I update listadapter using :
adap.notifyDataSetChanged();
this is all details and I hope any body know how can I fix this problem help me and tell me what is the right way to update the view without problems (return to first index,freeze) ..
Note : no problem with download and I can move the list when images downloading the problem only when loading and update .
Solve :
I checked my code the download function ok and the freeze from update Adapter :
adp.notifyDataSetChanged();
it's take a few seconds to update so I Change the update way :
int index = lst.getFirstVisiblePosition();
View v = lst.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
// ...
adap.notifyDataSetInvalidated();
// restore
lst.setSelectionFromTop(index, top);
I use this library for async downloading images.
You can examine my code and adapt it for your project.
class WallAdapter extends BaseAdapter {
WallInfo wallInfo;
Activity activity;
LayoutInflater inflater=null;
public WallAdapter(Activity a, WallInfo wallInfo) {
this.activity = a;
this.wallInfo = wallInfo;
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return wallInfo.getCount();
}
#Override
public Object getItem(int position) {
return wallInfo.getItem(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
if(convertView == null)
vi = inflater.inflate(R.layout.wall_item, null);
ImageView imagePost = (ImageView) vi.findViewById(R.id.imagePost);
WallMessage post = wallInfo.getItem(position);
if(post.attachments != null && post.attachments.size() != 0) {
UrlImageViewHelper.setUrlDrawable(imagePost, post.attachments.get(0).photo.src_big);
} else {
imagePost.setImageBitmap(null);
}
return vi;
}
}
try using google volley library it will help you to download faster.
This seems simple but for the life of me I can't figure this out.
I have an ExpandableListView with a customized ExpandableListAdapter descended from BaseExpandableListAdapter. When I add a new item I simply want to focus the new item. I can't get that to work.
I tried the following, which does not work. This is in the activity with the listview:
private void processNewItem(C360ItemOwner itemOwner, int pos){
Item item = itemOwner.newItem(pos);
expAdapter.notifyDatasetChanged();
expandList.requestFocus();
if (item.getParentType() == Item.PARENT_IS_CHECKLIST) {//main level
expandList.setSelectedGroup(pos);
} else //Item.PARENT_IS_ITEM //sub level
expandList.setSelectedChild(item.getParentAsItem().getPosition(), pos, true);
}
I tried doing it in the adapter on the getView method, which does not work:
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
if (convertView == null)
convertView = inf.inflate(R.layout.edit_group_item, null);
Item group = (Item) getGroup(groupPosition);
convertView.setTag(group);
EditText edtParent = (EditText) convertView.findViewById(R.id.edtParent);
edtParent.setTag(convertView);
scatterItemText(edtParent);
if (group.isNew()) {
Toast.makeText(edtParent.getContext(), "Found Focus Item " + groupPosition, Toast.LENGTH_SHORT).show();
edtParent.requestFocus();
}
return convertView;
}
I tried setting up a couple of fields in the adapter: focusItem, a write only Item object and focusEdit, read only EditText obect. So I assign the item and in getView, it stores the edit in a field. I call it like this:
private void processNewItem(C360ItemOwner itemOwner, int pos){
Item item = itemOwner.newItem(pos);
expAdapter.setFocusItem(item);//<--Assign it here
expAdapter.notifyDatasetChanged();
EditText edtFocus = expAdapter.getFocusEdit();
if (edtFocus != null)
edtFocus.requestFocus();
}
Then in getView of the adapter:
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
if (convertView == null)
convertView = inf.inflate(R.layout.edit_group_item, null);
Item group = (Item) getGroup(groupPosition);
convertView.setTag(group);
EditText edtParent = (EditText) convertView.findViewById(R.id.edtParent);
edtParent.setTag(convertView);
scatterItemText(edtParent);
if (group == focusItem) {
Toast.makeText(edtParent.getContext(), "Found Focus Item " + groupPosition, Toast.LENGTH_SHORT).show();
focusEdit = edtParent;
}
return convertView;
}
I was very hopeful for this last method but this line:
EditText edtFocus = expAdapter.getFocusEdit();
Occurs BEFORE the getView() method runs which would assign it (so it's always null)! :-( plz halp
EDIT: I know this would be sloppy but what if I did ran a thread to wait a few hundred milliseconds so that getView() might run and then try to get focusEdit?
OK, figured it out with the help of Foamy Guy. My hunch about waiting a bit to check for the focusEdit worked out. I initially tried with 1000 milliseconds to give it plenty of time:
private void setFocus() {
EditText edtFocus = expAdapter.getFocusEdit();
if (edtFocus != null)
edtFocus.requestFocus();
else
Toast.makeText(getActivity(), "BLAH!", Toast.LENGTH_SHORT).show();
}
final Runnable r = new Runnable()
{
public void run()
{
setFocus();
}
};
private void processNewItem(C360ItemOwner itemOwner, int pos){
Item item = itemOwner.newItem(pos);
if (itemOwner.getItemCount() == 1)
checkHasItems();
expAdapter.setFocusItem(item);
resetListAdapter(true);
Handler handler = new Handler();
handler.postDelayed(r, 1000);//this works
//handler.postDelayed(r, 10);//this works too
//handler.postDelayed(r, 1);//this works too
//handler.post(r);//this too!
//r.run();//NOT this. This fails.
}
Then I tried 10 milliseconds and it still worked. Then just one millisecond and amazingly that worked too. Foamy suggested to try to just post without the delay and indeed that works. I finally tried to just run the runnable directly per Foamy's request but that failed.
So not sure, but this gets the job done. Thanks for the help guys.
Tried using the following:
Populate Listview from JSON
To make a listview which uses a JsonArray containing Json Objects. For some reason, the
'public View getView(int position, View convertView, ViewGroup parent)'
code is fired more times than there are contents in the jsonarray.
I made a control test to check up on this and I found that even with just 1 Jsonobject within the jsonarray, I came up with 32 times the getView code was activated.
I am rather confused as to why this is happening, as my friends have managed to make similar codes to mine, but without the huge number of activations I am suffering from. Am I being rather slow, and this is because the individual Jsonobject has, not only the image and text in them, but about 15 other items within it? Or is ther another cause?
I would appreciate any aid towards this, I am posting the adapter code below:
public class ArticleAdapter extends BaseAdapter{
private JSONArray items;
private Context cont;
public ArticleAdapter(Context context, JSONArray array)
{
super();
this.items = array;
this.cont = context;
}
#Override
public int getCount() {
return items.length();
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
WebIView sath;
TextView sati;
Log.i("Seiji", "Checking! " + position);
try
{
if(!items.isNull(position))
{
JSONObject item = items.getJSONObject(position);
if (v == null) {
v = LayoutInflater.from(cont).inflate(R.layout.saved_articles_listitem, null);
}
sath = (WebIView) v.findViewById(R.id.sathumbnail);
sati = (TextView) v.findViewById(R.id.satitle);
if(item.has("image") && sath != null)
{
JSONObject thisImage = item.getJSONObject("image");
sath.reset();
sath.setImageUrl(thisImage.getString("thumbnail"));
sath.loadImage();
}
if(sati != null)
{
sati.setText(item.getString("title"));
}
}else{
return null;
}
}
catch(Exception e)
{
Log.e("num", "Saved Art Error! " + e.toString());
}
return v;
}
}
the code which activates this class is the following:
ListView savedArtList = (ListView) sav.findViewById(R.id.savelist);
ArticleAdapter savedadapter = new ArticleAdapter(cont, flip);
ArtList.setAdapter(savedadapter);
EDIT:
Thanks to some very helpful advice I was able to figure out what was going wrong. The Listview was resizing itself every time a new row was added because I had set the views height to be 'wrap_content'. I hadnt realised that this would cause problems, but once I had set it to 'fill_parent' (or a set value in other cases), the issue disappeared and I didnt have this problem any more.
Thank you againfor the helpful advice!
getView will be called many times - per visible cell when the list view is being laid out, per visible cell when the list view is being drawn + more. This is normal behaviour and getView should be efficient. Its possible your images and/or text are making the height of each cell change as they're loaded in, meaning other cells may become visible / go off screen etc.
I have a ListFragment where I want certain rows to be a certain color. I basically followed this: Creating a ListView and setting the background color of a view in each row
However, getView is never called. Does anyone know why?
public class TrackerFragment extends ListFragment
{
private String[] list;
#Override
public void onActivityCreated(Bundle myBundle)
{
super.onActivityCreated(myBundle);
list = null;
ListView lv = getListView();
setListAdapter(null);
setEmptyText("Touch a connection to view tracker information.");
lv.setTextFilterEnabled(true);
}
public void updateList(String[] list)
{
this.list = list;
setListAdapter(new ColoredArrayAdapter(getActivity(),R.layout.list_item,list));
}
}
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="7dp"
android:textSize="14sp"
android:id="#+id/line">
</TextView>
I am updating the list like this from my activity:
TrackerFragment tf = (TrackerFragment) fragmentManager.findFragmentById(R.id.tracker1);
tf.updateList(result);
My ColoredArrayAdapter
public class ColoredArrayAdapter extends ArrayAdapter{
private String[] list;
public ColoredArrayAdapter(Context context, int textViewResourceId,
Object[] objects) {
super(context, textViewResourceId, objects);
list = new String[objects.length];
for (int i = 0; i < list.length; i++)
list[i] = (String) objects[i];
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View vi = convertView;
ViewHolder holder;
if (convertView == null)
{
vi = LayoutInflater.from(getContext()).inflate(R.layout.list_item, null);
holder = new ViewHolder();
holder.line = (TextView) vi.findViewById(R.id.line);
vi.setTag(holder);
}
else
holder = (ViewHolder) vi.getTag();
for (int i = 0; i < list.length; i++)
{
if (list[i].contains("OUT OF LOCK"))
{
System.out.println("OUT OF LOCK");
holder.line.setText(list[i]);
//holder.line.setTextColor(R.color.white);
holder.line.setBackgroundResource(R.color.red);
}
else if(list[i].contains("IN LOCK"))
{
System.out.println("In LOCK");
holder.line.setText(list[i]);
//holder.line.setTextColor(R.color.white);
holder.line.setBackgroundResource(R.color.green);
}
else
holder.line.setText(list[i]);
}
return vi;
}
}
What am I doing wrong?
Edit: added list_item.xml, where line is found.
Edit2: added extended array adapter
Now my problem is that, every row is either all green or red, when I just want certain individual rows to be either red or green. Also, none of the text is showing up.
Your current getView implementation should be moved into a ListAdapter implementation instead of your TrackerFragment class. Since you're using ArrayAdapter, you can subclass that and put the code in there. ArrayAdapter already implements getView, but you'll override it to provide your specialized behavior.
The reason you're getting a NullPointerException is because you're calling getView and passing in the list view, which does not have a tag associated with it -- so holder = (ViewHolder) vi.getTag(); assigns null to holder. That said, you shouldn't be calling getView directly. The system will call that for you whenever it needs to display a list item. When the system calls the getView method, it initially passes in null to have the views created, and every call where convertView is not null is a view created by that method.
Looks like the same problem as the post you linked: the getView() method isn't nested inside the class.
Or your code doesn't show anything that would call it either.
The more I look over this, the more I wonder about the basic premise you are using. I think you're making it overly complicated. I would do it like this:
public View getView(View convertView)
{
View vi = convertView;
TextView viText = null;
if (vi == null)
vi = LayoutInflater.from(getActivity()).inflate(R.layout.list_item, null);
viText = (TextView)vi.findViewById(R.id.line);
if (viText == null) return vi;
String viString = viText.getText().toString();
if (viString.contains("OUT OF LOCK"))
{
viText.setBackgroundResource(R.color.red);
}
else if (viString.contains("IN LOCK"))
{
viText.setBackgroundResource(R.color.green);
}
return vi;
}
I don't think you are using the holder in the way you think... the loop you have in there will just loop through setting the background resource to whatever the last trigger to set the backgroundResource is, no matter what.
If I have missed the point in this, let me know. But, my basic thought would be to remove as much complexity as you can until it works, and if you've had to remove something important, slowly add it back in.