I'm developing an app for android with a ListView using a custom CursorAdapter to retrieve data from a SQLite database. The (cut-down) code for the adapter is supplied below, the problem is that in the onClick() method the pillButton01.setText(Integer.toString(test)); line is not actually updating the button text in the UI, any suggestions? Thanks in advance
public class PillTrackerAdapter extends CursorAdapter implements OnClickListener {
#Override
public void bindView(View view, Context context, Cursor cursor)
{
pillButton01 = (Button) view.findViewById(R.id.pillButton1);
pillButton01.setTag(0);
pillButton01.setOnClickListener(this);
}
#Override
public void onClick(View v)
{
int tag = (Integer) v.getTag();
switch(tag) {
case 0: pillButton01.setText(Integer.toString(test));
test++;
break;
}
}
}
That makes sens. pillButton01 is set to the last bound view. (which changes all the time.you need to find out on which listItem you are and act on the view on this item
Related
I'm new in Android development and I don't understand something, even if is a simple thing: I have a ListView that each item contains a textview and a button. When clicked on a button it fires setOnClickListener inside getView method. Also, I have a setOnItemClickListener adapter on this ListView but that method does not fire when the label is clicked. Why is this happening?
UPDATE
This is how the button click fires:
ImageButton btnToMap = view.findViewById(R.id.btnToMap);
btnToMap.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Coordinates coordinate = MainActivity.coordinates.get(position);
Toast.makeText(getApplicationContext(), "coordonata apasata: " + coordinate.latitude + ";"
+ coordinate.longitude, Toast.LENGTH_SHORT).show();
}
});
This is kind of View focus problem everyone newbie suffers from ;)
Simple solution:
Make all the children of your row_layout to
focusable=false, focusableInTouchMode = false and add android:descendantFocusability="blocksDescendants" to your parent.
Use focusable or requestfocus() as per your requirement.
In your xml file where you defined your button add:
android:focusable="false"
You can override onClick method in your Adapter class and check which view was clicked.
#Override
public void onClick(View v) {
if (v.getId() == R.id.button)
mButtonListener.buttonOnClick();
else
mItemListener.itemOnClick();
}
mButtonListener and mItemListener are listeners passed to Adapter in constructor.
private final OnButtonListener mButtonListener;
private final OnItemListener mItemListener;
public MyAdapter(OnButtonListener buttonListener, OnItemListener itemListener) {
mButtonListener = buttonListener;
mItemListener = itemListener;
}
public interface OnButtonListener {
void buttonOnClick();
}
public interface OnItemListener {
void itemOnClick();
}
I used this in my Inventory App you can find source code here
I have a ListView with Custom Adapter. I have seen this thread where people asked if the items in the custom view had a clickable item. And Yes, I have a clickable ImageView in the listrow. So clicking anywhere else(other than that ImageView) should perform some other action. I gave an onItemClickListener to the ListView. However, it doesn't work on first click and works on two-three clicks.
Update:
In my adapter's getView method, I set onClick of ImageView like this:
holder.chatImageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//Stuff here
}
});
It works fine, and in my activity, I gave onItemClickListener to listView likw this:
onlineListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView parent, View view,
int position, long id) {
//Stuff here
}
});
PS: I didn't explicitly give any focus to anything.
Some time ago I've faced the same problem but with a CheckBox, I've resolve it by creating a onClickListener as member of the Custom Adapter. For example
public MyAdapter extends BaseAdapter{
// ... ..
// ... ...
private OnClickListener imageClickListener = new OnClickListener()
{
#Override
public void onClick(View v)
{
/// your data .getTag()
// process onClickListener for image
}
};
}
And then, in your getView:
if (convertView == null){
//Create your views and register onClickListener
holder.chatImageView.setOnClickListener(imageClickListener);
}
Add android:focusable="false" to your image in xml.
Hope it helps, Here you have the entire example:
I'm trying to update a view displayed by a CursorAdapter just after its data modification.
Each row contains an image. When I touch this image, the database is update and I want to modify the image regarding this data update.
Except a requery on the cursor i didn't find a solution...My database is really big and a requery is too heavy. Do you have a better solution ?
Yes, sometimes requery is not the best solutions. What I usually do is creating helping methods in the database adapter to retrieve pieces of code. This is an excerpt of one of my adapters:
#Override
public void bindView(View view, final Context context, Cursor cursor) {
String name = cursor.getString(cursor.getColumnIndex("name"));
long id = cursor.getLong(cursor.getColumnIndex("_id"));
((TextView)view.findViewById(R.id.name)).setText(name);
Button btnFavorite = (Button) view.findViewById(R.id.item_add_favorite);
btnFavorite.setTag(id);
btnFavorite.setOnClickListener(mFavoriteCliked);
}
What I do is basically set a click listener to the image... and make sure it takes care of change its state properly:
private OnClickListener mFavoriteCliked = new OnClickListener() {
#Override
public void onClick(View v) {
// change the image of the v object
}
};
I am having a really difficult time trying to work with android's ListView multipleChoice mode. Here is what I am trying to do:
I have a "players" button in a game setup screen. When this is clicked it opens another activity with a multipleChoice ListView of all the players in the database in CheckedTextViews. I have this working properly and when you click on a player they will be added or removed from the game via a query to the game_players table.
The problem I am having is in setting up the ListView so that the players that have already been added to the game get checked initially when the activity opens.
I have tried to do this by iterating over the entire list in the ListView activity but this doesn't work because the Views that are not currently visible can't be accessed to check.
So now I'm trying to do this in my extended SimpleCursorAdapter in bindView but I can't even get this simple code to work:
#Override
public void bindView(View _view, Context _context, Cursor _cursor) {
String name = c.getString(c.getColumnIndexOrThrow(from[0]));
this.player = (CheckedTextView)_view.findViewById(to[0]);
this.player.setText(name);
this.player.setChecked(true);
}
It correctly sets the player's name with setText(), but I can't get any of the boxes to check in bindView. Is there somewhere else I should be doing this or am I just doing it incorrectly?
Call setItemChecked() on the ListView for each checked position.
I had trouble with this as well and pieced together this solution. I thought about passing the ListView directly into the Adapter, but chose to create an interface instead, to avoid a circular reference between the ListView and the Adapter.
An interface to loosely couple the adapter to the list:
public interface CheckControl {
public void setChecked(int position, boolean value);
}
Custom Adapter:
private class MyAdapter extends CursorAdapter {
private CheckControl checkControl;
public MyAdapter(Context context, Cursor cursor, CheckControl checkControl) {
super(context, cursor, 0);
this.checkControl = checkControl;
...
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
...
checkControl.setChecked(cursor.getPosition(), cursor.getInt(COL_ENABLED) == 1);
...
}
}
And here's how I use these two elements. In my case I'm extending ListViewFragment, but the same could done in any other class that contains the ListView.
public class MyListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
....
CheckControl checkControl = new CheckControl() {
public void setItemChecked(int position, boolean checked) {
getListView().setItemChecked(position, checked);
}
};
setListAdapter(new MyAdapter(getActivity(), null, checkControl));
// Kick off the loader
getLoaderManager().initLoader(LOADER_1, null, this);
}
}
I have list of checkboxes in list binded by Custom simpleCursorAdapter.
In my custom simpleCursorAdapter, I've overridden newView and bindView with my modifications.
I've managed somehow to do multichoice.
The wierd thing is, after I delete any item from my list, the first item's checkbox is being checked all of a sudden. How does that happen? How can I solve it?
My SimpleCursorAdapter class:
public class MyListCursorAdapter extends SimpleCursorAdapter
{
private Context context;
private int layout;
public MyCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to)
{
super(context, layout, c, from, to);
this.context = context;
this.layout = layout;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent)
{
Cursor c = getCursor();
final LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(layout, parent, false);
CheckBox chkBoxBtn = (CheckBox) v.findViewById (R.id.deleteTwittChkBox);
if (chkBoxBtn != null)
{
chkBoxBtn.setChecked(false);
}
return v;
}
#Override
public void bindView(View v, Context context, Cursor c)
{
--binding view to my textsview in my items
//now it's the importat part:
CheckBox chkBoxBtn = (CheckBox) v.findViewById(R.id.deleteTwittChkBox);
if (chkBoxBtn != null)
{
chkBoxBtn.setId(Integer.valueOf(c.getString(c
.getColumnIndex(MyUsers.User._ID))));
chkBoxBtn.setOnClickListener(new OnItemClickListener(chkBoxBtn, v));
chkBoxBtn.setChecked(false);
}
}
//i couldnt find another way of doing this, but this is how i set listeners to my checkboxses
static ArrayList<String> checkedItemsList = new ArrayList<String>();
private class OnItemClickListener implements OnClickListener
{
private int mPosition;
private CheckBox chkBox;
OnItemClickListener(CheckBox mChkBox, View v)
{
chkBox = mChkBox;
chkBox.setChecked(false);
}
#Override
public void onClick(View v)
{
if (chkBox.isChecked())
{
checkedItemsList.add(String.valueOf(chkBox.getId()));
}
else
{
checkedItemsList.remove(String.valueOf(chkBox.getId()));
}
}
}
}
Here is the code part from the ListActivity class which describes the button that deletes the checked box items:
OnClickListener btListener = new OnClickListener()
{
public void onClick(View view)
{
// long[] items = listView.getCheckItemIds();
int x = 0;
Uri myUri = Uri
.parse("content://com.idan.datastorageprovider/users");
String where = "_id" + "=?";
//here i am tatking all checkboxes which ive added from the adapter class
ArrayList<String> checkedItemsList = MySimpleCursorAdapter.checkedItemsList;
for (String itemID : checkedItemsList)
{
getContentResolver()
.delete(myUri, where, new String[] { itemID});
checkedItemsList.remove(itemID);
}
}
};
I doubt that SimpleCursorAdapter is the right class to extend here.
Is the "checked" state connected to the data XML in any way? No? So you need your own custom adapter!
Basically all adapters have to implement a way to generate a view from a given element (more precisely an element position!). This will be called at any time where the list wants to display an element. Now, the trick it uses is to re-use formerly created list view elements that cannot be seen on screen any more! Thus: when you scroll your list down and an element disappears at the top, EXACTLY this view object will be re-used for the next appearing item.
So, when this method is called with a given "old" view that should be re-used, all contained elements will have to be set according the elements data. If a checkbox is part of this game, you will have to have a storage for the checked state! It is not sufficient to have a checkbox as there will be less checkbox objects as there are list elements!
SimpleCursorAdapters are there to - yeah - represent SIMPLE things. An XML describing data (images and text, as the documentation states). Because of this simplicity all you have to do here is provide a method to create NEW element view objects - you are not intercepting the re-use process AT ALL! It basically only knows how to put the data into an existing view object - but it is lacking the knowledge of how to handle checked/unchecked boxes!
Your solution: write your own BaseAdapter extension and do what has to be done: implement "getView" (and some other methods like getItem, getItemId and getCount). It's not hard at all!
This API Demo uses a BaseAdapter and the mExpanded state here is basically identical to your checkbox states!
Good luck!
You might need to call notifyDataSetChanged when you modify the data.
The problem is probably that you're calling setChecked from within the onItemClickListener. One hacky way around this is to do the following before and after you call setChecked from within your listener:
chkBox.setClickable(false);
chkBox.setChecked(false);
checkBox.setClickable(true);
This will prevent your onItemClickListener from getting called when you manually call setChecked.