androidandroid-listviewandroid-adaptertextwatcherandroid-filterable

Problem with TextWatcher and position of ListView element after applying the filter to the list


I'm working on an android application which shows a list view with data from SQL database.

enter image description here

After a click on a list view element, a new Activity opens with information about it.

I already implemented a Textwatcher to do the search in a list and the list is updated as it should work.

enter image description here

The Problem is that when I click on a list element of updated list, it opens an activity with data from old position e.g by click on Zinn ID: 939. It opens a new Activity with Altbach ID: 940 data. What is the best way to updated the position for the element after TextWatcher works and pass it in onItemClick method? Or how can I get the updated list after TextWatcher works? Thank you in advance!

That is my class.

    public class LibraryActivity extends AppCompatActivity implements View.OnClickListener {

    static String db_name = "test_stolpersteine_db.sqlite";
    StolpersteineDAO stolpersteinedao;
    List<Stolpersteine> stolpersteine_list;
    ListView list_view;
    private ArrayAdapter<CharSequence> adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_library);

        ////////////////////////////// Database ////////////////////////////////////////////////////
        final File dbFile = this.getDatabasePath(db_name); // we create a database path

        if (!dbFile.exists()) {     // we checking if the directory is existing
            try {
                copyDatabaseFile(dbFile.getAbsolutePath());  // method to copy the database
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        ////// Database
        startDatabase();
        stolpersteine_list = stolpersteinedao.getAllStolpersteine();
        list_view = findViewById(R.id.list);
        adapter = createAdapterHtml(stolpersteine_list);
        list_view.setAdapter(adapter);
        ////////////////////////////// Database ////////////////////////////////////////////////////

        //////////////////////////// Buttons ///////////////////////////////////////////////////////
        Button button_home = findViewById(R.id.button_home);
        Button button_map = findViewById(R.id.button_map);
        Button button_library = findViewById(R.id.button_library);

        button_home.setOnClickListener(this);
        button_map.setOnClickListener(this);
        button_library.setOnClickListener(this);

        button_library.setEnabled(false);
        /////////////////////////// Buttons ////////////////////////////////////////////////////////

        EditText theFilter = findViewById(R.id.edit_text);

        theFilter.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
            }
            @Override
            public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
                (LibraryActivity.this).adapter.getFilter().filter(charSequence);
            }
            @Override
            public void afterTextChanged(Editable charSequence) {
            }
        });

        //// OnClickListener
        list_view.setOnItemClickListener(new AdapterView.OnItemClickListener() {


            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                String url = stolpersteine_list.get(position).getURL();
                Intent i = new Intent(getApplicationContext(), RecordActivity.class);

                i.putExtra("url", url);
                i.putExtra("first_name", stolpersteine_list.get(position).getFirst_Name());
                i.putExtra("name", stolpersteine_list.get(position).getName());
                startActivity(i);


            }  // end of listener

        });

    } // end of onCreate method
    

    ////////////////////////////// Database ////////////////////////////////////////////////////////
    private void copyDatabaseFile(String destinationPath) throws IOException {

        InputStream assetsDB = this.getAssets().open(db_name);
        OutputStream dbOut = new FileOutputStream(destinationPath);

        byte[] buffer = new byte[1024];

        int length;

        while ((length = assetsDB.read(buffer)) > 0) {

            dbOut.write(buffer, 0, length);
        }

        dbOut.flush();
        dbOut.close();
    } //end of copyDatabaseFile method


    private ArrayAdapter<CharSequence> createAdapterHtml(List<Stolpersteine> s_list) {

        Spanned[] html_array = new Spanned[s_list.size()];

        for (int i = 0; i < s_list.size(); i++) {
            html_array[i] = Html.fromHtml(
                    s_list.get(i).getName()
                    + " "
                    + "ID: "+ s_list.get(i).getID() + "<br></i>"

                    + s_list.get(i).getHouse_Number() +" "
                    + s_list.get(i).getHouse_Number_Extension() + "</i>");
        }

        ArrayAdapter<CharSequence> my_adapter =
                new ArrayAdapter<CharSequence>(this, R.layout.list_item, html_array);

        return my_adapter;

    } // end of createAdapterHtml

    private void startDatabase() {
        AppDatabase database = Room.databaseBuilder(this, AppDatabase.class, db_name)
                .allowMainThreadQueries()
                .build();

        stolpersteinedao = database.getStolpersteineDAO();

    } // end of startDatabase method
    ////////////////////////////// Database ////////////////////////////////////////////////////////

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.button_home:
                startActivity(new Intent(this, MainActivity.class));
                break;
            case R.id.button_map:
                startActivity(new Intent(this, MapboxActivity.class));
                break;
            case R.id.button_library:
                startActivity(new Intent(this, LibraryActivity.class));
                break;
        }

    }
} // end of class

Solution

  • The problem is that the onItemClick() callback has a parameter of the position after filtering the list; i.e. this position is relative to the filtered list not the original list, although you need to get the actual position from the original list.

    To get the original position (not the filtered one), you can get the filtered item first and get its position using indexOf() from the original list.

    To apply this, change the list_view OnItemClickListener to:

    //// OnClickListener
    list_view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            
            // Getting the clicked item from the filtered list
            Stolpersteine filteredItem = (Stolpersteine) ((ListView) parent).getAdapter().getItem(position);
    
            // Getting the original position
            int originalPos = getList().indexOf(filteredItem);
    
            // Use originalPos instead of position in your code:
            
            String url = stolpersteine_list.get(originalPos).getURL();
            Intent i = new Intent(getApplicationContext(), RecordActivity.class);
    
            i.putExtra("url", url);
            i.putExtra("first_name", stolpersteine_list.get(originalPos).getFirst_Name());
            i.putExtra("name", stolpersteine_list.get(originalPos).getName());
            startActivity(i);
    
    
        }  // end of listener
    });