Change row layout for custom CursorAdapter - android

I want to change the row layout when an specific button(action) is made.
public class DbCursorAdapter extends CursorAdapter {
private View selectedView;
private boolean isListSingleColumn = true;
private Cursor mCursor;
private final LayoutInflater mInflater;
private static final int TYPE_ITEM_SINGLE_COLUMN = 0;
private static final int TYPE_ITEM_MULTI_COLUMN = 1;
/**
* DbCursorAdapter constructor
*
* #param context
* - The context
* #param cursor
* - The cursor used to make queries
*/
public DbCursorAdapter(Context context, Cursor cursor) {
super(context, cursor, false);
this.mContext = context;
this.mCursor = cursor;
this.mInflater = LayoutInflater.from(context);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder holder = (ViewHolder) view.getTag();
String collection = mCursor.getString(mCursor
.getColumnIndex(DatabaseHelper.COLUMN_COLLECTION));
String fileName = mCursor.getString(mCursor
.getColumnIndex(DatabaseHelper.COLUMN_FILE_NAME));
holder.title.setText(fileName);
if (collection.equals("true")) {
// different folder icon for multi-column list
holder.icon
.setImageResource(isListSingleColumn ? R.drawable.ic_file_folder2
: R.drawable.ic_file_folder);
holder.details.setText("");
} else {
String extension = fileName
.substring(fileName.lastIndexOf(".") + 1);
extension = extension.toLowerCase();
String size = mCursor.getString(mCursor
.getColumnIndex(DatabaseHelper.COLUMN_RESOURCE_LENGTH));
String actualSize = MemoryManagerHelper.getInstance().getFileSize(
Float.parseFloat(size));
holder.icon.setImageResource(Utils.INSTANCE
.getImageResourceForFileType(extension));
holder.details.setText(actualSize);
}
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
/*
* Inflates the item layout. Stores resource IDs in a in a ViewHolder
* class to prevent having to look them up each time bindView() is
* called.
*/
final View itemView = mInflater.inflate(
isListSingleColumn ? R.layout.explorer_row_single_column
: R.layout.explorer_row_multi_column, viewGroup, false);
final ViewHolder holder = new ViewHolder();
holder.title = (TextView) itemView.findViewById(R.id.rowtitle);
holder.details = (TextView) itemView.findViewById(R.id.rowSubtitle);
holder.icon = (ImageView) itemView.findViewById(R.id.icon);
itemView.setTag(holder);
return itemView;
}
#Override
public int getItemViewType(int position) {
return isListSingleColumn ? TYPE_ITEM_SINGLE_COLUMN
: TYPE_ITEM_MULTI_COLUMN;
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
this.mCursor = cursor;
}
/**
* #return <b>true</b> if there is an item selected, <b>false</b> otherwise.
*/
public boolean getSelectedItemState() {
return null == selectedView ? false : true;
}
/**
* Set the selected item.
*
* #param view
* The item which will be set as selected
*/
public void setSelectedItem(View view) {
selectedView = view;
view.setBackgroundResource(R.drawable.explorer_row_selected);
}
/**
* If any item is selected we clear that item.
*/
public void clearSelectedItem() {
if (null != selectedView) {
selectedView.setBackgroundResource(android.R.color.transparent);
// invalidate the selected item
selectedView = null;
}
}
private class ViewHolder {
private TextView title;
private TextView details;
private ImageView icon;
}
public boolean isListSingleColumn() {
return isListSingleColumn;
}
public void setListSingleColumn(boolean isListSingleColumn) {
this.isListSingleColumn = isListSingleColumn;
}
The problem is that the layout isn't changed correctly for all items, some are displayed with the layout changed and some aren't. Also, when scrolling the layout for the items seem to change, sometimes it takes the correct layout, sometimes it takes the wrong layout.
I added an little workaround, detecting when the wrong layout is used and I tried to manually create the correct view, but this doesn't seem work.
Here is how I call my CursorAdapter:
/**
* Change the number of columns the list view will upgrade.
*
* #param item
* - The menu action button for the toggle option
*/
private void changeGridColumns(MenuItem item) {
if (isListSingleColumn) {
listview.setNumColumns(2);
item.setIcon(R.drawable.ic_menu_listgrid2);
mAdapter.setListSingleColumn(false);
mAdapter.notifyDataSetChanged();
} else {
// Set to display list with only 1 column
listview.setNumColumns(1);
item.setIcon(R.drawable.ic_menu_listgrid);
mAdapter.setListSingleColumn(true);
mAdapter.notifyDataSetChanged();
}
isListSingleColumn = !isListSingleColumn;
mAdapter.clearSelectedItem();
}
How can I fix this issue?

That's not how you should be managing the layout change of the items. You should use the getItemViewType() and getViewTypeCount methods:
public static final int SINGLE = 0;
public static final int MULTIPLE = 1;
#Override
public int getItemViewType(int position) {
return isListSingleColumn ? SINGLE : MULTIPLE; // call notifyDataSetChanged when you modify isListSingleColumn
}
#Override
public int getViewTypeCount() {
return 2;
}
// in the newView() method:
final int position = cursor.getPosition();
final int type = getItemViewType(position);
View itemView
if (type == SINGLE) {
itemView = mInflater.inflate(R.layout.explorer_row_single_column, viewGroup, false);
} else {
itemView = mInflater.inflate(R.layout.explorer_row_multi_column, viewGroup, false);
}
// rest of the code
Also, as its name implies, the bindView() method is to be used to bind data to the row view that you receive, I don't see why did you built the item's row again there.

I think I fixed the issue by using overriding getItemViewType method:
#Override
public int getItemViewType(int position) {
return isListSingleColumn ? TYPE_ITEM_SINGLE_COLUMN
: TYPE_ITEM_MULTI_COLUMN;
}
#Override
public int getViewTypeCount() {
return 2;
}
I will also updated the code.

Related

Slow listView due to weird SQLite database setup. Looking for an efficient way to handle this

I have a very specific problem. I'm creating a word list app that basically lets me enter some words and their meanings and stores them in an SQLite database and then it shows them back to me. I've made my own content provider to handle all these interactions. I store the words in one table and the definitions in another table and these two tables are linked together through the books' unique id. I'm also using a CursorLoader and a CursorAdapter to populate my listView in my MainActivity. Now my problem is when I get the list of the words that I have in my database, some of the words have more than one definition and I have to query against the database as I'm drawing each list item in my listView to find the definitions and put them on the list. This has caused my listView to stutter. I've even tried to use AsyncTask but I don't want to start a new thread every time a word is getting added to my listView at runtime. Is there any efficient, right way to do this?
This is my CursorAdapter class where I believe is the source of the problem.
public class WordAdapter extends CursorAdapter {
public static final String[] definitionsProjection = {
Definitions._ID,
Definitions.COLUMN_TEXT,
Definitions.COLUMN_POS
};
public static final String[] examplesProjection = {
Examples._ID,
Examples.COLUMN_TITLE,
Examples.COLUMN_TEXT,
Examples.COLUMN_URL
};
public static final int DEFINITIONS_COLUMN_TEXT = 1;
public static final int DEFINITIONS_COLUMN_POS = 2;
public static final int EXAMPLES_COLUMN_TITLE = 1;
public static final int EXAMPLES_COLUMN_TEXT = 2;
public static final int EXAMPLES_COLUMN_URL = 3;
private static final int VIEW_TYPE_TODAY = 0;
private static final int VIEW_TYPE_OLD = 1;
public WordAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getItemViewType(int position) {
if(position == 0) {
return VIEW_TYPE_TODAY;
} else {
return VIEW_TYPE_OLD;
}
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
int viewType = getItemViewType(cursor.getPosition());
int layoutId = -1;
if(viewType == VIEW_TYPE_TODAY) {
layoutId = R.layout.list_item_today;
} else if(viewType == VIEW_TYPE_OLD) {
layoutId = R.layout.list_item_word;
}
View view = LayoutInflater.from(context).inflate(layoutId, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
view.setTag(viewHolder);
return view;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder viewHolder = (ViewHolder) view.getTag();
int viewType = getItemViewType(cursor.getPosition());
String wordId = cursor.getString(MainFragment.WORDS_ID);
viewHolder.wordView.setText(wordId + " / " + cursor.getString(MainFragment.WORDS_COLUMN_WORD));
FetchDefinitions fetchDefinitions = new FetchDefinitions(context, view);
fetchDefinitions.execute(wordId);
switch (viewType) {
case VIEW_TYPE_TODAY:
{
viewHolder.noteView.setText(cursor.getString(MainFragment.WORDS_COLUMN_NOTE));
Cursor exampleCursor = context.getContentResolver().query(
Examples.CONTENT_URI.buildUpon().appendPath(wordId).build(),
examplesProjection,
null,
null,
null
);
exampleCursor.moveToFirst();
viewHolder.exampleOneView.setText(exampleCursor.getString(EXAMPLES_COLUMN_TITLE));
if(exampleCursor.getCount() > 1) {
exampleCursor.moveToNext();
viewHolder.exampleTwoView.setText(exampleCursor.getString(EXAMPLES_COLUMN_TITLE));
} else {
viewHolder.exampleTwoView.setVisibility(View.GONE);
}
exampleCursor.close();
break;
}
}
viewHolder.flagView.setText("Flag " + Utility.flagsToString(cursor.getInt(MainFragment.WORDS_COLUMN_FLAG)));
viewHolder.sourceView.setText("Source " + cursor.getString(MainFragment.WORDS_COLUMN_SOURCE));
viewHolder.dateView.setText(Utility.convertDateToUXFormat(cursor.getInt(MainFragment.WORDS_COLUMN_DATE)));
}
private static class ViewHolder {
public final TextView wordView;
public final TextView noteView;
public final TextView exampleOneView;
public final TextView exampleTwoView;
public final TextView flagView;
public final TextView sourceView;
public final TextView dateView;
private ViewHolder(View view) {
wordView = (TextView) view.findViewById(R.id.word_textView);
noteView = (TextView) view.findViewById(R.id.note_textView);
exampleOneView = (TextView) view.findViewById(R.id.example_1_textView);
exampleTwoView = (TextView) view.findViewById(R.id.example_2_textView);
flagView = (TextView) view.findViewById(R.id.flag_textView);
sourceView = (TextView) view.findViewById(R.id.source_textView);
dateView = (TextView) view.findViewById(R.id.date_textView);
}
}
private class FetchDefinitions extends AsyncTask<String, Void, Cursor> {
private Context bgContext;
private View bgView;
public TextView posOneView;
public TextView posTwoView;
public TextView definitionOneView;
public TextView definitionTwoView;
public FetchDefinitions(Context context, View view) {
this.bgContext = context;
this.bgView = view;
}
#Override
protected Cursor doInBackground(String... params) {
String wordId = params[0];
return bgContext.getContentResolver().query(
Definitions.CONTENT_URI.buildUpon().appendPath(wordId).build(),
definitionsProjection,
null,
null,
null
);
}
#Override
protected void onPostExecute(Cursor cursor) {
if(cursor != null) {
cursor.moveToFirst();
posOneView = (TextView) bgView.findViewById(R.id.pos_1_textView);
definitionOneView = (TextView) bgView.findViewById(R.id.definition_1_textView);
posOneView.setText(cursor.getString(DEFINITIONS_COLUMN_POS));
definitionOneView.setText(cursor.getString(DEFINITIONS_COLUMN_TEXT));
posTwoView = (TextView) bgView.findViewById(R.id.pos_2_textView);
definitionTwoView = (TextView) bgView.findViewById(R.id.definition_2_textView);
if(cursor.getCount() > 1) {
cursor.moveToNext();
posTwoView.setText(cursor.getString(DEFINITIONS_COLUMN_POS));
definitionTwoView.setText(cursor.getString(DEFINITIONS_COLUMN_TEXT));
} else {
posTwoView.setVisibility(View.GONE);
definitionTwoView.setVisibility(View.GONE);
}
}
}
}
}

How to handle when an item is not selected in spinner

I have a spinner in my application but selecting a value is not mandatory to go to next activity. When I select an item and pressed the button it goes to the next activity without any problem. But press the button without selecting any value then it gives me an exception.
In that case what should I do when any item is not selected.
addToCartButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
String instructions = instructionsET.getText().toString();
String Strquantity = quantityET.getText().toString();
String description = descriptionTV.getText().toString();
String extraCheese = extraCheseChTV.getText().toString();
String crust = crustSP.getSelectedItem().toString(); //go to exception from here
if (Utility.isNotNull(Strquantity) && Utility.isNotNull(crust)) {
if (Utility.validateItemQuantity(Strquantity)) {
if (instructionsET.getText().toString()
.equals(instructions)
&& quantityET.getText().toString()
.equals(Strquantity)) {
Toast.makeText(getApplicationContext(),
"Items processing to the cart...",
Toast.LENGTH_SHORT).show();
}
}
}
}
adapter
package lk.gamma.pizzakraft.customize;
import android.content.Context;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.SpinnerAdapter;
/**
* Decorator Adapter to allow a Spinner to show a 'Nothing Selected...' initially
* displayed instead of the first choice in the Adapter.
*/
public class NothingSelectedSpinnerAdapter implements SpinnerAdapter, ListAdapter {
protected static final int EXTRA = 1;
protected SpinnerAdapter adapter;
protected Context context;
protected int nothingSelectedLayout;
protected int nothingSelectedDropdownLayout;
protected LayoutInflater layoutInflater;
/**
* Use this constructor to have NO 'Select One...' item, instead use
* the standard prompt or nothing at all.
* #param spinnerAdapter wrapped Adapter.
* #param nothingSelectedLayout layout for nothing selected, perhaps
* you want text grayed out like a prompt...
* #param context
*/
public NothingSelectedSpinnerAdapter(
SpinnerAdapter spinnerAdapter,
int nothingSelectedLayout, Context context) {
this(spinnerAdapter, nothingSelectedLayout, -1, context);
}
/**
* Use this constructor to Define your 'Select One...' layout as the first
* row in the returned choices.
* If you do this, you probably don't want a prompt on your spinner or it'll
* have two 'Select' rows.
* #param spinnerAdapter wrapped Adapter. Should probably return false for isEnabled(0)
* #param nothingSelectedLayout layout for nothing selected, perhaps you want
* text grayed out like a prompt...
* #param nothingSelectedDropdownLayout layout for your 'Select an Item...' in
* the dropdown.
* #param context
*/
public NothingSelectedSpinnerAdapter(SpinnerAdapter spinnerAdapter,
int nothingSelectedLayout, int nothingSelectedDropdownLayout, Context context) {
this.adapter = spinnerAdapter;
this.context = context;
this.nothingSelectedLayout = nothingSelectedLayout;
this.nothingSelectedDropdownLayout = nothingSelectedDropdownLayout;
layoutInflater = LayoutInflater.from(context);
}
#Override
public final View getView(int position, View convertView, ViewGroup parent) {
// This provides the View for the Selected Item in the Spinner, not
// the dropdown (unless dropdownView is not set).
if (position == 0) {
return getNothingSelectedView(parent);
}
return adapter.getView(position - EXTRA, null, parent); // Could re-use
// the convertView if possible.
}
/**
* View to show in Spinner with Nothing Selected
* Override this to do something dynamic... e.g. "37 Options Found"
* #param parent
* #return
*/
protected View getNothingSelectedView(ViewGroup parent) {
return layoutInflater.inflate(nothingSelectedLayout, parent, false);
}
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
// Android BUG! http://code.google.com/p/android/issues/detail?id=17128 -
// Spinner does not support multiple view types
if (position == 0) {
return nothingSelectedDropdownLayout == -1 ?
new View(context) :
getNothingSelectedDropdownView(parent);
}
// Could re-use the convertView if possible, use setTag...
return adapter.getDropDownView(position - EXTRA, null, parent);
}
/**
* Override this to do something dynamic... For example, "Pick your favorite
* of these 37".
* #param parent
* #return
*/
protected View getNothingSelectedDropdownView(ViewGroup parent) {
return layoutInflater.inflate(nothingSelectedDropdownLayout, parent, false);
}
#Override
public int getCount() {
int count = adapter.getCount();
return count == 0 ? 0 : count + EXTRA;
}
#Override
public Object getItem(int position) {
return position == 0 ? null : adapter.getItem(position - EXTRA);
}
#Override
public int getItemViewType(int position) {
return 0;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public long getItemId(int position) {
return position >= EXTRA ? adapter.getItemId(position - EXTRA) : position - EXTRA;
}
#Override
public boolean hasStableIds() {
return adapter.hasStableIds();
}
#Override
public boolean isEmpty() {
return adapter.isEmpty();
}
#Override
public void registerDataSetObserver(DataSetObserver observer) {
adapter.registerDataSetObserver(observer);
}
#Override
public void unregisterDataSetObserver(DataSetObserver observer) {
adapter.unregisterDataSetObserver(observer);
}
#Override
public boolean areAllItemsEnabled() {
return false;
}
#Override
public boolean isEnabled(int position) {
return position == 0 ? false : true; // Don't allow the 'nothing selected'
// item to be picked.
}
}
you are getting your selected value via this code:-
String crust = crustSP.getSelectedItem().toString();
but is the value is not selected is provides null,so change it to
String crust = crustSP.getSelectedItem();
check that crust is not null and if it is not null then convert it to string

Spinner setOnItemSelectedListener doesn't call onItemSelected

In the last few days I tried to insert some spinner inside some cells of a table where the headers on the top and on the left are fix and only the other cells are scrollable as a unit. To implement this I found InQBarna/TableFixHeaders and changed the SampleTableAdapter.java to show spinner in some of the cells (not inside the header).
To work with the made selections of the spinners I added as usual an OnItemSelectedListener for every spinner with the following code:
final Spinner aufgabe = (Spinner) view.findViewById(android.R.id.custom);
ArrayAdapter<CharSequence> adapter = new ArrayAdapter(getContext(), android.R.layout.simple_spinner_item, ArbeitAnzeigen.allePunkte.get(column - 2));
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
aufgabe.setAdapter(adapter);
aufgabe.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView parent, View view, int position, long id) {
Log.d(TAG,"onItemSelected()");
}
#Override
public void onNothingSelected(AdapterView parent) {
Log.d(TAG,"onNothingSelected()");
}
});
But all of the OnItemSelectedListener don't work. They're called once the views are created, but there's no reaction, when one item (not only the preselected) of the spinners is selected.
Here are some information to the context of my app: The table with the spinner is placed inside a fragment. This fragment is part of an other fragment with tabhost. My app has a navigation drawer to select the wanted fragment. And the mainactivity is of the type ActionBarActivity.
I checked many variants of the code above, but I found no solution. Is there someone, who can help me? I would be happy about a hint, where I can search for and try.
Is it possible that another listener is getting the event? When this can be, how can I find out, which one?
Here's the whole code:
MainActivity.java
public class MainActivity extends Activity {
public static String[] tabellenueberschrift;
public static String[][] tabellenzeilen;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView ueberschrift = (TextView) findViewById(R.id.ueberschrift);
ueberschrift.setText("Beispieltabelle");
tabellenueberschrift = new String[6];
for (int i = 0; i < 6; i++) {
tabellenueberschrift[i] = "Header " + Integer.toString(i);
}
tabellenzeilen = new String[3][6];
for (int i = 0; i < 3; i++) {
tabellenzeilen[i][0] = "Zeile " + Integer.toString(i);
for (int j = 1; j < 6; j++) {
tabellenzeilen[i][j] = "3";
}
}
TableFixHeaders tableFixHeaders = (TableFixHeaders) findViewById(R.id.table);
tableFixHeaders.setAdapter(new TableTestAdapter(this));
}
TableTestAdapter.java:
public class TableTestAdapter extends SampleTableAdapter {
private static final String TAG = TableTestAdapter.class.getSimpleName();
private final static int WIDTH_DIP = 110;
private final static int HEIGHT_DIP = 48;
private final int width;
private final int height;
public TableTestAdapter(Context context) {
super(context);
Resources resources = context.getResources();
width = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, WIDTH_DIP, resources.getDisplayMetrics()));
height = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, HEIGHT_DIP, resources.getDisplayMetrics()));
}
#Override
public int getRowCount() {
return 3;
}
#Override
public int getColumnCount() {
return 5;
}
#Override
public int getWidth(int column) {
return width;
}
#Override
public int getHeight(int row) {
return height;
}
#Override
public String getCellString(int row, int column) {
if (row == -1) {
return MainActivity.tabellenueberschrift[column + 1];
}
else {
return MainActivity.tabellenzeilen[row][column + 1];
}
}
#Override
public int getLayoutResource(int row, int column) {
final int layoutResource;
switch (getItemViewType(row, column)) {
case 0:
layoutResource = R.layout.tabelle_header;
break;
case 1:
layoutResource = R.layout.tabelle;
break;
case 2:
layoutResource = R.layout.tabelle_spinner;
break;
default:
throw new RuntimeException("wtf?");
}
return layoutResource;
}
#Override
public int getItemViewType(int row, int column) {
if (row < 0) {
return 0;
} else {
if (column >= 0) {
return 2;
}
else {
return 1;
}
}
}
#Override
public int getViewTypeCount() {
return 3;
}
SampleTableAdapter.java:
public abstract class SampleTableAdapter extends BaseTableAdapter {
private static final String TAG = SampleTableAdapter.class.getSimpleName();
private final Context context;
private final LayoutInflater inflater;
/**
* Constructor
*
* #param context
* The current context.
*/
public SampleTableAdapter(Context context) {
this.context = context;
inflater = LayoutInflater.from(context);
}
/**
* Returns the context associated with this array adapter. The context is
* used to create views from the resource passed to the constructor.
*
* #return The Context associated with this adapter.
*/
public Context getContext() {
return context;
}
/**
* Quick access to the LayoutInflater instance that this Adapter retreived
* from its Context.
*
* #return The shared LayoutInflater.
*/
public LayoutInflater getInflater() {
return inflater;
}
#Override
public View getView(int row, int column, View converView, ViewGroup parent) {
if (converView == null) {
converView = inflater.inflate(getLayoutResource(row, column), parent, false);
}
if (getItemViewType(row, column) == 2) {
final Spinner spinner = (Spinner) converView.findViewById(android.R.id.custom);
ArrayAdapter<CharSequence> adapter = new ArrayAdapter(getContext(), android.R.layout.simple_spinner_item, getItems(Integer.valueOf(MainActivity.tabellenzeilen[row][column + 1])));
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView parent, View view, int position, long id) {
Log.d(TAG, "onItemSelected()");
}
#Override
public void onNothingSelected(AdapterView parent) {
Log.d(TAG, "onNothingSelected()");
}
});
}
else {
setText(converView, getCellString(row, column));
}
return converView;
}
/**
* Sets the text to the view.
*
* #param view
* #param text
*/
private void setText(View view, String text) {
((TextView) view.findViewById(android.R.id.text1)).setText(text);
}
/**
* #param row
* the title of the row of this header. If the column is -1
* returns the title of the row header.
* #param column
* the title of the column of this header. If the column is -1
* returns the title of the column header.
* #return the string for the cell [row, column]
*/
public abstract String getCellString(int row, int column);
public abstract int getLayoutResource(int row, int column);
private ArrayList<String> getItems(int anzahl) {
ArrayList<String> rueckgabe = new ArrayList<String>();
for (int i = 0; i < anzahl; i++) {
rueckgabe.add("Auswahl " + Integer.toString(i+1));
}
return rueckgabe;
}
You shouldn't override getView. Instead you should run your code in onResume to make sure the listener is properly initalized each time the activity comes to front.

