acvon
acvon

Reputation: 171

Listview in android did not refresh the view until dragged

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()

Upvotes: 9

Views: 11405

Answers (9)

Ray Chung
Ray Chung

Reputation: 31

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);
    }
};

Upvotes: 0

Yashar PourMohammad
Yashar PourMohammad

Reputation: 570

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.

Upvotes: 2

Lupinity Labs
Lupinity Labs

Reputation: 2464

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.

Upvotes: 0

cass
cass

Reputation: 1356

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
            }
        }
    }

Upvotes: 0

ac19
ac19

Reputation: 11

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 ...

Upvotes: 0

Aparna Suresh
Aparna Suresh

Reputation: 415

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.

Upvotes: 6

raj
raj

Reputation: 642

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.

Upvotes: 10

A. Abiri
A. Abiri

Reputation: 10810

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;
}

Upvotes: 2

Nguyen  Minh Binh
Nguyen Minh Binh

Reputation: 24423

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.

Upvotes: 0

Related Questions