如何在长自定义列表视图中滚动时跟踪项目的位置 [英] How to track the position of items while scrolling in a long custom listview

查看:25
本文介绍了如何在长自定义列表视图中滚动时跟踪项目的位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以列表视图的形式从数据库中显示的数据(此处为列表视图,其标题已禁用 onClick 标题).

The data which is displayed from the database in the form of listview (here listview with headers having disabled onClick of headers).

我试图从 getView()position 显示所选项目的描述.该列表非常大,因此它在滚动时动态分配视图 &position 滚动后给出错误的值

I tried to display the description of the selected item from position of getView(). The list is very large so it dynamically allocates the view while scrolling & the position gives wrong values after scrolling

我观看了 Google I/O 2010 - ListView 的世界 视频和它说明了这些事情.

所以我想我需要实现 notifyDataSetChanged()onScroll() , onScrollStateChanged() 方法.

So I think I need to implement notifyDataSetChanged() or onScroll() , onScrollStateChanged() methods.

但是怎么做?

代码:

public class MainActivity1 extends ListActivity implements OnTouchListener{

private MyCustomAdapter mAdapter;
Activity temp = this;
String []s = new String[500];
ArrayList<GS> q = new ArrayList<GS>();
CustomAdapter adapter;
ListView lv;
int c=1;

@Override
public void onCreate(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());
    mAdapter.addItem(q.get(0).getAS_name());
    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());
            c++;
        }
        mAdapter.addItem(q.get(i).getAS_name());

    }

    setListAdapter(mAdapter);        

}
 //Adapter Class
 private class MyCustomAdapter extends BaseAdapter {

    private static final int TYPE_ITEM = 0;
    private static final int TYPE_SEPARATOR = 1;
    private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;

    private ArrayList<String> mData = new ArrayList<String>();
    private LayoutInflater mInflater;

    private TreeSet<Integer> mSeparatorsSet = new TreeSet<Integer>();

    public MyCustomAdapter() {
        mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    public void addItem(final String item) {
        mData.add(item);
        notifyDataSetChanged();
    }

    public void addSeparatorItem(final String item) {
        mData.add(item);
        // save separator position
        mSeparatorsSet.add(mData.size() - 1);
        notifyDataSetChanged();
    }

    @Override
    public int getItemViewType(int position) {
        return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
    }

    @Override
    public int getViewTypeCount() {
        return TYPE_MAX_COUNT;
    }

    public int getCount() {
        return mData.size();
    }

    public String getItem(int position) {
        return mData.get(position);
    }

    public long getItemId(int position) {
        Log.v("getItemId Position", ""+position);
        return position;

    }

            public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        int type = getItemViewType(position);
     //   System.out.println("getView " + position + " " + convertView + " type = " + type);
        if (convertView == null) {
            holder = new ViewHolder();
            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);
                count++;
                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(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        AlertDialog.Builder x = new AlertDialog.Builder(
                                temp);
                        Log.v("position",""+position);
                               x.setIcon(R.drawable.ic_launcher)
                                .setTitle(q.get(position-count).getAS_name())
                                .setMessage(q.get(position-count).getDesc_art())
                                .setCancelable(true)
                                .setPositiveButton("OK",
                                        new DialogInterface.OnClickListener() {
                                            @Override
                                            public void onClick(DialogInterface arg,
                                                    int arg1) {
                                            }
                                        });
                               AlertDialog a = x.create();
                        a.show();
                    }
                });
           } else {
            holder.textView.setOnClickListener(null);
            count++;
        }   

        return convertView;
    }
}

public static class ViewHolder {
    public TextView textView;
}

  public boolean onTouch(View v, MotionEvent event) {
 // TODO Auto-generated method stub
  return false;
 }
}

所以,当点击TYPE_ITEM 时,我试图在Alertdialog 中显示所选项目的描述.每个TYPE_ITEM 的描述按顺序存储在数据库中.因此,我需要跟踪 TYPE_ITEM 的位置(意味着索引)以从数据库中获取数据.我希望你现在明白我的问题!

So,I am trying to display the description of the selected item in Alertdialog when TYPE_ITEM is clicked.The description of each TYPE_ITEM is stored in the database sequentially. Hence, I need to keep track the position(means indexing) of TYPE_ITEM for getting data from database. I hope u now understand my problem !

顺便说一句,我尝试在 getView() 中使用计数变量,但是如果我们再次向上滚动会怎样(在这种情况下 count 应该减少).我希望你现在明白这个问题 &我认为我们应该实现 notifyDataSetChanged 或 onScroll、onScrollStateChanged 方法.

