Custom Listview Index automatically changes - android

I've listview on which when a user clicks an item I show an image that user has clicked this item.The listview gets populated from notifications.
Suppose an item gets added in listview.Now I click that item and image is shown in front of it.Now when I enter the new item in the listview nothing will happen(there are 2 items in the listview now). When I add the third item in the listview the image from the item at position 0 hides and is shown in position 1. Why this is happening?
Now to add an image in the listview item. I have to first perform some checks on the next opening activity and if those checks are successful then I place an image in front of that clicked item in the activity.
So to do this what i did is I am sending the clicked item position in the next activity and perform those checks, if those checks are successful I place and image on the onActivityResult function of the previous activity by using that position which I sent to this activity.Meaning that I send that position value back to the previous activity in the onActivityResult function where I place the Image.
The placement of image works fine.But while I recieve notification I add the item in the listview which results in the getView function being called for each item in the listview again to populate the listview.Here the problem occurs i.e the image from the position 0 item in the list goes to the position 1 item.
Please ask if more explanation is needed or something is not clear.
Here's the code for listview Item Click:
TextView tv=(TextView)view.findViewById(R.id.clientip);
Intent i=new Intent(getApplicationContext(),ChatPageActivity.class);
i.putExtra("AdminEmail",ClientListActivity.AdminEmail);
i.putExtra("ClientIp",tv.getText().toString());
i.putExtra("ChildAt", position);
startActivityForResult(i, 200);
ChildAt is the position I am sending.
After performing checks in the next activity.
#Override
protected void onActivityResult(int requestCode,int resultCode,Intent data)
{
try {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode!= 23)
{
Integer integer=data.getExtras().getInt("ChildAt");
ImageView imageView=(ImageView)lv.getChildAt(integer).findViewById(R.id.onlineImg);
imageView.setImageResource(R.drawable.online);
}
}catch(Exception e)
{
Log.e("ClientList_onActivityResult",e.getMessage());
}
}
This is the onActivityResult if the code is not equal to 23 it means all the checks in the next activity are correct and you can place the image at the position which is sent back again to this activity from the next activity.
Here's the getView code of my BaseAdapter class:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view=convertView;
if(convertView==null)
view=inflater.inflate(R.layout.activeclientlist,null);
TextView ip=(TextView)view.findViewById(R.id.clientip);
String clientip=this.iplist.get(position);
ip.setText(clientip);
return view;
}
In the baseADapter I insert item in existing listview using this piece of code..
this.iplist.add(ip);
this.notifyDataSetChanged();
Important:
Also I wanted to mention that this behaviour happens in a pattern i.e as i've told above the image position changes when i insert the third item.After that it changes again when I'll add the fifth item and so on.

You should put the code to update the image view within the adapter itself.
The adapter should also be knowing all the info he needs to draw the data. So instead of having it store a list of Strings, store a list of objects like:
class Client {
String ipAddress;
boolean isConnected;
}
You then have to change your adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view=convertView;
if (convertView==null) {
view=inflater.inflate(R.layout.activeclientlist,null);
}
TextView ip=(TextView)view.findViewById(R.id.clientip);
ImageView status = (ImageView )view.findViewById(R.id.status);
Client c = getItem(position);
ip.setText(c.getIpAddress());
status.setImageDrawable(c.isConnected() ? R.drawable.ic_status_connected : 0);
return view;
}
Then just change the code to update the image when the connection status changes:
if (resultCode!= 23) {
Integer i = data.getExtras().getInt("ChildAt");
((Client)adapter.getItem(i)).isConnected = true;
adapter.notifyDataSetChanged();
}
This way you keep all the code related to representing your data within the adapter. Side effect, it should be correcting your code.
Also have a look at the ViewHolder pattern to avoid doing findViewById on each row when you have a convertView available.

Related

Custom listview click listener not working perfectly

