My ListView item consists of the following components - TextView and then under it there are two ImageViews - like and dislike.
So when I click on either like or dislike I want to be able to change the like or dislike ImageView from grey to blue.
At the moment when I click on like the like ImageView does change from grey to blue. But not only for the corresponding ListView item but for every 3rd item in the list - so if I have 10 items in my list and I click on the like of the first item in the list then 4th, 7th and 10th items like ImageView change from grey to blue.
In my post_list_item.xml in the root element of the file I specified the following android:descendantFocusability="blocksDescendants" but it doesn't help either.
My question is - what do I have to do so that when I click on either like or dislike I would be able to change the like or dislike ImageView from grey to blue without affecting other list items?
Here is my code of the CursorAdapter
public class PostCursorAdapter extends CursorAdapter {
public PostCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.post_list_item, parent, false);
}
#Override
public void bindView(View view, final Context context, Cursor cursor) {
String post = cursor.getString(cursor.getColumnIndex(DBOpenHelper.POST));
final String liked = cursor.getString(cursor.getColumnIndex(DBOpenHelper.LIKED));
String disliked = cursor.getString(cursor.getColumnIndex(DBOpenHelper.DISLIKED));
final String post_id = cursor.getString(cursor.getColumnIndex(DBOpenHelper.POST_ID));
String userliked = cursor.getString(cursor.getColumnIndex(DBOpenHelper.USER_LIKED));
TextView tvPost = (TextView) view.findViewById(R.id.tvPost );
tvJoke.setText(post);
TextView tvLikeCount = (TextView) view.findViewById(R.id.tvLikeCount);
tvLikeCount.setText(liked);
TextView tvDislikesCount = (TextView) view.findViewById(R.id.tvDislikeCount);
tvDislikesCount.setText(disliked);
final ImageView ivLike = (ImageView) view.findViewById(R.id.ivLike);
// has the user liked it?
int check = Integer.parseInt(userliked);
if(check == 1){
String uri = "#drawable/like_blue"; // where myresource (without the extension) is the file
int imageResource = context.getResources().getIdentifier(uri, null, context.getPackageName());
Drawable res = context.getResources().getDrawable(imageResource);
ivLike.setImageDrawable(res);
}
ivLike.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String uri = "#drawable/like_blue";
int imageResource = context.getResources().getIdentifier(uri, null, context.getPackageName());
Drawable res = context.getResources().getDrawable(imageResource);
ivLike.setImageDrawable(res);
}
});
}
}
You can use state drawables in your drawables/like.xml like this,
like.xml
<item android:state_activated="true"
android:drawable="#drawable/like_blue"/>
<item android:drawable="#drawable/like_grey"/>
you can add to your imageview's
android:src="#drawable/like"
android:clickable="true"
in your adapter,
ivLike.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(ivLike.isActivated())
ivLike.setActivated(false);
else
ivLike.setActivated(true);
}
});
for dislike image also you can do the same.
You are running into a view recycling issue. To learn more, look at the answer to this question. In your case, the easiest thing to do is to use the view that's passed into your onClick method:
ivLike.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ImageView icon = (ImageView) view;
String uri = "#drawable/like_blue";
int imageResource = context.getResources().getIdentifier(uri, null, context.getPackageName());
Drawable res = context.getResources().getDrawable(imageResource);
icon.setImageDrawable(res);
}
});
Related
The View for a ListView two buttons (for example to perform: call, send SMS). I want to set up a listeners for clicks on the buttons. It is my understanding that if I set up a OnItemSelectedListener, I won't know which of the two buttons was clicked.
When I set up listeners for a button, when I click on it, I get the listener of the corresponding button that is in the last item in the list (not the one I clicked on) (which makes sense programmatically) but this is not what I want. I can tell because it always calls the number that belongs to the last item in the list.
Any tips on how set up OnClick for multiple views in each item ?
#NonNull
#Override
public View getView(final int position, #Nullable final View convertView, #NonNull ViewGroup parent) {
LayoutInflater inf = ((Activity)context).getLayoutInflater();
View view = inf.inflate(R.layout.place , parent , false);
name = view.findViewById(R.id.name);
type = view.findViewById(R.id.type);
minAge = view.findViewById(R.id.minAge);
open = view.findViewById(R.id.open);
close = view.findViewById(R.id.close);
distance = view.findViewById(R.id.distance);
phone = view.findViewById(R.id.phone);
picture = view.findViewById(R.id.picture);
btnCall = view.findViewById(R.id.btnCall);
final Place place = placeList.get(position);
// btnCall.setOnClickListener(this);
// I tried both setups with the same result.
// the button is always the one belonging to the last item in the list.
btnCall.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String s = btnPhone.getText().toString();
}
});
// another button would have its own OnClick
name.setText(place.getName());
type.setText(place.getType());
minAge.setText(Integer.toString(place.getMinAge()));
open.setText(Integer.toString(place.getOpen()));
close.setText(Integer.toString(place.getClose()));
distance.setText(Integer.toString(place.getDistance()));
phone.setText(place.getPhone());
int id = context.getResources().getIdentifier("place"+position,"drawable" ,
((Activity)context).getPackageName());
//row_pic.setImageResource(id);
return view;
}
#Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:" + phone.getText()));
if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(intent);
}
if (intent.resolveActivity(context.getPackageManager()) != null)
{
context.startActivity(intent);
}
}
I found the solution just after posting the question.
USE TAGS to pass data.
'''
phone.setText(place.getPhone());
btnCall.setTag(phone.getText().toString());
btnCall.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String s = (String) ((Button) view).getTag().toString();
// now I can call the correct number
}
});
'''
I'm trying to develop an app to record debts in which I have a SwipeMenuListView from this github https://github.com/baoyongzhang/SwipeMenuListView for adding a swipe menu. Using a custom CursorAdapter, I populate the ListView with the name and total debt.
Now, I want to group each listview items depending on the due date. I've created a new column on my SQLite to add a header for each day. Now I just need to use different style for header and items of the ListView. By detecting the new column from bindView and depending on if it's a header or items, it will change, hide and show elements from the same layout.
The problem is that when I scroll the ListView, some of the listview items changed style. It get worse if I keep scrolling up and down. Here's the picture of the error from the listview. Notice that it's all in one session, the header style seems to have been used in some of the items and the header itself changed to red color which suppose to be color code for the items. If I click one of the item, it still get the correct item so I figure its a problem within the cursorAdapter but I just can't figure it out. It is not a mistake in the SQL database which I have checked.
Here's the cursorAdapter.
public class DebtCursorAdapterMain extends CursorAdapter {
public DebtCursorAdapterMain(Context context, Cursor c, int flags) {
super(context, c, flags);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.debt_list_item, parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
int x = Integer.parseInt(cursor.getString(cursor.getColumnIndex(DBHelper.DATE_SEPARATOR_COLUMN)));
TextView tvName = (TextView) view.findViewById(R.id.tvName);
TextView tvTotal = (TextView) view.findViewById(R.id.tvTotal);
if(x == 0) {
DecimalFormat df = new DecimalFormat("#.00");
String nameText = cursor.getString(cursor.getColumnIndex(DBHelper.NAME_COLUMN));
String totalText = "$ " + df.format(cursor.getDouble(cursor.getColumnIndex(DBHelper.TOTAL_COLUMN)));
String type = cursor.getString(cursor.getColumnIndex(DBHelper.TYPE_COLUMN));
if (tvName != null)
tvName.setText(nameText);
if (tvTotal != null)
tvTotal.setText(totalText);
if (type.equals("L"))
view.setBackgroundColor(Color.parseColor("#ff9999"));
if (type.equals("B"))
view.setBackgroundColor(Color.parseColor("#99ff99"));
}
if(x == 1){
String date = cursor.getString(cursor.getColumnIndex(DBHelper.DUE_DATE_COLUMN));
if (tvName != null && tvTotal != null) {
tvName.setText(date);
tvName.setTextSize(TypedValue.COMPLEX_UNIT_SP, 22);
tvTotal.setVisibility(View.GONE);
}
}
}
}
Here is the main activity in which the cursorAdapter is called.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Find SwipeMenuListView
final SwipeMenuListView swipeMenuList = (SwipeMenuListView) findViewById(R.id.swipeMenuList);
// Create Debt database cursor adapter
cursorAdapter = new DebtCursorAdapterMain(this, null, 0);
// Create SwipeMenuList and set item
SwipeMenuCreator creator = createMainActivitySwipeMenu();
swipeMenuList.setMenuCreator(creator);
swipeMenuList.setAdapter(cursorAdapter);
swipeMenuList.setSwipeDirection(SwipeMenuListView.DIRECTION_LEFT);
// Set SwipeMenuList on item's menu click
swipeMenuList.setOnMenuItemClickListener(new SwipeMenuListView.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(int position, SwipeMenu menu, int index) {
....
}
});
// Swipe menu on Click function
swipeMenuList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
....
}
});
// Initialize cursor and check database for updating top info
getLoaderManager().initLoader(0, null, this);
checkDataBase();
}
I'm still new in android development so please tell me if there's a better approach to this problem. Thanks guys.
This is a follow on from an earlier question: ImageButton within row of ListView android not working
But after suggestions from SO gurus it has been suggested I post a new question.
The issue is that I have a custom adapter that is not showing any data. I have looked into other questions, but it didn't provide a solution.
In my Main Activity I have a couple of buttons, one of them: ToDo, should create a row that displays data from a SQLite database, and depending on some factors (dates mainly), it shows a type of traffic light that is stored as a drawable.
Part of the Items in this Row is an Image Button that I want the user to be able to click and the image should change. The user should be able also to click on the actual row and a new activity starts.
The issue I have is that NO DATA is being displayed.
So, here is my code:
public class MainActivity extends Activity {
// definitions etc ...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// definitions etc ...
}
public void ToDo(View v){ // the user has clicked in the ToDo button
IgroDatabaseHelper helper = new IgroDatabaseHelper(getBaseContext()); // create instance of SQLIte database
numRows = helper.NumEntries("ToDo"); // Get the number of rows in table
int i = 1;
ArrayList<RowItem> rowItems = new ArrayList<>();
RowItem myItem1;
while (i <= numRows){
// get items from database
// depending on value select different drawable
// put data into List Array of RowItem
myItem1 = new RowItem(TheWhat, R.drawable.teamworka, R.drawable.redtrafficlight, R.drawable.checkbox, TheWhenBy);
rowItems.add(myItem1);
//
i = i+ 1;
}
ListView yourListView = (ListView) findViewById(R.id.list);
CustomListViewAdapter customAdapter = new CustomListViewAdapter(this, R.layout.todo_row, rowItems);
yourListView.setAdapter(customAdapter);
}
The CustomListViewAdapter looks like this:
public class CustomListViewAdapter extends ArrayAdapter<RowItem> {
Context context;
ArrayList<RowItem> _rowItems;
public CustomListViewAdapter(Context context, int resourceId,
ArrayList<RowItem> rowItems) {
super(context, resourceId);
this.context = context;
_rowItems = rowItems;
System.out.println("I am in the custom Adapter class "+ _rowItems);
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
System.out.println("This is the get view");
View row = convertView;
RowItem item = _rowItems.get(position);
// you can now get your string and drawable from the item
// which you can use however you want in your list
String columnName = item.getColumnName();
int drawable = item.getDrawable();
if (row == null) {
LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = mInflater.inflate(R.layout.todo_row, parent, false);
}
ImageButton chkDone = (ImageButton) row.findViewById(R.id.chkDone);
chkDone.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
View parentRow = (View) v.getParent();
ListView listView = (ListView) parentRow.getParent();
final int position = listView.getPositionForView(parentRow);
System.out.println("I am in position "+ position);
}
});
return row;
}
}
The RowItem Class looks like:
public class RowItem {
private String _heading;
private int _icon;
private int _lights;
private int _chkdone;
private String _date;
public RowItem(String heading, int icon, int lights, int chkDone, String date) {
_heading = heading;
_icon = icon;
_lights = lights;
_chkdone = chkDone;
_date = date;
System.out.println("adding stuff to my rows");
System.out.println("my column Name is " + heading);
System.out.println("My drawable int is "+ icon);
}
public String getColumnName() {
System.out.println("column Names is "+ _heading);
return _heading;
}
public int getDrawable() {
return _icon;
}
public int getLights(){
return _lights;
}
public int getchkDone(){
return _chkdone;
}
public String getDate(){
return _date;
}
}
I am obviously missing something, as I mentioned earlier, no data gets shown. I know that there are 2 row items that get passed to the CustomListViewAdapter. But I also know that the View getView inside the CustomListViewAdapter does not actually get called.
I hope I have put enough information/code, but if you feel I need to explain something further, please say.
Thanking all very much in advance!
I don't see a getCount() method. You should be overriding it like this:
#Override
public int getCount() {
return _rowItems.getCount();
}
Alternatively, calling super(context, resourceId, rowItems); should also fix it.
Your ListView thinks there are no items to display. If you are using your own array, you must override the getCount() method to indicate the number of items you want to display.
I am new in this stuff so i hope its not a ridiculous question...
I have a list view, and all the items including a video, numOfLikes(textview), like and dislike (Buttons).
When I click on the like or dislike button, I try to change the background of the button, but it changes the background of all the like/dislike buttons in the list view.
The same happens when I am trying to enable the dislike button while I am clicking on the like button, it disables all the dislike buttons in the list.
Code :
public class FeedAdapter extends ArrayAdapter<Feed> {
Context context;
ArrayList<Feed> feedsList;
ArrayList<String> listOfItems;
Dialog dialog;
public FeedAdapter(Context context, int resource, ArrayList<Feed> feeds) {
super(context, resource, feeds);
this.context = context;
this.feedsList = feeds;
}
public FeedAdapter(Context context, ArrayList<Feed> feeds){
super(context, R.layout.feed_listitem, feeds);
this.context = context;
this.feedsList = feeds;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final FeedHolder holder;
if(convertView == null){
convertView = LayoutInflater.from(context).inflate(R.layout.feed_listitem, parent, false);
holder = new FeedHolder();
holder.titleTextView = (TextView) convertView.findViewById(R.id.feed_title_textView);
holder.usernameTextView = (TextView) convertView.findViewById(R.id.feed_name_textview);
holder.likesTextView = (TextView) convertView.findViewById(R.id.feed_likes_textview);
holder.likeButton = (Button) convertView.findViewById(R.id.feed_like_button);
holder.unlikeButton = (Button) convertView.findViewById(R.id.feed_unlike_button);
holder.video = (VideoView) convertView.findViewById(R.id.feed_post_videoView);
holder.frameLayout = (FrameLayout) convertView.findViewById(R.id.feed_placeholder_framelayout);
holder.frameLayout.setTag(holder.video);
holder.likeButton.setTag(holder.unlikeButton);
holder.unlikeButton.setTag(holder.likeButton);
convertView.setTag(holder);
} else{
holder = (FeedHolder) convertView.getTag();
holder.frameLayout.setTag(holder.video);
holder.likeButton.setTag(holder.unlikeButton);
holder.unlikeButton.setTag(holder.likeButton);
}
holder.titleTextView.setText(feedsList.get(position).getTitle());
holder.usernameTextView.setText(feedsList.get(position).getUsername());
holder.likesTextView.setText(TrendliContract.showNumInNumK(feedsList.get(position).getLikesInLong()));
holder.titleTextView.setTypeface(TrendliContract.helvetica);
holder.usernameTextView.setTypeface(TrendliContract.helvetica);
holder.likesTextView.setTypeface(TrendliContract.helvetica);
holder.frameLayout.setBackground(feedsList.get(position).getDrawable());
holder.video.setVisibility(View.INVISIBLE);
holder.video.setMediaController(new MediaController(context));
holder.video.setVideoURI(Uri.parse(feedsList.get(position).getVideoImageGif()));
holder.video.setLayoutParams(new FrameLayout.LayoutParams(TrendliContract.screenW,
TrendliContract.screenW));
postImageViewOnClickListener(holder);
holder.likeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Feed f = feedsList.get(position);
//Button b = ((Button) v.getTag());
if(f.isLikePressed() == false){
new TrendliContract.Like().execute(f);
f.setLikePressed(true);
int numOfLikes = Integer.parseInt(holder.likesTextView.getText().
toString().replace(" ", ""));
holder.likesTextView.setText(String.valueOf(++numOfLikes));
//v.setBackgroundColor(R.color.black);
//b.setEnabled(false);
} else{
new TrendliContract.UnLike().execute(f);
f.setLikePressed(false);
int numOfLikes = Integer.parseInt(holder.likesTextView.getText().
toString().replace(" ", ""));
holder.likesTextView.setText(String.valueOf(--numOfLikes));
//v.setBackgroundColor(R.color.transparent);
//b.setEnabled(true);
}
}
});
holder.unlikeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Feed f = feedsList.get(position);
//Button b = ((Button) v.getTag());
if(f.isDislikePressed() == false){
new TrendliContract.DisLike().execute(f);
f.setDislikePressed(true);
int numOfLikes = Integer.parseInt(holder.likesTextView.getText().toString().replace(" ", ""));
holder.likesTextView.setText(String.valueOf(--numOfLikes));
//v.setBackgroundColor(R.color.black);
//b.setEnabled(false);
} else{
new TrendliContract.UnDisLike().execute(f);
f.setDislikePressed(false);
int numOfLikes = Integer.parseInt(holder.likesTextView.getText().toString().replace(" ", ""));
holder.likesTextView.setText(String.valueOf(++numOfLikes));
//v.setBackgroundColor(R.color.transparent);
//b.setEnabled(true);
}
}
});
convertView.setTag(holder);
return convertView;
}
private class FeedHolder{
TextView titleTextView;
TextView usernameTextView;
TextView likesTextView;
Button likeButton;
Button unlikeButton;
VideoView video;
FrameLayout frameLayout;
}
}
You must never change the properties of ListView item's view in the onClick event.
The reason is that the onClick event get called out of getView method in a different time and because of recycling system of ListView those affected properties will be applied to recycled views, so you get irrelevant results.
Instead you must store the properties in an array and then in the getView method use that array to apply properties to views...
In the Adapter class
boolean[] buttonState;
In the constructor:
buttonState = new boolean[feeds.size()];
In the onClick:
buttonState[position] = false; // or true...
In the getView:
holder.yourButton.setEnabled(buttonState[position]);
As a variant you can create custom button class extends Button and implements View.OnClickListener, then override it's onClick method to set background corresponding with the flag like mPressed. After that you can use this class for yours buttons. Here an example of custom button class:
public class CustomButton extends Button implements View.OnClickListener {
private boolean mPressed = false;
private static final int mRegularBackID = R.drawable.button_up;
private static final int mPressedBackID = R.drawable.button_down;
public CustomButton( Context context ) {
super( context );
}
public CustomButton( Context context, AttributeSet attrs ) {
super( context, attrs );
}
public CustomButton( Context context, AttributeSet attrs, int defStyle ) {
super( context, attrs, defStyle );
}
#Override
public void onClick( View view ) {
mPressed = !mPressed;
this.setBackgroundResource( mPressed ? mPressedBackID : mRegularBackID );
}
}
Instead of static drawable IDs with the flag (as in my example) you can also use drawable state list or constructor with corresponding drawables as additional parameters.
You can use this custom class in XML with full package name like
<my.own.project.package.CustomButton... />
View passed in onClick(View v) it's clicked Button object you can use it to change Button property.
public void onClick(View v) {
Button b = ((Button)v);
b.setEnabled(false);
}
You can setup background property of Button inside your xml layout. Even you can define drawable xml slector witch will define different background for different states like: pressed, focused, disabled, etc.
First you have to define selector xml and set it as Button background:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/selected" android:state_pressed="true"/>
<item android:drawable="#color/selected" android:state_selected="true"/>
<item android:drawable="#android:color/transparent"/>
</selector>
Instead of colors you can use drawable resources (images or shapes). Also you can define different states in this list. First element in selector witch match current satte will be used.
Try --
likeButton.setBackgroundColor(R.color.transparent);
unlikeButton.setEnabled(false);
I have a ListView backed by SimpleCursorAdapter and custom ViewBinder. I want to make items in this listview change their color on clicking. If I do that in the OnClickListener - it works paritally, changing the color of the item clicked, and of the items down the list, each 7th (I guess, the period depends on on the viewable area of the listview).
Can anyone suggest how to deal with this? Or, maybe point to a more elegant way of making items in the listView selectable?
Thank you.
UPD: (sorry for bad formatting - this is the first time I post a question):
Below is how I try to make an item in the ListView "selected":
private void setupListView(final ListView lv) {
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> adapterView, View view, int position, final long id) {
RelativeLayout layout = (RelativeLayout) view;
int color;
if (conditionMet) {
color = R.color.gray;
} else {
color = R.color.red;
}
for(int i = 0; i < layout.getChildCount(); i++) {
((TextView)layout.getChildAt(i)).setTextColor(getResources().getColor(color));
}
return;
}}
This is how I init the adapter:
final SimpleCursorAdapter adapter =
new SimpleCursorAdapter(
this,
itemId,
cursor,
from,
to
);
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
final TextView textView = (TextView) view;
// do necessary conversions
return true;
}
});
listView.setAdapter(adapter);
You can use the property android:listSelector to set the theme or any drawable or color of the currently selected item in a list.
Since no other answer, and, I think, I had some troubles with the suggestion below, I post how I did it:
I store ids of the items clicked in a special map
in the listview onclick I check whether the id of the just clicked item is in the map: if yes, I remove it and make the item and its children color A, otherwise I add the id to the map and set the color to B
public void onItemClick(AdapterView<?> adapterView, View view, int position, final long id) {
Context ctx = MainActivity.this;
RelativeLayout layout = (RelativeLayout) view;
try {
int color;
if (items.containsKey(id)) {
items.remove(id);
color = R.color.gray;
tempIds.remove(id);
} else {
items.put(id, sum);
color = R.color.red;
tempIds.add(id);
}
for (int i = 0; i < layout.getChildCount(); i++) {
final TextView textView = (TextView) layout.getChildAt(i);
textView.setTextColor(getResources().getColor(color));
}
} catch (ParseException e) {
Log.e(MainActivity.class.toString(), "Exception parsing", e);
}
return;
}
}