Search code examples
androidlistviewarraylistsearchview

Getting original position of arraylist for listview


I was trying to create a search function for listview, and putting the intent activity for the results. However, as the search results appear, the position changed. For example, original position should be 0,1. But after searched, the results turn the position to 3,4. I tried finding many solutions for this, but nothing helps. Thanks in advance !

Here the homefragment.java

package com.example.testtt;

import androidx.cardview.widget.CardView;
import androidx.fragment.app.Fragment;;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.SearchView;
import android.widget.Toast;

import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.Arrays;


public class HomeFragment extends Fragment implements View.OnClickListener {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {


        View v = inflater.inflate(R.layout.fragment_home, container, false);

        //define
        RelativeLayout drawerlayouthome;
        ListView list;
        ListViewAdapter adapter;
        SearchView editsearch;
        ArrayList<DoaSearchIndex> arrayList = new ArrayList<DoaSearchIndex>();




        String[] doasearchindex = getActivity().getResources().getStringArray(R.array.doasearchindex);

        //locatelistview
        list = v.findViewById(R.id.searchlistview);
        list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {

                Intent intent;
                switch (i){
                    case 0:
                        intent = new Intent(getActivity(), firstactivity.class);
                        startActivity(intent);
                        getId();
                        break;
                    case 1:
                        intent = new Intent(getActivity(), secondactivity.class);
                        startActivity(intent);
                        getId();
                        break;

                }
                list.setVisibility(View.INVISIBLE);
            }
        });

        for (int i = 0; i < doasearchindex.length; i++) {
            DoaSearchIndex doaSearchIndex = new DoaSearchIndex(doasearchindex[i]);

            arrayList.add(doaSearchIndex);
        }

        //pass result to listviewadapter.class

        adapter = new ListViewAdapter(this.getActivity(), arrayList);

        //binds adapter to listview

        list.setAdapter(adapter);
        list.setVisibility(View.INVISIBLE);

        //locatesearchview

        editsearch = v.findViewById(R.id.searchview);

        editsearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                if(TextUtils.isEmpty(newText)){
                    list.clearTextFilter();
                    adapter.filter("");
                    list.setVisibility(View.INVISIBLE);
                } else {
                    adapter.filter(newText);
                    list.setVisibility(View.VISIBLE);
                }
                return true;
            }
        });

        drawerlayouthome = v.findViewById(R.id.drawer_layouthome);
        drawerlayouthome.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                list.setVisibility(View.INVISIBLE);
                editsearch.clearFocus();
            }
        });





        //definingcardview

        CardView test = v.findViewById(R.id.test);
        CardView testcard = v.findViewById(R.id.testcard);

        //setcardviewlistener

        test.setOnClickListener(this);
        testcard.setOnClickListener(this);






        return v;

    }




    @Override
    public void onClick(View view) {

        Intent i ;

        switch (view.getId()) {

            case R.id.test:
                i = new Intent(getActivity(), test.class);
            startActivity(i);
            break;
            case R.id.testcard:
                i = new Intent(getActivity(), testcard.class);
                startActivity(i);
                break;

        }

    }

}

and ListViewAdapter.java

