Problems with ListView inside a PopupWindow

2019-02-15 06:15发布

问题:

I have a ListView in a PopupWindow. The PopupWindow is initialized like this

    window.setContentView(root);
    window.setTouchable(true);
    window.setFocusable(true);
    window.setOutsideTouchable(true);
    window.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
    window.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);

Then the ListView:

    fileList = (ListView) root.findViewById(R.id.explorer_list);
    fileList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    fileList.setSelector(android.R.drawable.screen_background_light_transparent);
    fileList.setOnItemClickListener(this);

    [...]

    @Override
    public void onItemClick(AdapterView<?> adapter, View v, int pos, long id) {
        selected = (File) fileList.getItemAtPosition(pos);      
    }

Like this, everything works correctly except that the selector will not show up on selection until ListView is scrolled (nothing shows visually as selected until the list is scrolled, although the item is correctly selected).

If I set the PopupWindow not focusable, then the visual selection works correctly (the item is visually selected right when clicked into) but onItemClick() is never called and thus I cannot get the selected item.

ListView.getSelectedItem() always returns null in both cases, even if there's a selected item.

Any idea on how to solve this situation? Thanks in advance.

回答1:

I finally used a custom adapter to store the selected value and use it from there to mark it:

public class FileExplorerAdapter extends ArrayAdapter<File> {

    /** File names */
    private List<File> values = new ArrayList<File>();

    /** Currently selected position */
    private int selected = -1;

    public FileExplorerAdapter(Context context, List<File> values) {
        super(context, R.layout.explorer_row, values);
        this.values = values;
    }

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

        // I know that my layout is always a TextView
        TextView row = (TextView) convertView;
        if (row == null) {
            row = (TextView) ViewHelper.inflateViewById(getContext(),
                    R.layout.explorer_row);
        }

        // More code...

        // Set up the background for selected element
        if (selected == position) {
            row.setBackgroundColor(Color.LTGRAY);

        // Override background selector
        } else {
            row.setBackgroundColor(Color.TRANSPARENT);
        }

        // More code...

        return row;
    }

    /** This sets the selected position */
    public void setSelected(int position) {
        selected = position;
    }
}

And on the class that implements the OnItemClickListener for the associated ListView, I set up the currently selected item in the adapter.

@Override
public void onItemClick(AdapterView<?> adapter, View v, int pos, long id) {
    FileExplorerAdapter fileadapter = (FileExplorerAdapter) fileList
            .getAdapter();
    fileadapter.setSelected(pos);
}


回答2:

//Set listview focusable properties to false

android:focusable="false"



回答3:

I had similar problem, but in my case PopupWindow.setFocusble(false) was required (and using ListPopupWindow was not a solution in my case as a lot of stuff in the project already used base PopupWindow's functionality including extending).

If someone in the same situation there is a kind of workaround based on bug discusson here (post #9)

The main idea is that ListView's hierarchy is still receives touch events so we can manually trigger onItemClick().

However this approach is not 100% identical to real ListView's touch handling (like there is no glow of selection while tapping a row) this done pretty well for me for the moment.

If someone has more precise solution of this problem, please share.

So, here is complete Adapter's code which can be used with ListView inside PopupWindow which is setFocusable(false):

private class CustomAdapter extends ArrayAdapter {

private LayoutInflater mInflater;
private ListView mOwningListView;

public CustomAdapter(Context context, List<String> objects, ListView listView) {
    super(context, android.R.layout.simple_list_item_1, objects);
    mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mOwningListView = listView;
}


@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.font_pick_row, null);
    }
    // this is the key point of workaround
    convertView.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            /*
             *  as every row is still receiving their touches
             *  we can use this to manually trigger onItemClick
             *  since it doesn't firing in popupWindow.setFocusable(false)  
             */
            mOwningListView.getOnItemClickListener().onItemClick(mOwningListView, v, position, getItemId(position));

        }
    });
    //... other stuff
    return convertView;
}

}



回答4:

use this

fileList.setOnItemSelectedListener(new OnItemSelectedListener() {

        public void onItemSelected(AdapterView<?> arg0, View arg1,
                int pos, long arg3) {
            selected = (File) fileList.getItemAtPosition(pos);    

        }

        public void onNothingSelected(AdapterView<?> arg0) {
            // TODO Auto-generated method stub

        }

    } );