I just needed help with a tricky situation in listview click listner. The thing is that I have populated a listview with custom adapters from remote servers. My listview contains 4 elements 2 images and 2 textview so the listview is loading perfectly from database but the issue is that I want listview to work only if the certain conditions match. Have a look at my code:
if (convertView == null) {
convertView = inflater.inflate(resource, null);
}
ImageView team1_photo,team2_photo;
TextView tournament;
team1_photo = (ImageView) convertView.findViewById(R.id.team1);
team2_photo = (ImageView) convertView.findViewById(R.id.team2);
tournament=(TextView) convertView.findViewById(R.id.tournament);
status=(TextView) convertView.findViewById(R.id.status);
ImageLoader.getInstance().displayImage(live_conveyerList.get(position).getTeam1_photo(), team1_photo);
ImageLoader.getInstance().displayImage(live_conveyerList.get(position).getTeam2_photo(), team2_photo);
tournament.setText(live_conveyerList.get(position).getTournament());
status.setText(live_conveyerList.get(position).getStatus());
liveListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 0:
if(status.getText().equals("play on"))
{
Toast.makeText(getActivity(),"Result will be declared shortly",Toast.LENGTH_LONG).show();
}
else {
Intent fix1 = new Intent(getActivity(), Leaderboard.class);
startActivity(fix1);
}
break;
case 1:
if(status.getText().equals("play on"))
{
Toast.makeText(getActivity(), "Result will be declared shortly", Toast.LENGTH_LONG).show();
}
else{
Intent fix2 = new Intent(getActivity(), Leaderboard_1.class);
startActivity(fix2);
}
break;
default:
}
getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out);
}
}
);
return convertView;
}
}
So here, status is one of the textviews and it has two type of outputs. One is "play on" and other is "play off" so I want to open new activity only when it has populated play on in that peculiar row. Els,e there should be a toast only,
but it is not working for me. Any help would be nice, thanks.
my json is like this
{"list":[{"tournament":"Xavier college League","team1_photo":"http:\/\/thehostels.in\/judgement_files\/images\/India.png","team2_photo":"http:\/\/thehostels.in\/judgement_files\/images\/Australia.png","status":"play on"},{"tournament":"ITM college League","team1_photo":"http:\/\/thehostels.in\/judgement_files\/images\/India.png","team2_photo":"http:\/\/thehostels.in\/judgement_files\/images\/Australia.png","status":"play off"}]}
In your case 0 you have "Play on" with a capital P, but equals takes the case into account by default, could it be your issue?
Edit : Ok I think I got it
You define the onItemClickListener not once but for each and every item, so that at the end of list population, your listener is based on the status of the last item of your list
You should define onItemClickListener only once, and you will have clicked item with view and position arguments
For instance you may want to store the list of all your items as a field, and in your onItemClickListener you will get clicked item from the position, and thus get its status
With the view parameter, you should be able to access the fields' values through findViewById
Edit 2:
What I meant is the following : there is only one onItemClickListener that needs to be defined and onItemClick method will provide the position and the view of clicked item.
That means that each time user will click any item of the list, the listener will be called, but there is not one different listener for each different row.
With that in mind, in the onItemClick, with the view and/or the position, you can access the status textview, get its value and then do all the stuff you need to do with your toast and intent. I hope it's clearer than before
Edit 3:
That should do your thing : in your listener, before your switch,
TextView currentStatus=(TextView) convertView.findViewById(R.id.status);
And then you replace all the references to status inside your listener by currentStatus. And you will need to define the listener only once outside of your getView method, even outside your adapter, rather where your listView is defined and your adapter is added to it

Showing/hiding items - listview

