I've seen other similar problems to this around, but nothing that quite addresses the issues.
My problem is that Checkboxes and CheckedTextViews are randomly disappearing from my list. That is, they are the the first time the ListView is loaded, as well as the first scroll all the way to the bottom. Any random scrolling causes the CheckBoxes to just drop out and disappear.
Here is the code for my extended SimpleCursorAdapter.
public class CheckBoxCursorAdapter extends SimpleCursorAdapter{
static final String TAG = "CheckBoxCursorAdapter";
final Context contextMain;
Cursor c;
SQLiteDatabase db;
public CheckBoxCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to, SQLiteDatabase db) {
super(context, layout, c, from, to);
this.contextMain = context;
this.c = c;
this.db = db;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
CheckedTextView cb = (CheckedTextView)view.findViewById(R.id.cb);
cb.setOnClickListener(null);
TextView cbFull = (TextView)view.findViewById(R.id.fullname);
TextView cbAbbrev = (TextView)view.findViewById(R.id.abbrev);
cb.setCheckMarkDrawable(android.R.drawable.btn_default);
final String abbrv = cursor.getString(cursor.getColumnIndexOrThrow(BuildingOpenHelper.C_ABBRV));
cbFull.setText(cursor.getString(cursor.getColumnIndexOrThrow(BuildingOpenHelper.C_NAME)));
cbAbbrev.setText(abbrv);
cb.setChecked(cursor.getInt(cursor.getColumnIndexOrThrow(BuildingOpenHelper.C_DISPLAYED)) == 1 ? true : false);
Log.v(TAG, "displayed = " + Long.toString(cursor.getInt(cursor.getColumnIndexOrThrow(BuildingOpenHelper.C_DISPLAYED))));
cb.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(((CheckedTextView) v).isChecked()) {
Log.v(TAG, "Calling isChecked==true!");
db.execSQL("UPDATE buildinglist SET displayed='1' WHERE abbrv='"+ abbrv +"'");
((CheckedTextView) v).setChecked(true);
}
else {
Log.v(TAG, "Calling isChecked==false!");
db.execSQL("UPDATE buildinglist SET displayed='0' WHERE abbrv='"+ abbrv +"'");
//(CheckedTextView) v).setChecked(false);
((CheckedTextView) v).setChecked(false);
}
}
});
final int latitude = cursor.getInt(cursor.getColumnIndexOrThrow(BuildingOpenHelper.C_YCOORD));
final int longitude = cursor.getInt(cursor.getColumnIndexOrThrow(BuildingOpenHelper.C_XCOORD));
view.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
// Log.v(TAG, "Calling isChecked==true!");
// db.execSQL("UPDATE buildinglist SET displayed='1' WHERE abbrv='"+ abbrv +"'");
// ((CheckedTextView) v).setChecked(true);
Intent i = new Intent(contextMain,View_map.class);
i.putExtra("lat", latitude);
i.putExtra("long", longitude);
Log.d(TAG,"Latitude onLongClick" + latitude + "");
Log.d(TAG,"Longitude onLongClick" + longitude +"");
contextMain.startActivity(i);
return false;
}
});
}
}
I am using a ListActivity, here is the initiation.
public class Buildings_List extends ListActivity{
BuildingOpenHelper opener;
SQLiteDatabase db;
SQLiteDatabase dbWrite;
static final String[] FROM = {BuildingOpenHelper.C_NAME, BuildingOpenHelper.C_ABBRV, BuildingOpenHelper.C_DISPLAYED};
static final int[] TO = { R.id.fullname, R.id.abbrev, R.id.cb };
private static final String TAG = "Buildings_List";
SimpleCursorAdapter adapt;
Cursor myCur = null;
Bundle savedInstanceState2;
#Override
public void onCreate(Bundle savedInstanceState) {
savedInstanceState2 = savedInstanceState;
super.onCreate(savedInstanceState2);
ListView awesome = getListView();
opener = new BuildingOpenHelper(this);
db = opener.getReadableDatabase();
dbWrite = opener.getWritableDatabase();
try {
myCur = db.query(BuildingOpenHelper.TABLE,null,null,null,null,null,BuildingOpenHelper._ID + " DESC");
}
catch(SQLException e){
Log.d(TAG,"Query Went Bad");
}
startManagingCursor(myCur);
//using depracated SimpleCursorAdapter. Not quite sure what the flags need to be when using updated constructor
adapt = new CheckBoxCursorAdapter(this, R.layout.buildinglisting, myCur, FROM, TO, dbWrite);
setListAdapter(adapt);
}
I know it's not the onClickListeners that are the problem, as I have tried removing those and the problem still persists. I have read multiple accounts of the CheckBoxes not recycling correctly, but the problem is still there with CheckTextViews as well. Is there something really simple that I have missed?
Its because listview reuses the rows.
You may refer to a similar question of mine: scrolling listview causes buttons to be invisible
Related
Okay what I'm trying to do is have a search query in my app, that will allow the user after finding the correct grocery item result to add it to another data structure (eventually being a recipe). The adding component I have not worked on yet...
Right now I have chosen Button btnaddToRecipe to start a new activity
AddGroceryItemActivity after being clicked by the user (See CustomAdapter activity for that code).
This new activity will open its fragment, where some calculations will be done, and when I get around to it a new data structure will be created/modified to allow the user to add that grocery item to the data structure with the required amount of item.
The data regarding the name of the grocery item, as well as the Grams-Cups and Cups-Grams conversion ratio (stored in my database) need to be passed to this new fragment in order to do the calculations (depending on what the user will choose to enter in).
(You can see the database columns in my search query code)
The problem:
When I go to select any of the search results, hit the btnaddToRecipe I get the same search result open up in a new activity (AddGroceryItemActivity, which hosts AddGroceryItemFragment) with one of my database entries - which incorrectly matches up with the one I chose. It is the same incorrect entry EVERY option I choose.
So I think this has something do with the way my cursor is cycling through the options in AddGroceryItemFragment.
Or it's because I'm not passing the correct data about which search result the user chose, to the new activity.
The fragment class to be displayed:
public class AddGroceryItemFragment extends Fragment {
private static final String TAG = "AddGroceryItemFragment";
private OnSaveClicked mSaveListener;
private AppDatabase dbHelper;
interface OnSaveClicked {
void onSaveClicked();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
Log.d(TAG, "onAttach: starts");
dbHelper = AppDatabase.getInstance(context);
Activity activity = getActivity();
if (!(activity instanceof OnSaveClicked)) {
throw new ClassCastException(activity.getClass().getSimpleName()
+ " must implement AddGrocerItemFragment.OnSaveClicked interface");
}
mSaveListener = (OnSaveClicked) activity;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "onCreateView: starts");
View view = inflater.inflate(R.layout.fragment_addgroceryitem, container, false);
final ViewHolder holder = new ViewHolder();
holder.mCalculate = (Button) view.findViewById(R.id.calculate);
holder.mAddToList = (Button) view.findViewById(R.id.addToList);
holder.mCupsToGrams = (TextView) view.findViewById(R.id.CupsToG);
holder.mGramsToCups = (TextView) view.findViewById(R.id.gToCups);
holder.name = (TextView) view.findViewById(R.id.name);
holder.mQuantity = (EditText) view.findViewById(R.id.entry);
SQLiteDatabase db = dbHelper.getReadableDatabase();
String selectQuery = "SELECT rowid as " +
GroceriesContract.Columns._ID + ", " +
GroceriesContract.Columns.GROCERIES_NAME + ", " +
GroceriesContract.Columns.GROCERIES_DESCRIPTION + ", " +
GroceriesContract.Columns.GROCERIES_GRAMSTOCUPS + ", " +
GroceriesContract.Columns.GROCERIES_CUPSTOGRAMS +
" FROM " + GroceriesContract.TABLE_NAME;
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.moveToFirst()) {
do {
holder.name.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_NAME)));
holder.mGramsToCups.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_GRAMSTOCUPS)));
holder.mCupsToGrams.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_GRAMSTOCUPS)));
} while (cursor.moveToNext());
}
cursor.close();
return view;
}
static class ViewHolder {
TextView name;
EditText mQuantity;
Button mCalculate;
Button mAddToList;
TextView mGramsToCups;
TextView mCupsToGrams;
}
The fragment's actitvity:
public class AddEditRecipeActivity extends AppCompatActivity implements AddEditRecipeActivityFragment.OnSaveClicked {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_edit_recipe);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
AddEditRecipeActivityFragment fragment = new
AddEditRecipeActivityFragment();
Bundle arguments = getIntent().getExtras();
fragment.setArguments(arguments);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment, fragment);
fragmentTransaction.commit();
}
#Override
public void onSaveClicked() {
finish();
}
}
You can see the button that starts the activity - does the cursor information from this need to be pulled out?
public class SearchActivity extends AppCompatActivity {
private static final String TAG = "SearchActivity";
private Cursor mCursor;
private CustomAdapter mCustomAdapter;
private GroceriesContract mGroceriesContract;
ListView mListView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
mGroceriesContract = new GroceriesContract(this);
mCursor = mGroceriesContract.getGroceryList();
mCustomAdapter = new CustomAdapter(SearchActivity.this, mCursor, 0);
mListView = (ListView) findViewById(R.id.lstStudent);
mListView.setAdapter(mCustomAdapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options_menu, menu);
SearchManager manager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView search = (SearchView) menu.findItem(R.id.search).getActionView();
search.setSearchableInfo(manager.getSearchableInfo(getComponentName()));
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
search.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
Log.d(TAG, "onQueryTextSubmit: on");
mCursor = mGroceriesContract.getGroceryName(query);
if (mCursor == null) {
Toast.makeText(SearchActivity.this, "No records found", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(SearchActivity.this, mCursor.getCount() + " records found!", Toast.LENGTH_SHORT).show();
}
mCustomAdapter.swapCursor(mCursor);
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
Log.d(TAG, "onQueryTextChange: ");
mCursor = mGroceriesContract.getGroceryName(newText);
if (mCursor != null) {
mCustomAdapter.swapCursor(mCursor);
}
return false;
}
});
return true;
}
#Override
protected void onResume() {
super.onResume();
}
}
I'm wondering if this problem is actually the result of not passing data from the activity that started this the activity that then leads to starting AddGroceryItemFragment
This initial class extends CursorAdapter:
public class CustomAdapter extends CursorAdapter {
TextView txtId,txtName, txtDescription;
private LayoutInflater mInflater;
private Context mContext;
public CustomAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = mInflater.inflate(R.layout.item, parent, false);
ViewHolder holder = new ViewHolder();
holder.txtId = (TextView) view.findViewById(R.id.txtId);
holder.txtName = (TextView) view.findViewById(R.id.txtName);
holder.txtDescription = (TextView) view.findViewById(R.id.txtEmail);
holder.btnaddToRecipe = (Button) view.findViewById(R.id.add);
view.setTag(holder);
return view;
}
#Override
public void bindView(View view, final Context context, final Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
holder.txtId.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns._ID)));
holder.txtName.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_NAME)));
holder.txtDescription.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_DESCRIPTION)));
holder.btnaddToRecipe.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(context, AddGroceryItemActivity.class);
context.startActivity(intent);
}
});
}
static class ViewHolder {
TextView txtId;
TextView txtName;
TextView txtDescription;
Button btnaddToRecipe;
}
}
NEW EDIT:
Thank you for your reply MikeT.
What you wrote makes sense in terms of passing the data, however I still get the same result each time. I changed my query as you wrote it, as well as adding the putExtra() on the intent. I then retrieve the id as you wrote it in the fragment, created a bundle object, put the string in the bundle and add it to the bundle with the setArguments method. I perform the transaction etc an start the fragment. Then from the fragment, I created a new string and called getArguments().getString("id"). Set the whereargs to the new string and wrote the query as you said.
I think it's in how my cursor code is written. I'm realizing a typo on the mCupsToGrams - should be the GROCERIES_CUPSTOGRAMS. But this is irrelevant to the issue.
When I logd the data passed in my onClick method, the same cursor is being passed no matter what button I press. I think this is because I have to mark cursor as final in order to access it from the anonymous class. Does that make sense?
It would appear that AddGroceryItemFragment extracts a Cursor consisting of all rows from the table as per :-
SQLiteDatabase db = dbHelper.getReadableDatabase();
String selectQuery = "SELECT rowid as " +
GroceriesContract.Columns._ID + ", " +
GroceriesContract.Columns.GROCERIES_NAME + ", " +
GroceriesContract.Columns.GROCERIES_DESCRIPTION + ", " +
GroceriesContract.Columns.GROCERIES_GRAMSTOCUPS + ", " +
GroceriesContract.Columns.GROCERIES_CUPSTOGRAMS +
" FROM " + GroceriesContract.TABLE_NAME;
Cursor cursor = db.rawQuery(selectQuery, null);
It then goes on to loop through every extracted row setting the text to be displayed resulting in the display eventually displaying the data for the last extracted row.
I believe that you would need a WHERE clause in the query so that it could select the appropriate Grocery Item as opposed to the last extracted (potentially any) grocery item.
The query could be :-
String[] columns = new String[]{
"rowid AS " +
GroceriesContract.Columns._ID,
GroceriesContract.Columns.GROCERIES_NAME,
GroceriesContract.Columns.GROCERIES_DESCRIPTION,
GroceriesContract.Columns.GROCERIES_GRAMSTOCUPS,
GroceriesContract.Columns.GROCERIES_CUPSTOGRAMS
};
String whereclause = GroceriesContract.Columns._ID + "=?";
String[] whereargs = new String[]{the_id_passed_to_the_fragment};
Cursor cursor =db.query(GroceriesContract.TABLE_NAME,columns,whereclause,whereargs,null,null,null);
Notes
This uses the preferred query method as opposed to the rawQuery query
The variable the_id_passed_to_the_fragment is as it's name suggests, as you suspect by saying Or it's because I'm not passing the correct data about which search result the user chose, to the new activity. and or I'm wondering if this problem is actually the result of not passing data from the activity that started this the activity that then leads to starting AddGroceryItemFragment
You would the follow this with :-
if (cursor.moveToFirst()) {
holder.name.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_NAME)));
holder.mGramsToCups.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_GRAMSTOCUPS)));
holder.mCupsToGrams.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_GRAMSTOCUPS)));
}
else {
//... do something just in case Grocery Item wasn't found here.
}
cursor.close();
I believe that you would pass the id from the staring activity by adding an intent extra along the lines of (see 1 line that has been added):-
#Override
public void bindView(View view, final Context context, final Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
holder.txtId.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns._ID)));
holder.txtName.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_NAME)));
holder.txtDescription.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_DESCRIPTION)));
holder.btnaddToRecipe.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(context, AddGroceryItemActivity.class);
intent.putExtra("GROCERYITEMID",cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns._ID))); //<<<< ADDED
context.startActivity(intent);
}
});
}
You the retrieve this in the started activity using :-
String retrieved_id = getIntent().getStringExtra(
"GROCERYITEMID");
You then need to make retrieved_id available to the fragment as the_id_passed_to_the_fragment.
Regarding comment :-
When I logd the data passed in my onClick method, the same cursor is
being passed no matter what button I press. I think this is because I
have to mark cursor as final in order to access it from the anonymous
class. Does that make sense?
You do not have to mark cursor as final instead use something like :-
#Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
holder.txtId.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns._ID)));
holder.txtName.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_NAME)));
holder.txtDescription.setText(cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns.GROCERIES_DESCRIPTION)));
final String id_to pass = cursor.getString(cursor.getColumnIndex(GroceriesContract.Columns._ID));
holder.btnaddToRecipe.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(context, AddGroceryItemActivity.class);
intent.putExtra("GROCERYITEMID",id_to pass); //<<<< ADDED
context.startActivity(intent);
}
});
}
I read some posts and found that reQuery() is deprecated and some suggested using SwapCursor() or ChangeCursor().
I have a Favorite button on whose click I update DB and change color of the Button. When I scroll and come back to particular view(and Button) color is reset.
I know it is because view is recycled. I have a condition based on a DB column value to set the color of the Button.
I want view to get updated values from DB after I press the Button. For which I have to refresh/requery Cursor/DB.
How do I do that with CursorAdapter keeping in mind that my min. API is 19?
UPDATE
CursorAdapter code:
public class ToDoCursorAdapter extends CursorAdapter {
SparseBooleanArray selectionArrayAr = new SparseBooleanArray();
SparseBooleanArray selectionArrayRef = new SparseBooleanArray();
SparseBooleanArray selectionArrayFav = new SparseBooleanArray();
//Boolean isSet = false;
private MainButtons_Interface mAdapterCallback;
public ToDoCursorAdapter(Context context, Cursor cursor) {
super(context, cursor, 0);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
ViewHolderItem viewHolder = new ViewHolderItem();
View rowView = LayoutInflater.from(context).inflate(R.layout.listview, parent, false);
viewHolder.engTextV = (TextView) rowView.findViewById(R.id.engText);
viewHolder.arTextV = (TextView) rowView.findViewById(R.id.arabText);
viewHolder.buttonIAV = (Button) rowView.findViewById(R.id.buttonIA); //For Arabic Text
viewHolder.refTextV = (TextView) rowView.findViewById(R.id.refText);
viewHolder.buttonIRV = (Button) rowView.findViewById(R.id.buttonIR); //For Ref Text
viewHolder.buttonIFV = (ImageButton) rowView.findViewById(R.id.buttonF);
rowView.setTag(viewHolder);
return rowView;
}
#Override
public void bindView(final View view, final Context context, final Cursor cursor) {
final ViewHolderItem viewHolder = (ViewHolderItem) view.getTag();
String arabic = cursor.getString(cursor.getColumnIndexOrThrow("PlainArab_Text")).trim().replaceAll("[\n]{2,}", "TWOFEEDS").replaceAll("\n", " ").replaceAll(" +", " ").replaceAll("<br/>", "\n").replaceAll("TWOFEEDS", "\n") + "\n";
String english = cursor.getString(cursor.getColumnIndexOrThrow("PlainEng_Text")).trim().replaceAll("[\n]{2,}", "TWOFEEDS").replaceAll("\n", " ").replaceAll(" +", " ").replaceAll("<br/>", "\n").replaceAll("TWOFEEDS", "\n") + "\n";
String ref = cursor.getString(cursor.getColumnIndexOrThrow("REF")).trim().replaceAll("<br/> <br/>", " ").replaceAll("<br/>", "\n");
final Integer HadithID = cursor.getInt(cursor.getColumnIndexOrThrow("ID"));
final Integer IsFav = cursor.getInt(cursor.getColumnIndexOrThrow("IsFavorite"));
viewHolder.arTextV.setText(arabic);
viewHolder.engTextV.setText(english);
viewHolder.refTextV.setText(ref);
final int position = cursor.getPosition();
boolean isSelectedA = selectionArrayAr.get(position);
boolean isSelectedR = selectionArrayRef.get(position);
boolean isSelectedF = selectionArrayFav.get(position);
if (isSelectedA) {
viewHolder.arTextV.setVisibility(view.GONE);
viewHolder.buttonIAV.setText("Show Arabic Version");
} else if (!isSelectedA){
viewHolder.arTextV.setVisibility(view.VISIBLE);
viewHolder.buttonIAV.setText("Hide Arabic Version");
}
if (isSelectedR) {
viewHolder.refTextV.setVisibility(view.GONE);
viewHolder.buttonIRV.setText("Show Refrence");
} else if (!isSelectedR){
viewHolder.refTextV.setVisibility(view.VISIBLE);
viewHolder.buttonIRV.setText("Hide Refrence");
}
//boolean isSelectedF = selectionArrayFav.get(position);
if(isSelectedF) {
viewHolder.buttonIFV.setImageResource(R.drawable.favoritebutton_afterclick);
} else if (!isSelectedF){
viewHolder.buttonIFV.setImageResource(R.drawable.favoritebutton);
}
//Arabic Button
viewHolder.buttonIAV.setOnClickListener(
new View.OnClickListener()
{ #Override
public void onClick(View v) {
boolean isSelectedAc = selectionArrayAr.get(position);
if(!isSelectedAc) {
viewHolder.arTextV.setVisibility(v.GONE);
viewHolder.buttonIAV.setText("Show Arabic Version");
setSelectedAr(position, true);
} else if (isSelectedAc){
viewHolder.arTextV.setVisibility(v.VISIBLE);
setSelectedAr(position, false);
viewHolder.buttonIAV.setText("Hide Arabic version");
}
}
}
);
//Ref Button
viewHolder.buttonIRV.setOnClickListener(
new View.OnClickListener()
{ #Override
public void onClick(View v) {
boolean isSelectedRc = selectionArrayRef.get(position);
if(!isSelectedRc) {
viewHolder.refTextV.setVisibility(v.GONE);
viewHolder.buttonIRV.setText("Show Reference");
setSelectedRef(position, true);
} else if (isSelectedRc){
viewHolder.refTextV.setVisibility(v.VISIBLE);
setSelectedRef(position, false);
viewHolder.buttonIRV.setText("Hide Reference");
}
}
}
);
//Fav Button
viewHolder.buttonIFV.setOnClickListener(
new View.OnClickListener()
{ #Override
public void onClick(View v) {
boolean isSelectedF = selectionArrayFav.get(position);
boolean IsSet = ((ListViewActivity) context).addRemFav(HadithID);
String mess ="";
if(IsSet){
mess = "Hadith add to Favorite list";
} else if(!IsSet){
mess = "Hadith removed from Favorite list";
}
if(!isSelectedF) {
viewHolder.buttonIFV.setImageResource(R.drawable.favoritebutton_afterclick);
setSelectedF(position, true);
} else if (isSelectedF){
viewHolder.buttonIFV.setImageResource(R.drawable.favoritebutton);
setSelectedF(position, false);
}
Toast.makeText(v.getContext(), mess, Toast.LENGTH_SHORT).show();
}
}
);
}
// our ViewHolder.
static class ViewHolderItem {
TextView engTextV;
TextView arTextV;
TextView refTextV;
Button buttonIAV;
Button buttonIRV;
ImageButton buttonIFV;
}
// Method to mark items in selection
public void setSelectedAr(int position, boolean isSelected) {
selectionArrayAr.put(position, isSelected);
}
public void setSelectedRef(int position, boolean isSelected) {
selectionArrayRef.put(position, isSelected);
}
public void setSelectedF(int position, boolean isSelected) {
selectionArrayFav.put(position, isSelected);
}
UPDATE
I added this logic to my function which was called on clicking the Button.
Cursor todoCursor1 = hadDB.rawQuery("SELECT ID as _id, * FROM HAD_TABLE WHERE ID < 7001 ", null);
todoAdapter.changeCursor(todoCursor1);
Basically, you just need to requery DB so that you get updated records/Data and then change your current cursor with new one, todoCursor1 is my case above.
Also, changeCursor() will close your current cursor, in case you would want to go back to old cursor you should use swapCursor() instead as it will return you old cursor.
Now my only thing I want to know is, if this will work for APIs 19 and up.
I added this logic to my function which was called on clicking the Button.
Cursor todoCursor1 = hadDB.rawQuery("SELECT ID as _id, * FROM HAD_TABLE WHERE ID < 7001 ", null);
todoAdapter.changeCursor(todoCursor1);
Basically, you just need to requery DB so that you get updated records/Data and then change your current cursor with new one, todoCursor1 is my case above.
Also, changeCursor() will close your current cursor, in case you would want to go back to old cursor you should use swapCursor() instead as it will return you old cursor.
Here is my first question on StackOverFlow, I usually always find an answer by myself but I am really stuck on a weird problem that I will explain here:
I implemented a ListView in a fragment activity, this listview contains a list of categories related to the current record that I get from the SQLLite database.
All is working fine, I created a SimpleCursorAdapter to retrieve the data from the DB and I display the categories correctly in the ListView.
The problem is related to the pre-fill of the checkboxes (it is a multiselection list), depending on how I try to pre-check the checkboxes, I get 2 cases:
First, the checkboxes are well pre-checked, but I cannot toggle the checkboxes anymore by clicking them. Second the click toggle well the checkboxes, but they are not pre-checked anymore...
Here is the part of the code where I have the problem:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//super.onCreate(savedInstanceState);
View v = inflater.inflate(R.layout.rate_fragment, container,false);
dbCategories = "";
displayCategories = resources.getText(R.string.no_categories).toString();
/** INITIALIZATION */
mViewSwitcher = (ViewSwitcher)v.findViewById(R.id.profileSwitcher);
/** Edition view */
rateGroup = (RadioGroup)v.findViewById(R.id.rate_group);
rateOne = (RadioButton)v.findViewById(R.id.one_button);
rateOne.setTag(1);
rateTwo = (RadioButton)v.findViewById(R.id.two_button);
rateTwo.setTag(2);
rateThree = (RadioButton)v.findViewById(R.id.three_button);
rateThree.setTag(3);
rateFour = (RadioButton)v.findViewById(R.id.four_button);
rateFour.setTag(4);
rateFive = (RadioButton)v.findViewById(R.id.five_button);
rateFive.setTag(5);
descET = (EditText)v.findViewById(R.id.editdescription);
descTextSize = descET.getTextSize();
descET.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
categoriesTV_edit = (TextView)v.findViewById(R.id.edit_categories);
categoriesBT = (Button) v.findViewById(R.id.select_categories);
categoriesBT.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
View categoriesListTitle = getActivity().getLayoutInflater().inflate(R.layout.category_list_title, null);
AlertDialog.Builder alt_bld = new AlertDialog.Builder(v.getContext()).setCustomTitle(categoriesListTitle);
categories = db.getAllCategoriesByRate(currentRate);
categoriesList = new ListView(getActivity());
categoriesList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
categoriesList.setClickable(true);
String[] fromColumns = new String[] {
DatabaseHandler.CATEGORY_NAME
};
int[] toViews = new int[]{
R.id.cat_checked
};
//mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_multiple_choice, categories, fromColumns, toViews, 0);
mAdapter = new SimpleCursorAdapter(getActivity(), R.layout.category_item, categories, fromColumns, toViews, 0);
mAdapter.setViewBinder(new ViewBinder() {
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if (columnIndex == 1) {
CheckedTextView categRow = (CheckedTextView) view;
String catName = cursor.getString(1);
mAdapter.setViewText((TextView) view, catName);
int catChecked = cursor.getInt(2);
//boolean checkedCat = catChecked==1;
//categoriesList.setItemChecked(cursor.getPosition(),checkedCat);
categRow.setChecked(catChecked==1);
int catID = cursor.getInt(0);
categRow.setTag(catID);
return true;
}
else {
return false;
}
}
});
categoriesList.setAdapter(mAdapter);
alt_bld.setView(categoriesList);
To have one case or another, all depends on these 2 lines:
//boolean checkedCat = catChecked==1;
//categoriesList.setItemChecked(cursor.getPosition(),checkedCat);
If they are commented, the checkboxes are not pre-checked, but the toggle on the clicks is working. But if I comment these lines out, the toggle is not working anymore but the categories are prechecked.
What I also don't understand is that this line is not working:
categRow.setChecked(catChecked==1);
But this one is working well (I succeed to retrieve the tag):
categRow.setTag(catID);
So I hope someone will succeed to explain to me what I do wrong, I guess there is something I misunderstood here...
NOTE: I get 3 columns from the cursor "categories", first one is the ID of the category, second one is the name, and third one is the status: checked or not (1 or 0).
Thanks in advance for your time.
Finally I ended up creating my own custom adapter, this way I could at least understand more easily what was happening.
I had to create actually several multiselect lists, some populated with data from the database, others from the shared preferences.
For this one displaying data from the DB, I created the following adapter (I commented out the lines about the icons because I did not set them up yet):
public class CategoriesLVAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater mInflater;
private List<Category> categoriesList;
// Constructor
public CategoriesLVAdapter(Context c, List<Category> categories_list){
mContext = c;
mInflater = LayoutInflater.from(c);
categoriesList = categories_list;
}
public List<Category> getCategoriesList(){
return categoriesList;
}
#Override
public int getCount() {
return categoriesList.size();
}
#Override
public Object getItem(int position) {
return categoriesList.get(position);
}
#Override
public long getItemId(int position) {
return categoriesList.get(position).getID();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.categories_list_row, null);
//convertView.setLayoutParams(new ListView.LayoutParams(200, 90));
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.categories_list_row_tv);
//holder.icon = (ImageView) convertView.findViewById(R.id.categories_list_row_iv);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
//holder.icon.setImageResource(categoriesList.get(position).getDrawableID());
//holder.icon.setAdjustViewBounds(true);
//holder.icon.setScaleType(ImageView.ScaleType.CENTER_CROP);
holder.title.setText(categoriesList.get(position).getName());
return convertView;
}
static class ViewHolder {
TextView title;
//ImageView icon;
}
}
In my activity, I use this adapter when the AlertDialog is called to populate the ListView, then I pre-select the categories using the last ones saved in the shared preferences:
private void categoriesFilter(){
AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
alt_bld.setTitle(resources.getText(R.string.select_categories).toString());
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.categories_list,(ViewGroup) findViewById(R.id.categories_layout_root));
categoriesLV = (ListView) layout.findViewById(R.id.categories_list);
alt_bld.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String selectedCategoriesString = getSelectedValues(categoriesLV);
//Update the shared preferences
prefs.edit().putString(RateDayApplication.PREF_KEY_CATEGORIES, selectedCategoriesString).commit();
updateFilterDisplay(resources.getText(R.string.cat_title).toString(), selectedCategoriesString, searchedCategoriesTV, "Category");
}
});
alt_bld.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.cancel();
}
});
String selectedCategoriesString = prefs.getString(RateDayApplication.PREF_KEY_CATEGORIES, new String());
categoriesLV.setAdapter(new CategoriesLVAdapter(this, categoriesList));
String[] selectedCategoriesArray = selectedCategoriesString.split(",");
int categoriesLVLength = categoriesLV.getCount();
for(int i = 0; i < categoriesLVLength; i++){
int categoryID = ((Category) categoriesLV.getItemAtPosition(i)).getID();
if(Arrays.asList(selectedCategoriesArray).contains(String.valueOf(categoryID))){
categoriesLV.setItemChecked(i, true);
}
}
alt_bld.setView(layout);
AlertDialog alert = alt_bld.create();
alert.show();
}
Finally here is the function I call from my database handler to get the list of catagories:
// Getting All Categories By ID desc
public List<Category> getCategoriesList() {
String selectQuery = "SELECT " + CATEGORY_ID + ", " + CATEGORY_NAME + " FROM " + CATEGORY_TABLE + " ORDER BY " + CATEGORY_ID + " ASC";
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
List<Category> categoriesList = new ArrayList<Category>();//String[] categoriesList = {};
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Category category = new Category(cursor.getInt(0), cursor.getString(1), false);
categoriesList.add(category);
} while (cursor.moveToNext());
}
cursor.close();
db.close();
return categoriesList;
}
I think my problem before was coming from the fact that the function "setItemChecked" is a little misleading because it does not mean necessarily that anything is checked.
When you use the function "setItemChecked", the item in the list view becomes selected, with or without a checkbox (my rows only contain text views).
The rows selected in my list appear in a different color, and that's enough in my opinion for a simple multi selection list.
The layouts I used are quite simple, "categories_list" contains a ListView in a LinearLayout and "categories_list_row" contains a TextView in a LinearLayout.
Hope it may guide someone!
Well, I have read almost 50 links related to this question, but my code still not working.
I have a Custom adapter which extends SimpleCursorAdapter class, and I use that adapter to fill the ListView on onCreate method
private void populateListView()
{
String[] from = new String[] { SchemaHelper.TASK_DESCRIPTION, SchemaHelper.TASK_CREATED_ON, SchemaHelper.TASK_ID };
int[] to = new int[] {R.id.lv_row_description, R.id.lv_row_created_on};
tasksCursor = schemaHelper.getTasks();
startManagingCursor(tasksCursor);
tasksAdapter = new TasksAdapter(this, R.layout.tasks_listview_row, tasksCursor, from, to);
setListAdapter(tasksAdapter);
}
The App is a simple task manager, I want to update the ListView contents when the user submits a new task without calling setListAdapter() again.
I have tried notifyDataSetChanged (running on ui thread), invalidate, requery(deprecated)... almost everything.
I'm doing something wrong?
EDIT:
This is the method where I add a new task to the database
private void addTask(String description)
{
String message = "";
schemaHelper.open();
if(schemaHelper.isAlreadyInDatabase(description))
{
message = getString(R.string.task_already_exists);
}
else
{
message = getString(R.string.task_succesfully_added);
schemaHelper.insertTask(description);
populateListView();
newTask.setText("");
}
schemaHelper.close();
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
ADAPTER CLASS:
private class TasksAdapter extends SimpleCursorAdapter
{
private LayoutInflater layoutInflater;
private Cursor cursor;
public TasksAdapter(Context context, int layout, Cursor c, String[] from, int[] to)
{
super(context, layout, c, from, to);
cursor = c;
cursor.moveToFirst();
layoutInflater = LayoutInflater.from(context);
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if(cursor.getPosition() < 0)
{
cursor.moveToFirst();
}
else
{
cursor.moveToPosition(position); // Here throws the error
}
View row = layoutInflater.inflate(R.layout.tasks_listview_row, null);
TextView description = (TextView) row.findViewById(R.id.lv_row_description);
TextView createdOn = (TextView) row.findViewById(R.id.lv_row_created_on);
description.setText(cursor.getString(cursor.getColumnIndexOrThrow(SchemaHelper.TASK_DESCRIPTION)));
createdOn.setText(getString(R.string.added_on) + " " + TaskHelper.formatDateWithSuffix(cursor.getString(cursor.getColumnIndexOrThrow(SchemaHelper.TASK_CREATED_ON))));
return row;
}
}
i dont know much of the taskCursor and taskAdapter but i used ArrayAdapter i guess, well have a look in my code and take your own conclusions.
//LISTVIEW database CONTATO
ListView user = (ListView) findViewById(R.id.lvShowContatos);
//String = simple value ||| String[] = multiple values/columns
String[] campos = new String[] {"nome", "telefone"};
list = new ArrayList<String>();
Cursor c = db.query( "contatos", campos, null, null, null, null, "nome" + " ASC ");
c.moveToFirst();
String lista = "";
if(c.getCount() > 0) {
while(true) {
list.add(c.getString(c.getColumnIndex("nome")).toString());
if(!c.moveToNext()) break;
}
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, list);
user.setAdapter(adapter);
If you don't want to use requery() you can simply pass a new Cursor with the same query:
tasksCursor.close();
tasksCursor = schemaHelper.getTasks();
startManagingCursor(tasksCursor);
tasksAdapter.changeCursor(tasksCursor);
I assume that when you call addTask() you have already called populateListView() once. Try changing addTask() to this:
private void addTask(String description)
{
String message = "";
schemaHelper.open();
if(schemaHelper.isAlreadyInDatabase(description))
{
message = getString(R.string.task_already_exists);
}
else
{
message = getString(R.string.task_succesfully_added);
schemaHelper.insertTask(description);
// Remove call to populateListView(), just update the Cursor
tasksCursor.close();
tasksCursor = schemaHelper.getTasks();
startManagingCursor(tasksCursor);
tasksAdapter.changeCursor(tasksCursor);
newTask.setText("");
}
schemaHelper.close();
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
If this "doesn't work", please be more specific. Is it throwing an error, if so what kind?
You are doing a little too much work in your adapter. Please watch Android's Romain Guy at Google Talks discuss adapters and getView(). However since you only want to pass one special string to your createdOn TextView, let's do something very different and override setViewText():
Try this:
public class TasksAdapter extends SimpleCursorAdapter {
String prefix;
public TasksAdapter(Context context, int layout, Cursor cursor, String[] from, int[] to) {
super(context, layout, cursor, from, to);
// This is constant so set it once and consider adding the space to the end of the String in strings.xml
prefix = getString(R.string.added_on) + " ";
}
#Override
public void setViewText(TextView v, String text) {
if(v.getId() == R.id.lv_row_created_on)
v.setText(prefix + TaskHelper.formatDateWithSuffix(text));
else
super.setViewText(v, text);
}
}
The rest of the data is taken care of with SimpleCursorAdapter's existing methods.
I've been struggeling in the past few days trying to figure this out, I hope you can help me...
I have an Activity that shows a list of Players by setting a listadapter like this:
PlayerCursorAdapter playerAdapter = new PlayerCursorAdapter(this,
R.layout.players_row, c, columns, to);
setListAdapter(playerAdapter);
When clicking an item in the list, this code will be executed showing a dialog with an "Edit" and "Delete" option for editing and removing players:
private class OnPlayerItemClickListener implements OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View view, int position,
long rowId) {
Toast.makeText(view.getContext(),
"Clicked Item [" + position + "], rowId [" + rowId + "]",
Toast.LENGTH_SHORT).show();
// Prepare Dialog with "Edit" and "Delete" option
final CharSequence[] choices = {
view.getContext().getString(R.string.buttonEdit),
view.getContext().getString(R.string.buttonDelete) };
AlertDialog.Builder builder = new AlertDialog.Builder(
view.getContext());
builder.setTitle(R.string.title_edit_delete_player);
builder.setItems(choices, new EditOrDeleteDialogOnClickListener(
view, rowId));
AlertDialog alert = builder.create();
// Show Dialog
alert.show();
}
Based on your choice (Edit or delete player), the following listener will be executed:
private class EditOrDeleteDialogOnClickListener implements
DialogInterface.OnClickListener {
private View view;
private long rowId;
public EditOrDeleteDialogOnClickListener(View view, long rowId) {
this.view = view;
this.rowId = rowId;
}
public void onClick(DialogInterface dialog, int item) {
if (item == 0) {
// Edit
showDialog(PlayGameActivity.DIALOG_EDIT_PLAYER_ID);
} else if (item == 1) {
// Delete from database
DatabaseHelper databaseHelper = new DatabaseHelper(
view.getContext());
databaseHelper.deletePlayer(rowId);
// Requery to update view.
((PlayerCursorAdapter) getListAdapter()).getCursor().requery();
Toast.makeText(
view.getContext(),
view.getContext().getString(
R.string.message_player_removed)
+ " " + rowId, Toast.LENGTH_SHORT).show();
}
}
}
The code for the adapter is here:
public class PlayerCursorAdapter extends SimpleCursorAdapter {
private LayoutInflater layoutInflater;
private int layout;
public PlayerCursorAdapter(Context context,
int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
this.layout = layout;
layoutInflater = LayoutInflater.from(context);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
Cursor c = getCursor();
View view = layoutInflater.inflate(layout, parent, false);
// Get Data
int nameCol = c.getColumnIndex(Player.COLUMN_PLAYER_NAME);
String name = c.getString(nameCol);
int gamesPlayedCol = c.getColumnIndex(Player.COLUMN_GAMES_PLAYED);
String gamesPlayed = c.getString(gamesPlayedCol);
int gamesWonCol = c.getColumnIndex(Player.COLUMN_GAMES_WON);
String gamesWon = c.getString(gamesWonCol);
// Set data on fields
TextView topText = (TextView) view.findViewById(R.id.topText);
if (name != null)
topText.setText(name);
TextView bottomText = (TextView) view.findViewById(R.id.bottomText);
if (gamesPlayed != null && gamesWon != null)
bottomText.setText(view.getContext().getString(
R.string.info_played_won)
+ gamesPlayed + "/" + gamesWon);
CheckBox checkBox = (CheckBox) view.findViewById(R.id.checkBox);
// Set up PlayerViewHolder
PlayerViewHolder playerViewHolder = new PlayerViewHolder();
playerViewHolder.playerName = name;
playerViewHolder.gamesPlayed = gamesPlayed;
playerViewHolder.gamesWon = gamesWon;
playerViewHolder.isChecked = checkBox.isChecked();
view.setTag(playerViewHolder);
return view;
}
private class PlayerViewHolder {
String playerName;
String gamesPlayed;
String gamesWon;
boolean isChecked;
}
#Override
public void bindView(View view, Context context, Cursor c) {
PlayerViewHolder playerViewHolder = (PlayerViewHolder) view.getTag();
TextView topText = (TextView) view.findViewById(R.id.topText);
topText.setText(playerViewHolder.playerName);
TextView bottomText = (TextView) view.findViewById(R.id.bottomText);
bottomText.setText(view.getContext()
.getString(R.string.info_played_won)
+ playerViewHolder.gamesPlayed
+ "/"
+ playerViewHolder.gamesWon);
CheckBox checkBox = (CheckBox) view.findViewById(R.id.checkBox);
checkBox.setChecked(playerViewHolder.isChecked);
}
}
Now, the problem is that after removing a few of the players in the list, the list gets screwed up, eg. it shows something different than what is actually available.
I've experimented a little and if I stop using the PlayerViewHolder in bindView and instead read the text from the cursor and assign it directly to the text fields, then it works.... So question is, why is my ViewHolder screwing up things???
Any help will be greatly appreciated!
Thanks!
Zyb3r
Found a solution...
Basically I reinitialize the Cursor and ListAdapter plus assigns the ListAdapter to the ListView all over again when I change the data in the database.
I'm not entirely sure why this is nessasary, but notifyDataSetChanged(), notifyDataSetInvalidated() and all the other things I tried didn't work, so now I'm using this approach. :o)
Zyb3r