Maintain filtered listview after rotation in android

I'm using customized ListView in android, and using search with customized EditText. When I search for a name, I will get the result using getFilter().filter(cs);. But, when I rotate, the ListView shows the full list (without filtering), then after a delay, it shows the filtered list.

When I rotate without filtering, it maintains the listview position.

So I have to maintain the filtered ListView when I rotate the screen.

Please help. Thanks in advance...

import java.util.ArrayList;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.LoaderManager;
import android.content.Context;
import android.content.Loader;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.ContactsContract;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.TypedValue;
import android.view.Display;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Filter;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;

@SuppressLint("HandlerLeak")
public  class       ContactsListActivity 
        extends     Activity 
        implements  LoaderManager.LoaderCallbacks<ArrayList<ListViewItem>> {

    private LetterScrollListView    mLSListView;
    private ProgressBar             mProgressBar;
    private EditText                mSearchText;
    private ProgressBar             mSearchProgressBar;
    private LinearLayout            mViewsLl;
    private ContactAdapter          mFullContactAdapter = null;
    private ArrayList<ListViewItem> mFullContactsArrayList = null;

    public boolean  isSoftKeyOn = false;
    public boolean  isPortrait;

    private long            mLastTimeTextChanged    = 0L;
    private long            mCurrentTime = 0L;
    private Filter          mFilter;

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

        RelativeLayout windowRl = (RelativeLayout) findViewById(R.id.window_RelativeLayout);
        LinearLayout progressBarLl = (LinearLayout) windowRl.findViewById(R.id.progressBar_LinearLayout);
        mViewsLl = (LinearLayout) windowRl.findViewById(R.id.views_LinearLayout);
        mSearchText = (EditText) mViewsLl.findViewById(R.id.contact_SearchEditText);
        mSearchProgressBar = (ProgressBar) mViewsLl.findViewById(R.id.search_ProgressBar);
        mLSListView = (LetterScrollListView) mViewsLl.findViewById(R.id.contacts_LetterScrollListView);
        mLSListView.setSearchText(mSearchText);
        if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            isPortrait = true;
        } else {
            isPortrait = false;
        }

        final View activityRootView = windowRl;
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
                new OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        int heightDiff = activityRootView.getRootView()
                                .getHeight() - activityRootView.getHeight();
                        // if heightDiff > 100 pixels, its probably a keyboard...
                        if (heightDiff > 100) {
//                          Toast.makeText(getApplicationContext(), "Key ON",
//                                  Toast.LENGTH_SHORT).show();
                            isSoftKeyOn = true;
                        } else {
//                          Toast.makeText(getApplicationContext(), "Key OFF",
//                                  Toast.LENGTH_SHORT).show();
                            isSoftKeyOn = false;
                            mSearchText.clearFocus();
                        }
                    }
                });     


        mSearchText.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence cs, int arg1, int arg2,
                    int arg3) {
                mCurrentTime = System.currentTimeMillis();              
                if( (mCurrentTime - mLastTimeTextChanged) <= USER_TYPE_DELAY ) {
                    mSearchInputHandler.removeMessages(WHAT_SEARCH);
                }

                // preparing message
                Bundle bundle = new Bundle();
                bundle.putCharSequence(USER_INPUT_KEY, cs);
                Message message = mSearchInputHandler.obtainMessage(WHAT_SEARCH);
                message.setData(bundle);

                mSearchInputHandler.sendMessageDelayed(message, USER_TYPE_DELAY);
                mLastTimeTextChanged = mCurrentTime;
            }

            @Override
            public void beforeTextChanged(CharSequence arg0, int arg1,
                    int arg2, int arg3) {
                // TODO Auto-generated method stub
            }

            @Override
            public void afterTextChanged(Editable arg0) {
                // TODO Auto-generated method stub
            }
        });


        // Create a progress bar to display while the list loads
        mProgressBar = new ProgressBar(this);
        mProgressBar.setIndeterminate(true);
        progressBarLl.addView(mProgressBar);

        getLoaderManager().initLoader(0, null, this).forceLoad();
    }

    @SuppressLint("HandlerLeak")
    private final Handler mSearchInputHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            CharSequence cs = msg.getData().getCharSequence(USER_INPUT_KEY);
            int what = msg.what;

            switch(what) {
            case WHAT_SEARCH:

                // When user changed the Text after a delay
                mSearchProgressBar.setVisibility(View.VISIBLE);
                mFilter.filter(cs);

                break;
            }
        }
    };

    public void removeSearchProgressBar() {
        mSearchProgressBar.setVisibility(View.INVISIBLE);
    }

    /** 
     * Called when a new Loader needs to be created 
     */
    public Loader<ArrayList<ListViewItem>> onCreateLoader(int id, Bundle args) {
        // Now create and return a CursorLoader that will take care of
        // creating a Cursor for the data being displayed.
        Uri uri = ContactsContract.Contacts.CONTENT_URI;
        String[] projection = new String[] {
                ContactsContract.Contacts._ID,
                ContactsContract.Contacts.DISPLAY_NAME
        };
        boolean showInvisible = false;
        String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" +
                (showInvisible ? "0" : "1") + "'";
        String[] selectionArgs = null;
        String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
        return new ContactArrayListLoader(this, uri, projection, selection, selectionArgs, sortOrder);
    }

    /** 
     * Called when a previously created loader has finished loading 
     */
    public void onLoadFinished(Loader<ArrayList<ListViewItem>> loader, ArrayList<ListViewItem> data) {
        mProgressBar.setVisibility(View.INVISIBLE);

        if(mLSListView.getAdapter() == null) {
            mFullContactsArrayList = data;
            mFullContactAdapter = new ContactAdapter(this, R.layout.contact_tuple, mFullContactsArrayList);
            mFilter = mFullContactAdapter.getFilter();
            mLSListView.setAdapter(mFullContactAdapter);
        }
    }

    /** 
     * Called when a previously created loader is reset, making the data unavailable
     */
    public void onLoaderReset(Loader<ArrayList<ListViewItem>> loader) {
        // This is called when the last Cursor provided to onLoadFinished()
        // above is about to be closed. We need to make sure we are no
        // longer using it.
        //////mListView.setAdapter(new ContactAdapter(this, R.layout.contact_tuple, null));
        mProgressBar.setVisibility(View.VISIBLE);
    }
}

