I'm in the process of making a grocery list app and I wrote the code to where when I click an item, it'll mark it off.
This is my code for that section:
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
TextView text = (TextView) view;
if (!text.getPaint().isStrikeThruText()) {
text.setPaintFlags(text.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}else{
text.setPaintFlags(text.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
}
}
});
}
It works exactly like i want but when I add another item after an item is marked off, all of the items that are marked off, the marks disappear.
When I add an item, it's like it resets. it doesn't delete any of my items, just the strike_thru part of it. any help would be greatly appreciated! thanks
Your ListView's Adapter contains a method called getView, which is called when a list view item needs to be displayed in an actual View. The Views in your ListView will be discarded if you scroll too far off screen, or invalidate the whole ListView.
My guess is that adding an item is invalidating the ListView.
Your getView method should set the paint flags on the view that it returns. Assuming your list view is displaying a String[], you will also need a boolean[] to hold whether or not an item is complete. You would need to initialize this to all falses, add a completed[i] = !completed[i] at the beginning of your onItemClick. Then you can check competed[i] instead of isStrikeThruText in your if statement, later in that method. Finally, your getView can look like this
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(android.R.layout.simple_list_item_1, parent, false);
}
TextView textView = (TextView) convertView;
textView.setText(items[position]);
if (completed[position]) {
textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
textView.setPaintFlags(textView.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
}
return textView;
}
Related
listContent.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
for(int i=0;i<showLists.size();i++){
//
TextView v=(TextView)listContent.getChildAt(i).findViewById(R.id.txtDes);
v.setTextColor(Color.BLACK);
}
TextView v=(TextView)listContent.getChildAt(position).findViewById(R.id.txtDes);
v.setTextColor(Color.RED);
Toast.makeText(context,"POS "+showLists.get(position).getDes(),Toast.LENGTH_SHORT).show();
}
});
I have been the problem with get position item of listview. Android just has shown about 12 row on devide's creen, when I click another item on listview (my listview have 30 item ) which shown the error.
And this is error:
"Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference" .
Thanks for read.
listContent.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
for (int i = 0; i < showLists.size(); i++) {
TextView v = (TextView) view.findViewById(R.id.txtDes);
v.setTextColor(Color.BLACK);
}
TextView v = (TextView) view.findViewById(R.id.txtDes);
v.setTextColor(Color.RED);
Toast.makeText(context, "POS " + showLists.get(position).getDes(), Toast.LENGTH_SHORT).show();
}
});
Your question is indeed about Null Pointer Exception, but is harder to identify why this is happening. The problem can be found here:
for(int i=0;i<showLists.size();i++){ <-- this line actually causes the crash
//
the crash is in the next line, at the findViewById
TextView v=(TextView)listContent.getChildAt(i).findViewById(R.id.txtDes);
v.setTextColor(Color.BLACK);
}
Your for has the wrong upper limit because of a mechanism known as recycling, and because of this mechanism your list view will never have the same number of rows as the amount of data that needs to be displayed (read about recycling to understand this). Given this fact, we know for sure that the number of views found in list view (listContent.getChildCount()) will be smaller than showLists.size() and thus making the call listContent.getChildAt(i) to return a NULL value when the index equals listContent.getChildCount() creating the crash.
Now you might be tempted to change showLists.size() with listContent.getChildCount() and see that the app doesn't crash anymore, but if you click a row, then other rows are coloured also as you scroll the list (the recycling is again the problem). To really fix the problem you should save the index of the selected row and call notifyDatasetChanged, so when getView is called in adapter you simply check the current position to be displayed with the selected position. In case of equality you change the color of text to red, otherwise to black. Below, you will find some parts of an example:
int currentPosition = -1;
// Just a basic adapter. The getView method is the key here
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1) {
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View view = super.getView(position, convertView, parent);
if (position == currentPosition) {
((TextView) view).setTextColor(Color.RED);
} else {
((TextView) view).setTextColor(Color.BLACK);
}
return view;
}
};
// and here is the onItemClick
listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
currentPosition = position;
adapter.notifyDataSetChanged();
}
});
public View getView(int position, View convertView, ViewGroup parent)
{
LayoutInflater inflater= (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View viewRow=convertView;
if(viewRow==null){
viewRow=inflater.inflate(layout,parent,false);
viewHolder viewHolder=new viewHolder();
viewHolder.imgIcon = (ImageView) viewRow.findViewById(R.id.imgIcon);
viewHolder.txtDes = (TextView) viewRow.findViewById(R.id.txtDes);
viewRow.setTag(viewHolder);
}
viewHolder holder= (viewHolder) viewRow.getTag();
holder.imgIcon.setImageResource(listMoiNhat.get(position).getIcon());
holder.txtDes.setText(listMoiNhat.get(position).getDes());
if(position==currentpos){
holder.txtDes.setTextColor(Color.RED);
}
else {
holder.txtDes.setTextColor(Color.RED);
}
return viewRow;
}
//and here is create customListMoiNhat object
final customListMoiNhat customListMoiNhat=new customListMoiNhat(context,R.layout.moinhat_customlistview,showLists,currentpos);
customListMoiNhat.notifyDataSetChanged();
listContent.setAdapter(customListMoiNhat);
listContent.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
currentpos=position;
customListMoiNhat.notifyDataSetChanged();
}
});
I want to make a first child item of grid view as "Add" button like below the image Link. Data should be appear on from position 1, not a position 0. Don't suggest the headerview, I tried it. please give me some great ideas to do that.
Click here to view
In your adapter's getView method, do this check:
#NonNull
#Override
public View getView(final int position, View convertView, #NonNull ViewGroup parent) {
// ...
// ...
if (position == 0) {
yourImageView.setImageResource(mContext.getResources().getDrawable(R.drawable.your_add_img));
}
return convertView;
}
It should be quite easy. If you have an adapter, just check if you're in the first indexing position and then add the button. If you're not you add your images.
I have a arraylist of buttons (reserveButtons) that I can display in a listview. I have made a search function which searches in my database and outputs a list of integers (resultID). They correspond to the indexes of reserveButtons I want to display.
Simply put, I want to do something like this when the search button is clicked:
ArrayAdapter<ReserveButton> adapter = new MyListAdapter();
ListView list = (ListView) myView.findViewById(R.id.resultslist);
list.setAdapter(adapter);
for (int result : resultID) {
adapter.add(reserveButtons.get(result));
}
So, for each result, I want to add the corresponding button to the listview.
Here is the private class MylistAadapter :
private class MyListAdapter extends ArrayAdapter<ReserveButton> {
public MyListAdapter() {
super(getActivity(), R.layout.list_item, reserveButtons);
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = convertView;
if(itemView == null) {
itemView = gettActivity().getLayoutInflater().inflate(R.layout.list_item, parent, false);
}
ReserveButton currentButton = reserveButtons.get(position);
//the resultItem is the id of the buttons
Button butt = (Button) itemView.findViewById(R.id.resultItem);
butt.setBackground(currentButton.getImage());
return itemView;
}
}
I know that getView will just display every reserveButton, but I want the code in getView to be executed when I add each button, but the position doesn't change since position = result in the for loop of the first code block.
//This code is inside MyListAdapter
#Override
public void add(ReserveButton object) {
/* What do I write here to inflate a list_item and give it
the background image reserveButton.get(result).getImage() */
super.add(object);
}
How do I override the add method of MyListAdapter so that I can add a reserveButton and change its background image for each result in the resultID list.
If the same thing can be accomplished without the add method, please do tell.
P.S: I do not want to just list every reserveButton and then filter them with the search; I want to display ONLY the buttons that the user is looking for.
I figured it out myself!
Basically, what I did was create a separate ArrayList of ReserveButtons and do the foreach loop like so:
int index = 0;
for (int result : resultID) {
//result is the single ID of an answer
answerButtons.add(index,reserveButtons.get(result));
index ++;
}
populateListView();
So I end up storing ONLY the buttons I want to display in the answerButtons list. And here is what happens in populateListView()
private void populateListView() {
ArrayAdapter<ReserveButton> adapter = new MyListAdapter();
ListView list = (ListView) myView.findViewById(R.id.resultslist);
list.setAdapter(adapter);
}
and the getView() method:
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = convertView;
if(itemView == null) {
itemView = getActivity().getLayoutInflater().inflate(R.layout.list_item, parent, false);
}
//Just set the image of the corresponding answerButton
ReserveButton currentButton = answerButtons.get(position);
Button butt = (Button) itemView.findViewById(R.id.resultItem);
butt.setBackground(currentButton.getImage());
return itemView;
}
Problem solved. I haven't seen any answers to a problem like this, so this post should make it easily google-able for any newcomer who stumbles upon this problem.
I got a ListView with a custom Adapter.
In the Adapter a layout is inflated from xml.
All works fine, and I can see the items, until the Screen Orientation is changed.
I know that the Activity is recreated (or resumed) then, and the ListView is recreated too, as well as the Adapter.
But there are no items in the ListView now. The Adapter isn't empty, I use toasts to display the count of items in the Adapter.
I guess there is an inflating problem, because if I use the same Adapter (or an adapter with the same data) to a new ListView nothing is shown as well.
But the most crazy thing I don't understand is, that if I let my getView() method return a simple TextView, all works fine, even after orientation change.
I tried several things, like don't recycle a View so that it is inflated every time, or save the View to the matching Item (from getItem(position) from the Adapter).
I'm grateful for all hints :)
EDIT: so I was asked for some code.
Here is the getView() of my Adaptar
#Override
public View getView(final int position, View convertView,
ViewGroup parent) {
View view = convertView;
final Event event = getItem(position);
if (view == null) {
view = inflater.inflate(R.layout.new_event_item_layout, parent,
false);
view.setTag(R.id.eventDate, view.findViewById(R.id.eventDate));
view.setTag(R.id.eventTime, view.findViewById(R.id.eventTime));
view.setTag(R.id.eventName, view.findViewById(R.id.eventName));
view.setTag(R.id.eventBemerkungen,
view.findViewById(R.id.eventBemerkungen));
view.setTag(R.id.eventIcon, view.findViewById(R.id.eventIcon));
}
((TextView) view.getTag(R.id.eventDate)).setText(event.getDate());
((TextView) view.getTag(R.id.eventTime)).setText(event.getName());
((TextView) view.getTag(R.id.eventName)).setText(event.getTime());
((TextView) view.getTag(R.id.eventBemerkungen)).setText(event
.getDescription());
SquaredImageView icon = (SquaredImageView) view.getTag(R.id.eventIcon);
Picasso.with(context).load(event.getUri())
.placeholder(R.drawable.ic_reload).into(icon);
view.setBackgroundColor(event.getBackgroundColor());
view.setVisibility(View.VISIBLE);
return view;
//return getDummyTextView();
}
public TextView getDummyTextView()
{
TextView tv=new TextView(context);
tv.setText("YOLO BIATCHSES");
tv.setTextColor(Color.WHITE);
return tv;
}
I have had the same exact problem although it wasn't because of orientation change and I found the solution by just setting listView.setAdapter(adapter) again after the recreation of the activity or whatever case you have. I suspect the listview is basically losing the pointer to the adapter.
I want to set the background color of a specific item in the listview.
My listview is generated by ArrayAdapter using a ArrayList.
I have a specific item in the listview that I plan to change the background color.
I know the item's position in the list.
This is my code for generating the listview.
respondMessageListView = (ListView) findViewById(R.id.respondMessageListView);
respondMessageListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, autoRespondMessages.getMessages()));
Thank you!
[edit]
According to this post, using setSelection makes no effect if is used in onCreate(), the work around is "remove the method onAttachedToWindow in PullToRefreshListView". I am not quite understanding the solution. May I ask how should I accomplish this? I am a subclass of Activity, so I cannot subclass any other class anymore.
You will have to subclass ArrayAdapter and override the getView(...) method. For simplicity's sake you could just call through to the base class implementation and set the background color for the returned View.
Edit:
The following example colors the items' backgrounds alternating black and white.
private class MyAdapter extends ArrayAdapter {
...
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
v.setBackgroundColor(position % 2 == 0 : 0xff000000, 0xffffffff);
}
}
This code is for the when you select the listitem.
Try this code...
listview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> myAdapter, View myView, int pos, long mylng) {
if( pos == 1) {
// to change the listview background
listview.setBackgroundColor(getResources().getColor(R.color.your_color_id));
// to change the selected item background color
myView.setBackgroundColor(getResources().getColor(R.color.your_color_id));
}
}
});
Good luck.