I have a application that has an activity that shows message logs. The thing is the user must to be able to select some messages and then delete them as you can do when you want to remove some sms messages.
Which is the best way of doing that? Adding more details:
Here is a capture with the messages only.
Here is a capture with the selection layout.
I want to know how they do it to show the CheckBoxes. Did they use two layouts?
I wanna know how they do it to show the checkbox, does they use 2
layouts?
I doubt they use two layouts files. As the rows are very similar it very easy to switch from the layout that doesn't have the CheckBox to the layout that does have the CheckBox just by changing the visiblity. A way to do it would be to have a flag in your adapter that indicates the presence of the CheckBox in the rows and update the CheckBox visibility based on that flag:
boolean checkStatus = false;
//...
public void getView(int position, View convertView, ViewGroup parent) {
//...
if (checkStatus) {
checkBox.setVisibility(View.VISIBLE); //show the CheckBox for each row
} else {
checkBox.setVisibility(View.GONE); // hide the CheckBox for each row
}
//...
}
Then when it's time to show those CheckBoxes(on a Button click, menu click etc) you just have to set the checkStatus flag to the desired value(true for CheckBoxes present, false otherwise) and call notifyDataSetChanged() on your adapter.
Of course you could use two layouts, one that contains the CheckBox and one that doesn't have it. But, generally, you would use two layouts in a ListView when the two layouts are very(or substantial) different(which is not your case). To implement the two layouts you would have to use the methods getItemViewType and getViewTypeCount.
boolean checkStatus = false;
public int getViewTypeCount() {
return 2; // you have two layouts
}
public int getItemViewType(int position) {
if (checkStatus) {
return 1;
} else {
return 0;
}
}
Then in your getView method:
public void getView(int position, View convertView, ViewGroup parent) {
int which = getItemViewType(position)
// make a switch statement and inflate the correct layout file
// based on the which variable if the convertView is null
// do stuff;
}
Of course when it's time to show the CheckBoxes you would have to set the checkStatus flag again and call notifyDataSetChanged() on your adapter.
if you use a listView and an adapter , my guess is that the adapter holds a (pure java) list of the messages . so , when the user removes an item , remove it from the list , based on the item's location in the list .
right after that , call notifyDataSetChanged on the adapter . that's it .
Related
I'm trying to implement a custom listview. Everything works fine until I use a if () statement inside the getView() method
Without the if() condition a single item gets selected when I select an item but when I add the if() condition, the views are displayed properly but two items (non-adjacent) get selected (1st and last 1st or and second-last, any such combination).
View getView(...){
....
if (!item.getPriceTo().equals(""))
priceToTV.setText(item.getPriceTo());
else
priceToTV.setText(item.getPriceFrom());
return view;
}
Also I'm using saving the previous view to show the selection so the current selection has a red_border and when it is selected a black_border is set to it.:
subItemsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Log.d("New Order", "........");
if (previousViewOfSubItems != null && previousViewOfSubItems != view) {
previousViewOfSubItems.setBackgroundResource(R.drawable.black_border);
if (quantity.getText().toString().equals("xx") || quantity.getText().toString().equals("0")) {
viewForVisibility.setVisibility(View.GONE);
layoutForQuantity.setVisibility(View.GONE);
}
}
if (previousViewOfSubItems == view)
return;
previousViewOfSubItems = view;
previousViewOfSubItems.setBackgroundResource(R.drawable.red_border);
viewForVisibility = previousViewOfSubItems.findViewById(R.id.viewForVisibility);
viewForVisibility.setVisibility(View.VISIBLE);
layoutForQuantity = (LinearLayout) previousViewOfSubItems.findViewById(R.id.layoutForQuantity);
layoutForQuantity.setVisibility(View.VISIBLE);
quantity = (TextView) previousViewOfSubItems.findViewById(R.id.subTypeQuantity);
}
});
previousViewOfSubItems = view; seems to be causing the problem,
In Listviews with adapter you should avoid saving view instances, because views are reused by adapters so view can be same for two rows so rather than saving view instance's reference use ViewHolder Design pattern and use view tagging
You need to use ViewHolder Pattern and view tagging to properly identify every view in different position. ListView always recycle the view instead of re-inflating the view again and again.
You can refer to Android Training documentation on how to implement ViewHolder pattern.
ListViews recycle the views in the list. so as you scroll, top views are reused and content replaced using the methods.
Where you set background to Red etc, use Else statements to set it back to your default black.
its beacause it listview reuses view to display items, once the first view is scrolled out the the same view is reused to display the view at bottom of the listview. instead of comparing the view try compairing the position of the view clicked
I have explained about this abnormal behavior in my blog on recyclerview you refer that to solve this problem.
use pojo class to get the status and update the view accordingly
I'm trying to add some extra space between the 4th and 5th items in the listview. What are my options?
I tried doing that in adapter's getView(), as well as manually getting access to the fourth element and adding padding to it.
Is there a better way to do this?
Another way to do this would be to use a different layout for the the 4th item (that has additional padding). It's similar to your solution but maybe a bit "cleaner". I'm assuming that you're extending ArrayAdapter.
In your adapter override the getViewTypeCount() method:
#Override public int getViewTypeCount() {
return 2;
}
This way you're telling your adapter that you will use two different layouts for your items. Next, you have to specify which items will be of which type by overriding another method:
#Override public int getItemViewType(int position) {
if(position == 3) {
return 0;
} else {
return 1;
}
}
This will tell your adapter to use a different view (only) for the 4th element in the list, and it will not be reused for other elements. Now for the last part, override onCreateView():
#Override public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
LayoutInflater inflater = LayoutInflater.from(context);
if(position == 3) {
convertView = inflater.inflate(R.layout.your_layout_with_padding, parent, false);
} else {
convertView = inflater.inflate(R.layout.your_regular_item_padding, parent, false);
}
//TODO this is the place to initialize your view holder
} else {
//TODO this is the place to restore your view holder
}
//TODO setup your view here
return convertView;
}
For the item with position == 3 (4th item in the list) convertView argument of the getView() method will be null, because that is the first (and only) item of the type 1 in the list. Therefore you can inflate a different layout that includes a padding for that item.
I thought to some ways but if i have to be honest the only way to do this well is to change the layout in the adapter when the position is equal to 4. I meant that you can do an xml file with a RelativeLayout of the height that you want as space between the 4th and 5th element and set the visibility to gone and put him above all your adapter's elements. When the position is equal to 4 in your getView you set the visibility of that item to visible with nameOfYourRelativeLayout.setVisibility(View.VISIBLE)
So you can add this blank space only between 4th and 5th element. Mine is just a suggestion but i think it can work well.
Layout all of your items in the listview to include your data as well as a header view, maybe a textview or even a Viewgroup like another layout. Keep the header invisible until some logic in your code triggers (i.e. pos ==4) and make the header visible
In my android app my Main Activity extends List Activity. I store there elements called Items, their layout is defined by itemLayout xml file and I use custom adapter (called ItemAdapter) to transfer data to List View in Main Activity. In itemLayout there's an ImageView and my aim is to change its image when user clicks on the particular item in list view. In my opinion the easiest way to achieve that is to get access to particular item's (the one that was clicked) layout, there find the ImageView and call method setImageBitmap. How can I "find" this layout of clicked item? I tried many things in method:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
}
but nothing worked. Do I have to add anything to my ItemAdapter? Or is it possible to do in onListItemClick(…) but I can't find out how?
You're thinking about it in slightly the wrong way. Adapter's are a way to map data to views. So if you want to change how a particular view looks for a given position, you need to change it's correlating data so that the rendered View then changes. Attempting to modify a view directly kinda goes against how adapters are meant to be used.
So in your case, when a user clicks on an item. Find that item in your adapter via the position number. Then update the item and ensure notifydataset is called. Meanwhile, your adapter's getView() will then handle displaying the appropriate image.
As I understood you need to modify clicked item layout, right? Use argument from onListItemClicked:
v.findViewById(<your_img_view_id>)
For better performance use view holder.
onListItemClick is fired when you press on an element of the ListView. If you want to retrieve the element in the dataset, you can simply invoke
l.getItemAtPosition(position)
the returned value has to be casted to the specific object
Yes It is possible in your custom adapter class in the getView() mtheod u can change imageview bitmap by clicking on it
see this code
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View rowView = convertView;
rowView = inflater.inflate(R.layout.playlist_item, null);
final ImageView im = (ImageView) rowView.findViewById(R.id.favorite);
im.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Do your stuff here
}
});
return rowView;
}
I'm new to Android programing. I need to create a list of 1000 goods for users to click and check or the one they want to buy. I've created the array list and added it to my custom adapter and I have also added it to my list view. my problem is how to get the position for each item selected and I need clarification on the getView and the ViewHolder. I'm not working with toast
See the below tutorials,
http://sunil-android.blogspot.in/2013/04/android-listview-checkbox-example.html
http://developerandro.blogspot.in/2013/09/listview-with-checkbox-android-example.html
http://aboutyusata.blogspot.in/2013/11/how-to-make-listview-with-checkbox-in.html
Hope it helps.
You have to use ListView getCheckedItemPositions()
/**
* Returns the set of checked items in the list. The result is only valid if
* the choice mode has not been set to {#link #CHOICE_MODE_NONE}.
*
* #return A SparseBooleanArray which will return true for each call to
* get(int position) where position is a checked position in the
* list and false otherwise, or <code>null</code> if the choice
* mode is set to {#link #CHOICE_MODE_NONE}.
*/
public SparseBooleanArray getCheckedItemPositions() {
if (mChoiceMode != CHOICE_MODE_NONE) {
return mCheckStates;
}
return null;
}
getView() method is called when new list item that is adapted is being shown on your screen. That is why you need to take good care of the memory and setTag() for every item you inflate. Then when old item is viewed again you will not render it like a new one but get it by the tag you submitted for that item.
Example: If you have 1000 items only couple of those will be shown on the screen and your program will call getView() for those items that are visible plus for couple of items bellow those that are visible so you don't see the lag in inflation while scrolling.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if(view==null)
{
LayoutInflater inflater=getLayoutInflater();
view=inflater.inflate(R.layout.list, parent, false);
TextView textView=(TextView)view.findViewById(R.id.text_hint);
ImageView imageView=(ImageView)view.findViewById(R.id.img);
// here we will setTag() our view later
}
//... (here we will manage the views and set some click listeners)
Now, what is a viewHolder?
The ViewHolder basically is the private class inside your adapter that is used to keep you inflated layout elements together and to let you manipulate those view after you got the using findViewById(int resId).
Some code:
private class ViewHolder{
public ImageView imageView;
public TextView textView;
public ViewHolder(ImageView imageView,TextView textView) {
this.imageView = imageView;
this.textView = textView;
}
}
Now to setTag() as promised.
view.setTag(new Holder(imageView,textView));
Now with this in mind you can get your ViewHolder using the code above and views that you got in the first part of my code. (this is what we will write instead of 3 dots)
ViewHolder h = (Holder) view.getTag();
h.textView.setText(ar[position]);
int resID = getResources().getIdentifier(pic[position], "drawable", getPackageName());
h.imageView.setImageResource(resID);
// here I did set some source to my imageView that is in my layout, but
// you can do whatever you want with these views. And that is what I'm
// going to explain later in this text.
return view;
}
Okay, what next?
After getting the ViewHolder from tag you have your entire layout as a View that can get his OnClickListener easily. Or just a checkBox can get OnCheckedChangeListener.
Inside methods for this listeners you can send the data to you controller (in case you are using MVC model) or to your activity that hosts this View, where you can save the state of the checkbox and title of the item that has been clicked.
For example you can do something like this on your corresponding listener method:
(MainActivity)context.markAsChecked(String title);
But in this case you will also need to have the opposite method for unchecking
(MainActivity)context.markAsUnchecked(String title);
and you will have to handle this in your MainActivity properly by browsing through the array of data that has been selected.
The second solution is to have :
(MainActivity)context.toggleState(String title);
And to handle both events checked and unchecked.
Your method in your Activity would need to do something similar to this:
public void toggleState(String title){
if (data.contains(title))
data.remove(title);
else data.add(title);
}
Then after your user checks what he wants you will have all the checked elements in your data array that in this case is ArrayList. You can also you HashMap for this if you like or something else too.
Hope this helps.
I will be more than happy to answer all of your questions if have some more.
Maybe controller implementation is something that you would like to consider in this case. That would mean that you would be using MVC model for better control of your app and data, and to delegate the tasks to responsible classes and methods. Not to put everything in one Activity :)
Bye
I am doing a project where i am a using a custom list view, which contains a textview. Data is coming from the server. I want to change the height of the cell, based on the data size. If the content is more than two lines, i have to trim it to two lines and show a see more button. On clicking the see more button expand the cell to show full details.I googled about it. But i cannot find any useful resource.Could any one help me to solve this, any useful links or suggestions?`
One way to do this is to first invalidate the listView forcing it to redraw the visible elements and then handle the layout change in the getView method of your adapter.
Try this:
Add a showTrimmed(int position, boolean value) method to your adapter. This will update a structure inside your adapter that keeps track of the list items that you want to trim or not trim. Then in the getView method, when creating the item view, you test if the current element should be trimmed or not and based on the result you create the proper view.
I did something similar to achieve a different but similar result and it works.
Remember to call invalidate after calling showTrimmed to force the listView to redraw the displayed items.
Edit: I post my code which is different from what you want to do but it's quite similar in the idea:
public class HeroListViewAdapter extends ArrayAdapter<Hero> {
public static int NO_POS = -1;
private List<Hero> heroes;
private int selected_pos = NO_POS;
public HeroListViewAdapter(Context context, List<Hero> heroes){
super(context, R.layout.hero_list_item_view, heroes);
this.heroes = heroes;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = GuiBuilder.createHeroListItemView(heroes.get(position),getContext(),parent,false);
if(position == selected_pos){
rowView.setBackgroundColor((rowView.getResources().getColor(R.color.list_item_selected_color)));
}
return rowView;
}
public void setSelectedPosition(int selected_pos){
this.selected_pos = selected_pos;
}
public int getSelectedPosition(){
return selected_pos;
}
public Hero getSelectedHero(){
if(selected_pos>=0 && selected_pos<heroes.size())
return this.getItem(selected_pos);
else
return null;
}
}
instead of setSelectedPosition you should have the showTrimmed method which updates an inner member as setSelectedPos does, in order to keep track of the trimmed state of every list item. Then in the getView method you do the test (like I do in if(position == selected_pos)) and then you build your custom trimmed or not trimmed list item based on the result of the test. My listener which uses these functions is:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
public void onItemClick(AdapterView<?> parent, View view, int position, long id){
list_adapter.setSelectedPosition(position);
listView.invalidate();
}
}
);
you can try to update this idea to your needs. In particular in my case I do this to highlight the clicked listview item by programmatically changing its background.