I'm using below code to travers my list it is working fine if List Items are visible.
If List is scrollable then non visible items are not accessing using this code. How to traverse all the list items which are visble + non visible items.
for(int i=0;i<list.getCount;i++)
{
View v = list.getChildAt(i);
TextView tv= (TextView) v.findViewById(R.id.item);
Log.d("","ListItem :"+tv.getText());
}
Here is how you can get your ListView in the Activity and traverse it.
ListView myList = getListView();
int count = myList.getCount();
for (int i = 0; i < count; i++)
{
ViewGroup row = (ViewGroup) myList.getChildAt(i);
TextView tvTest = (TextView) row.findviewbyid(R.id.tvTestID);
// Get your controls from this ViewGroup and perform your task on them =)
}
I hope this will help
Recently i did this thing.
Suppose i want to invisible a button on a listItem. Then in the getView of the list adapter add that button in a global vector. like below.
Button del_btn = viewCache.getFrame();
view_vec.add(del_btn);
Here viewCache is a object of ViewCache class, which is sumthing like below -
class ViewCache
{
private View baseView;
private Button button;
public ViewCache(View baseView)
{
this.baseView = baseView;
}
public Button getButton() {
if(button == null) {
button = (Button) baseView.findViewById(R.id.DeleteChatFrndBtn);
}
return button;
}
}
//it is necessary sometimes because otherwise in some cases the list scroll is slow.
Now you like to visible the listItem's button onClicking some other button. Then the code is like below -
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()) {
case R.id.EditChatFrndBtn:
length = view_vec.size();
for(int i = 0; i < length; i++) {
Button btn = (Button) view_vec.elementAt(i);
btn.setVisibility(View.VISIBLE);
}
doneBtn.setVisibility(View.VISIBLE);
editBtn.setVisibility(View.INVISIBLE);
break;
}
}
Instead of R.id.EditChatFrndBtn put your button id on clicking of which you will invisible/visible the listItem's button.
Related
The code below saves the current text of the TextView when the favourite button is clicked and the button Image changes to Fav_Checked when I click again the favourite button is unchecked and the text in array is removed but the issue is when I scroll down the ListView updates and the favourite buttons are unchecked. kindly help.
public class MainActivity extends AppCompatActivity {
// ArrayList<String> names = new ArrayList<>();
private ArrayList<String> names;
int x = names.size();
// private boolean[x] favorites;
private SharedPreferences sharedPreferences;
Context context = this;
MediaPlayer mPlayer;
Boolean isPlay = true;
ImageView playPauseGlobelBtn;
int maxVolume,resID,position;
String fname;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView=(ListView)findViewById(R.id.listView);
mPlayer = MediaPlayer.create(context, R.raw.asdf);
CustomAdapter customAdapter=new CustomAdapter();
listView.setAdapter(customAdapter);
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
String favoriteItems = sharedPreferences.getString("FAVORITE_ITEMS", "");
if(favoriteItems.isEmpty()) {
names = new ArrayList<>();
}
else {
names = new ArrayList<>(Arrays.asList(favoriteItems.split(",")));
}
}
#Override
public View getView(final int i, View view, ViewGroup viewGroup) {
view = getLayoutInflater().inflate(R.layout.customlayout, null);
final TextView textView_name = (TextView)
view.findViewById(R.id.textView_name);
final Button favoritebutton = (Button) view.findViewById(R.id.tgbFav);
buttonInfo.setOnClickListener(new View.OnClickListener() {
String tag = v.getTag().toString();
if(tag != null) {
int i = Integer.parseInt(tag);
System.out.println(tag);
}
}
});
private ArrayList<String> names;
private SharedPreferences sharedPreferences;
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
String favoriteItems = sharedPreferences.getString("FAVORITE_ITEMS", "");
if(favoriteItems.isEmpty())
names = new ArrayList<>();
else
names = new ArrayList<>(Arrays.asList(favoriteItems.split(",")); //Update like this
favoritebutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick( View v) {
if (!names.contains(textView_name.getText())){
names.add((String) textView_name.getText());
for (int i=0; i<names.size(); i++) {
System.out.println(names.get(i));
favoritebutton.setBackgroundResource(R.drawable.fav_checked);
}
}
else {
System.out.println(textView_name.getText() + " is already present in the Array at index " + names.indexOf(textView_name.getText()));
int currentIndex = names.indexOf(textView_name.getText());
names.remove(currentIndex);
for (int i=0; i<names.size(); i++) {
System.out.println(names.get(i));
favoritebutton.setBackgroundResource(R.drawable.star_off);
}
}
sharedPreferences.edit().putString("FAVORITE_ITEMS", TextUtils.join(",", names)).apply();
}
});
<ToggleButton
android:id="#+id/tgbFav"
android:layout_width="30dp"
android:layout_height="30dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:textOff=""
android:layout_toLeftOf="#+id/button"
android:textOn=""/>
Mate, what i have get from your question and snippet you post is you're saving all the favourite item name in shared-preference. But as you know you get that 'FAVORITE_ITEMS' in onCreate so it will only provide you the previously added favourite item. And in your you know if your scroll list view the item get update because getView() called for every item as such your favourite item that you clicked only seems as favourite until you didn't scroll list. For showing that item you need to check your item is present in favourite items or not for that add this snippet in your getView()
#Override
public View getView(final int i, View view, ViewGroup viewGroup) {
Your code....
String favoriteItems = sharedPreferences.getString("FAVORITE_ITEMS", "");
if (favoriteItems.contains(textView_name.getText())) {
favoritebutton.setBackgroundResource(R.drawable.fav_checked);
} else {
favoritebutton.setBackgroundResource(R.drawable.star_off);
}
....
}
Remember to notify list when any favouriteButton clicked.
When you scroll the ListView, the few visible view items will be reused for the entire list. If you aren't handling the getView correctly, the item that was previously used for a name without favorite will be reused for a name that is set as favorite. Hence, it will look like the favorite button gets unchecked while scrolling the ListView. You have to keep track of the favorite status of all the names and use it in getView to show the proper UI.
Now, when the favoritebutton is clicked, you are updating it's background, and updating the ArrayList "names". You need to use this ArrayList in your getView, check the current favorite status of the name, and use that to set the proper background resource for the favoritebutton in your getView.
Can someone please explain why the first piece of code here works but not the last piece? The only difference is the index of which i insert my view, i-1 and i+1. Is i+1 just not possible with index? I can write any other number in there and it works.
upButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for (int i = 0; i < drawerViewGroup3.getChildCount(); i++) {
View view = drawerViewGroup3.getChildAt(i);
TextView tv = (TextView) view.findViewById(R.id.drawer_name);
if (tv.getText().toString().equals(drawerName.getText().toString()) && i != 0) {
drawerViewGroup3.removeView(view);
drawerViewGroup3.addView(view, i - 1);
}
}
}
});
downButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for (int i = 0; i < drawerViewGroup3.getChildCount(); i++) {
View view = drawerViewGroup3.getChildAt(i);
TextView tv = (TextView) view.findViewById(R.id.drawer_name);
if (tv.getText().toString().equals(drawerName.getText().toString())) {
drawerViewGroup3.removeView(view);
drawerViewGroup3.addView(view, i + 1);
}
}
}
});
Some context on the app. I have a vertical oriented LinearLayout with multiple LinearLayouts inside. When clicking the two Buttons in the code, one of the children is to move up or down, switching their positions.
EDIT: Ok so I figured it out.
downButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
View viewToMove = null;
int viewToMovePos = drawerViewGroup3.getChildCount();
for (int i = 0; i < drawerViewGroup3.getChildCount(); i++) {
View view = drawerViewGroup3.getChildAt(i);
TextView tv = (TextView) view.findViewById(R.id.drawer_name);
if (tv.getText().toString().equals(drawerName.getText().toString()) && i != drawerViewGroup3.getChildCount() - 1) {
viewToMovePos = i;
viewToMove = view;
}
}
alert.dismiss();
if (viewToMovePos != drawerViewGroup3.getChildCount()) {
drawerViewGroup3.removeView(viewToMove);
drawerViewGroup3.addView(viewToMove, viewToMovePos + 1);
}
}
});
Not the prettiest code and probably not gonna help others as it's very specific, but that was the answer.
When you remove a view from your LinearLayout, the view at index i is now the view which was after the one which you removed. Then you add a view at i - 1 which is before this view (the one which was previously after the removed view). The final result is that you remove a view and insert back where it used to be.
Instead, you need to add the view at i - 2.
I suggest that you look at RecyclerView. It is specifically designed to efficiently create a dynamic list of views for given data. You only have to manipulate the data and RecyclerView does all the hard work for you.
I am adding a Customized View using an array.The array elements are intialized by inflating a layout and adding those elements to a ViewGroup as shown in the image.
When I am setting onClickListener in a way to make the clicked view's background as Accent Color It happens but in order to make it mutually exclusive so that once a view is clicked other View's background should become transparent as they were intially I have used the following code But my when I click on the View, my applications stops responding.If my approach is not correct Please suggest me the right way to get desired result.
This should happen:
this should not happen:
if(noOfChild>1) {
for (j = 0; j < noOfChild; j++) {
LayoutInflater inflater = (LayoutInflater) getSystemService(getApplicationContext().LAYOUT_INFLATER_SERVICE);
childButton[j] = (inflater.inflate(R.layout.child_selection_button, null));
childButton[j].setId(j);
children.addView(childButton[j], new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f));
childButton[j].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
v.setBackgroundColor(ContextCompat.getColor(MainScreen.this, R.color.dimAccent));
// for (int k =0;k<noOfChild;k++){
// while(k!=v.getId()){
// childButton[k].setBackgroundColor(ContextCompat.getColor(MainScreen.this, R.color.transparent));
// }
// }
}
});
}
}
You can keep a local variable which shows the position of the last selected item. Then in your onClick() method do the switch in the position and the backgroundColor:
private View lastSelected;
//... rest of code ...
//Inside for loop
public void onClick(View v){
if (lastSelected == null){
lastSelected = v;
selectItem(lastSelected);
}
else
{
deselectItem(lastSelected);
lastSelected = v;
selectItem(lastSelected);
}
}
private void selectItem(View v){
v.setBackgroundColor(ContextCompat.getColor(MainScreen.this,R.color.dimAcent));
}
private void deselectItem(View v){
v.setBackgroundColor(ContextCompat.getColor(MainScreen.this, R.color.transparent));
}
In my android applicattion i have a list view with custom adapter.The adapter has a textview and two buttons(plus & minus button) like in the image below :
when i click on the plus or minus button the textview value changes...but as soon as i scroll the listview the values are changed of all the textviews.
The code i used is as follows :
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
if(convertView==null)
{
viewHolder=new ViewHolder();//
convertView = inflater.inflate(R.layout.inventory_list_item, null);
viewHolder.txt_product_name = (TextView)convertView.findViewById(R.id.txt_product_name);
viewHolder.txt_product_units = (TextView)convertView.findViewById(R.id.txt_product_units);
viewHolder.txt_price = (TextView) convertView.findViewById(R.id.txt_price);
viewHolder.txt_qty = (TextView) convertView.findViewById(R.id.txt_qty);
viewHolder.btn_minus = (Button)convertView.findViewById(R.id.btn_minus);
viewHolder.btn_plus= (Button)convertView.findViewById(R.id.btn_plus);
convertView.setTag(viewHolder);
}
else
{
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.btn_minus.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
LinearLayout ll = (LinearLayout) v.getParent();
TextView tv = (TextView) ll.getChildAt(1);
int qty = Integer.parseInt(tv.getText().toString());
if(qty == 0)
{
}
else
{
qty = qty - 1;
tv.setText(String.valueOf(qty));
}
}
});
viewHolder.btn_plus.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
LinearLayout ll = (LinearLayout) v.getParent();
TextView tv = (TextView) ll.getChildAt(1);
int qty = Integer.parseInt(tv.getText().toString());
qty = qty + 1;
tv.setText(String.valueOf(qty));
}
});
HashMap<String, String> map = new HashMap<String, String>();
map = data.get(position);
viewHolder.txt_product_name.setText(map.get("ProductName"));
viewHolder.txt_price.setText(map.get("ProductPrice"));
viewHolder.txt_product_units.setText(map.get("ProductUnits"));
return convertView;
}
How do i address this issue?
Please help ! thanks in advance !
Try defining TextView tv inside viewHolder instead of inside click listener
ViewHolder{
TextView tv;
}
Do something like you have done for txt_product_name TextView
You need to use a model class for that . Create getter setter for each element in your list view Whether is String int or status of open and close .And Pass it to constructor of your adapter class .If you already have a model class then add these parameters to it . And change the property of text change on button click And call notifyDataSetChanged() after it . then you just need if-else in your getView() to change the text .Try it and let me know if you still face some problem.
I have created demo according your scenario ,what i did inside onClick i have updated my data and called notifyDataSetChanged();
whatever you did is very long and heavy process;make it simple .just update your data and called notifyDataSetChanged();if you need sample i can provide you my sample
don't update manually the field of textview .always update the data and get updated data and set to textview
Firstly you can't update the plus(+) and minus(-) values in your listview directly as your tried. Because your not handling any position in your code. So it seams if you change one item count (plus or minus) it will effect on the other items as well (or) else it will lose the data after scrolling..
So to achieve your task, you need to follow these steps.
Firstly you need to create a data structure, which holds the positions of your items and count of your items in the list view. Suppose third item in your list view having 5 count (plus values) we can hold both the position and count as well in this data structure.
So, i am taking a HashMap , where key is String.valueOf(position) and Value is count int. like below
HashMap< String,Integers > countDetails= new HashMap< String,Integers >;
You know the keys since it will be from position "0" to listitem.length. Using the key you can get the count values(probably to hold your item cont details for each and every list item).
Why Hashmap: because everytime you change count the new value will be overriden. (Only one unique key is maintained in hashmap).
Usage:: if the user click's the first item minus (-) or plus (+) button you need to hold that count and need to store in the hashmap with the position and count. for example (position = 0, count = 3) --> - clicked on 1st row--> plus (+) items for 3 times like that.
after clicking plus(+) and (-) values in your list item call the notifyDataChanged() in your list so, that it will update the data in listview.
So, this is the idea how you can achieve your implementation, here i am giving a sample code for it .
class AdapterClass extends ArrayAdapter<String> {
HashMap<String,Integers> countDetails = new HashMap<String,Integers>;
// you can use/ declare all your remaining data product here
public AdapterClass(Context c, String[] items, int imgs[]) {
// TODO Auto-generated constructor stub
super(c, R.layout.rowlayout, R.id.quantity, items);
this.context = c;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
if(convertView==null)
{
viewHolder=new ViewHolder();//
convertView = inflater.inflate(R.layout.inventory_list_item, null);
viewHolder.txt_product_name = (TextView)convertView.findViewById(R.id.txt_product_name);
viewHolder.txt_product_units = (TextView)convertView.findViewById(R.id.txt_product_units);
viewHolder.txt_price = (TextView) convertView.findViewById(R.id.txt_price);
viewHolder.txt_qty = (TextView) convertView.findViewById(R.id.txt_qty);
viewHolder.btn_minus = (Button)convertView.findViewById(R.id.btn_minus);
viewHolder.btn_plus= (Button)convertView.findViewById(R.id.btn_plus);
convertView.setTag(viewHolder);
}
else
{
viewHolder = (ViewHolder) convertView.getTag();
}
HashMap<String, String> map = new HashMap<String, String>();
map = data.get(position);
viewHolder.txt_product_name.setText(map.get("ProductName"));
viewHolder.txt_price.setText(map.get("ProductPrice"));
viewHolder.txt_product_units.setText(map.get("ProductUnits"));
viewHolder.btn_minus.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
int pos=position;
HashMap<String,Integers> tmpDetails = new HashMap<String,Integers>;
tmpDetails=countDetails.get(pos);
int count=tmpDetails.get(pos); // probably your getting the previous count for the item
if(count == 0)
{
}
else
{
count = count - 1;
holder.txt_qty.setText(count.toString());
}
countDetails.put(holder.uniqueKey,count); //Key -> String.valueOf(position) and Value -> int count
}
});
viewHolder.btn_plus.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
int pos=position;
HashMap<String,Integers> tmpDetails = new HashMap<String,Integers>;
tmpDetails=countDetails.get(pos);
int count=tmpDetails.get(pos); // probably your getting the previous count for the item
if(count == 0)
{
}
else
{
count = count + 1;
holder.txt_qty.setText(count.toString());
}
countDetails.put(holder.uniqueKey,count); //Key -> String.valueOf(position) and Value -> int count
}
});
return row;
}
}
p.s. I have typed in editor, there might be some compile time error. Also you can store objects of each row against the key so that when you get the count, you can get any other reference too..
I've created a ListView that in each row has a button with UP and DOWN arrow. Pressing these buttons makes the row to be shifted one position up or down.
I've achieved it by implementing OnClickListener for both buttons in a the override method getView. It works as it should however I fill bad with that cuz it seems to be highly memory consuming and lots of code is doubled.
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View rowView = inflater.inflate(R.layout.row_layout, parent, false);
TextView textView = (TextView) rowView.findViewById(R.id.label);
CheckBox checkBox = (CheckBox) rowView.findViewById(R.id.checkbox);
checkBoxes.add(position, checkBox);
String address = this.getItem(position).getAddress();
String tokenizedAddress = tokenizeAddress(address);
textView.setText(tokenizedAddress);
ImageButton buttonUp = (ImageButton)rowView.findViewById(R.id.button_up);
ImageButton buttonDown = (ImageButton)rowView.findViewById(R.id.button_down);
buttonUp.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
ListAdapter adapter = ListAdapter.this;
if(position != 0 ){
GameTask current = adapter.getItem(position);
ArrayList<GameTask> list = new ArrayList<GameTask>();
for( int i = 0; i < adapter.getCount(); i++ )
list.add(adapter.getItem(i));
list.remove(position);
list.add(position-1, current);
adapter.clear();
for( int i = 0; i < list.size(); i++ ){
adapter.add(list.get(i));
}
adapter.notifyDataSetChanged();
}
}
});
buttonDown.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
ListAdapter adapter = ListAdapter.this;
if(position != adapter.getCount()-1 ){
GameTask current = adapter.getItem(position);
ArrayList<GameTask> list = new ArrayList<GameTask>();
for( int i = 0; i < adapter.getCount(); i++ )
list.add(adapter.getItem(i));
list.remove(position);
list.add(position+1, current);
adapter.clear();
for( int i = 0; i < list.size(); i++ ){
adapter.add(list.get(i));
}
adapter.notifyDataSetChanged();
}
}
});
return rowView;
}
Both listeners do almost the same, the only difference is the condition and the value of shifting +1/-1. I was wondering about creating the inner class implementing OnClickListener in the extended ArrayAdapter class however, I have no idea, how I could then pass the position of the row clicked to this inner class.
Instead of adding and removing elements from your ArrayList, you can better implement Collections.swap(List list, int firstElementIndex, int secondElementIndex) it would be much easier as you don't have to iterate through the whole Collection. A simple example for the same can be found here.
You could make a method that would be used by both buttonUp and buttonDown. This method could take as a parameter the type of action that was pressed (UP/DOWN), and the position of item in ListView, and then call this method in both of your click listener passing the appropriate action.
Example:
// 2 new constants
private static final int UP = 0;
private static final int DOWN = 1;
// Based on "type", increment or decrement the position.
private void changeRow(int type, int position){
if(type==UP){
position=position-1;
}else if(type==DOWN){
position=position+1;
}
// ........
// Then in your "for" cicle you specify:
list.add(position, current);
// ........
}
Then in the onClick() method of buttonUp you specify:
changeRow(UP, position);
and for buttonDown:
changeRow(DOWN, position);
This can be easily achieved
The way to do this is to store the data to be displayed in an List
Then when the user clicks the up or down arrow
Swap the references of data items which are shifting position using Collections.swap(List, int, int)
Then call notifyDataSetChanged on the adapter