i Am creating an app where one of my activities will be making use of fragments. In each fragment view, I am going to be pulling from a data base using three files, three adapters in each view. The first fragment works as expected. However, when I add my json parser code to the second swipe view, I see it tries to fire as soon as the activity is loaded ( i make the second swipe view toast) and The json parser will not load any data into my second view. Should I attach the JSON to the main activity and switch the adapters and URL's as i switch views or should i try to load the JSON from every fragment (which is what I am doing now). Any insight would be greatly appreciated thank you
public class chatfragment extends Fragment {
ListView l;
List<chatitem> listItem;
ChatListAdapter adapter;
String category = "430";
chatitem item;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.chatfragment, container, false);
l = (ListView) rootView.findViewById(R.id.chatlist);
listItem = new ArrayList<chatitem>();
adapter = new ChatListAdapter(getActivity(),R.layout.chatfragment, listItem);
l.setAdapter(adapter);
new ReadWeatherJSONFeedTask().execute("myurl");
return rootView;
}
public String readJSONFeed (String URL){
StringBuilder stringBuilder = new StringBuilder();
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(URL);
try{
HttpResponse response = httpClient.execute(httpGet);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if(statusCode == 200){
HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent();
BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) !=null){
stringBuilder.append(line);
}
inputStream.close();
}else{
Log.d("readJSONFeed", "Failed to download file");
}
}catch (Exception e){
Log.d("readJSONFeed", e.getLocalizedMessage());
}
return stringBuilder.toString();
}
private class ReadWeatherJSONFeedTask extends AsyncTask<String, Void, String>{
protected String doInBackground(String...urls){
return readJSONFeed(urls[0]);
}
protected void onPostExecute(String result){
try{
JSONObject jsonObject = new JSONObject(result);
JSONArray subcat = new
JSONArray(jsonObject.getString("results"));
for(int i = 0; i < subcat.length(); i++){
JSONObject p = subcat.getJSONObject(i);
item = new chatitem(p.getString("hubusername"),p.getString("picture"),p.getString("timeadjust"),p.getString("comment"));
listItem.add(item);
// Toast.makeText(getApplicationContext(), p.getString("subcategory"), Toast.LENGTH_LONG).show();
adapter.notifyDataSetChanged();
}
}catch (Exception e){
Log.d("ReadWeatherJSONFeedTask", e.getLocalizedMessage());
}
// Toast.makeText(getApplicationContext(), "done", Toast.LENGTH_LONG).show();
};
}
}
Well you can do a lot of things. I'd recommend preparing the data (download from Web etc.) in the activity and then loading it into the fragments.
ViewPager prepares not only the current fragment but also the next fragment in the list. If you have an issue with this behaviour you'll have to use the pageChangeListener and updating the fragments when they are active.
Related
I am new to Android development. In my project I'm trying to get data from a site and show that data in a listview.
I am getting String data with httpGet and I'm parsing it with json and then put the data in adapter to show. However, it doesn't look smooth and it's working very slowly.
I should get 10 initial elements, and then when scrolling down get new 10 elements (but very smoothly).
videoList = new ArrayList<Videos>();
new JSONAsyncTask().execute(
"http://speechyard.com/api/content/?type=videolist&page=1&perPage=10");
ListView listview = (ListView)getView().findViewById(R.id.list);
VideoAdapter(Context context, int resource, ArrayList<Videos> objects)
adapter = new VideoAdapter(getActivity().getApplicationContext(), R.layout.content_row, videoList);
listview.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
} #Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
final int lastItem = firstVisibleItem + visibleItemCount;
if(lastItem +2> pageNumber*10) {
pageNumber+=1;
Log.d(TAG,pageNumber.toString());
new JSONAsyncTask().execute("http://speechyard.com/api/content/?type=videolist&page="+pageNumber+"&perPage=10");
}}});
}
//*******get data from http
class JSONAsyncTask extends AsyncTask<String, Void, Boolean> {
ProgressDialog dialog;
//*****set progress dialog
#Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(getActivity());
dialog.setCancelable(false);
}
//*************HTTP Get save in String data
#Override
protected Boolean doInBackground(String... urls) {
try {//------------------>>
HttpGet httppost = new HttpGet(urls[0]);
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(httppost);
int status = response.getStatusLine().getStatusCode();
if (status == 200) {
HttpEntity entity = response.getEntity();
String data = EntityUtils.toString(entity);
//**********Optimization String data for JSON parsing
String newdata="{\"video\":"+data+"}";
JSONObject jsono = new JSONObject(newdata);
JSONArray jarray = jsono.getJSONArray("video");
for (int i = 0; i < jarray.length(); i++) {
JSONObject object = jarray.getJSONObject(i);
Videos video = new Videos();
video.setName(object.getString("name"));
video.setId(object.getString("id"));
video.setCategory(object.getString("category"));
video.setTime(object.getString("time"));
video.setSubtitles(object.getString("subtitles"));
video.setYoutube(object.getString("youtube"));
video.setImage(object.getString("image"));
videoList.add(video);
}
return true;
suggestion : Use HttpURLConnection !
1. create JSONParser.java
public class JSONParser extends Thread
{
static InputStream is = null;
static JSONObject jObj = null;
static String json = "";
public JSONObject getJSONFromUrl(String url) {
// Making HTTP request
try {
URL myUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) myUrl.openConnection();
connection.setConnectTimeout(2000);
connection.setReadTimeout(2000);
connection.connect();
is = connection.getInputStream();
} catch (Exception e) {
is = null;
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
is, "utf-8"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "n");
}
is.close();
json = sb.toString();
} catch (Exception e) {
json = "";
}
try {
jObj = new JSONObject(json);
} catch (JSONException e)
{
jObj = null;
}
return jObj;
}
}
Use like this.
private class JSONParse extends AsyncTask
{
#Override
protected void onPreExecute()
{
}
#Override
protected JSONObject doInBackground(String... args)
{
JSONParser jParser = new JSONParser();
JSONObject json = jParser.getJSONFromUrl(url);
return json;
}
#Override
protected void onPostExecute(JSONObject json)
{
//json.get......
}
I can see a lot of things you could be doing better.
The best way to load data onto a Fragment is using an ArrayAdapter (because you want to load a list) and a Loader.
Your App could get so much better if you use Google Volley as network provider and GSON or Jackson for JSON so you don't need to write this kind of code
Videos video = new Videos();
video.setName(object.getString("name"));
video.setId(object.getString("id"));
Fragments should be only used to display data, not to get it from network or have network code on the Fragment. To get data from the server you always need to be working with a Loader.
Basically you need to create a Loader class that extends from AsyncTaskLoader<ArrayList<Videos>> and your fragment needs to implement LoaderManager.LoaderCallbacks<ArrayList<Videos>> on the Loader public ArrayList<Videos> loadInBackground() you should be requesting the data from server and returning an ArrayList of Videos.
I am attempting to execute an async task in a fragment after converting my activities into fragments. When I call my async task from the activity I have to pass 'this' with it in order to allow the async task to change text and things after it receives the information. I am a bit confused on how to do this all with fragments. Here is what I got so far:
I execute the asynck task with:
new GetYourTopTasteBeers(this).execute(url);
the code for the async task is:
public class GetYourTopTasteBeers extends AsyncTask<String, Void, String> {
Context c;
private ProgressDialog Dialog;
public GetYourTopTasteBeers (Context context)
{
c = context;
Dialog = new ProgressDialog(c);
}
#Override
protected String doInBackground(String... arg0) {
// TODO Auto-generated method stub
return readJSONFeed(arg0[0]);
}
protected void onPreExecute() {
Dialog.setMessage("Getting beers");
Dialog.setTitle("Loading");
Dialog.setCancelable(false);
Dialog.show();
}
protected void onPostExecute(String result){
//decode json here
try{
JSONArray jsonArray = new JSONArray(result);
//acces listview
ListView lv = (ListView) ((Activity) c).findViewById(R.id.topTasteBeers);
//make array list for beer
final List<ShortBeerInfo> tasteList = new ArrayList<ShortBeerInfo>();
for(int i = 0; i < jsonArray.length(); i++) {
String beer = jsonArray.getJSONObject(i).getString("beer");
String rate = jsonArray.getJSONObject(i).getString("rate");
String beerID = jsonArray.getJSONObject(i).getString("id");
String breweryID = jsonArray.getJSONObject(i).getString("breweryID");
int count = i + 1;
beer = count + ". " + beer;
//create object
ShortBeerInfo tempTaste = new ShortBeerInfo(beer, rate, beerID , breweryID);
//add to arraylist
tasteList.add(tempTaste);
//add items to listview
ShortBeerInfoAdapter adapter1 = new ShortBeerInfoAdapter(c ,R.layout.brewer_stats_listview, tasteList);
lv.setAdapter(adapter1);
//set up clicks
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
ShortBeerInfo o=(ShortBeerInfo)arg0.getItemAtPosition(arg2);
String tempID = o.id;
String tempBrewID = o.brewery;
Toast toast = Toast.makeText(c, tempID, Toast.LENGTH_SHORT);
toast.show();
//todo: change fragment to beer page
Intent myIntent = new Intent(c, BeerPage2.class);
myIntent.putExtra("id", tempID);
myIntent.putExtra("breweryID", tempBrewID);
c.startActivity(myIntent);
}
});
}
}
catch(Exception e){
}
Dialog.dismiss();
}
public String readJSONFeed(String URL) {
StringBuilder stringBuilder = new StringBuilder();
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(URL);
try {
HttpResponse response = httpClient.execute(httpGet);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent();
BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
inputStream.close();
} else {
Log.d("JSON", "Failed to download file");
}
} catch (Exception e) {
Log.d("readJSONFeed", e.getLocalizedMessage());
}
return stringBuilder.toString();
}
}
My problem is that I can not pass 'this' from a fragment...
Bonus Question:
Also as you can see I am not done converting my code for the fragments. I need to change this to load a fragment instead of change activity:
//todo: change fragment to beer page
Intent myIntent = new Intent(c, BeerPage2.class);
myIntent.putExtra("id", tempID);
myIntent.putExtra("breweryID", tempBrewID);
c.startActivity(myIntent);
HOw can I pass values between fragments like I am doing between activities in that above code?
Since Fragments don't admit this, you may call getActivity() instead which will provide the context you need to execute the AsyncTask.
Be careful, though, as running an AsyncTask within a Fragment may lead to return a result to the caller Activity that has already been destroyed at the time your AsyncTask concluded its process. It's necessary to take additional precautions, and always check whether the Fragment hasn't already been destroyed. This can be done using this.isAdded() in your Fragment.
A good practice is to cancel your AsyncTask in the onStop() and onPause() methods. That will make the onPostExecute() not execute code if the Fragment is not active anymore (getActivity() would return null).
You cannot pass this because in this situation this referrers to your fragment, which doesn't extend Context. But you need Context (your GetYourTopTasteBeers takes Context as a parameter):
public GetYourTopTasteBeers (Context context)
Instead, pass your Activity like so:
new GetYourTopTasteBeers(getActivity()).execute(url);
hi friends i just want the data show in a list view i using async task and i complete get the data in json and filtering it by id and title now i show id and title in a listview can you help me thanks in advance
public class runActivity extends Activity implements OnClickListener {
String returnString="";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViewById(R.id.my_button).setOnClickListener(this);
}
#Override
public void onClick(View arg0) {
Button b = (Button)findViewById(R.id.my_button);
b.setClickable(false);
new LongRunningGetIO().execute();
}
private class LongRunningGetIO extends AsyncTask <Void, Void, String> {
protected String getASCIIContentFromEntity(HttpEntity entity) throws IllegalStateException, IOException {
InputStream in = entity.getContent();
StringBuffer out = new StringBuffer();
int n = 1;
while (n>0) {
byte[] b = new byte[4096];
n = in.read(b);
if (n>0) out.append(new String(b, 0, n));
}
return out.toString();
}
#Override
protected String doInBackground(Void... params) {
HttpClient httpClient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpGet = new HttpGet("http://192.168.1.156/recess/document/document.json");
HttpClient client = new DefaultHttpClient();
HttpResponse response=null;
try{
response = client.execute(httpGet);}
catch(Exception e){}
System.out.println(response.getStatusLine());
String text = null;
try {
response = httpClient.execute(httpGet, localContext);
HttpEntity entity = response.getEntity();
text = getASCIIContentFromEntity(entity);
} catch (Exception e) {
return e.getLocalizedMessage();
}
String var =text;
try{
JSONObject jObj = new JSONObject(var);
JSONArray jArray = jObj.getJSONArray("document");
for(int i=0;i<jArray.length();i++){
JSONObject json_data = jArray.getJSONObject(i);
Log.i("log_tag","id: "+json_data.getString("id")+
", title: "+json_data.getString("title")
);
returnString += "\n" +"id:"+ json_data.getString("id")+" "+"Title:"+ json_data.getString("title");
}
}
catch(JSONException e){
Log.e("log_tag", "Error parsing data "+e.toString());
}
return returnString;
}
protected void onPostExecute(String results) {
if (results!=null) {
ListView listView = (ListView) findViewById(R.id.mylist);
listView.setFilterText(results);
}
Button b = (Button)findViewById(R.id.my_button);
b.setClickable(true);
}
}
}
You will need to build an Array to use with ListAdapter.
Here is a guide from Google: http://developer.android.com/resources/tutorials/views/hello-listview.html
I think the best solution would be to create a Handler in your activity. You can then send a message to the handler and get the data and put it in the ListView.
In doInBackground "for" loop just either create the array of your data or put data in Array list of object (then need to write custom adapter)
for
1- option
http://www.java-samples.com/showtutorial.php?tutorialid=1516
http://www.ezzylearning.com/tutorial.aspx?tid=1659127&q=binding-android-listview-with-string-array-using-arrayadapter
For
2- option
http://www.ezzylearning.com/tutorial.aspx?tid=1763429&q=customizing-android-listview-items-with-custom-arrayadapter
I have read plenty of examples ,but if I wish to maintain my scroll position after a ListView is updated from JSON ,then can I do that without using an AsyncTask instance ???
the code for my list is
String wrd;
//ArrayList<HashMap<String,String>> mylist;
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent i2=getIntent();
wrd=i2.getStringExtra("entrd");
Log.v("keyis",wrd);
final Handler handler = new Handler();
Runnable runable = new Runnable() {
#Override
public void run() {
//call the function
LoadData();
//also call the same runnable
handler.postDelayed(this, 40000);
}
};
handler.postDelayed(runable, 10);
}public void LoadData(){
JSONObject j2=JSONfunctions.getJSONfromURL("/webservice_search.php?keyword="+wrd+"&format=json");
ArrayList<HashMap<String,String>> mylist = new ArrayList<HashMap<String,String>>();
try{JSONArray jray=j2.getJSONArray("listings");
for(int i=0;i<jray.length();i++){
Log.v("state","json data being read");
JSONObject j3= jray.getJSONObject(i);
String first=j3.getString("listing");
Log.v("sublist", first);
JSONObject j4=j3.getJSONObject("listing");
String sec=j4.getString("links");
int maxLength = (sec.length() < 30)?sec.length():27;
sec.substring(0, maxLength);
String cutsec=sec.substring(0,maxLength);
Log.v("links are",cutsec);
String img=j4.getString("image_name");
Log.v("image name is ",img);
//Uri dimg=Uri.parse("http://zeesms.info/android_app_images/Koala.jpg");
HashMap<String,String> map=new HashMap<String,String>();
map.put("Id",String.valueOf(i));
map.put(Li_nk,cutsec);
map.put(Image_name,j4.getString("image_name"));
map.put(KEY_THUMB_URL,"http://zeesms.info/android_app_images/"+img);
mylist.add(map);
}
}
catch(JSONException e){
Log.e("loG_tag","Error parsing"+e.toString());
}
LazyAdapter adapter = new LazyAdapter(this,mylist);
adapter.notifyDataSetChanged();
ListView list=(ListView)findViewById(R.id.lv1);
list.setEmptyView(findViewById(R.id.empty));
list.setAdapter(adapter);
list.setItemsCanFocus(false);
and my adapter is
public class LazyAdapter extends BaseAdapter {
private Activity activity;
private ArrayList<HashMap<String, String>> data;
private static LayoutInflater inflater=null;
public ImageLoader imageLoader;
public LazyAdapter(Activity a, ArrayList<HashMap<String, String>> d) {
activity = a;
data=d;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader=new ImageLoader(activity.getApplicationContext());
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return data.size();
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.custom_row_view1, null);
TextView title = (TextView)vi.findViewById(R.id.linkname); // merchnts name
TextView artist = (TextView)vi.findViewById(R.id.imagename); // address
//TextView duration = (TextView)vi.findViewById(R.id); // distance
ImageView thumb_image=(ImageView)vi.findViewById(R.id.mClogo); // logo
HashMap<String, String> jsn = new HashMap<String, String>();
jsn = data.get(position);
// Setting all values in listview
title.setText(jsn.get(Second.Li_nk));
artist.setText(jsn.get(Second.Image_name));
//duration.setText(song.get(CustomizedListView.KEY_DURATION));
imageLoader.DisplayImage(jsn.get(Second.KEY_THUMB_URL), thumb_image);
return vi;
}
and finally the class being used for json parsing is
public class JSONfunctions {
public static JSONObject getJSONfromURL(String url){
InputStream is = null;
String result = "";
JSONObject jArray = null;
String str1="http://zeesms.info"+url;
// ArrayList<NameValuePair> namevaluepairs = new ArrayList<NameValuePair>();
Log.v("url result",url);
//namevaluepairs.add(new BasicNameValuePair("location",str1));
//http post
try{
HttpClient httpclient= new DefaultHttpClient();
HttpGet request = new HttpGet();
request.setURI(new URI(str1));
HttpResponse response = httpclient.execute(request);
is = response.getEntity().getContent();
if(is==null){
Log.v("url result","is null");
}
else
{
Log.v("url result","is not null");
}
/* BufferedReader buf = new BufferedReader(new InputStreamReader(is,"UTF-8"));
StringBuilder sb = new StringBuilder();
String s;
while(true )
{
s = buf.readLine();
if(s==null || s.length()==0)
break;
sb.append(s);
}
buf.close();
is.close();
sb.toString(); */
// httppost.setEntity(new UrlEncodedFormEntity(namevaluepairs));
//HttpResponse response=httpclient.execute(httppost);
//HttpEntity entity=response.getEntity();
//is=entity.getContent();
/*
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
*/
}catch(Exception e){
Log.v("log_tag", "Error in http connection "+e.toString());
AlertDialog.Builder alert=new AlertDialog.Builder(null);
alert.setMessage("Invalid Keyword").setPositiveButton("Ok", new OnClickListener(){
#Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
}
});
}
//convert response to string
try{
Log.v("url result","getting result starts");
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
StringBuilder sb = new StringBuilder();
String line = null;
Log.v("url result","getting result");
while ((line = reader.readLine()) != null) {
Log.v("url result","getting result");
sb.append(line + "\n");
}
is.close();
result=sb.toString();
Log.v("url result",result);
}catch(Exception e){
Log.e("log_tag", "Error converting result "+e.toString());
}
try{
jArray = new JSONObject(result);
}catch(JSONException e){
Log.e("log_tag", "Error parsing data "+e.toString());
}
return jArray;
}
}
along with this if the data is updated from the webpage, what would be the simplest way to show the updated item on top ??
It is easier to maintain scroll position by calling notifydatasetchanged() only. The problem there is that you are creating a new adapter every time the data gets updated... you should do something like this:
if(listView.getAdapter()==null)
listView.setAdapter(myAdapter);
else{
myAdapter.updateData(myNewData); //update adapter's data
myAdapter.notifyDataSetChanged(); //notifies any View reflecting data to refresh
}
This way, your listview will mantain the scrolling position.
In case you want to scroll to a new position, use:
list.smoothScrollToPosition(int position);
In case for some reason you don't want to call notifyDataSetChanged(), the you can maintain the position by using setSelectionFromTop()
Before updating the adaptor:
lastViewedPosition = listView.getFirstVisiblePosition();
//get offset of first visible view
View v = listView.getChildAt(0);
topOffset = (v == null) ? 0 : v.getTop();
After updating the adaptor:
listView.setSelectionFromTop(lastViewedPosition, topOffset);
list.smoothScrollToPosition(int position); //my favorite :)
It may also help you to scroll nice'n'smooth to a particular item
listview.setSelection( i );
this will help you to set particular row at top
For overall picture:
In your API response callback, call this function(example) below:
MyAdapter mAdapter;
ArrayList<Users> mUsers;
private void updateListView(ArrayList<Users> users) {
mUsers.addAll(users);
if(mAdapter == null) {
mAdapter = new MyAdapter(getContext(), mUsers);
mListView.setAdapter(mAdapter);
}
mAdapter.notifyDataSetChanged(); // Add this one
}
If you're using an ArrayAdapter (or a subclass of it), the problem may be caused by that the adapter updates the list when you clean it before adding the new items:
adapter.clear();
adapter.addAll(...);
You can fix it by wrapping the code that modifies the adapter like this:
adapter.setNotifyOnChange(false); // Disable calling notifyDatasetChanged() on modification
adapter.clear();
adapter.addAll(...); // Notify the adapter about that data has changed. Note: it will re-enable notifyOnChange
adapter.notifyDataSetChanged();
I'm trying to add an animation to my ListView so text will fade into separate rows as results come in from an HTTP GET request. I know how to do the fade in effect and i already have a custom ListView adapter but the problem is that the ListView updates all the rows each time a result comes in, thus triggering the fade in effect each time for the entire list.
How would I be able to control a single row so the ListView won't animate every row on each data change?
This is the code i use to fill the ListView:
private class CustomAdapter extends ArrayAdapter<Row> {
public CustomAdapter() {
super(Results.this, R.layout.row, dataList);
}
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
RowHolder holder = null;
if (row == null) {
LayoutInflater inflater = getLayoutInflater();
row = inflater.inflate(R.layout.row, parent, false);
holder = new RowHolder(row);
row.setTag(holder);
} else {
holder = (RowHolder) row.getTag();
}
try {
holder.populateRow(dataList.get(position));
} catch (NullPointerException e) {
e.printStackTrace();
}
return row;
}
}
private class RowHolder {
private TextView label = null;
private TextView count = null;
private TextView result = null;
public RowHolder(View row) {
label = (TextView) row.findViewById(R.id.list_label);
count = (TextView) row.findViewById(R.id.list_count);
result = (TextView) row.findViewById(R.id.list_result);
}
public void populateRow(Row r) {
label.setText(r.getLabel());
count.setText(r.getCount());
result.setText(r.getResult());
label.startAnimation(fadeIn);
count.startAnimation(fadeIn);
result.startAnimation(fadeIn);
}
}
Any help is appreciated, thank you in advance!
Edit 1:
My AsyncTask:
private class CheckSource extends AsyncTask<String, Void, String> {
protected void onPreExecute() {
results.setUnixTime(getUnixTime());
results.setLabel(getString(R.string.label));
results.setCount(null);
results.setResult(null);
results.setResultLabel("");
results.setShowProgress(true);
results.setIconType(null);
results.setShowIcon(false);
results.setHasResults(false);
adapter.notifyDataSetChanged();
}
#Override
protected String doInBackground(String... params) {
String query = params[0];
String httpResults = null;
try {
httpResults = getResults(query, "source");
jsonObject = new JSONObject(httpResults);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
return httpResults;
}
protected void onPostExecute(String results) {
try {
parseJSON(results);
} catch (JSONException e) {
e.printStackTrace();
results.setResultLabel("<br />"
+ getString(R.string.source_not_available) + "<br />");
}
results.setShowProgress(false);
adapter.notifyDataSetChanged();
}
// Parse the retrieved json results
private void parseJSON(String jsonResults) throws JSONException {
if (jsonResults == null) {
results.setResult(null);
results.setHasResults(false);
results.setResultLabel("<br />"
+ getString(R.string.source_not_available) + "<br />");
return;
}
jsonObject = new JSONObject(jsonResults);
String result = null;
String resultLabel = null;
switch (jsonObject.getInt("count")) {
case -1:
results.setCount(null);
results.setHasResults(false);
resultLabel = getString(R.string.no_results);
break;
case 0:
results.setCount(null);
results.setHasResults(false);
resultLabel = getString(R.string.no_results);
break;
case 1:
results.setHasResults(true);
results.setCount(jsonObject.get("count").toString() + " "
+ getString(R.string.one_result));
result = jsonObject.get("url").toString();
resultLabel = getString(R.string.hyperlink_text);
break;
default:
results.setHasResults(true);
results.setCount(jsonObject.get("count").toString() + " "
+ getString(R.string.multiple_results));
result = jsonObject.get("url").toString();
resultLabel = getString(R.string.hyperlink_text);
break;
}
results.setResult(result);
results.setResultLabel("<br />" + resultLabel + "<br />");
}
}
The method that executes the HTTP request:
private String getResults(String query, String source)
throws IllegalStateException, IOException, URISyntaxException {
/* Method variables */
StringBuilder builder = new StringBuilder();
String URL = "url";
URI uri;
String phrase = "phrase";
List<NameValuePair> params = new ArrayList<NameValuePair>();
/* HTTP variables */
HttpGet httpGet;
DefaultHttpClient httpClient;
HttpResponse httpResponse;
HttpEntity httpEntity;
HttpParams httpParams;
int socketTimeout = 10000;
int connectionTimeout = 10000;
// Set the socket and connection timeout values
httpParams = new BasicHttpParams();
HttpConnectionParams
.setConnectionTimeout(httpParams, connectionTimeout);
HttpConnectionParams.setSoTimeout(httpParams, socketTimeout);
httpClient = new DefaultHttpClient(httpParams);
// Add parameters to the GET request
params.add(new BasicNameValuePair("query", query));
params.add(new BasicNameValuePair("type", source));
String paramString = URLEncodedUtils.format(params, "utf-8");
uri = new URI(URL + paramString);
httpGet = new HttpGet(uri);
// Execute the GET request
httpResponse = httpClient.execute(httpGet);
/* Read http response if http status = 200 */
if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
httpEntity = httpResponse.getEntity();
InputStream content = httpEntity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(
content));
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
}
return builder.toString();
}
As Romain Guy explained a while back during the Google I/O session, the most efficient way to only update one view in a list view is something like the following (this one update the whole view data):
ListView list = getListView();
int start = list.getFirstVisiblePosition();
for(int i=start, j=list.getLastVisiblePosition();i<=j;i++)
if(target==list.getItemAtPosition(i)){
View view = list.getChildAt(i-start);
list.getAdapter().getView(i, view, list);
break;
}
Assuming target is one item of the adapter.
This code retrieve the ListView, then browse the currently shown views, compare the target item you are looking for with each displayed view items, and if your target is among those, get the enclosing view and execute the adapter getView on that view to refresh the display.
As a side note invalidate doesn't work like some people expect and will not refresh the view like getView does, notifyDataSetChanged will rebuild the whole list and end up calling getview for every displayed items and invalidateViews will also affect a bunch.
One last thing, one can also get extra performance if he only needs to change a child of a row view and not the whole row like getView does. In that case, the following code can replace list.getAdapter().getView(i, view, list); (example to change a TextView text):
((TextView)view.findViewById(R.id.myid)).setText("some new text");
In code we trust.
Method notifyDataSetChanged force to call getView method to all visible elements of the ListView. If you want update only 1 specific item of the ListView you need to path this item to the AsynhTask.