package com.example.testtt;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class ListViewAdapter extends BaseAdapter {

    //Declare variables first

    Context mContext;
    LayoutInflater inflater;
    private List<DoaSearchIndex> doaSearchIndexList = null;
    private ArrayList<DoaSearchIndex> arrayList;

    public ListViewAdapter(Context context, List<DoaSearchIndex> doaSearchIndexList) {
        mContext = context;
        this.doaSearchIndexList = doaSearchIndexList;
        inflater = LayoutInflater.from(mContext);
        this.arrayList = new ArrayList<DoaSearchIndex>();
        this.arrayList.addAll(doaSearchIndexList);
    }
    public class ViewHolder {
        TextView name;
    }

    @Override
    public int getCount() {
        return doaSearchIndexList.size();
    }

    @Override
    public DoaSearchIndex getItem(int position) {
        return doaSearchIndexList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    public View getView(final int position, View view, ViewGroup parent) {
        final ViewHolder holder;
        if (view == null) {
            holder = new ViewHolder();
            view = inflater.inflate(R.layout.listview_item, null);
            // Locate the TextViews in listview_item.xml
            holder.name = (TextView) view.findViewById(R.id.name);
            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }
        // Set the results into TextViews
        holder.name.setText(doaSearchIndexList.get(position).getDoaSearchIndex());
        return view;
    }

    // Filter Class
    public void filter(String charText) {
        charText = charText.toLowerCase(Locale.getDefault());
        doaSearchIndexList.clear();
        if (charText.length() == 0) {
            doaSearchIndexList.addAll(arrayList);
        } else {
            for (DoaSearchIndex wp : arrayList) {
                if (wp.getDoaSearchIndex().toLowerCase(Locale.getDefault()).contains(charText)) {
                    doaSearchIndexList.add(wp);
                }
            }
        }
        notifyDataSetChanged();
    }
    

}

and the DoaSearchIndex.java


public class DoaSearchIndex {

    private String doaSearchIndex;
    private int originalPosition;

    public DoaSearchIndex(String doaSearchIndex) {
        this.doaSearchIndex = doaSearchIndex;
    }

    public String getDoaSearchIndex() {
        return this.doaSearchIndex;
    }

    private int getPosition() {
        return this.originalPosition;
    }
}


Solution

  • Here is my example Fragment:

        public class MyDefaultFragment extends Fragment {
    
        //Default items
        public static ArrayList<MyItem> myItems = new ArrayList<>();
    
        //Search filter
        public static String filter = "";
    
        public MyDefaultFragment(){
            //require a empty constructor
        }
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
    
            View view = inflater.inflate(R.layout.defaultfragment,container,false);
    
                //I used a EditText instead of a SearchView
                EditText editText = view.findViewById(R.id.filterEditText);
    
                RecyclerView recyclerView = view.findViewById(R.id.recyclerView);
                recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
                recyclerView.setHasFixedSize(true);
    
    
            //Checks for text change
            Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
    
                filter = editText.getText().toString();
                handler.postDelayed(this,500);
    
                }
            },500);
    
    
                //Updates the Database every second | 1000 millisecond = 1 second
                Handler update = new Handler();
                update.postDelayed(new Runnable() {
                    @Override
                    public void run() {
    
                        new MyDatabase.AsyncTask(MyDatabase.getInstance(getContext())).execute();
                        update.postDelayed(this,1000);
                    }
                },1000);
    
                //Creates a new object in the Adapter.class
                Adapter adapter = new Adapter();
                myItems.clear();
    
    
                //example items
                myItems.add(new MyItem(R.drawable.bcn100, "firstActivity"));
                myItems.add(new MyItem(R.drawable.ada100, "secondActivity"));
                myItems.add(new MyItem(R.drawable.cos100, "otherActivity"));
                recyclerView.setAdapter(adapter);
    
                //Updates the Database on open the Fragment
                new MyDatabase.AsyncTask(MyDatabase.getInstance(getContext())).execute();
                
                //observe the LiveData<List<MyItem>> to an List<MyItem>
                MyItemViewModel myItemViewModel = new ViewModelProvider(this).get(MyItemViewModel.class);
                myItemViewModel.getAllItems().observe(getViewLifecycleOwner(), adapter::setItems);
                
            return view;
        }
    }
    

    Here is the Adapter for the CardView:

    public class Adapter extends RecyclerView.Adapter<Adapter.CardHolder> {
    
    
        //Item list of the Adapter
        public static List<MyItem> items = new ArrayList<>();
    
        @NonNull
        @Override
        public CardHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                                                                    //CardView the name of the layout file smallcard
            View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.smallcard, parent, false);
            CardHolder smallCardHolder = new CardHolder(itemView);
            return smallCardHolder;
        }
    
        @Override
        public void onBindViewHolder(@NonNull final CardHolder holder, int position) {
    
            //get the item of the position
            MyItem myItem = items.get(position);
            int image = myItem.getMyImage();
            String name = myItem.getMyName();
    
            holder.imageView.setImageResource(image);
            holder.textViewName.setText(name);
    
            //onClickListener for the cardView
            if(holder.cardView!= null) {
                holder.cardView.setOnClickListener(v -> {
                    openActivity(holder.textViewName.getText().toString());
                });
            }
        }
    
    
    
        //You have to put here the method to open the activitys
        public void openActivity(String name){
    
            switch (name){
    
                case "firstActivity":{
                    System.out.println("Open firstActivity");
                    break;
                } case "secondActivity":{
                    System.out.println("Open secondActivity");
                    break;
                } case "otherActivity":{
                    System.out.println("Open otherActivity");
                    break;
                }
    
                default:
                    break;
            }
    
    
        }
    
        //Amount of items
        @Override
        public int getItemCount () {
            return items.size();
        }
    
    
        public void setItems(List <MyItem> items) {
            this.items = items;
            notifyDataSetChanged();
        }
    
    
    
        //Card Holder
        class CardHolder extends RecyclerView.ViewHolder {
    
    
            private CardView cardView;
            private ImageView imageView;
            private TextView textViewName;
    
            public CardHolder(@NonNull View itemView) {
                super(itemView);
                cardView = itemView.findViewById(R.id.card_View);
                imageView = itemView.findViewById(R.id.card_Image);
                textViewName = itemView.findViewById(R.id.card_Name);
    
            }
        }
    }
    

    Here's the Database to insert/delete the CardView

            //If you change the database schema you have to update the current version +1
    @Database(entities = {MyItem.class},version = 1)
    public abstract class MyDatabase extends RoomDatabase {
    
        private static MyDatabase instance;
    
        public abstract MyItemDao myItemDao();
    
    
        public static synchronized MyDatabase getInstance(Context context){
    
            //if the instance is null
            if(instance ==  null){
                instance = Room.databaseBuilder(context,MyDatabase.class,"my_Database")
                        .fallbackToDestructiveMigration()
                        .addCallback(callback)
                        .setAutoCloseTimeout(1, TimeUnit.SECONDS)
                        .build();
            }
            return instance;
        }
        private static Callback callback = new Callback() {
            @Override
            public void onCreate(@NonNull SupportSQLiteDatabase db) {
                super.onCreate(db);
                new AsyncTask(instance).execute();
            }
        };
    
        // The Async Task is deprecated you can use the Kotlin CoroutineAsyncTask instead of android.os.AsyncTask
        // If you want to change it, this will help you
        // https://stackoverflow.com/questions/68136577/how-to-replace-asynctask-in-this-android-code/68137304#68137304
        public static class AsyncTask extends android.os.AsyncTask<Void,Void,Void>{
    
            MyItemDao myItemDao;
    
            public AsyncTask(MyDatabase myDatabase){
                this.myItemDao = myDatabase.myItemDao();
            }
    
            @Override
            protected Void doInBackground(Void... voids) {
    
                //default Items
                List<MyItem> allDefaultItems = MyDefaultFragment.myItems;
    
    
                //Current listed items in the RecyclerView
                List<MyItem> allAdapterItems = Adapter.items;
                String filter = MyDefaultFragment.filter;
                int length = filter.length();
    
    
                //Search method
                if(length >0){
                    List<MyItem> deleteItems = new ArrayList<>();
                    List<MyItem> updateItems = new ArrayList<>();
    
                    allDefaultItems.forEach(myItem -> {
        if(myItem.myName.toLowerCase().startsWith(filter)){
                updateItems.add(myItem);
               }else{
              deleteItems.add(myItem);
            }
        });
    
                    myItemDao.delete(deleteItems);
                    myItemDao.insertAll(updateItems);
    
                }else{
                    myItemDao.insertAll(allDefaultItems);
                }
                return null;
            }
        }
    }
    

    MyItem:

    //If you want can change the tableName you have also change the name in the MyItemDao interface
    @Entity(tableName = "myTable")
    public class MyItem {
    
        int myImage;
    
    
        //A PrimaryKey is require for a Database
        @PrimaryKey
        @NonNull
        String myName;
    
        public MyItem(int myImage,String myName){
            this.myImage = myImage;
            this.myName = myName;
        }
    
        public int getMyImage() {
            return myImage;
        }
    
        public void setMyImage(int myImage) {
            this.myImage = myImage;
        }
    
        public String getMyName() {
            return myName;
        }
    
        public void setMyName(String myName) {
            this.myName = myName;
        }
    }
    

    MyItemDao.java:

    @Dao
    public interface MyItemDao {
        
        //Select all data from myTable
        @Query("Select * From myTable")
        LiveData<List<MyItem>> getAllItems();
        
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        void insertAll(List<MyItem> myItems);
    
        @Delete
        void delete(List<MyItem> myItems);
    
        @Query("Delete from myTable")
        void deleteAllItems();
    }
    

    Repository:

    public class MyItemRepository {
    
        private LiveData<List<MyItem>> allItems;
        private MyItemDao myItemDao;
        
        public MyItemRepository(Application application){
    
            MyDatabase myDatabase = MyDatabase.getInstance(application);
            myItemDao = myDatabase.myItemDao();
            allItems = myItemDao.getAllItems();
        }
        public void insertAll(List<MyItem> yItems){ new InsertAllItemsAsyncTask(myItemDao).execute(myItems); }
    
        public void deleteItems(List<MyItem> myItems){
            new DeleteItemListAsyncTask(myItemDao).execute(myItems);
        }
    
        public void deleteAllItems(){
            new DeleteAllItemsAsyncTask(myItemDao).execute();
        }
        public LiveData<List<MyItem>> getAllItems(){
            return allItems;
        }
    
    
        //Async tasks to put the items to the Database
        private static class InsertAllItemsAsyncTask extends AsyncTask<List<MyItem>,Void,Void>{
            MyItemDao myItemDao;
    
            private InsertAllItemsAsyncTask(MyItemDao myItemDao){
                this.myItemDao = myItemDao;
            }
    
            @Override
            protected Void doInBackground(List<MyItem>... lists) {
                myItemDao.insertAll(lists[0]);
                return null;
            }
        }
        private static class DeleteAllItemsAsyncTask extends AsyncTask<Void,Void,Void>{
            MyItemDao myItemDao;
    
            private DeleteAllItemsAsyncTask(MyItemDao myItemDao){
                this.myItemDao = myItemDao;
            }
    
            @Override
            protected Void doInBackground(Void... voids) {
                myItemDao.deleteAllItems();
                return null;
            }
        }
        private static class DeleteItemListAsyncTask extends AsyncTask<List<MyItem>,Void,Void>{
            MyItemDao myItemDao;
    
            private DeleteItemListAsyncTask(MyItemDao myItemDao){
                this.myItemDao = myItemDao;
            }
    
            @Override
            protected Void doInBackground(List<MyItem>... myItems) {
                myItemDao.delete(myItems[0]);
                return null;
            }
        }
    }
    

    ViewModel:

    public class MyItemViewModel extends AndroidViewModel {
    
        //LiveData is require for the RoomDatabase
        private LiveData<List<MyItem>> allItems;
        private MyItemRepository myItemRepository;
    
        public MyItemViewModel(Application application){
            super(application);
            this.myItemRepository = new MyItemRepository(application);
            allItems = myItemRepository.getAllItems();
        }
    
        public LiveData<List<MyItem>> getAllItems(){
            return allItems;
        }
        public void insertAll(List<MyItem> myItems){
            myItemRepository.insertAll(myItems);
        }
    
        //deletes a List of items
        public void delete(List<MyItem> myItems){
            myItemRepository.deleteItems(myItems);
        }
        public void deleteAllItems(){
            myItemRepository.deleteAllItems();
        }
    }
    

    CardView I called it smallcard.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    android:layout_width="match_parent"
                    android:orientation="horizontal"
                    android:layout_height="wrap_content">
        
        <androidx.cardview.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginHorizontal="10dp"
                android:layout_marginVertical="5dp"
                android:backgroundTint="#ffffff"
                android:id="@+id/card_View">
    
            <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">
    
                <ImageView
                        android:id="@+id/card_Image"
                        android:layout_width="100px"
                        android:layout_height="100px"
                        android:layout_marginStart="10dp"
                        android:scaleType="fitCenter"/>
    
                    <TextView
                            android:id="@+id/card_Name"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_centerHorizontal="true"
                            android:layout_centerVertical="true"
                            android:text="Name"
                            android:textColor="?attr/colorPrimary"
                            android:textSize="18sp"
                            android:textStyle="bold"/>
                
            </RelativeLayout>
        </androidx.cardview.widget.CardView>
    </RelativeLayout>
    

    The test Fragment.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    xmlns:tools="http://schemas.android.com/tools"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    tools:context=".MyDefaultFragment"
                    android:background="#ffffff">
    
        <EditText
                android:id="@+id/filterEditText"
                android:layout_marginTop="10dp"
                android:layout_width="match_parent"
                android:layout_height="40dp"
        />
    
        <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:clipToPadding="false"
                android:layout_below="@id/filterEditText"
                android:layout_marginTop="5dp"
                android:layout_marginBottom="5dp"
        />
    
    </RelativeLayout>
    

    some build.gradle(Module) implementations:

    dependencies{
    
    implementation 'androidx.room:room-runtime:2.3.0'
    annotationProcessor 'androidx.room:room-compiler:2.3.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel:2.3.1'
    implementation 'androidx.lifecycle:lifecycle-livedata:2.3.1'
    annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.3.1'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    ...
    }
    

    You will find more information as comments in the code.


    UPDATE

    In addition I provide you the whole project on github. I added different intents after clicking the Cards. It will open the same Fragment for the same Card even you are searching and the positions are changed.