How To Track The Position Of Items While Scrolling In A Long Custom Listview
Solution 1:
All you need to do is move the setting of OnClickListener
on holder.textview
.
The problem here is:
: Since you set the OnClickListener in the if (convertView == null)
block, it isn't changed
when convertView is not null. So, the position
used in AlertDialog
is the one you set
when convertView
was null.
: Solution is to set the OnClickListener every time a position is processed - we don't care if the view is recycled or not!!! We need to reset the OnClickListener to reflect the correct/current position.
: Although, we never set an OnClickListener on holder.textview when item is TYPE_SEPARATOR
, its
safe to remove the OnClickListener using holder.textview.setOnClickListener(null)
.
Try the updated code below. I hope the logic here is quite clear.
//Adapter ClassprivateclassMyCustomAdapterextendsBaseAdapter {
....
....
public View getView(finalint position, View convertView, ViewGroup parent) {
ViewHolderholder=null;
inttype= getItemViewType(position);
System.out.println("getView " + position + " " + convertView + " type = " + type);
if (convertView == null) {
holder = newViewHolder();
switch (type) {
case TYPE_ITEM:
convertView = mInflater.inflate(R.layout.activity_main1, null);
holder.textView = (TextView)convertView.findViewById(R.id.text);
break;
case TYPE_SEPARATOR:
convertView = mInflater.inflate(R.layout.activity_main2, null);
holder.textView = (TextView)convertView.findViewById(R.id.textSeparator);
break;
}
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
holder.textView.setText(mData.get(position));
// We set the OnClickListener here because it is unique to every// item. Although views can be recycled & reused, an OnClickListener cannot be.if (type == TYPE_ITEM) {
holder.textView.setOnClickListener(newView.OnClickListener() {
@OverridepublicvoidonClick(View v) {
AlertDialog.Builderx=newAlertDialog.Builder(
temp);
Log.v("position",""+position);
x.setIcon(R.drawable.ic_launcher)
.setTitle(q.get(position-1).getAS_name())
.setMessage(q.get(position-1).getDesc_art())
.setCancelable(true)
.setPositiveButton("OK",
newDialogInterface.OnClickListener() {
@OverridepublicvoidonClick(DialogInterface arg,
int arg1) {
}
});
AlertDialoga= x.create();
a.show();
}
});
} else {
holder.textview.setOnClickListener(null);
}
return convertView;
}
....
....
}
Edit:
Wrapper class (can be implemented as an inner class of MainActivity1
or independently):
publicclassContentWrapper {
privateString mItem, mItemDescription;
publicContentWrapper(String item, String itemDescription) {
mItem = item;
mItemDescription = itemDescription;
}
publicStringgetItem() {
return mItem;
}
publicStringgetItemDescription() {
return mItemDescription;
}
}
Your data-setup will also change:
@Override
publicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DBAdapter db = DBAdapter.getDBAdapter(getApplicationContext());
if (!db.checkDatabase())
{
db.createDatabase(getApplicationContext());
}
db.openDatabase();
q = db.getData();
mAdapter = new MyCustomAdapter();
// mAdapter.addSeparatorItem(q.get(0).getA_name());// First separator item// No description
mAdapter.addSeparatorItem(new ContentWrapper(q.get(0).getA_name(), null));
// mAdapter.addItem(q.get(0).getAS_name());// First TYPE_ITEM// Pass the description
mAdapter.addItem(new ContentWrapper(q.get(0).getAS_name(), q.get(0).getDesc_art()));
for (int i = 1; i < 460; i++) {
if (!(q.get(i).getA_name().trim().equals(q.get(i-1).getA_name().trim()))) {
// mAdapter.addSeparatorItem(q.get(i).getA_name());
mAdapter.addSeparatorItem(new ContentWrapper(q.get(i).getA_name(), null));
c++;
}
// mAdapter.addItem(q.get(i).getAS_name());
mAdapter.addItem(new ContentWrapper(q.get(i).getAS_name(), q.get(i).getDesc_art()));
}
setListAdapter(mAdapter);
}
Next, we make changes to the adapter:
// private ArrayList<String> mData = new ArrayList<String>();
private ArrayList<ContentWrapper> mData = new ArrayList<ContentWrapper>();
The add*
methods
publicvoidaddItem(ContentWrapper value) {
mData.add(value);
notifyDataSetChanged();
}
publicvoidaddSeparatorItem(ContentWrapper value) {
mData.add(value);
// save separator position
mSeparatorsSet.add(mData.size() - 1);
notifyDataSetChanged();
}
public ContentWrapper getItem(int position) {
return mData.get(position);
}
The getView(...)
method:
@Overridepublic View getView(finalint position, View convertView, ViewGroup parent) {
....
....
holder.textView.setText(mData.get(position).getItem());
if (type == TYPE_ITEM) {
holder.textView.setOnClickListener(newView.OnClickListener() {
@OverridepublicvoidonClick(View v) {
AlertDialog.Builderx=newAlertDialog.Builder(temp);
Log.v("position",""+position);
x.setIcon(R.drawable.ic_launcher)
// .setTitle(q.get(position-count).getAS_name())
.setTitle(mData.get(position).getItem())
// .setMessage(q.get(position-count).getDesc_art())
.setMessage(mData.get(position).getItemDescription())
.setCancelable(true)
.setPositiveButton("OK",
newDialogInterface.OnClickListener() {
@OverridepublicvoidonClick(DialogInterface arg,
int arg1) {
}
});
AlertDialoga= x.create();
a.show();
}
});
} else {
holder.textView.setOnClickListener(null);
}
}
And that's about it.
[I] think we should implement notifyDataSetChanged or onScroll, onScrollStateChanged methods.
notifyDataSetChanged()
is used to tell the adapter that the underlying data has changed and thus a refresh is required. For example, if description for an item changes, you would update that item in mData
and call notifyDataSetChanged()
. But in your case (and from what your code tells me), your data is initialized before you set the adapter using - setListAdapter(mAdapter)
. So, calls to notifyDataSetChanged()
inside the add*
methods are not even required. Calling notifyDataSetChanged()
before attaching an adapter to a listview does nothing.
onScroll
and onScrollChanged
are meant for a different purpose. For example, say that you show a Go To Top of the List
button when the user scroll past the 50th item - and hide it when they scroll up the 50th position. In your case, the problem was that you were trying to get data from two different sources(mData, q) and there were issues with synchronization. Nothing else.
Solution 2:
You can detect the scroll position easily and smooth by putting onScrollListener to your ListvView in your Acvivity. That contains two basic methods: onScroll and onScrollStateChanged.
EDIT
OnScrollListener has wide range of usage. This is OnScrollListener, which detects end of scroll and loads more data. Popular load-more feature.
mListView.setOnScrollListener(newOnScrollListener() {
@OverridepublicvoidonScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
if(mListView.getRefreshableView().getCount()!=0&&mListView.getRefreshableView().getCount()>0&&mAdapter.getCount()!=0){
if (mListView.getRefreshableView().getLastVisiblePosition() == mListView.getRefreshableView().getAdapter().getCount() - 1
&& mListView.getRefreshableView()
.getChildAt(mListView.getRefreshableView().getChildCount() - 1)
.getBottom() <= mListView.getRefreshableView().getHeight()) {
if(SplashScreen.countie == mAdapter.getCount()){
if(footie.isShown()) {
mListView.getRefreshableView().removeFooterView(footie);
}
}
else{
if(loading!=true&&dontupdate==false){
updateMoreData();
}
else{}
}
}
}
}
@OverridepublicvoidonScrollStateChanged(AbsListView view,
int scrollState) {
//the int scrollState is what are you looking for if (SCROLL_STATE_TOUCH_SCROLL == scrollState) {
ViewcurrentFocus= getActivity().getCurrentFocus();
if(currentFocus != null) {
currentFocus.clearFocus();
}
}
}
});
If you want to get the scroll position use onScrollStateChanged
method. The int scrollState in code below is actually what you want. You can do whatever you want with it.
BTW
This was just a example of usage of OnScrollListener
but what you have to do is just set it without inner setting (in my case load more feature) but just use onScrollStateChanged
and its int
.
hope it helps!
Post a Comment for "How To Track The Position Of Items While Scrolling In A Long Custom Listview"