Answers


SOLVED

The problem is on the method

public void onLoadFinished(Loader<ArrayList<ListViewItem>> loader, ArrayList<ListViewItem> data) {....}

On rotation/starting the app, the parameter data in this method is always the complete list and not the filtered list. So to get the filtered list on rotation, just create a static member

private static ArrayList<ListViewItem>  sCurrentContactsArrayList = null;

and save before screen rotation like this:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if( TextUtils.isEmpty(mSearchText.getText()) ) {
        sCurrentContactsArrayList = null;
    } else {
        sCurrentContactsArrayList = mFullContactAdapter.getCurrentItems();
    }
}

also modify onLoadFinished():

public void onLoadFinished(Loader<ArrayList<ListViewItem>> loader, ArrayList<ListViewItem> data) {
    mProgressBar.setVisibility(View.INVISIBLE);

    if(mLSListView.getAdapter() == null) {
        ArrayList<ListViewItem> contactsAL = null;
        if(sCurrentContactsArrayList == null) {
            contactsAL = data;
        } else {
            contactsAL = sCurrentContactsArrayList;
        }
        mFullContactAdapter = new ContactAdapter(this, R.layout.contact_tuple, contactsAL, data);
        if(mFilter == null) mFilter = mFullContactAdapter.getFilter();
        mLSListView.setAdapter(mFullContactAdapter);
    }
    mSearchText.setVisibility(View.VISIBLE);
}

Need Your Help

Updating listview android on textchanged StringIndexOutOfBoundsException

android listview textchanged

I'm having trouble with updating my listview ontextchanged. When I enter one letter works fine, but on the second letter the app crashes. What can be the problem?

How to encode video from multiple cameras into one mkv stream?

java c++ c mkv

How to encode multiple video and audio streams (we want streams not to be any how encoded) from cameras into one mkv so, that sound from camera A is encoded with respect to video from camera A, using