BTW I tried using a count variable in getView() that but in that what if we again scroll up(in that case the count should decrement). i hope u understand the problem now & i think we should implement notifyDataSetChanged or onScroll, onScrollStateChanged methods.

如果问题不清楚,可以在评论中问我

If the problem is not clear then u can ask me in the comments

请帮忙!

推荐答案

您需要做的就是移动 holder.textview 上的 OnClickListener 设置.

All you need to do is move the setting of OnClickListener on holder.textview.

这里的问题是:

:由于您在 if (convertView == null) 块中设置了 OnClickListener,因此不会更改当 convertView 不为 null 时.所以,AlertDialog 中使用的 position 就是你设置的当 convertView null 时.

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

:解决方案是在每次处理位置时设置 OnClickListener - 我们不关心是否视图是否被回收!!!我们需要重置 OnClickListener 以反映正确/当前位置.

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

:虽然,当 item 为 TYPE_SEPARATOR 时,我们从未在 holder.textview 上设置 OnClickListener,它的使用 holder.textview.setOnClickListener(null) 删除 OnClickListener 是安全的.

: 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 Class
private class MyCustomAdapter extends BaseAdapter {

    ....
    ....     
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        int type = getItemViewType(position);
        System.out.println("getView " + position + " " + convertView + " type = " + type);
        if (convertView == null) {
            holder = new ViewHolder();
            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(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        AlertDialog.Builder x = new AlertDialog.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",
                                        new DialogInterface.OnClickListener() {
                                            @Override
                                            public void onClick(DialogInterface arg,
                                                    int arg1) {

                                            }
                                        });
                        AlertDialog a = x.create();
                        a.show();
                    }
              });
        } else {
            holder.textview.setOnClickListener(null);
        }   

        return convertView;
    }

    ....
    ....

}

Wrapper 类(可以作为 MainActivity1 的内部类实现,也可以独立实现):

Wrapper class (can be implemented as an inner class of MainActivity1 or independently):

public class ContentWrapper {

    private String mItem, mItemDescription;

    public ContentWrapper(String item, String itemDescription) {
        mItem = item;
        mItemDescription = itemDescription;
    }

    public String getItem() {
        return mItem;
    }

    public String getItemDescription() {
        return mItemDescription;
    }
}

您的数据设置也会发生变化:

Your data-setup will also change:

@Override
public void onCreate(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>();

add* 方法

public void addItem(ContentWrapper value) {
    mData.add(value);
    notifyDataSetChanged();
}

public void addSeparatorItem(ContentWrapper value) {
    mData.add(value);
    // save separator position
    mSeparatorsSet.add(mData.size() - 1);
    notifyDataSetChanged();
}

public ContentWrapper getItem(int position) {
    return mData.get(position);
}

getView(...) 方法:

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    ....
    ....

    holder.textView.setText(mData.get(position).getItem());

    if (type == TYPE_ITEM) {
        holder.textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AlertDialog.Builder x = new AlertDialog.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",
                                new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface arg,
                                            int arg1) {
                                    }
                                });
                 AlertDialog a = x.create();
                 a.show();
             }
         });
    } else {
        holder.textView.setOnClickListener(null);
    }
}

仅此而已.

[I] 认为我们应该实现 notifyDataSetChanged 或 onScroll,onScrollStateChanged 方法.

[I] think we should implement notifyDataSetChanged or onScroll, onScrollStateChanged methods.

notifyDataSetChanged() 用于告诉适配器底层数据已更改,因此需要刷新.例如,如果某个项目的描述发生变化,您将在 mData 中更新该项目并调用 notifyDataSetChanged().但是在你的情况下(以及你的代码告诉我的),你的数据在你使用 - setListAdapter(mAdapter) 设置适配器之前被初始化.因此,甚至不需要在 add* 方法内调用 notifyDataSetChanged().在将适配器附加到列表视图之前调用 notifyDataSetChanged() 没有任何作用.

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.

onScrollonScrollChanged 用于不同的目的.例如,假设您在用户滚动到第 50 个项目时显示 Go To Top of the List 按钮 - 当他们向上滚动到第 50 个位置时隐藏它.在您的情况下,问题在于您试图从两个不同的来源(mData,q)获取数据并且存在同步问题.没有别的.

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.

这篇关于如何在长自定义列表视图中滚动时跟踪项目的位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