I have a special question which i havent't found the answer. I can't deal with this problem.
So... is there any way to hide or show items(images) dynamically on ListView?
I mean, after tap on toolbar menu item called "Edit", on ListView next to the texts should appear images. When user click on this image, this text should be removed from ListView and database. And, after tap on "Done", images that have previously appeared should gone.
I spent all night for this and I haven't found the answer, so guys please help me!
I have this:
And want this after click on Edit:
That's part of my Adapter (extends from BaseAdapter)
public View getView(final int position, View convertView, final ViewGroup parent) {
if(convertView == null){
convertView = inflater.inflate(R.layout.list, null);
TextView textView = (TextView) convertView.findViewById(R.id.textView);
textView.setText(todo.get(position));
ImageView circleImage = (ImageView) convertView.findViewById(R.id.circleImage);
Picasso.with(mActivity).load(R.drawable.circle_image).transform(new CircleTransform()).into(circleImage);
}
if(editMode){
circleImage.setVisibility(View.GONE);
} else circleImage.setVisibility(View.VISIBLE);
return convertView;
}
public void setMode(boolean editMode){
this.editMode=editMode;
notifyDataSetChanged();
}
onOptionItemSelected from MainActivity:
else if(id == R.id.edit){
mToDoFragment = new ToDOFragment();
mFToDoFragment.setEditable(true);
invalidateOptionsMenu();
return true;
}
And, last part from ToDoFragment:
public void setEditable(boolean editable){
mFavouritesListAdapter.setMode(editable);
}
After that, I'm getting such beutiful NullPointerException :(
java.lang.NullPointerException: Attempt to invoke virtual method 'void adapters.ToDoListAdapter.setMode(boolean)' on a null object reference
Hi please follow the blow steps. I hope it will help you.
First you have flag that indicate that current mode for example the list view in edit mode or in normal mode (view mode)
After define the mode. Take the array list with your model or object. bind you array list with BaseAdapter. I guess you are using the ListView. If you are using the RecyclerView then the process or logic will remain the same.
In inside the getView(....). Define you layout using LayoutInflater
After checking the convert-view is not null and inflating the layout.
Put the logic for check the flag of mode.
Check whether the mode is edit mode or normal view mode. If edit then show the [x] icon for each row item otherwise hide/gone the image view.
If the mode is edit when click on [x] image view remove position of the model or object from the dataset that is bind with the BaseAdapter. And call notifyDatasetChanged(). This will refresh the list view and remove the item from listview.
If edit mode is complete and user click on "Done" Option from menu item. Then simply change the flag of mode from edit to done mode. call again notifyDatasetChanged() method of your BaseAdapter
That's it. You done let me know if you have any query.

listview getting new instance when swiping the listview

