Android Listview clickable textview - java

I have made an Listview populated with list_row_layout.xml(which is populated with json serializable class), i have clickable textview and onclick changing text from "Accept" to "Accepted". But when i click it on first listview item, another textview listview items below are changing.
Here's some photos to descibe you better
this is the adapter class
public class CustomListAdapter extends BaseAdapter {
private ArrayList<FeedItem> listData;
private LayoutInflater layoutInflater;
private Context mContext;
public CustomListAdapter(Context context, ArrayList<FeedItem> listData) {
this.listData = listData;
layoutInflater = LayoutInflater.from(context);
mContext = context;
}
#Override
public int getCount() {
return listData.size();
}
#Override
public Object getItem(int position) {
return listData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.list_row_layout, null);
holder = new ViewHolder();
holder.headlineView = (TextView)convertView.findViewById(R.id.sex);
holder.reportedDateView = (TextView) convertView.findViewById(R.id.confid);
holder.approve = (TextView) convertView.findViewById(R.id.approveTV);
holder.approve.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View argView)
{
holder.approve.setText("Accepted");
}
}
);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
FeedItem newsItem = (FeedItem) listData.get(position);
holder.headlineView.setText(Html.fromHtml(newsItem.getTitle()));
holder.reportedDateView.setText(Html.fromHtml(newsItem.getContent()));
return convertView;
}
static class ViewHolder {
TextView approve;
TextView headlineView;
TextView reportedDateView;
ImageView imageView;
}
}

Remember that views can be recycled via convertView.
In your onClick method you set the approve text to "Accepted" but when the view is recycled, you never set it back to "Accept"
Actually you need to update (something in) the list in response to an click and have the Accept/Accepted value toggle based on that value rather than simply updating what is currently visible on the screen.
-- to answer the "how" question (asked below)--
Add a new field to ViewHolder
static class ViewHolder {
TextView approve;
TextView headlineView;
TextView reportedDateView;
ImageView imageView;
FeedItem newsItem;
}
Change the onClick method:
public void onClick(View argView)
{
// note that holder no longer needs to be final in the parent class
// because it is not used here.
View parent = (View)argView.getParent();
ViewHolder clickedHolder = (ViewHolder)parent.getTag();
clickedHolder .newsItem.setAccepted(true); /// a new method
clickedHolder .approve.setText ("Accepted");
Log.d(TAG, "Accepted item #" + position);
}
After you have convertView created (if necessary)
FeedItem newsItem = (FeedItem) listData.get(position);
holder.newsItem = newsItem; // populate the new field.
holder.headlineView.setText(Html.fromHtml(newsItem.getTitle()));
holder.reportedDateView.setText(Html.fromHtml(newsItem.getContent()));
if(newsItem.isAccepted ()){ // another new method!
holder.approve.setText ("Accepted");
Log.d(TAG, "Set text to Accepted for item #" + position);
}else{
holder.approve.setText("Accept");
Log.d(TAG, "Set text to Accept for item #" + position);
}
Once it is working you should consider removing the Log.d() lines to cut down on the noise in LogCat.

Related

Listview lag when scrolling, and crash when add item

