I have a custom adapter which extends from BaseAdapter..
Custom adapter code :
public class SearchListViewAdapter extends BaseAdapter {
private LayoutInflater layoutInflater;
private JsonArray searchResults;
public SearchListViewAdapter(Context context, JsonArray searchResults) {
this.searchResults = searchResults;
layoutInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return searchResults.count();
}
#Override
public Object getItem(int position) {
return searchResults.get(position);
}
/*public ListAdapter updateResults(JsonArray results) {
searchResults = results;
notifyDataSetChanged();
return null;
}*/
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
JsonObject searchResult = (JsonObject)getItem (position);
ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.custom_search_result, null);
holder = new ViewHolder();
holder.txtFullName = (TextView) convertView.findViewById(R.id.FullName);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.txtFullName.setText(searchResult.getString ("FirstName") + searchResult.getString ("LastName"));
return convertView;
}
static class ViewHolder {
TextView txtFullName;
}
}
Activity code :
// After displaying the list in an onPostExecute Method of an AsyncTask class
// I call another async task : BarcodeAction by giving the param : records
new BarcodeAction(records).execute("");
private class BarcodeAction extends AsyncTask<String, Void, JsonArray> {
private JsonArray records;
public BarcodeAction(JsonArray result)
{
this.records = result;
}
#Override
protected JsonArray doInBackground(String... params) {
// Processing... if it's success the onPostExecute method receive : records
if (resultType.equals("success"))
return records;
return null;
}
#Override
protected void onPostExecute(final JsonArray records) {
final ListView lv1 = (ListView) findViewById(R.id.ListViewSearchResults);
// EDIT : notifyDataSetChange doesn't work
SearchListViewAdapter svla1 = new SearchListViewAdapter(SearchActivity.this, records);
lv1.setAdapter(svla1);
svla1.notifyDataSetChanged();
lv1.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
Object o = lv1.getItemAtPosition(position);
JsonObject response = (JsonObject)o;
SearchActivity.VISITOR_BARCODE = response.getString("Barcode");
new BarcodeAction(records).execute("");
}
});
}
}
But my list is not getting refreshed..
Do you have any idea about this ? Thnak you.
getItemId() should return unique value for each entry in the list. Try returning position only.
I think the main cause is missing calling notifyDataSetChanged() method.
In fact, your "activity code" is not exactly the activity but a asnyctask that fetch the data. A more common way to use adaper is having a Adaper in your activty along with ListView. In your fetching-data-method(asynctask or loader), call the adaper's change underlying data interface to change the data, and call the adaper's notifyDataSetChanged() method.
A bit psudeo-code may looks like:
Adaper:
public class SearchListViewAdapter extends BaseAdapter {
private JsonArray searchResults;
......
public setDataSet(JsonArray newData) {
searchREsults = newData;
}
......
}
Activity:
public class MyActivity extends Activity {
ListView mResultListView;
SearchListViewAdaper mResultViewAdaper;
#override
OnCreate(...) {
......
//init mResultListView
mResultListView = (ListView) findViewById(R.id.xxxx);
mResultViewAdaper = new SearchListViewAdapter();
mResultListView.setAdapter(mResultViewAdaper);
......
}
......
}
AsyncTask:
public fetchDataTask extends AsyncTask {
......
onPostExecute(JsonArray records) {
mResultViewAdaper.setDataSet(records);
// IMPORTANT: notify data change
mResultViewAdaper.notifyDataSetChanged();
}
Related
I'm making a list of links and for that I have made a custom adapter, but the list is not ready when the adapter starts so I get the following error:
java.lang.RuntimeException: Unable to start activity ComponentInfo{}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference
this is because when the adapter is started the list is empty, and just moments after the list is filled but it's too late here is my code:
UPDATE: the code has been changed so now I du not get the error but it doesn't run getView in the adapter:
public class Controller extends Activity {
private String TAG = Controller.class.getSimpleName();
private String http;
CustomAdapter adapter;
public Controller con = null;
private ListView lv;
private static String url;
ArrayList<Selfservice> linkList = new ArrayList<Selfservice>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_view);
con = this;
http = this.getString(R.string.http);
url = this.getString(R.string.path1);
new GetLinks().execute();
lv = (ListView)findViewById(R.id.list);
//Resources res = getResources();
//adapter = new CustomAdapter(con, linkList, res);
//lv.setAdapter(adapter);
}
private class GetLinks extends AsyncTask<Void, Void, List<Selfservice>> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected List<Selfservice> doInBackground(Void... arg0) {
Document doc;
Elements links;
List<Selfservice> returnList = null;
try {
doc = Jsoup.connect(url).timeout(0).get();
links = doc.getElementsByClass("processlink");
returnList = ParseHTML(links);
} catch (IOException e) {
e.printStackTrace();
}
return returnList;
}
#Override
protected void onPostExecute(final List<Selfservice> result) {
super.onPostExecute(result);
runOnUiThread(new Runnable() {
#Override
public void run() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
//setSupportActionBar(toolbar);
//getSupportActionBar().setDisplayShowTitleEnabled(false);
toolbar.setTitle("");
toolbar.setSubtitle("");
Resources res = getResources();
Log.e(TAG, linkList.toString());
linkList = (ArrayList<Selfservice>) result;
adapter = new CustomAdapter(con, result, res);
adapter.notifyDataSetChanged();
lv.setAdapter(adapter);
}
});
}
}
and my adapter:
public class CustomAdapter extends BaseAdapter implements OnClickListener {
private String TAG = CustomAdapter.class.getSimpleName();
Context context;
List<Selfservice> data;
private Activity activity;
public Resources res;
Selfservice self = null;
private static LayoutInflater inflater;
int layoutResourceId = 0;
public CustomAdapter(Activity act, List<Selfservice> dataList, Resources resources) {
res = resources;
activity = act;
data = dataList;
}
private class Holder {
TextView title;
TextView link;
}
#Override
public int getCount() {
return data.size();
}
#Override
public Object getItem(int pos) {
return pos;
}
#Override
public long getItemId(int pos) {
return pos;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = convertView;
Holder holder;
if(rowView == null){
rowView = inflater.inflate(R.layout.list_item, null);
holder = new Holder();
holder.title = (TextView) rowView.findViewById(R.id.title);
holder.link = (TextView) rowView.findViewById(R.id.link);
rowView.setTag(holder);
}else{
holder = (Holder)rowView.getTag();
}
if(data.size()<=0){
holder.title.setText("did not work");
}else{
self = null;
self = (Selfservice) data.get(position);
holder.title.setText(self.getTitle());
holder.link.setText(self.getLink());
Log.i(TAG, "adapter");
rowView.setOnClickListener(new OnItemClickListener(position));
}
return rowView;
}
#Override
public void onClick(View v){
Log.v("CustomAdapter", "row clicked");
}
private class OnItemClickListener implements OnClickListener{
private int mPos;
OnItemClickListener(int position){
mPos = position;
}
#Override
public void onClick(View arg0){
Controller con = (Controller)activity;
con.onItemClick(mPos);
}
}
}
So How do I get the adapter to wait to the list is full?
firstly use ArrayAdapter<Selfservice> instead of BaseAdapter
use constructor
public CustomAdapter(Context context, int resource, List<Selfservice> objects) {
super(context, resource, objects);
data = objects;
}
then override only two methods
public int getCount()
public View getView(int position, View convertView, ViewGroup parent)
then return list.size() in getCount()
in getView() method
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.list_item, null);
}
in doInBackground() method in the try block
instead of linkList = ParseHTML(links);
do linkList.addAll(ParseHTML(links));
and in onPostExcecute() method
adapter.notifyDatasetChanged(); in the ui thread
You can create the adapter in the onPostExecute;
Change the Async Task to this:
private class GetLinks extends AsyncTask<Void, Void, List<Selfservice>> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... arg0) {
Document doc;
Elements links;
List<Selfservice> returnList
try {
doc = Jsoup.connect(url).timeout(10000).get();
links = doc.getElementsByClass("processlink");
returnList = ParseHTML(links);
} catch (IOException e) {
e.printStackTrace();
}
return returnList;
}
#Override
protected void onPostExecute(List<Selfservice> result) {
super.onPostExecute(result);
runOnUiThread(new Runnable() {
#Override
public void run() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
//setSupportActionBar(toolbar);
//getSupportActionBar().setDisplayShowTitleEnabled(false);
toolbar.setTitle("");
toolbar.setSubtitle("");
linkList = result
adapter = new CustomAdapter(con, result, res);
lv.setAdapter(adapter);
}
});
}
This way you will only create the adapter when your list is ready.
Edit:
You are not creating the variable context inside your adapter. Change your constructor to this:
public CustomAdapter(Context context, Activity act, List<Selfservice> dataList, Resources resources) {
res = resources;
activity = act;
data = dataList;
this.context = context;
}
And you will stop seeing the NullPointerExecption
How can I pass the current Hashmap key position to my inner class?
I need to pass data.get(position) to my inner class Click. Normally I would declare it final, but I'm not seeing how this is done in this case?
public class UpcomingGridViewAdapter extends BaseAdapter {
public boolean pressedMovieItem;
Context context;
ArrayList<HashMap<String, String>> data;
HashMap<String, String> mylist = new HashMap<>();
public UpcomingGridViewAdapter(Context a, ArrayList<HashMap<String, String>> d) {
context = a;
data = d;
}
public int getCount() {
return data.size();
}
public HashMap<String, String> getItem(int position) {
return data.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.upcoming_grid_item, parent, false);
}
final ImageView poster = (ImageView) convertView.findViewById(R.id.upcoming_image);
mylist = data.get(position);
final String posterPath = mylist.get("poster_path");
// set image url correctly
// sizes for image 45, 92, 154, 185, 300, 500
final String url = "http://image.tmdb.org/t/p/w185" + posterPath;
// load image url into poster
Picasso.with(context).load(url).into(poster);
// load image url into poster
// Get onclick of item and pass data to singleitemview for upcoming
convertView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// need to pass data.get(position) to the inner class click
new Click().execute();
}
});
return convertView;
}
// Downloading data asynchronously
class Click extends AsyncTask< Void, Void, Void> {
#Override
protected void onPreExecute() {
// TODO setup progressdialog
}
#Override
protected Void doInBackground(Void... params) {
mylist = data.get(position); // need to get position
Intent intent = new Intent(context, UpcomingSingleItem.class);
intent.putExtra("poster_path", mylist.get(Upcoming.TAG_POSTER));
intent.putExtra("title", mylist.get(Upcoming.TAG_TITLE));
intent.putExtra("release_date", mylist.get(Upcoming.TAG_RELEASE));
intent.putExtra("overview", mylist.get(Upcoming.TAG_OVERVIEW));
intent.putExtra("id", mylist.get(Upcoming.TAG_ID));
intent.putExtra("vote_average", mylist.get(Upcoming.TAG_VOTE_AVG));
context.startActivity(intent);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// TODO stop progressdialog
}
}
}
I think you want position in your Click asynctask for that pass your position in asynctask call :
new Click().execute(position);
Then receive it using params like this :
mylist = data.get(params[0]); // get position here which is passed
If i get you right you only need to execute your AsyncTask with params. See the documentation:
http://developer.android.com/reference/android/os/AsyncTask.html#execute(Params...)
have gridview for which set adapter called ImageAdapter with parameter of arraylist. Inside the adapter have onclicklistener during which from the arraylist one item is removed and then when i use this line ImageAdapter.notifyDataSetChanged in the gridview item is removed. Now i need the changed arrayList in my activity so how can i get it.
Here's my code:
public class ImageAdapter extends BaseAdapter {
Context context;
ArrayList<String> listCheck = new ArrayList<String>();
ImageAdapter adapter = this;
public ImageAdapter(Context context, ArrayList<String> list) {
this.context = context;
listCheck = list;
}
#Override
public int getCount() {
return listCheck.size();
}
#Override
public Object getItem(int arg0) {
return null;
}
#Override
public long getItemId(int arg0) {
return 0;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder121 holder;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(
R.layout.item_gridmain, null);
holder = new ViewHolder121();
holder.imageView = (ImageView) convertView
.findViewById(R.id.img_selected_image);
holder.close = (ImageButton) convertView
.findViewById(R.id.img_btn_cancel);
convertView.setTag(holder);
}
else {
holder = (ViewHolder121) convertView.getTag();
}
holder.close.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg1) {
listCheck.remove(position);
adapter.notifyDataSetChanged();
}
}); Bitmap bm = decodeSampledBitmapFromUri(listCheck.get(position), 220,
220);
holder.imageView.setImageBitmap(bm);
return convertView;
} }
Fragment code:
ImageAdapter mainActivityAdapter = new ImageAdapter(getActivity(), ar1);
gridview_withimage.setAdapter(mainActivityAdapter);
question: How to get changed arraylist from ImageAdapter to called Fragement
How to get changed arraylist from ImageAdapter to called Fragement
Create a method in ImageAdapter which will return ArrayList used as data-source in Adapter:
public ArrayList<String> getModifyList() {
return listCheck;
}
Call getModifyList method in Fragment for getting ArrayList using Adapter object:
gridview_withimage.setAdapter(mainActivityAdapter);
gridview_withimage.postDelayed(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
ArrayList<String> arrNewList=mainActivityAdapter.getModifyList();
}
}, 100);
You can pass Fragment instance to Adapter.
private Fragment fragment;
public ImageAdapter(Context context, ArrayList<String> list,Fragment mfragment) {
this.context = context;
listCheck = list;
this.fragment=mfragment;
}
and Make one public method in your fragment and call using this instance of Fragment fragment;
You can create an interface like this
public interface ImageAdapterListener{
void onListChange(List<String> list);
}
And declare a member in ImageAdapter
private ImageAdapterListener listener;
public setListener(ImageAdapterListener listener){
this.listener = listener}
override the notifyDataSetChanged
#Override
void notifyDataSetChanged{
super.notifyDataSetChanged();
if(listener!= null) listener.onListChange(listCheck);
}
and make your fragment implement that interface.
class Myfragment extends Fragment implements ImageAdapterListener {
ImageAdapter mainActivityAdapter = new ImageAdapter(getActivity(), ar1);
gridview_withimage.setAdapter(mainActivityAdapter);
mainActivityAdapter.setListener(this);
#Override
void onListChange(List<String> list){
//do your stuff
}
}
I am using the RefreshableListView (Pull to refresh) from here .But I am using a Custom ListAdapter and not the ArrayAdapter. It working all fine but the only issue is that it is adding new items to the bottom of the list while I want new items to be displayed on top. .
I have referred to a lot of articles on the net addressing similar issues but have not found a solution for my issue. It will be great if anybody could point out where I am going wrong.
Here are the code snippets:
public void onCreate(Bundle savedInstanceState) {
final CustomAdapter adapter = new CustomAdapter(this, R.layout.mylist, mItems);
mListView = (RefreshableListView) findViewById(R.id.listview);
mListView.setAdapter(adapter);
// Callback to refresh the list
mListView.setOnRefreshListener(new OnRefreshListener() {
#Override
public void onRefresh(RefreshableListView listView) {
ivFooter.setImageResource(R.drawable.back);
new NewDataTask().execute();
}
});
}
and the AsyncTask
private class NewDataTask extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... params) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
String retVal = myArrayList.get(i);
if (i < myArrayList.size() - 1) {
i++;
} else {
i = 0;
}
return retVal;
}
#Override
protected void onPostExecute(String result) {
mItems.add(0, result);
mListView.completeRefreshing();
super.onPostExecute(result);
}
}
and Custom Adapter class
class CustomAdapter extends BaseAdapter {
public int getCount() {
return mItems.size();
}
public Object getItem(int position) {
return mItems.get(position);
}
public long getItemId(int position) {
return position;
}
public boolean areAllItemsEnabled() {
return false;
}
public boolean isEnabled(int position) {
return false;
}
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.mylist, null);
}
String quote = myArrayList.get(position);
if (quote != null) {
TextView tt = (TextView) v.findViewById(R.id.tvQuote);
ImageView iv = (ImageView) v.findViewById(R.id.ivPicture);
if (tt != null) {
tt.setText(quote);
}
if (iv != null) {
//Setting image
}
return v;
}
'mItems' is the original list with one item and I am adding 'myArrayList' to it.
Like I said its working fine but adding elements (on refresh) one by one at the bottom ..while I want the new elements to be added on top.
Any inputs would be of great help.
Thanks in advance.
Replace String quote = myArrayList.get(position); with String quote = RefreshableListViewActivity.this.mItems.get(position); and it works as expected.
Thanks to #Dante for helping me out.
I use ListActivity with SpecialAdapter that show some log information.
I do not use background thread but I get an error when scroll list:
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(16908298, class android.widget.ListView) with Adapter(class ru.FoxGSM.ui.DebugActivity$SpecialAdapter)]
Please, help me.
(FoxLog is static class that get log information, perhaps from another thread. But in list a want show FoxLog snapshot)
public class DebugActivity extends ListActivity {
private class SpecialAdapter extends BaseAdapter {
private LayoutInflater mInflater;
public SpecialAdapter(Context context) {
super();
// Cache the LayoutInflate to avoid asking for a new one each time.
mInflater = LayoutInflater.from(context);
}
public int getCount() {
return FoxLog.count();
}
public long getItemId(int index) {
return index;
}
public Object getItem(int index) {
return FoxLog.get(FoxLog.count()-index-1);
}
// Get the type of View
public View getView(int position, View convertView, ViewGroup parent) {
TextView text;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.debug_list, null);
text = (TextView)convertView.findViewById(R.id.text);
convertView.setTag(text);
} else
text = (TextView) convertView.getTag();
String s = (String) getItem(position);
if (s==null)
return convertView;
text.setText(s);
boolean isError = false;
if (s!=null && s.length()>0) {
String prefix = s.substring(0, 1);
if (prefix.equals("E") || prefix.equals("W"))
isError = true;
}
if (isError)
text.setBackgroundResource(R.color.red);
else
text.setBackgroundDrawable(null);
return convertView;
}
}
private void RefreshData() {
FoxLog.ReInit(this);
SpecialAdapter adapter = (SpecialAdapter)this.getListAdapter();
adapter.notifyDataSetChanged();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.debug);
FoxLog.ReInit(this);
SpecialAdapter adapter = new SpecialAdapter(this);
setListAdapter(adapter);
Button mExit = (Button)findViewById(R.id.exit);
mExit.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
finish();
}
});
}
}