An Android ActionBar + ListFragment example

The source code below corresponds to an article I wrote titled, Android ActionBar example: How to create an options menu item.

package com.valleyprogramming.justbe;

import android.app.ListFragment;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.valleyprogramming.justbe.QuotesDatabaseHelper.QuoteCursor;
import com.example.android.swipedismiss.SwipeDismissListViewTouchListener;

public class QuotesListFragment extends ListFragment {

    private static final String TAG = "VPQuotesListFragment";
    private QuoteCursor quoteCursor;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        getActivity().setTitle(R.string.title_quotes_screen);

        // config the database and adapter
        quoteCursor = DatabaseManager.get(getActivity()).queryQuotes();

        // create an adapter to point at this cursor
        QuoteCursorAdapter adapter = new QuoteCursorAdapter(getActivity(), quoteCursor);
        setListAdapter(adapter);
    }

    @Override
    public void onDestroy() {
        quoteCursor.close();

        //TODO working on possible memory leaks
        quoteCursor = null;
        setListAdapter(null);
        
        super.onDestroy();
    }

    @Override
    public void onActivityCreated(Bundle bundle) {
        super.onActivityCreated(bundle);
        // get a handle to our list view to implement 'swipe to delete'
        ListView listView = getListView();
        // Create a ListView-specific touch listener. ListViews are given special treatment because
        // by default they handle touches for their list items... i.e. they're in charge of drawing
        // the pressed state (the list selector), handling list item clicks, etc.
        SwipeDismissListViewTouchListener touchListener =
            new SwipeDismissListViewTouchListener(
                listView,
                new SwipeDismissListViewTouchListener.DismissCallbacks() {
                    @Override
                    public boolean canDismiss(int position) {
                        return true;
                    }
                    @Override
                    public void onDismiss(ListView listView, int[] reverseSortedPositions) {
                        for (int position : reverseSortedPositions) {
                            String quoteToDelete = ((QuoteCursor)(getListAdapter()).getItem(position)).getQuote().getQuote();
                            DatabaseManager.get(getActivity()).deleteQuote(quoteToDelete);

                            // need the next two lines to update the list after the delete
                            refreshQuoteCursorDataSet();

                            handleCasesWhereThisQuoteWasTheCurrentlyDisplayedQuote(quoteToDelete);
                        }
                    }
                });
        listView.setOnTouchListener(touchListener);
        // setting this scroll listener is required to ensure that during ListView scrolling,
        // we don't look for swipes.
        listView.setOnScrollListener(touchListener.makeScrollListener());

    }
    
    private void handleCasesWhereThisQuoteWasTheCurrentlyDisplayedQuote(String quoteThatWasDeleted) {
        if (GlobalState.currentlyDisplayedQuote.equals(quoteThatWasDeleted)) {
            if (GlobalState.pinnedStateEnabled) {
                // the quote that was deleted was the current quote and it was pinned
                PreferencesUtils.handleUserUnpinsQuoteAction(getActivity().getBaseContext());
                GlobalState.currentlyDisplayedQuote = "";
            } else {
                // the quote that was deleted was the current quote but it was not pinned
                GlobalState.currentlyDisplayedQuote = "";
            }
        }
    }

    /**
     * When a list item is clicked, perform the 'Edit' action.
     */
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        String quote = ((QuoteCursor)(getListAdapter()).getItem(position)).getQuote().getQuote();
        Log.d(TAG, quote + " was clicked");

        // i'm doing this in the long form just for my own knowledge
        Context context = getActivity();
        Class destination = EditQuoteActivity.class;
        Intent intent = new Intent(context, destination);
        intent.putExtra(EditQuoteFragment.KEY_QUOTE_EXTRA, quote);
        startActivityForResult(intent, 0);
    }

    /**
     * update the list of quotes when the user returns, presumably after
     * using the 'edit' screen. p. 197.
     * 
     * the book's code example actually uses onActivityResult instead of
     * onResume. i just tested it, and they both get called, so either one
     * will work.
     */
    @Override
    public void onResume() {
        super.onResume();
        refreshQuoteCursorDataSet();
    }

    /**
     * as the code shows, the cursor needs to be refreshed before
     * notifyDataSetChanged is called
     */
    private void refreshQuoteCursorDataSet() {
        quoteCursor.requery();
        ((QuoteCursorAdapter)getListAdapter()).notifyDataSetChanged();
    }

    /**
     * the menu layout has the 'add/new' menu item
     */
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
        menuInflater.inflate(R.menu.menu_quotes_list, menu);
        super.onCreateOptionsMenu(menu, menuInflater);
    }

    /**
     * react to the user tapping/selecting an options menu item
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_item_new_quote:
                Intent addQuoteIntent = new Intent(getActivity(), NewQuoteActivity.class);
                startActivityForResult(addQuoteIntent, 0);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    /**
     * The adapter class that will work with the ListView.
     */
    private static class QuoteCursorAdapter extends CursorAdapter {

        private QuoteCursor mRunCursor;

        public QuoteCursorAdapter(Context context, QuoteCursor cursor) {
            super(context, cursor, 0);
            mRunCursor = cursor;
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            return inflater.inflate(R.layout.quote_item_view, parent, false);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            // get the quote for the current row
            Quote quote = mRunCursor.getQuote();

            // this works with my custom view (quote_item_view.xml)
            TextView textView = (TextView)view.findViewById(R.id.the_quote);
            textView.setText(quote.getQuote());

            // set the text size
            textView.setTextAppearance(context, R.style.listViewText);

            // confirmed: this works. can also specify alpha as '00-ff' before the rgb
            // component, but that won't do anything in this case
            textView.setBackgroundColor(Color.parseColor("#ffffff"));
        }

    }

}

The reason I show the code here is because it shows how to enable the Android ActionBar, in this case from a Fragment, and more specifically a ListFragment.

Add new comment

The content of this field is kept private and will not be shown publicly.

Anonymous format

  • Allowed HTML tags: <em> <strong> <cite> <code> <ul type> <ol start type> <li> <pre>
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.