I have a custom listview with a button to add more elements
but when I add and element the app crash, but when I restart I find that the element is added, (rarely it doesn't crash)
And i
I use custom adapter
class CustomAdapter extends BaseAdapter {
ArrayList<ListItem> listItems = new ArrayList<ListItem>();
CustomAdapter(ArrayList<ListItem> list){
this.listItems = list;
}
#Override
public int getCount() {
return listItems.size();
}
#Override
public Object getItem(int position) {
return listItems.get(position).name;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int i, View convertView, ViewGroup parent) {
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.list_item,null);
TextView name = (TextView) view.findViewById(R.id.name);
TextView lastm = (TextView) view.findViewById(R.id.last);
TextView time = (TextView) view.findViewById(R.id.time);
CircleImageView pic= (CircleImageView) view.findViewById(R.id.pic);
name.setText(listItems.get(i).name);
lastm.setText(listItems.get(i).lastm);
time.setText(listItems.get(i).time);
Bitmap bmp = BitmapFactory.decodeByteArray(listItems.get(i).pic,0,listItems.get(i).pic.length);
pic.setImageBitmap(bmp);
return view;
}
}
and when I add an element the app crashs
add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText editText=(EditText) mView.findViewById(R.id.name);
String name=editText.getText().toString();
boolean result=myDataBase.insertData(imageViewToByte(img),name,"no messages yet","");
if (result) {
Toast.makeText(Main2Activity.this, "Saved in DATABASE", Toast.LENGTH_SHORT).show();
viewLastData();
dialog.dismiss();
}
If your ListView lags it's because you used wrap_content for the listView's layout_height. It forces your ListView to count all the items inside and it has a huge performance impact.
So replace wrap_content by match_parent to avoid those lags.
EDIT: Use a ViewHolder pattern too in your Adapter, see this link.
Here is an example:
// ViewHolder Pattern
private static class ViewHolder {
TextView name, status;
}
#Override #NonNull
public View getView(int position, View convertView, #NonNull ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
LayoutInflater vi = LayoutInflater.from(getContext());
convertView = vi.inflate(R.layout.list_item, parent, false);
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.text_name);
holder.status = (TextView) convertView.findViewById(R.id.another_text);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// Then do the other stuff with your item
// to populate your listView
// ...
return convertView
}

multiselection listView contains checkBoxes

I have ListView with check boxes the app delete the checked items ;; and should delet teh items that have the same id with the checked items :
here's my code
private class CAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private ArrayList<Entity> list;
private Context context;
String Status;
CAdapter(Context context,
ArrayList<Entity> getC) {
this.context = context;
this.list = getC;
Status="";
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
class ViewHolder {
TextView Name;
TextView Desc;
Button deleteBtn;
CheckBox CBox;
}
public int getCount() {
return list.size();
}
public Object getItem(int position) {
return list.get(position);
}
public long getItemId(int position) {
return position;
}
#SuppressLint("NewApi")
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
final CEntity CObj = list.get(position);
if (convertView == null) {
convertView = mInflater.inflate(
R.layout.custom_list_view_confirmed, parent,
false);
holder = new ViewHolder();
holder.Name = (TextView) convertView
.findViewById(R.id.Name);
holder.Desc = (TextView) convertView
.findViewById(R.id.activity1);
holder.deleteBtn = (Button) convertView
.findViewById(R.id.deleteBtn);
holder.CBox=(CheckBox) convertView.findViewById(R.id.isCheck);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if (CObj.getMystatus().equals(
context.getResources().getString(R.string.course_status_delete))) {
holder.status.setTextColor(Color.RED);
} else if (attemptedCourseObj.getMystatus().equals(
context.getResources().getString(R.string.course_status_pending))) {
holder.status.setTextColor(Color.GREEN);
} else if (attemptedCourseObj.getMystatus().equals(
context.getResources().getString(R.string.course_status_update))) {
holder.status.setTextColor(Color.BLUE);
}
holder.Name.setText(attemptedCourseObj.getCourseName());
holder.CBox.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(holder.CBox.isChecked()){
if(list.contains(getItem(position))){
list.remove(getItem(position));
}
}
}
});
//
return convertView;
}
}
the problem is when delete the checked it dosent delete the item that have the same id .
int pos=Data.CList.size();
SparseBooleanArray checked=CListView.getCheckedItemPositions();
for (int n = pos; n > 0; n--){
if (checked.get(n)){
code=Data.inList.get(n).getCCode();
Data.inList.remove(n);
}else if(CList.get(n).equal(code){
Data.inList.remove(n);
}
try to refresh the list with notifydatasetchanged and be sure that you delete the entire object from the list
You need to inform your adapter with the new modifications by giving the adapter the new dataset after removing the deleted elements from it and notify with the changes. You can do this as the following:
1)Add method into your adapter to set the new data as following:
public void setNewData(ArrayList<Entity> newEntities){
this.list = newEntities;
}
2)From the activity or the fragment call the previous method with the new data and call this line to notify the adapter with the changes
myAdapter.setNewData(myNewEntities);
myAdapter.notifyDataSetChanges();
Read this answer for more info about NotifyDataSetChanges() method

Android Listview clickable textview conflict