I have one listview in my application,it contains two rows one for task and another one for alarm,date,severity. Initially first row of the list item only displayed for all list item and the second one is invisible. When i click the list item the second row displayed for that item as well as click another list item at that time the above list item closed that second row. Its working fine for me...My problem is if i open one list item and then swipe the listview at then i click the another list item at that time the above one cannot be closed because the above list item instance will be chnaged.please any one help me how to solve this problem...
int lastselectedPosition == -1
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position,
long id) {
TextView textviewDate=(TextView)view.findViewById(R.id.taskTimeidDaytoDay);
selectedtaskDate=textviewDate.getText().toString().trim();
if (lastselectedPosition == -1) {
Log.i(TAG,"Loopif:"+lastselectedPosition);
TextView twTaskTime = (TextView) view
.findViewById(R.id.taskTimeidDaytoDay);
TextView twSeverity = (TextView) view
.findViewById(R.id.severityidDaytoDay);
TextView twAlarm = (TextView) view
.findViewById(R.id.alarmidDaytoDay);
twAlarm.setVisibility(view.VISIBLE);
twSeverity.setVisibility(view.VISIBLE);
twTaskTime.setVisibility(view.VISIBLE);
lastselectedPosition = position;
lastSelectedItem = arg0.getChildAt(position);
} else {
// Log.i(TAG,"LoopElse:"+lastselectedPosition);
lastSelectedItem.findViewById(R.id.taskTimeidDaytoDay)
.setVisibility(lastSelectedItem.GONE);
lastSelectedItem.findViewById(R.id.severityidDaytoDay)
.setVisibility(lastSelectedItem.GONE);
lastSelectedItem.findViewById(R.id.alarmidDaytoDay).setVisibility(
lastSelectedItem.GONE);
if (lastselectedPosition != position) {
view.findViewById(R.id.taskTimeidDaytoDay).setVisibility(
view.VISIBLE);
view.findViewById(R.id.severityidDaytoDay).setVisibility(
view.VISIBLE);
view.findViewById(R.id.alarmidDaytoDay).setVisibility(
view.VISIBLE);
lastselectedPosition = position;
lastSelectedItem = arg0.getChildAt(position);
} else {
lastselectedPosition = -1;
lastSelectedItem = null;
}
}
GetView():
#Override
public View getView(int position, View view, ViewGroup parent) {
Log.i("XXXX", "Inside getView");
final DaytoDayTaskGetterSetter objDaytoDaygetset=getItem(position);
TextView textviewTask;
TextView txtviewAlarm ,txtviewTaskTime ,txtviewSeverity;
Log.i(TAG,"InsideGetView:"+position);
LayoutInflater inflater=(LayoutInflater)context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if(view==null)
{
view=inflater.inflate(R.layout.daytodaylistlayout,null);
}
Log.i("XXXX", "before first test");
textviewTask=(TextView)view.findViewById(R.id.tasknameidDaytoDay);
txtviewAlarm=(TextView)view.findViewById(R.id.alarmidDaytoDay);
txtviewSeverity=(TextView)view.findViewById(R.id.severityidDaytoDay);
txtviewTaskTime=(TextView)view.findViewById(R.id.taskTimeidDaytoDay);
return view;
}
In first i click the "gdfgdtet" list item it show another row and then i click the second list item "dfgsdgsd" at that time the above list item "gdfgdtet" closed the second row.This is a normal output.Suppose if i open the "gdfgdtet" list item and then swipe the listview at that time both of "gdfgdtet" "dfgsdgsd" will be opened and crashed...because the above one list item reference changed when i am swiping please how to solve this problem...
I'll try to provide you a good answer that explains why you are having this problems, but the general idea is that you have to see this video - http://www.youtube.com/watch?v=wDBM6wVEO70
please take my words kindly - you don't seems to understand what ListView + BaseAdapter recycling mechanism is all about, and I strongly recommend you see the full video I linked you to, and read more about that.
in general, the specific problem in your code is that you are holding reference to listview item (lastSelectedItem), then trying to use it latter assuming it's still representing same list item. that's wrong. in that stage (after scrolling) the view already been recycled to represent another item in the list (based on the adapter implementation).
listView's number of childs is not the size of adapter.getCount()!!!!!!!!
listViews's number of childs = number of visible list items on screen + 1 + headers + footers
let's say you have the 5 first items visible on screen, then you are scrolling down. when you see the 7 item you actually see the same view instance that used to show the first list item and been recycled.
getView will call in this stage with convertView != null and position in the adapter to let you reuse the item by putting new values such different text/image to the same instance
this mechanism provides ability to display list of "infinite" number of items in the list, and holding in memory only a few number of views. imagine that you have list of 5000 items in the list, and each one of them have different view instance - you would get outOfMemory exception in a sec!
complete explanation about that would be hard to write in stackoverflow answer's context.
it just too long trying to explain one of the most important and complex UI components in android, but this links would be a good start:
http://www.youtube.com/watch?v=wDBM6wVEO70
How ListView's recycling mechanism works
http://mobile.cs.fsu.edu/the-nuance-of-android-listview-recycling-for-n00bs/
if you are interstead in "quick" fix for your specific problem, the solution would be:
hold in the data structure represents your list item additional field indicating if it in "close" or "open state. when item been clicked - change the data accordinly and call notifyDatasetChanged(). inside the getView() check if item is open or close and populate it accordinly
by the way - it's not only "quick fix" solution, but also the right thing to do anyway
You should pay attention to Tal Kanel's answer and consider this one to be an extension to it. His advice will help you in the long run.
Add a boolean field to DaytoDayTaskGetterSetter class:
public class DaytoDayTaskGetterSetter {
....
....
boolean open;
public DaytoDayTaskGetterSetter (.., .., boolean o) {
....
....
open = o;
}
....
....
public boolean shouldOpen() {
return open;
}
public void setOpen(boolean o) {
open = o;
}
}
In your getView(), check if the object has its open value set:
DaytoDayTaskGetterSetter obj = (DaytoDayTaskGetterSetter) getItem(position);
if (obj.shouldOpen()) {
// Set visibility to true for the items
} else {
// Set visibility to false for the items
}
On list item click, traverse the list and set open for all list items to false. Use the position to retrieve DaytoDayTaskGetterSetter and set its open to true:
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
for (DaytoDayTaskGetterSetter obj : listContainingObjects) {
obj.setOpen(false);
}
DaytoDayTaskGetterSetter clickedItem = (DaytoDayTaskGetterSetter)
yourAdapter.getItem(position);
clickedItem.setOpen(true);
yourAdapter.notifyDataSetChanged();
}
Edit 1:
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
DaytoDayTaskGetterSetter clickedItem = (DaytoDayTaskGetterSetter)
yourAdapter.getItem(position);
if (clickedItem.shouldOpen()) {
clickedItem.setOpen(false);
} else {
for (DaytoDayTaskGetterSetter obj : listContainingObjects) {
obj.setOpen(false);
}
clickedItem.setOpen(true);
}
yourAdapter.notifyDataSetChanged();
}

