/* * @copyright 2013 Philip Warner * @license GNU General Public License * * This file is part of Book Catalogue. * * Book Catalogue is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Book Catalogue is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Book Catalogue. If not, see <http://www.gnu.org/licenses/>. */ package com.eleybourn.bookcatalogue.widgets; import java.util.ArrayList; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.ViewParent; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; import com.eleybourn.bookcatalogue.R; import com.eleybourn.bookcatalogue.utils.Logger; import com.eleybourn.bookcatalogue.utils.ViewTagger; /** * ArrayAdapter to manage rows of an arbitrary type with row movement via clicking on predefined * sub-views, if present. * * @author Philip Warner */ public abstract class SimpleListAdapter<T> extends ArrayAdapter<T> { private int mRowViewId; private boolean mCheckedFields = false; private boolean mHasPosition = false; private boolean mHasUp = false; private boolean mHasDown = false; private boolean mHasDelete = false; private ArrayList<T> mItems; public SimpleListAdapter(Context context, int rowViewId, ArrayList<T> items) { super(context, rowViewId, items); mRowViewId = rowViewId; mItems = items; } protected void onListChanged() {}; protected void onRowClick(T object, int position, View v) {}; protected void onRowDelete(T object, int position, View v) {}; protected void onRowDown(T object, int position, View v) {}; protected void onRowUp(T object, int position, View v) {}; /** * Call to set up the row view. * * @param target The target row view object * @param object The object (or type T) from which to draw values. */ abstract protected void onSetupView(T object, int position, View target); private OnClickListener mRowClickListener = new OnClickListener() { @Override public void onClick(View v) { try { int pos = getViewRow(v); T item = getItem(pos); onRowClick(item, pos, v); } catch (Exception e) { Logger.logError(e); } } }; private OnClickListener mRowDeleteListener = new OnClickListener() { @Override public void onClick(View v) { if (v == null) return; int pos = getViewRow(v); T old = getItem(pos); try { onRowDelete(old, pos, v); remove(old); notifyDataSetChanged(); onListChanged(); } catch (Exception e) { // TODO: Allow a specific exception to cancel the action Logger.logError(e); } } }; private OnClickListener mRowDownListener = new OnClickListener() { @Override public void onClick(View v) { int pos = getViewRow(v); if (pos == (getCount()-1) ) return; T old = getItem(pos); try { onRowDown(old, pos, v); mItems.set(pos, getItem(pos+1)); mItems.set(pos+1, old); notifyDataSetChanged(); onListChanged(); } catch (Exception e) { // TODO: Allow a specific exception to cancel the action Logger.logError(e); } } }; private OnClickListener mRowUpListener = new OnClickListener() { @Override public void onClick(View v) { int pos = getViewRow(v); if (pos == 0) return; T old = getItem(pos-1); try { onRowUp(old, pos, v); mItems.set(pos-1, getItem(pos)); mItems.set(pos, old); notifyDataSetChanged(); onListChanged(); } catch (Exception e) { // TODO: Allow a specific exception to cancel the action Logger.logError(e); } } }; /** * Interface to allow underlying objects to determine their vewi ID. */ public interface ViewProvider { public int getViewId(); } @Override public View getView(int position, View convertView, ViewGroup parent) { // Get the object, if not null, do some processing final T o = this.getItem(position); // Get the view; if not defined, load it. View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); // If possible, ask the object for the view ID if (o != null && o instanceof ViewProvider) { v = vi.inflate(((ViewProvider)o).getViewId(), null); } else { v = vi.inflate(mRowViewId, null); } } // Save this views position ViewTagger.setTag(v, R.id.TAG_POSITION, Integer.valueOf(position)); { // Giving the whole row an onClickListener seems to interfere // with drag/drop. View details = v.findViewById(R.id.row_details); if (details == null) { details = v.findViewById(R.id.row); } if (details != null) { details.setOnClickListener(mRowClickListener); details.setFocusable(false); } } // If the object is not null, do some processing if (o != null) { // Try to set position value if (mHasPosition || !mCheckedFields) { TextView pt = (TextView) v.findViewById(R.id.row_position); if(pt != null){ mHasPosition = true; pt.setText(Long.toString(position+1)); } } // Try to set the UP handler if (mHasUp || !mCheckedFields) { ImageView up = (ImageView) v.findViewById(R.id.row_up); if (up != null) { up.setOnClickListener(mRowUpListener); mHasUp = true; } } // Try to set the DOWN handler if (mHasDown || !mCheckedFields) { ImageView dn = (ImageView) v.findViewById(R.id.row_down); if (dn != null) { dn.setOnClickListener(mRowDownListener); mHasDown = true; } } // Try to set the DELETE handler if (mHasDelete || !mCheckedFields) { ImageView del = (ImageView) v.findViewById(R.id.row_delete); if (del != null) { del.setImageResource(android.R.drawable.ic_delete); del.setOnClickListener(mRowDeleteListener); mHasDelete = true; } } // Ask the subclass to set other fields. try { onSetupView(o, position, v); } catch (Exception e) { Logger.logError(e); } v.setBackgroundResource(android.R.drawable.list_selector_background); mCheckedFields = true; } return v; } /** * Find the first ancestor that has the ID R.id.row. This * will be the complete row View. Use the TAG on that to get * the physical row number. * * @param v View to search from * * @return The row view. */ private Integer getViewRow(View v) { View pv = v; while(pv.getId() != R.id.row) { ViewParent p = pv.getParent(); if (!(p instanceof View)) throw new RuntimeException("Could not find row view in view ancestors"); pv = (View) p; } Object o = ViewTagger.getTag(pv, R.id.TAG_POSITION); if (o == null) throw new RuntimeException("A view with the tag R.id.row was found, but it is not the view for the row"); return (Integer) o; } }