I have made an Listview populated with list_row_layout.xml(which is populated with json serializable class), i have clickable textview and onclick changing text from "Accept" to "Accepted". But when i click it on first listview item, another textview listview items below are changing. Here's some photos to descibe you better
Activity class
feedListView.setAdapter(new CustomListAdapter(this, feedList));
adapter class
public class CustomListAdapter extends BaseAdapter
{
private ArrayList<FeedItem> listData;
private LayoutInflater layoutInflater;
private Context mContext;
public CustomListAdapter(Context context, ArrayList<FeedItem> listData)
{
this.listData = listData;
layoutInflater = LayoutInflater.from(context);
mContext = context;
}
#Override
public int getCount()
{
return listData.size();
}
#Override
public Object getItem(int position)
{
return listData.get(position);
}
#Override
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
final ViewHolder holder;
if (convertView == null)
{
convertView = layoutInflater.inflate(R.layout.list_row_layout, null);
holder = new ViewHolder();
holder.headlineView = (TextView)convertView.findViewById(R.id.title);
holder.reportedDateView = (TextView) convertView.findViewById(R.id.confid);
holder.approve = (TextView) convertView.findViewById(R.id.approveTV);
holder.approve.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View argView)
{
holder.approve.setText("accepted");
}
}
);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
FeedItem newsItem = listData.get(position);
holder.headlineView.setText(Html.fromHtml(newsItem.getTitle()));
holder.reportedDateView.setText(Html.fromHtml(newsItem.getContent()));
holder.approve.setTag(newsItem);
return convertView;
}
static class ViewHolder
{
TextView approve;
TextView headlineView;
TextView reportedDateView;
ImageView imageView;
}
}
textview code
<TextView
android:id="#id/approveTV"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginRight="5dp"
android:background="#drawable/pressed"
android:gravity="fill"
android:text="Accept"
android:clickable="true"
android:textColor="#0D98BA"
android:textSize="17sp" />
You have to write some tracking position functionality which remember the position which you have changed and make sure that only text of position changed .
you must set id to each row, and save it in one array,like integer and when you click on one row you must change the value of that, and in changing text or anything you must check the value of that row,if 0 then set default and if 1 then so what you want: my idea is create int[] with length of that is size of your list,
first you must define you int[] like below and set to zero every index.
int[] selected = new int[listData.getsize()]
something like this in getview:
holder.approve.setId(Html.fromHtml(newsItem.getId());
and after else statement in getView:
if (selected[position] == 0)
{
// set default
}
else
{
// any changing that you want
}
and in onClick:
selected[holder.approve.getId()] = 1;
// any change that you want
Try the below code:-
public View getView(final int position, View convertView, ViewGroup parent)
{
final ViewHolder holder;
if (convertView == null)
{
holder = new ViewHolder();
convertView = layoutInflater.inflate(R.layout.list_row_layout, null);
holder.headlineView = (TextView)convertView.findViewById(R.id.title);
holder.reportedDateView = (TextView) convertView.findViewById(R.id.confid);
holder.approve = (TextView) convertView.findViewById(R.id.approveTV);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
FeedItem newsItem = listData.get(position);
holder.headlineView.setText(Html.fromHtml(newsItem.getTitle()));
holder.reportedDateView.setText(Html.fromHtml(newsItem.getContent()));
holder.approve.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View argView)
{
holder.approve.setText("accepted");
}
}
);
return convertView;
}
Thanks!!

my images get shuffled or changed when i scroll in list view

my images get shuffled or changed when i scroll in list view.......
Images get shuffled when i scroll down and keep on changing own its own...............
public class CustomListAdapter extends BaseAdapter {
private ArrayList<NewsItem> listData;
private LayoutInflater layoutInflater;
public CustomListAdapter(Context context, ArrayList<NewsItem> listData) {
this.listData = listData;
layoutInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return listData.size();
}
#Override
public Object getItem(int position) {
return listData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.activity_list_search, null);
holder = new ViewHolder();
holder.headlineView = (TextView) convertView.findViewById(R.id.title);
holder.reporterNameView = (TextView) convertView.findViewById(R.id.reporter);
holder.reportedDateView = (TextView) convertView.findViewById(R.id.date);
holder.imageView = (ImageView) convertView.findViewById(R.id.thumbImage);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
NewsItem newsItem = (NewsItem) listData.get(position);
holder.headlineView.setText(newsItem.getHeadline());
holder.reporterNameView.setText("Model: " + newsItem.getReporterName());
holder.reportedDateView.setText(newsItem.getDate());
if (holder.imageView != null) {
new ImageDownloaderTask(holder.imageView).execute(newsItem.getUrl());
}
return convertView;
}
static class ViewHolder {
TextView headlineView;
TextView reporterNameView;
TextView reportedDateView;
ImageView imageView;
}
}
The problem is very generic for new Android developers.
Your ImageDownloaderTask should take care of whether the downloaded image should still be posted on screen.
For example, after ImageDownloaderTask finishes one image downloading task (e.g. for index 15), it should make sure the 15th item is still displaying on screen. Since ListView is reusing all the item views, if you directly set the bit map to the original referenced view, your imageview will be shuffled.