Automatic click on ListView if has only one item

I am trying to search for some data in Mysql database, then display it Listview
When more than 1 result is found, clicking on any one of them will take you to its respective details page.
But.. I am looking to go to details page directly if there is only one result in list view. Put simply, how would I call onItemClick on the list view automatically when there is only one item in the list.
My Listview's onClickItemListener looks like this:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// when a student is selected then the following methods are called
DetailsFragment.setLUNCH_TYPE(searchResult.get(position).lunchtype);
Log.d("Lunch type selected", ""+DetailsFragment.getLUNCH_TYPE());
((HomeActivity) getActivity()).startStudentSale(searchResult
.get(position).name.toString(),searchResult
.get(position).isStudent,searchResult.get(position).id);
// it was comming null here when running this this need to find out why
// ((HomeActivity)getActivity()).setmSaleType(SALE_TYPE.SEARCHED_STUDENT);
}
When you know the count of the resultSet, say cnt and if it is 1, i.e.
if(cnt==1){
Intent intent=new Intent(getApplicationContext(),DetailActivity.class);
startActivity(intent);
}

How can I animate one single item (entry) after the List is already updated and drawn?

I have this main ListView (retrieved using this.getListView()) in a ListActivity properly populated from a SQLite database. A click on one of its items (entries) calls another activity A2 using startActivityForResult(). I would like to animate that SINGLE entry WHEN the user gets back to the ListActivity. (So I suppose I need to override the onActivityResult() method)
How can I animate a single entry once the window gained focus and after the list is updated using notifyDataSetChanged()?
I managed to animate the whole ListView using
getListView().setAnimation(anim)
and worked fine. However, when I do:
getListView().getChildAt(position).setAnimation(anim)
nothing happens.
EDIT: Here's my onActivityResult code
protected void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if(resultCode==RESULT_OK && requestCode==SOME_REQUEST_CODE){
Animation anim = new AlphaAnimation(1.0f,0.0f);
anim.setFillAfter(true);
anim.setDuration(400);
anim.setRepeatMode(Animation.REVERSE);
anim.setRepeatCount(7);
// Just for testing, I chose the first item of the ListView (position=0)
tr_adapter.getView(0, getListView().getChildAt(0), getListView()).setAnimation(anim);
tr_adapter.notifyDataSetChanged(); //Nothing happens
}
}
Set a flag in your table and call notifyDataSetChanged() on your list adapter. Have getView() in your list adapter determine what to do (animate or not) based on the flag in your table. I'd post some sample code but I'm mobile.
EDIT
In your onActivityResult(), flag (with some value, boolean, 1/0, etc) the data point in your data set that you'd like to animate. Then call notifyDataSetChanged() on the adapter. Assuming you have a custom list adapter, have your getView() function flip the animation on or off (or default to off and flip it on given the flag).
public View getView (int position, View convertView, ViewGroup parent){
// All the standard getView() stuff
if (listitem.shouldAnimate())
// animate
}

Categories

Resources