如何在长自定义列表视图中滚动时跟踪项目的位置

时间:2014-03-17 09:48:48

标签: android listview android-listview arraylist baseadapter

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

我尝试在position的{​​{1}}中显示所选项目的说明。该列表非常大,因此它在滚动时动态分配视图。滚动后getView()给出错误的值

我观看了Google I/O 2010 - The world of ListView 视频& it states these things

所以我认为我需要实施positionnotifyDataSetChanged()onScroll()方法。

但是怎么样?

代码:

onScrollStateChanged()

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

BTW我尝试在TYPE_ITEM中使用一个count变量,但是如果我们再次向上滚动(在这种情况下getView()应该递减)。我希望你现在能理解这个问题。我认为我们应该实现notifyDataSetChanged或onScroll,onScrollStateChanged方法。

如果问题不明确,那么你可以在评论中问我

请帮助!

2 个答案:

答案 0 :(得分:1)

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

这里的问题是:

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

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

:虽然,当项目为holder.textview.setOnClickListener(null)时,我们从未在holder.textview上设置OnClickListener,      使用//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; } .... .... } 安全删除OnClickListener。

尝试下面的更新代码。我希望这里的逻辑非常明确。

MainActivity1

修改

Wrapper类(可以作为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; } } 的内部类实现或独立实现):

@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);        
}

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

// 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); } } 方法:

notifyDataSetChanged()

就是这样。

  

[我]我们应该实现notifyDataSetChanged或onScroll,   onScrollStateChanged方法。

mData用于告诉适配器底层数据已更改,因此需要刷新。例如,如果项目的说明发生变化,您可以在notifyDataSetChanged()中更新该项目,然后致电setListAdapter(mAdapter)。但在您的情况下(以及您的代码告诉我的内容),在使用 - notifyDataSetChanged()设置适配器之前,您的数据已初始化。因此,甚至不需要在add*方法内调用notifyDataSetChanged()。在将适配器附加到列表视图之前调用onScroll不会做任何事情。

onScrollChangedGo To Top of the List用于不同目的。例如,假设您在用户滚动浏览第50个项目时显示{{1}}按钮 - 并在向上滚动第50个位置时隐藏它。在您的情况下,问题是您尝试从两个不同的源(mData,q)获取数据,并且存在同步问题。没别了。

答案 1 :(得分:0)

您可以通过将onScrollListener放入Acvivity中的ListvView来轻松平滑地检测滚动位置。它包含两个基本方法:onScroll和onScrollStateChanged。

修改

OnScrollListener具有广泛的用途。这是OnScrollListener,它检测滚动结束并加载更多数据。流行的负载更多功能。

mListView.setOnScrollListener(new OnScrollListener() {

                    @Override
                    public void onScroll(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{}
                            }
                        }
                    }

                    }
                    @Override
                    public void onScrollStateChanged(AbsListView view,
                            int scrollState) {
                        //the int scrollState is what are you looking for                           

                        if (SCROLL_STATE_TOUCH_SCROLL == scrollState) {
                            View currentFocus = getActivity().getCurrentFocus();
                            if(currentFocus != null) {
                                currentFocus.clearFocus();
                            }
                        }



                    }

                });

如果要使用onScrollStateChanged方法获取滚动位置。下面代码中的int scrollState实际上就是你想要的。你可以用它做任何你想做的事。

顺便说一句 这只是使用OnScrollListener的一个示例,但您要做的就是设置它而不使用内部设置(在我的情况下加载更多功能),但只使用onScrollStateChanged及其int

希望它有所帮助!