Why conditions from Adapters getView(..) method doesn`t apply correctly to ListView

I have a ListView in which I display some products. I use an object that extends the BaseAdapter class to populate the ListView, more exacly using the getView(..) method. I have a TextView "link" on every itemView on which if the user taps it will go to a web page. In my base adapter I set a listener on the TextView only if my product contains a link.
I have done debugging in my getView(..) method and it all works just fine, but after it exits the getView method, if there is an item that doesn`t have a link it will take the link/listener from another item from the listView.
Adapter Class:
public class MatchListBaseAdapter extends BaseAdapter {
private static ArrayList<Match> matchesArrayList;
private LayoutInflater l_Inflater;
private OnClickListener onClickListener;
public MatchListBaseAdapter(Context context, View.OnClickListener listener, ArrayList<Match> results,Activity a) {
matchesArrayList = results;
onClickListener = listener;
l_Inflater = LayoutInflater.from(context);
}
public int getCount() {
return matchesArrayList.size();
}
public Object getItem(int position) {
return matchesArrayList.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = l_Inflater.inflate(R.layout.itemlist_match, null);
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.oferNameMLI2);
holder.expireDate = (TextView) convertView.findViewById(R.id.expireDateMLI);
holder.price = (TextView) convertView.findViewById(R.id.priceMLI);
holder.companyName = (TextView) convertView.findViewById(R.id.compNameMLI);
holder.productImage = (ImageView) convertView.findViewById(R.id.productImageMLI);
holder.companyImage = (ImageView) convertView.findViewById(R.id.companyImageMLI);
holder.description = (TextView) convertView.findViewById(R.id.moreDetailsMLI);
holder.digitalySigned = (ImageView) convertView.findViewById(R.id.digitalSignatureImageView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
//populating the holder.. doesn`t have any relevance..
if(matchesArrayList.get(position).getCompanyLink() != null){
holder.companyImage.setOnClickListener(onClickListener);
holder.companyImage.setTag(position);
}
return convertView;
}
static class ViewHolder {
TextView name;
TextView expireDate;
TextView price;
TextView companyName;
TextView description;
ImageView productImage;
ImageView companyImage;
ImageView digitalySigned;
}
}
Activity onCreate:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listview_layout);
matches = DataManager.getInstance().getListSubscription().get(DataManager.getInstance().getSubscriptionPosition()).getMatchesList();
ListView lv1 = (ListView) findViewById(R.id.listView_layout);
DataManager.getInstance().setAdapterMatch(new MatchListBaseAdapter(this, this, matches,this));
lv1.setAdapter(DataManager.getInstance().getAdapterMatch());
}
Just want to mention once again that i ve done debugging in the getView(..) method and it was ok, the flow was the right one, but after the items in the listView that doesn't supposed to have a listener on the TextView it had from the other items.
Also this happens always for the first item in the listView .. and it is populated with the link from the last item in the listView that contains a link.
I've searched a lot for this issue but didn't find anything relevant, but i think that there is a problem on my convertView, but i can`t figure it out..
Thx a lot
The problem is that when you're reusing the view and not setting an explicit onClickListener to it, it still contains the old listener from another product - from the view that was reused. Try making a change as following:
if(matchesArrayList.get(position).getCompanyLink() != null){
holder.companyImage.setOnClickListener(onClickListener);
holder.companyImage.setTag(position);
}
else {
holder.companyImage.setOnClickListener(null);
}

Categories