Is it possible to change layout of selected item in ListView?
I made simple app with ListView using Custom Adapter. It's working great, but I would like to change the layout of selected item to different layout.
In practice, the clicked item would expand it's height and some new buttons would appear. Once user deselects the items, it would go back to classic layout.
This is my Custom Adapter code
public class ListAdapter extends ArrayAdapter<Polozka> {
// context mem variable
private Context mContext;
// Konstruktory
public ListAdapter(Context context,int textViewResourceId){
super(context,textViewResourceId);
}
public ListAdapter(Context context, int resource, List<Polozka> items){
super(context,resource,items);
this.mContext=context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
View v = convertView;
if (v == null){
LayoutInflater vi;
vi = LayoutInflater.from(getContext());
v = vi.inflate(R.layout.polozkanakupniseznam,null);
}
final Polozka p = getItem(position);
if (p != null){
final TextView tt1 = (TextView) v.findViewById(R.id.seznam_nazevPolozky);
TextView tt2 = (TextView) v.findViewById(R.id.seznam_pocetPolozky);
CheckBox cb1 = (CheckBox) v.findViewById(R.id.checkBox_koupeno);
Button btnPridat = (Button) v.findViewById(R.id.buttonPridat);
Button btnOdebrat = (Button) v.findViewById(R.id.buttonOdebrat);
if (tt1 != null){
tt1.setText(p.getNazev());
}
if (tt2 != null){
tt2.setText(Integer.toString(p.getPocet()));
}
return v;
}}
This is how I set adapter to my listview
lv = (ListView) findViewById(R.id.lv);
adapter = new ListAdapter(this,R.layout.polozkaseznam,list);
lv.setAdapter(adapter);
I figured out how to change layout of every item by creating new custom adapter and asigning it to the ListView, but that's sadly not what I need.
Thank you very much!
The getView() method of the adapter is used to create the original view. In there you can use the view's setOnTouchListener() method to add a View.OnTouchListener implementation. The onTouch() handler of that class receives the selected view.
A useful member to add to the OnTouchListener implementation is a GestureDetector; this is created with an OnGestureListener implementation to which you can pass the MotionEvent received in the OnTouchListener's onTouch() callback. This listener is then used to distinguish various events (single tap, double tap, etc); GestureDetector.SimpleOnGestureListener is good implementation to use with a number of callbacks handling such events you can override.
At some point (e.g., in the SimpleGestureListener's onDown() callback, which fires when an item is first touched), you can modify the layout of the view handed to the OnTouchListener. To change it back, you could either use a handler for the "on up" phase of a gesture, or, if you wish the item to remain as is until something else is selected, hold a reference to the last selected view and change it when another one is selected.
In case this last point is not clear, the idea is that the SimpleGestureListener is a member object of the view's OnTouchListener instance, therefore it can access the view belonging to the enclosing OnTouchListener.
Related
I want to add a feature on my project where if the user clicks one of the items on the listView then a checkbox would appear allowing the user to delete 1 or many items at once. Similar to Samsung Notes when you want to delete notes and or folders, etc. However, this concept is completely foreign to me and currently, I don't know where to begin to start this or what topic/resource/sample code I should look for. Also, I have a custom Array Adapter class that I used to order to work with my ListView but it came to my knowledge that you only need 1 array adapter class to make this work which made me confused since I don't know where to begin to manipulate it even further. Any help would be amazing!
Here is my Array Adapter that I have at the moment
//want to create our own custom ArrayAdapter. Going to extends the base class ArrayAdapter and hold our
//Word object
public class WordAdapter extends ArrayAdapter<WordFolder> {
//constructor - it takes the context and the list of words
WordAdapter(Context context, ArrayList<WordFolder> word){
super(context, 0, word);
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
View listItemView = convertView;
if(listItemView == null){
listItemView = LayoutInflater.from(getContext()).inflate(R.layout.folder_view, parent, false);
}
//Getting the current word
WordFolder currentWord = getItem(position);
//making the 3 text view to match our word_folder.xml
TextView date_created = (TextView) listItemView.findViewById(R.id.date_created);
TextView title = (TextView) listItemView.findViewById(R.id.title);
TextView desc = (TextView) listItemView.findViewById(R.id.desc);
//using the setText to get the text and set it in the textView
date_created.setText(currentWord.getDateCreated());
title.setText(currentWord.getTitle());
desc.setText(currentWord.getTitleDesc());
return listItemView;
}
}```
In R.layout.folder_view add one and make it invisible or gone. OnLongClick make them Visible.
I have a Activity which shows json that has objects set as name and link. Name is displayed to the user by using getName(). Now I want to implement the onClicklistener such that new activity will open based on the position and equivalent url i.e getLink() of same position and sent it to next activity as extras.
Here is my listview adapter
public class ListViewAdapter extends ArrayAdapter<Hero> {
//the hero list that will be displayed
private List<Hero> heroList;
//the context object
private Context mCtx;
//here we are getting the herolist and context
//so while creating the object of this adapter class we need to give herolist and context
public ListViewAdapter(List<Hero> heroList, Context mCtx) {
super(mCtx, R.layout.vault_list, heroList);
this.heroList = heroList;
this.mCtx = mCtx;
}
//this method will return the list item
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//getting the layoutinflater
LayoutInflater inflater = LayoutInflater.from(mCtx);
//creating a view with our xml layout
View listViewItem = inflater.inflate(R.layout.vault_list, null, true);
//getting text views
Button btn_one = listViewItem.findViewById(R.id.btn_one);
//Getting the hero for the specified position
Hero hero = heroList.get(position);
//setting hero values to textviews
btn_one.setText(hero.getName());
//returning the listitem
return listViewItem;
}
If I understand you correctly, you want to let use click on item of your listview and go to next activity with the related link。You can implement the onItemclick method to achieve your function.
Is it possible to have methods and actual coding inside each item for a GridView?
The app that I am attempting to create is a currency converter, and I am currently displaying 3 images in the gridView: Euros, Pesos, and Rupees.
Once the user clicks on one, I want the open to open up a new XML which displays a textView. The user enters the value of US dollars in the textView and clicks the compute button. The app then displays the converted amount in the bottom of the screen.
The problem is that I am unable to figure out how to open up a new XML every time a picture is clicked on in the gridView. Assuming that I am able to do this, I am also unsure of where to place the code that goes behind the conversions. Would I make a new .java or just place is all in MainActivity.java?
Thanks.
What you might be best doing is when the user clicks on a currency it takes them to another activity where you would then load another xml for whatever you want to show.
In order to detect which item had been clicked you can implement an onItemClickListener for example
gridView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
//this assumes you give the gridview a list of currency which it then displays. Here we get the currency selected to then pass to our new activity
String selectedCurrency = myArrayOfCurrencies.get(position);
//then start new activity and give it the currency. This means we won't have to create an activity for each currency. You just need to create 1 and then based on what currency you give it will change the functionality
Intent intent = new Intent(this, Converter.class);
Intent.putExtra("currency", selectedCurrency);
startActivity(intent);
}
First you should be able to detect the clicks on each item of the GridView by calling the setOnItemClickListener() method.
If you set the clicklistener and you still can't detect the clicks, then most probably you need to add those attribtutes to your imageView in the xml
android:focusable="false"
android:focusableInTouchMode="false"
Second, once you are able to detect the clicks you can start new activity or add fragment that contains that edit text that will promote the user to enter the value.
Third, I would suggest to put the code responsible for the currency conversion in a class separately and create static methods that takes a value and convert it to the other curreny such as:
public class CurrencyConverter {
public static double convertToRupees (String currencyType, double currencyValue){
....
return currencyInRupees;
}
}
and by the way I would suggest you to use RecyclerView with grid layout manager instead of GridView.
I would create more classes.
You asked how to open a different XML file for each gridView item.
Create a custom adapter that extends BaseAdapter.
Override getView and for each view attach the right Xml file, according to the position.
For example:
YourActivity.java:
GridView gridView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gridView = (GridView) findViewById(R.id.gridView);
gridView.setAdapter(new MyAdapter(getApplicationContext());
}
MyAdapter.java:
...
#Override
public int getCount() {
return XmlArr.length;
}
#Override
public Object getItem(int position) {
return XmlArr[position];
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Xml myXml = (Xml) getItem(position);
Holder holder;
if (convertView == null) {
// Set your view's layout. Consider using LayoutInflater.
// Use a static holder to prevent re-initialization:
holder = new Holder();
// holder.textView = ...
// holder.Xml = ...
// Or whatever you decided to have in each gridView item.
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
holder.Xml = myXml;
...
return convertView;
}
static class Holder() {
TextView tv;
Xml xml;
...
}
I assumed you would used an Xml array (xmlArr).
Now you have option to play with each gridView item as you wish. You can set each view/button/textView an onItemClickListener, or you can also set the whole gridView an onItemClickListener (from YourActivity.java).
Hope this helps.
I have a ListView that gets its layout for each item from "rowlayout".
I want to do something to one of the buttons in the rowlayout in my main activity, how do i get a reference to it findviewbyid doesn't work.
I guess i'm essentially not understanding a general concept of how to get a reference to a view in a custom layout- unless getting a view from a ListView is different. Can anyone help? thanks.
ListView is a ViewGroup so you can just iterate over its children:
int n = getChildCount();
for (int i = 0; i < n; i++) {
doSomethingToView(getChildAt(i));
}
However, you must be careful. ListView is pretty special and only has a subset of children that you might expect. It only holds (roughly) references to children that are currently visible and reuses them as they disappear.
What you might consider is changing underlying adapter instead and than notifying the ListView that its adapter changed, so it needs to redraw children. Or alternatively, you can make the child of ListView directly listen for events that are supposed to change it and then adjust itself.
If you are inflating a custom layout in the getView method of your adapter, you can get a view like this
LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.your_custom_layout, parent, false);
TextView tv = (TextView) rowView.findViewById(R.id.your_tv_id);
You can use partial refresh principle if you implement ListView in the way of ViewHolder, just like this:
private void refreshPartially(int position){
int firstVisiblePosition = listview.getFirstVisiblePosition();
int lastVisiblePosition = listview.getLastVisiblePosition();
if(position>=firstVisiblePosition && position<=lastVisiblePosition){
View view = listview.getChildAt(position - firstVisiblePosition);
if(view.getTag() instanceof ViewHolder){
ViewHolder vh = (ViewHolder)view.getTag();
//holder.play.setBackgroundResource(resId);//Do something here.
...
}
}
}
Possibly you can use the answer from this android - listview get item view by position to get the child view at a position then call findViewById() on that child view.
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.