I have a custom list view adapter populated through an asynctask, I'm calling notifydatasetchanged in the onprogress function, and getCount() returns 10, yet my list never shows, Ive set a breakpoint and determined that getView() simply never is called. any ideas? Ive tried for hours and Im just stumped. Ive done the exactly same thing in another activity except that one used viewholders, this one only holds text based data so I didn't bother.
Adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if(row == null) {
row = inflater.inflate(R.layout.podcastepisode, null);
}
PodcastItem item = items.get(position);
TextView episodeTitle = (TextView)row.findViewById(R.id.episodeTitle);
TextView episodeDate = (TextView)row.findViewById(R.id.episodeDate);
episodeTitle.setText(item.title);
episodeDate.setText(API.FormatPodcastDate(item.date));
return row;
}
My task:
protected void onProgressUpdate(PodcastItem... progress) {
AddPodcastActivity.episodes.add(progress[0]);
AddPodcastActivity.adapter.notifyDataSetChanged();
}
I'd recommend moving your list adapter from inside the Activity file to it's own file, and using something like this:
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
public class EpisodeArrayAdapter extends BaseAdapter {
public EpisodeArrayAdapter(Context context) {
mContext = context;
items = new ArrayList<PodcastItem>();
}
private Context mContext;
private ArrayList<PodcastItem> items;
public void add(PodcastItem item) {
items.add(item);
notifyDataSetChanged();
}
public void remove(int index) {
items.remove(index);
notifyDataSetChanged();
}
public void clear() {
items.clear();
notifyDataSetChanged();
}
#Override
public int getCount() { return items.size(); }
#Override
public Object getItem(int position) { return items.get(position); }
#Override
public long getItemId(int position) { return position; }
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if(row == null) row = LayoutInflater.from(mContext).inflate(R.layout.podcastepisode, null);
PodcastItem item = items.get(position);
TextView episodeTitle = (TextView)row.findViewById(R.id.episodeTitle);
TextView episodeDate = (TextView)row.findViewById(R.id.episodeDate);
episodeTitle.setText(item.title);
episodeDate.setText(API.FormatPodcastDate(item.date));
return row;
}
}
This is the type of code we use for all the list adapters in Boid :) Also, notice that the add/remove/clear functions call notifyDataSetChanged(), which makes it so you don't have to call it yourself when adding items.
When you initialize it, you would just use:
EpisodeArrayAdapter adapter = new EpisodeArrayAdapter(this);
listView.setAdapter(adapter);
Adding items with the add() function will cause the list to update immediately. Make sure you call setAdapter for the list view that's using the adapter, otherwise there won't be any connection and nothing will show up in the list (didn't see a call to this in your code).
Related
Super hard one to explain.
This is the error I get in my reporting:
Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference
This seems to happen intermittently when you go into and out of the fragment. The error seems to happen in the adaptor.
This is where it is called:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
getActivity().setTitle("Shipments");
myView = inflater.inflate(R.layout.shipments_out_layout, container, false);
listView = myView.findViewById(R.id.listView);
fetchShipments();
return myView;
}
/**
* Fetch shipments
*/
public void fetchShipments()
{
shipmentsService.fetchFromServer(getActivity());
}
/**
* Show shipments
*/
public void showShipments(){
RealmResults<Shipment> savedShipments = shipmentsService.all();
ShipmentsAdaptor adaptor = new ShipmentsAdaptor(savedShipments, this.getContext());
listView.setAdapter(adaptor);
}
And this is where the error is in the adaptor:
public class ShipmentsAdaptor extends ArrayAdapter<Shipment> {
private RealmResults<Shipment> dataSet;
Context mContext;
// View lookup cache
private static class ViewHolder {
TextView stockItemId;
TextView technicianName;
TextView shipmentDate;
}
public ShipmentsAdaptor(RealmResults<Shipment> data, Context context){
super(context, R.layout.shipments_out_row_item, data);
this.dataSet = data;
this.mContext = context;
}
It's this line specifically: super(context, R.layout.shipments_out_row_item, data);
I thought it may be something to do with the way we are inserting the context into the adaptor and then changing the page before its finished but that proved inconclusive.
Paste bin with adaptor:Adaptor
The Fragment#getContext() is nullable. This method returns null when your fragment is detached from activity. The app crashes because you create the adapter while the fragment is not attached which results into a null passed to the constructor.
The method showShipments should only be called when the fragment is attached to the activity. There are callbacks onAttach() and onDetach() that will help you to detect the state. Also isAdded() returns you a boolean saying if the fragment is attached or not. Choose what is convenient for you.
Good luck!
Try refactor your adapter using BaseAdapter as follow
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class ShipmentsAdaptor extends BaseAdapter {
private RealmResults<Shipment> dataSet;
private Context mContext;
// View lookup cache
private static class ViewHolder {
TextView stockItemId;
TextView technicianName;
TextView shipmentDate;
}
public ShipmentsAdaptor(RealmResults<Shipment> dataSet, Context context) {
this.dataSet = dataSet;
this.mContext = context;
}
#Override
public int getCount() {
return dataSet.size();
}
#Override
public Object getItem(int position) {
return dataSet.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
Shipment shipment = (Shipment) getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
ViewHolder viewHolder; // view lookup cache stored in tag
final View result;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(mContext);
convertView = inflater.inflate(R.layout.shipments_out_row_item, parent, false);
viewHolder.stockItemId = convertView.findViewById(R.id.stockItemId);
viewHolder.technicianName = convertView.findViewById(R.id.technicianName);
viewHolder.shipmentDate = convertView.findViewById(R.id.shipmentDate);
result = convertView;
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
result=convertView;
}
lastPosition = position; //use getItemId() instead
if(shipment != null){
viewHolder.stockItemId.setText(String.valueOf(shipment.id));
if(shipment.technician != null){
viewHolder.technicianName.setText(shipment.technician.name);
}
viewHolder.shipmentDate.setText(shipment.shippingDate);
}
// Return the completed view to render on screen
return convertView;
}
}
Looks like you are calling fetchShipments(); before the fragment layout view (myView) is returned hence it is null when the adaptor is instantiated.
Try:
Move fetchShipments(); from onCreateView() and place it in onResume() or override onStart() and call it from there
You can check for null during your adapter setup to avoid this. In a Fragment, getActivity can sometimes return null at different points during the Fragment lifecycle. For example, in showShipments
Activity a = getActivity();
if( a == null || a.isFinishing() ) {
// Not in a valid state to show things anyway, so just stop and exit
return;
}
ShipmentsAdaptor adaptor = new ShipmentsAdaptor(savedShipments, a);
You can also check isAdded(), and if that is false you can get null from getActivity().
Also, consider moving the call to fetchShipments() from onCreateView to onActivityCreated instead.
I'm beginner in programming in Android Studio and I'm making now some kind of messenger via bluetooth. So I have my own ArrayAdapter class which extends ArrayAdapter class and it is for outgoing and incoming messages. I want incoming messages to be at the left side ang outgoing ones at the right, so I made two layouts for this Adapter. I know, that on stackoverflow there is a lot of solutions to make ArrayAdapter with few diffrent layouts for each row, but every one of them doesn't work - changing layouts cause change view of every row. So my solution is to make another ArrayList of booleans, and in getView() I check what I have in this List - true or false - and use right layout on that row in ArrayAdapter (I'm checking it by position field from getView()). And when I send a lot of messages to second device and try to response to first device there is NullPointerException in line with
(TextView)row.findViewById(R.id.singleIncomingMessage);
or
(TextView)row.findViewById(R.id.singleOutgoingMessage);
This exceptions seems to appear in random situation, but of course there must be some pattern. Here's the whole code. What it's wrong? And I'm sorry for my language if there is some misspells ;)
public class MyArrayAdapter extends ArrayAdapter<String> {
public MyArrayAdapter(Context context, ArrayList<String> list) {
super(context, 0, list);
this.list =list;
this.context=context;
}
ArrayList<String> list;
Context context;
#Override
public int getCount() {
return list.size();
}
#Override
public String getItem(int position) {
return list.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
if(Messenger.inOut){
return 1;
}
else{
return 0;
}
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
int type=getItemViewType(position);
View row=convertView;
if(row==null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(Messenger.inOutList.get(position)==0){
row=inflater.inflate(R.layout.outgoing_message_layout, parent, false);
}
if(Messenger.inOutList.get(position)==1){
row=inflater.inflate(R.layout.incoming_message_layout, parent, false);
}
}
String message=getItem(position);
TextView label;
if(Messenger.inOutList.get(position)==0){
label=(TextView)row.findViewById(R.id.singleOutgoingMessage);
label.setText(message);
}
if(Messenger.inOutList.get(position)==1){
label=(TextView)row.findViewById(R.id.singleIncomingMessage);
label.setText(message);
}
return row;
}
}
I think the issue is from the case where row is non-null. It may have previously been inflated as an outgoing message layout, now you are recycling it and trying to treat it as an incoming message layout, so it can't find the TextView.
It's hard to say for sure this is the issue though since I don't really know how the Messenger.* calls behave in your code. Since you already get type in getView you should use that rather than Messenger.inOutList.get(position)==X to determine which view logic to use if you keep it this way. This question has some good answers on how to do this consistently.
Also keep in mind that for this to work getItemViewType must always return the same type for a given position, or else you have to detect the change and inflate a new layout in getView. If Messenger.inOut is constant, there's not reason to use this format (multi-layout format). If it's not a constant and it gets changed, then you need to detect this in getView
Okay, thank you very much, I finally did it. I was using two list (of booleans and messages) and after I read about ArrayAdapter I decided to make class with this two fields and make a list of its object. That error was probably because of that second list of booleans. Here's below my final code if anyone have similiar problem.
public class MyArrayAdapter extends ArrayAdapter<MessageWithType> {
public MyArrayAdapter(Context context, ArrayList<MessageWithType> list) {
super(context, 0, list);
this.list =list;
this.context=context;
}
public static final int TYPE_OUT = 0;
public static final int TYPE_IN = 1;
ArrayList<MessageWithType> list;
Context context;
#Override
public int getCount() {
return list.size();
}
#Override
public MessageWithType getItem(int position) {
return list.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
if(list.get(position).inOut){
return 1;
}
else{
return 0;
}
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
MessageWithType item=getItem(position);
int type=getItemViewType(position);
View row=convertView;
ViewHolder viewHolder=null;
if(row==null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(type==TYPE_OUT){
row=inflater.inflate(R.layout.outgoing_message_layout, parent, false);
}
if(type==TYPE_IN){
row=inflater.inflate(R.layout.incoming_message_layout, parent, false);
}
TextView label=(TextView)row.findViewById(R.id.singleMessage);
viewHolder=new ViewHolder(label);
row.setTag(viewHolder);
}
else{
viewHolder=(ViewHolder)row.getTag();
}
viewHolder.textView.setText(item.message);
return row;
}
public class ViewHolder{
TextView textView;
public ViewHolder(TextView textView){
this.textView=textView;
}
}
}
I try to remove a specific item from a listView but it's always remove the last item.
I create a custom adapter to my listview.
I try to search for a solution and i found some posts about this problem but I still didn't success to solve the problem
custom adapter below:
public class ListViewAdapter extends BaseAdapter{
public ArrayList<HashMap<String, String>> list;
public static final String WORD_COLUMN="First";
public static final String TRAN_COLUMN="Second";
Activity activity;
TextView txtFirst;
TextView txtSecond;
public ListViewAdapter(Activity activity,ArrayList<HashMap<String, String>> list){
super();
this.activity=activity;
this.list=list;
}
#Override
public int getCount() {
return list.size();
}
#Override
public Object getItem(int position) {
return list.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater=activity.getLayoutInflater();
if(convertView == null){
convertView=inflater.inflate(R.layout.column_row, null);
txtFirst=(TextView) convertView.findViewById(R.id.wordColumn);
txtSecond=(TextView) convertView.findViewById(R.id.tranColumn);
}
HashMap<String, String> map=list.get(position);
txtFirst.setText(map.get(WORD_COLUMN));
txtSecond.setText(map.get(TRAN_COLUMN));
return convertView;
}
}
activity code below:
public class MainActivity extends AppCompatActivity{
private ListView lv;
private ArrayList<HashMap<String, String>> hashList;
private ListViewAdapter adapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView)findViewById(R.id.gvWords);
hashList = new ArrayList<HashMap<String, String>>();
adapter=new ListViewAdapter(this, hashList);
lv.setAdapter(adapter);
onClickButtons();
}
public void onClickButtons()
{
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
hashList.remove(i);
adapter.notifyDataSetChanged();
}
});
}
Thank's :)
You can try to access and remove clicked object via getItemAtPosition(position) and then remove it from ArrayList via .remove(Object o)
Your listener will therefore look like this:
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
hashList.remove(lv.getItemAtPosition(i));
adapter.notifyDataSetChanged();
}
});
This approach is mentioned in official doc: https://developer.android.com/reference/android/widget/AdapterView.OnItemClickListener.html
Implementers can call getItemAtPosition(position) if they need to access the data associated with the selected item.
after refreshing it's remove the right item
You've cached the two TextViews, which aren't updated as part of notifying the adapter.
Alter the convertView check and remove those fields, then lookup how to implement the ViewHolder pattern (or use a RecyclerView)
I found this problem,
and removed the if(convertView == null){ It's working,
Please let us know for this reason.
How can I create a custom setOnClickListener for my class?
I currently have a listAdapter with items (Made a Item Class). Whenever I click on a item in that list, I want to activate the the functionality that I described in my MainActivity. I would like to set onClickListeners (custom onClickListeners) on classes that I have in my list adapter. How can I do so?
If you did not understand me, or would like to help, I zipped the project and put it on my Google Drive
I've looked through dozens of answer, but I just couldn't find the one that was working.. Please help, as I've invested way to much time in this.
Item item = new Item().setOnClickListener(new ItemListAdapter.ClickListener() {
#Override
public void onClick() {
Toast.makeText(MainActivity.this, "Clicked me", Toast.LENGTH_SHORT).show();
}
});
EDIT: Because I would like to later turn it into a library, it would be useful for me and the user that you can set a onclick on a item rather than the listview, which I want to hide from the user eventually
From my understanding you are looking for some thing like this , Change the class like this ,
public class Item {
private int id;
private ItemListAdapter.ClickListener mClickListener;
public Item(int id) {
this.id = id;
}
public Item(int id, #NonNull ItemListAdapter.ClickListener clickListener) {
this.id = id;
this.mClickListener = clickListener;
}
public Item setOnClickListener(#NonNull ItemListAdapter.ClickListener clickListener) {
this.mClickListener = clickListener;
return this;
}
public ItemListAdapter.ClickListener getClickListener() {
return this.mClickListener;
}
}
Adapter class
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
public class ItemListAdapter extends ArrayAdapter<Item> {
public ItemListAdapter(Context context, int resource) {
super(context, resource, SettingsStorage.getInstance().getItems());
}
#NonNull
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item, parent, false);
}
final Item item = getItem(position);
LinearLayout linearLayout = (LinearLayout) convertView.findViewById(R.id.item);
linearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (item.getClickListener() != null) {
item.getClickListener().onClick();
}
}
});
return convertView;
}
public interface ClickListener {
void onClick();
}
}
and you can set the listener like this ,
Item item1 = new Item(1);
item1.setOnClickListener(new ItemListAdapter.ClickListener() {
#Override
public void onClick() {
Toast.makeText(MainActivity.this, "It works", Toast.LENGTH_SHORT).show();
}
});
Item item2 = new Item(2, new ItemListAdapter.ClickListener() {
#Override
public void onClick() {
}
});
If you want to set click listener to the whole row then "OnItemClickListner" is the option you are looking for.
Check this: ListView with OnItemClickListener android
But if you want to set click listener on one of the item of the row then first you have to get that item from row and then you can do whatever you want.
Check this: ListView with OnItemClickListener android
Get list of items in custom adapter
You can use OnItemClickListener :
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Item item = adapter.getItem(position);
}
});
EDIT :
The is that Item is just a POJO, you can't "click" on it. What you can do is kind of "forwarding" the listener :
Class Item {
private ItemClickListener listener;
// getter / setter
interface ItemClickListener{
void onItemClick(Item item);
}
}
// you implement this in your library
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Item item = adapter.getItem(position);
item.getListener().onItemClick(item);
}
});
I'm trying to figure out how to implement an infinitely scrolling list. It will display a calendar and events and it should start from now or selected date. It should be scrollable in both directions, past and future. The solutions with OnScrollListener here seem to work pretty well if I only need to go to future (index just grows bigger). But I don't see how I would go to the past.
This solution seems to be very wasteful for my case. getView is called thousands of times. Maybe ListView isn't the solution, and I'll have to go with lower-level code. Any ideas?
EDIT: getView being called thousands of times wasn't the fault of the latter solution. However, it still gets called too many times and with wrong values. If I set selection like this:
myList.setSelection(Integer.MAX_VALUE/2)
I get getView calls with indexes starting from zero. For example, I get getView calls like this:
getView pos 0
...
getView pos 26
and then
getView pos 1073741823
...
getView pos 1073741847
Which are the correct ones. Then:
getView pos 0
...
getView pos 26
again
This all happens before I scroll or touch the screen at all. Doesn't seem to make much sense.
Here is an implementation of this task.
EndlessScrollBaseAdapter.java
package com.example.endlessscrollinbothdirections;
import java.util.Map;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.TextView;
/** A child class shall subclass this Adapter and implement method getDataRow(int position,
* View convertView, ViewGroup parent), which supplies a View present data in a ListRow.
* This parent Adapter takes care of displaying ProgressBar in a row or indicating that it
* has reached the last row. */
public abstract class EndlessScrollBaseAdapter<T> extends BaseAdapter implements
OnScrollListener {
private int mVisibleThreshold = 5;
// the main data structure to save loaded data
protected Map<Integer, T> mItems;
protected Context mContext;
// the serverListSize is the total number of items on the server side,
// which should be returned from the web request results
protected int mServerListSize = -1;
// Two view types which will be used to determine whether a row should be displaying
// data or a Progressbar
public static final int VIEW_TYPE_LOADING = 0;
public static final int VIEW_TYPE_ACTIVITY = 1;
public static final int VIRTUAL_MIDDLE_OFFSET = Integer.MAX_VALUE / 2;
public EndlessScrollBaseAdapter(Context context, Map<Integer, T> items) {
mContext = context;
mItems = items;
}
public void setServerListSize(int serverListSize) {
this.mServerListSize = serverListSize;
}
/** disable click events on indicating rows */
#Override
public boolean isEnabled(int position) {
return getItemViewType(position) == EndlessScrollBaseAdapter.VIEW_TYPE_ACTIVITY;
}
/** One type is normal data row, the other type is Progressbar */
#Override
public int getViewTypeCount() {
return 2;
}
/** the size of the List plus one, the one is the last row, which displays a
* Progressbar */
#Override
public int getCount() {
return Integer.MAX_VALUE;
}
/** return the type of the row, the last row indicates the user that the ListView is
* loading more data */
#Override
public int getItemViewType(int position) {
return mItems.containsKey(position
- EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET) ? EndlessScrollBaseAdapter.VIEW_TYPE_ACTIVITY
: EndlessScrollBaseAdapter.VIEW_TYPE_LOADING;
}
#Override
public T getItem(int position) {
return mItems.get(position - EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET);
}
#Override
public long getItemId(int position) {
return position;
}
/** returns the correct view */
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (getItemViewType(position) == EndlessScrollBaseAdapter.VIEW_TYPE_LOADING) {
return getFooterView(position, convertView, parent);
}
return getDataRow(position, convertView, parent);
};
/** A subclass should override this method to supply the data row.
*
* #param position
* #param convertView
* #param parent
* #return */
public abstract View getDataRow(int position, View convertView, ViewGroup parent);
/** returns a View to be displayed in the last row.
*
* #param position
* #param convertView
* #param parent
* #return */
public View getFooterView(int position, View convertView, ViewGroup parent) {
if (position >= mServerListSize && mServerListSize > 0) {
// the ListView has reached the last row
TextView tvLastRow = new TextView(mContext);
tvLastRow.setHint("Reached the last row.");
tvLastRow.setGravity(Gravity.CENTER);
return tvLastRow;
} else {
TextView tvLastRow = new TextView(mContext);
tvLastRow.setHint("Loading...\n position: " + position);
tvLastRow.setGravity(Gravity.CENTER);
return tvLastRow;
}
}
// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int virtualPosition);
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
for (int i = -mVisibleThreshold; i < visibleItemCount + mVisibleThreshold; i++) {
int virtualPosition = firstVisibleItem
- EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET + i;
onLoadMore(virtualPosition);
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
}
EndlessScrollAdapter.java
package com.example.endlessscrollinbothdirections;
import java.util.Map;
import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class EndlessScrollAdapter extends EndlessScrollBaseAdapter<Integer> {
public EndlessScrollAdapter(Activity activity, Map<Integer, Integer> list) {
super(activity, list);
}
#Override
public View getDataRow(int position, View convertView, ViewGroup parent) {
TextView TextView;
if (convertView == null) {
TextView = new TextView(mContext);
} else {
TextView = (TextView) convertView;
}
TextView.setText("virtualPosition: "
+ (position - EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET) + "\n"
+ "row data: "
+ mItems.get(position - EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET));
return TextView;
}
#Override
public void onLoadMore(int virtualPosition) {
// here you might launch an AsyncTask instead
if (!mItems.containsKey(virtualPosition)) {
mItems.put(virtualPosition, virtualPosition);
notifyDataSetChanged();
}
}
}
MainActivity.java
package com.example.endlessscrollinbothdirections;
import java.util.HashMap;
import java.util.Map;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.ListView;
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.lvItems);
Map<Integer, Integer> items = new HashMap<Integer, Integer>();
EndlessScrollAdapter endlessScrollAdapter = new EndlessScrollAdapter(this, items);
listView.setAdapter(endlessScrollAdapter);
listView.setSelection(EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET);
listView.setOnScrollListener(endlessScrollAdapter);
}
}
activity_main.xml
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/lvItems"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>