CursorAdapter with two row layouts

Okey so, since I've not found a consistent way of doing this when I searched, I decided that maybe putting my code for somebody elses eyes to see might help.
I'm creating a game where you use cards. These cards can either be locked or unlocked depending on different factors. The important thing is, that I want to check the sqlite db whether they are locked, then display different row layouts for the two possible outcomes.
Here's my code:
public class AllCardsAdapter extends CursorAdapter {
LockedHolder viewLocked;
UnlockedHolder viewUnlocked;
private LayoutInflater mInflater;
public static final int LOCKED = 0;
public static final int UNLOCKED = 1;
public AllCardsAdapter(Context context, Cursor cursor) {
super(context, cursor);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
final int type;
int viewType = this.getItemViewType(cursor.getInt(cursor.getColumnIndex("unlocked")));
if (viewType == 1){
type = UNLOCKED;
} else {
type = LOCKED;
}
if (type == LOCKED){
viewLocked = (LockedHolder) view.getTag();
viewLocked.nameHolder.setText(cursor.getString(cursor.getColumnIndex("name")));
viewLocked.awardedAtHolder.setText("Awarded at level:");
viewLocked.reqLvlHolder.setText(cursor.getString(cursor.getColumnIndex("reqlvl")));
String imagePath = cursor.getString(cursor.getColumnIndex("image"));
if (imagePath.equals("card_obj_plus_1")){
Picasso.with(context).load(R.drawable.card_obj_plus_1).placeholder(R.drawable.card_placeholder).into(viewLocked.imageHolder);
}
if (imagePath.equals("card_obj_plus_2")){
Picasso.with(context).load(R.drawable.card_obj_plus_2).placeholder(R.drawable.card_placeholder).into(viewLocked.imageHolder);
}
if (imagePath.equals("card_obj_plus_3")){
Picasso.with(context).load(R.drawable.card_obj_plus_3).placeholder(R.drawable.card_placeholder).into(viewLocked.imageHolder);
}
if (imagePath.equals("card_slowdown")){
Picasso.with(context).load(R.drawable.card_slowdown).placeholder(R.drawable.card_placeholder).into(viewLocked.imageHolder);
}
if (imagePath.equals("card_speed_up")){
Picasso.with(context).load(R.drawable.card_speed_up).placeholder(R.drawable.card_placeholder).into(viewLocked.imageHolder);
}
} else {
viewUnlocked = (UnlockedHolder) view.getTag();
viewUnlocked.nameHolder.setText(cursor.getString(cursor.getColumnIndex("name")));
String imagePath = cursor.getString(cursor.getColumnIndex("image"));
if (imagePath.equals("card_obj_plus_1")){
Picasso.with(context).load(R.drawable.card_obj_plus_1).placeholder(R.drawable.card_placeholder).into(viewUnlocked.imageHolder);
}
if (imagePath.equals("card_obj_plus_2")){
Picasso.with(context).load(R.drawable.card_obj_plus_2).placeholder(R.drawable.card_placeholder).into(viewUnlocked.imageHolder);
}
if (imagePath.equals("card_obj_plus_3")){
Picasso.with(context).load(R.drawable.card_obj_plus_3).placeholder(R.drawable.card_placeholder).into(viewUnlocked.imageHolder);
}
if (imagePath.equals("card_slowdown")){
Picasso.with(context).load(R.drawable.card_slowdown).placeholder(R.drawable.card_placeholder).into(viewUnlocked.imageHolder);
}
if (imagePath.equals("card_speed_up")){
Picasso.with(context).load(R.drawable.card_speed_up).placeholder(R.drawable.card_placeholder).into(viewUnlocked.imageHolder);
}
}
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
viewLocked = new LockedHolder();
viewUnlocked = new UnlockedHolder();
final int type;
int viewType = this.getItemViewType(cursor.getInt(cursor.getColumnIndex("unlocked")));
if (viewType == 1){
type = UNLOCKED;
} else {
type = LOCKED;
}
System.out.println(viewType);
System.out.println(type);
if (type == LOCKED){
View lockedView;
lockedView = mInflater.inflate(R.layout.card_list_row_disabled, parent, false);
viewLocked.nameHolder = (TextView) lockedView.findViewById(R.id.txtTitle);
viewLocked.imageHolder = (ImageView) lockedView.findViewById(R.id.imgThumbnail);
viewLocked.reqLvlHolder = (TextView) lockedView.findViewById(R.id.tvLevelNr);
viewLocked.awardedAtHolder = (TextView) lockedView.findViewById(R.id.tvAwardedAt);
lockedView.setTag(viewLocked);
return lockedView;
} else {
View unlockedView;
unlockedView = mInflater.inflate(R.layout.card_list_row, parent, false);
viewUnlocked.nameHolder = (TextView) unlockedView.findViewById(R.id.txtTitle);
viewUnlocked.imageHolder = (ImageView) unlockedView.findViewById(R.id.imgThumbnail);
unlockedView.setTag(viewUnlocked);
return unlockedView;
}
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getItemViewType(int position) {
return position % 2;
}
public class LockedHolder {
public TextView nameHolder;
public ImageView imageHolder;
public TextView awardedAtHolder;
public TextView reqLvlHolder;
}
public class UnlockedHolder {
public TextView nameHolder;
public ImageView imageHolder;
}
}
The error I'm getting is on the row "viewLocked = (LockedHolder) view.getTag();"
AllCardsAdapter$UnlockedHolder cannot be cast to AllCardsAdapter$LockedHolder
The error only shows up when I have a list containing cards that are both locked and unlocked.
See I know what it's trying to do, I just don't know why. Also, if I'm overcomplicating things for no good reason, or missing something obvious, I'd be much appreciated if you find it..
getItemViewType implementation does not look right (type does not depend on item position) it should be something like the following
private int getItemViewType(Cursor cursor) {
return cursor.getInt(cursor.getColumnIndex("unlocked")) % 2;
}
#Override
public int getItemViewType(int position) {
Cursor cursor = (Cursor) getItem(position);
return getItemViewType(cursor);
}
You also need to update bindView and newView
int viewType = this.getItemViewType(cursor);

setListAdapter is not working properly

I'm having a trouble for using a 'baseAdapter' to fill a 'fragmentList'.
I'm trying to show a list with all the musics stored in my device using 'MediaStore.Audio.Media', but the problem is when the program calls the function setListAdapter. If the number of items into the baseAdapter is a little large, only a part of the list is correctly filled.
The baseAdapter code:
public class MListAdapter extends BaseAdapter {
public static final Integer KEY_LAYOUT_TITLE = 0;
public static final Integer KEY_LAYOUT_SUBTITLE = 1;
public static final Integer KEY_LAYOUT_OTHERS = 2;
public static final Integer KEY_LAYOUT_IMAGE_ID = 3;
public static final Integer KEY_LAYOUT_LIST = 4;
private ArrayList<String> arrayString;
private LayoutInflater lInflater = null;
private Context context;
public MListAdapter(Context ctx, ArrayList<String> arrString){
context = ctx;
arrayString = arrString;
lInflater = LayoutInflater.from(ctx);
}
public int getCount() {
return arrayString.size();
}
public String getItem(int position) {
return arrayString.get(position);
}
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View mView = convertView;
if (convertView == null) {
mView = lInflater.inflate(R.layout.frag_music_list, null);
TextView mTitle = (TextView) mView.findViewById(R.id.musicNameTextView);
//TextView mSubtitle = (TextView) mView.findViewById(R.id.musicArtistAlbumTextView);
//TextView mOthers = (TextView) mView.findViewById(R.id.musicDurationTextView);
//ImageView mImage = (ImageView) mView.findViewById(R.id.thumbImageView);
mTitle.setText(getItem(position));
Log.d("DEBUG",String.valueOf(position));
Log.d("DEBUG",String.valueOf(getCount()));
//mSubtitle.setText(hashItem.get(KEY_LAYOUT_SUBTITLE));
//mOthers.setText(hashItem.get(KEY_LAYOUT_OTHERS));
}
return mView;
}
The FragmentList:
public class MListFragment extends ListFragment {
MListFragmentListener mCallback;
InterfaceFragmentMusic typeMusicCallback;
// --- Global Variables
static ArrayList<HashMap<Integer, String>> mapString = null;
static ArrayList<HashMap<Integer, Long>> mapImage = null;
// ---The URIs used to get a group of music and its informations
Uri uriMedias = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
/**
* ---The following vectors of strings are used to choose what kind of
* information will be retrieved from the database in each case (the
* columns)
*/
final String[] columnsMedias = {
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.DURATION
};
// The container Activity must implement this interface so the frag can
// deliver messages
public interface MListFragmentListener {
/**
* Called by MListFragment when a list item is selected It has been
* implemented in the FragMusicActivity class!
**/
public void onMusicSelected(String musicName);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The system initially shows the list with all the musics
updateMList(MusicTypeFragment.KEY_POSITION_ALLSONGS);
}
#Override
public void onStart() {
super.onStart();
// When in two-pane layout, set the listview to highlight the selected
// list item
// (We do this during onStart because at the point the listview is
// available.)
//if (getFragmentManager().findFragmentById(R.id.music_fragment) != null) {
// getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
//}
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception.
try {
mCallback = (MListFragmentListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement MListFragmentListener");
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Set the item as checked to be highlighted when in two-pane layout
getListView().setItemChecked(position, true);
}
/**
* This fragment will be updated/refreshed whatever the user choose a music
* option on the menu (on the left side)
**/
/* It refreshes/updates the current list with a new type */
public void updateMList(int position) {
Cursor cursor;
MListAdapter mAdapter = null;
ContentResolver cr = getActivity().getContentResolver();
cursor = cr.query(uriMedias, columnsMedias, null, null, null);
ArrayList<String> arrString = new ArrayList<String>();
populateMap(cursor, arrString);
mAdapter = new MListAdapter(getActivity(),arrString);
int a = mAdapter.getCount();
for (int i = 0; i < a; i++) {
Log.d("MLISTFRAG", mAdapter.getItem(i));
}
this.setListAdapter(mAdapter);
cursor.close();
}
/*
* It populates an arrayList with the information about the musics using the
* data passed by a cursor
*/
private void populateMap(Cursor c, ArrayList<HashMap<Integer, String>> array, ArrayList<String> arrString) {
Cursor mCursor = c;
while (mCursor.moveToNext()) {
// creating new HashMap
HashMap<Integer, String> map = new HashMap<Integer, String>();
// Values by default
//map.put(MListAdapter.KEY_LAYOUT_TITLE,
// getString(R.string.inBlank));
//map.put(MListAdapter.KEY_LAYOUT_SUBTITLE,
// getString(R.string.inBlank));
//map.put(MListAdapter.KEY_LAYOUT_OTHERS,
// getString(R.string.inBlank));
// New values
map.put(MListAdapter.KEY_LAYOUT_TITLE,
mCursor.getString(MListAdapter.KEY_LAYOUT_TITLE));
arrString.add(mCursor.getString(MListAdapter.KEY_LAYOUT_TITLE));
array.add(map);
}
mCursor.close();
}
The results of these codes are that it's showed a list partially correct, I mean the first half of the list is okay, but the second half is the repetition of the first part.
I put a Log.d into the getView (in the baseAdapter) to verify if the the size of the array is correct and how many times the setListAdapter calls this method and the results (only to exemplify) are:
--> size = 30
--> how many times the method is called: 16
Thanks for your help!
You do not recycle the view properly.
In the getView method you only affect the TextView when the convertView is null. You need to update the TextView every time. Like this :
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View mView = convertView;
if (mView == null) {
mView = lInflater.inflate(R.layout.frag_music_list, null);
}
TextView mTitle = (TextView) mView.findViewById(R.id.musicNameTextView);
mTitle.setText(getItem(position));
return mView;
}

Categories

Resources