I have implemented an ArrayAdapter for customizing a GridView. The layout of each item is defined in xml code that produce something like this:
In my Activity I have implemented the following code:
GridView gridview = (GridView) findViewById(R.id.gridview);
adapter = new myArrayAdapter(this, articlesList);
gridview.setAdapter(new myArrayAdapter(this, articlesList));
gridview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
// here I want to change the text of the `TextView` in the right bottom corner to "CLICKED"
}
});
As explained in the commented section of the code, I simple want to update the TextView in the right bottom corner, writing in it the text "CLICKED".
The View parameter to AdapterView.OnItemClickListener onItemClick() is the view that was clicked. In your case it will be the item root RelativeLayout. Call findViewById() on it or use the ViewHolder pattern to obtain a reference to the TextView you want to update and just update it.
Also note that when the views are recycled i.e. your adapter's getView() is called with a non-null convertView, you need to reset the TextView to its default state.
You may not need to set a TextView to "Clicked", if all you want to do is give the user the feedback that they have checked an item. An alternative might be to do the following. Note, this method will also help you set a textview to "clicked" if you still need to.
To check an item I would extend your RelativeLayout as follows:
public class CheckableRelativeLayout extends RelativeLayout implements Checkable {
private boolean mChecked;
private static final int[] CHECKED_STATE_SET = {
android.R.attr.state_checked
};
public CheckableRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
public void toggle() {
setChecked(!mChecked);
}
public boolean isChecked() {
return mChecked;
}
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
}
}
}
Then you can call parent.setItemChecked(position,true); in onItemClick which will actually set the item as checked within the AdapterView.
Doing this will then allow you to set a background selector on the extended RelativeLayout and the items will perform selected, pressed, checked etc actions. However, you don't need to use a background selector.
There is more, you can alway see whether the view is checked with gridview.getCheckedItemPosition() which you may find useful if you want to actually set the textview to "Clicked".
gridview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
parent.setItemChecked(position,true);
yourAdapter.ViewHolder vh = (yourAdapter.ViewHolder) v.getTag();
vh.textView.setText("Clicked");
}
});
In your adapter you can hold a reference to you extended relative layout in your static ViewHolder class and if checked (e.g. holder.extendedrelativeLayout.isChecked()) then make sure you set the textView to "Clicked".
try gridView.invalidateViews(); it automatically calls getView() method of the adapter and updates the view.
Related
I have a list view with its own custom adapter and I have put a spinner in every item of that list view but the problem is, the items are added by the user so i don't know how to catch it when he makes a selection in the spinners.
PS. i have put a spinner in every item of that list view but i don't know how to listen for selections in those inner spinners.
So do i put the onIitemClickListener inside the GetView() of the custom adapter or in the MainActivity()'s onCreate function?
The spinner does have an id but since i'm using a custom list view, each spinner is almost as if all have the same id unless i specify the item of the list view inside the GetView() but even then is it possible to put a listener inside getView() and have it running 24/7?
Thanks in advance!
You should add the OnItemClickListener inside the OnCreate() method using GetView(). You don't nessecarily need an id as long as you are using GetView() correctly. There is also an OnItemSelectedListener that you can use if you want to. You can learn more about it here.
Hope this helps!
First I advice you to use Recyclerview, you can take item selected from spinner with this
public class TestSpinnerInListView extends RecyclerView.Adapter<TestSpinnerInListView.TestSpinnerHolder> {
#NonNull
#Override
public TestSpinnerHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new TestSpinnerHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_test,parent, false));
}
#Override
public void onBindViewHolder(#NonNull TestSpinnerHolder holder, int position) {
holder.spTest.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//Do something
holder.tvTest.setText((String) parent.getItemAtPosition(position));
}
});
}
#Override
public int getItemCount() {
return 3;
}
class TestSpinnerHolder extends RecyclerView.ViewHolder{
public TextView tvTest;
public Spinner spTest;
public TestSpinnerHolder(#NonNull View itemView) {
super(itemView);
tvTest = itemView.findViewById(R.id.tvTest);
spTest = itemView.findViewById(R.id.sptest);
}
}
}
I have a layout for list item, which consists of two LinearLayouts. What I want to achieve is: when item is clicked, second LinearLayout should become visible/gone, depending on the current visibility.
I am experimenting with this code:
getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
view.getViewById(R.id.id_of_the_second_linear_layout).setVisibility(View.GONE);
}
});
However when item is clicked, several other linear layouts (in different items) become visible/hidden. Why?
Update:
Adapter:
public class ExpensesCursorAdapter extends SimpleCursorAdapter implements SimpleCursorAdapter.ViewBinder {
public ExpensesCursorAdapter(Context context, Cursor cursor) {
super(context, R.layout.single_expense, cursor,
new String[]{
ExpenseContract._AMOUNT,
CategoryContract._NAME,
ExpenseContract._DATE
},
new int[]{
R.id.expense_amount,
R.id.expense_category,
R.id.expense_date
},
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
setViewBinder(this);
}
public View getView(int position, View convertView, ViewGroup viewGroup) {
View v = super.getView(position, convertView, viewGroup);
final View expandablePanel = v.findViewById(R.id.expandable_panel);
expandablePanel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
expandablePanel.setVisibility(view.getVisibility() == View.GONE ? View.VISIBLE : View.GONE);
}
});
return v;
}
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if (columnIndex == cursor.getColumnIndex(ExpenseContract._AMOUNT)) {
return handleAmountView((TextView) view, cursor);
}
else ...
return false;
}
private boolean handleAmountView(TextView view, Cursor cursor) {
TextView textView = (TextView) view;
Double amount = ExpenseDbHelper.getAmount(cursor);
String formattedAmount = new DecimalFormat("##.00").format(amount);
textView.setText(formattedAmount);
return true;
}
}
Each item has LinearLayout already added in XML, I want to toggle visibility flag, if possible.
You are writing your logic on wrong places. You want to listen clicks of views inside listitem. Write your logic in Adapter's getView method. In your getView logic can be like this
ll1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ll2.setVisibility(View.GONE);
}
});
ll2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ll1.setVisibility(View.GONE);
}
});
Something like this.
When you scroll through the list items, some layouts will hide and un-hide, in that case, if you are targeting just one view to be visible at a time (that is just considering one cell at a time), then you could maintain the position of the item clicked, or the id, since you are using a cursorAdapter. Else if you are considering more than one cell then maintain a list where in you store each id of the cell that has been tapped on.
Pass the list or the single position value to the adpater, and in the getview compare the id or position and then perform the visiblity code.
Hope this hint helps.
I have the list of travelers with custom adapter what consist two EditText - edtFirstName and edtLastName. I want when user enters text save changes to List, and when next button click send this List to another activity.
My code:
public class TravellersAdapter extends BaseAdapter {
private List<Traveler> itemsList;
private LayoutInflater inflater;
private Activity context;
public TravellersAdapter(Activity context, List<Traveler> itemsList) {
super();
this.itemsList = itemsList;
this.context = context;
inflater = LayoutInflater.from(context);
}
public int getCount() { return itemsList.size(); }
public Object getItem(int i) { return itemsList.get(i); }
public View getView(final int position, View view, ViewGroup viewGroup) {
if (view == null) {
view = inflater.inflate(R.layout.traveller_item, null);
}
Traveler currentItem = (Traveler) getItem(position);
EditText firstNameView = (EditText) view.findViewById(R.id.edtFirstName);
firstNameView.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable editable) {
currentItem.setFirstName(editable.toString());
}
});
return view;
}
}
For exemple List itemsList consist 5 items. When I edit 2-4 element all ok, but when I edit first or last element edited value assigned to all element in List. In dubugger i saw that method afterTextChanged calls 5 times with different values of position.
How to fix it?
in getView method, the parameter position gives the position of the newly created childView, not the clicked childView's position.
use this to get the correct position:
final int actual_position = myList.getPositionForView((View) v.getParent());
in onClick(View v); of the onClickListener of any View. In you case, you must implement onTextChangedListener for that EditText.
here:
myList is the ListView
v is the View you clicked, in this case the childView of the parent(myList).
The issue happens because views are reusable (that is by design in Android API). So eventually you may assign more than 1 text watcher to the same text view. And all of the assigned watchers are fired when text inside of the text view is changed.
A quick fix (and non-optimal if the list is really long, say, of 1000+ items) would be to have a map of Traweller -> TextWatcher.
Then inside of getView() you can do this (pseudo-code):
check the map if there is a TextWatcher for this Traweller
if map does not have any, then create a new TextWatcher, put in the map and assign to EditText
otherwise detach the TextWatcher from the EditText and remove from the map
create a new TextWatcher, put in the map and assign to EditText
Create one more EditText in the screen that is invisible with name invivisbleEt.
And do the following thing in the addTextChangedListener
firstNameView.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable editable) {
if(!firstNameView.isFocused())
currentItem.setFirstName(editable.toString());
}
});
Also add this code in the onCreate method for ListView object.
lv.setOnScrollListener(new AbsListView.OnScrollListener() {
//public boolean scrolling;
#Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
invivisbleEt.requestFocus();
}
#Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {
}
});
I want to add gradient for each row. What I've tried to do is on listview label put this attribut, android:background="#drawable/mygradient". But All I get is this gradient displayed all along the list. What I want is this gradient to be displayed for each item.
Thanks in advance
You can provide a custom adapter for your list view and set the gradient as the background in its getView method. Something like this:
public class MyAdapter extends BaseAdapter {
List<MyDataType> myData;
Context context;
public MyAdaptor(Context ctx, List<MyDataType> myData) {
this.myData = myData;
this.context = ctx;
}
public int getCount() { return myData.size() };
public Object getItem(int pos) { return myData.get(pos); }
public long getItemId(int pos) { return 0L; }
public View getView(int pos, View convertView, ViewGroup parent) {
//this is where you can customise the display of an individual
//list item by setting its background, etc, etc.
...
//and return the view for the list item at the end
return <List item view>;
}
}
Then you can set this adapter as the adapter for your list:
ListView myList = <initialise it here>
myList.setAdapter(new MyAdapter(getContext(), listData);
Now whenever a list item needs to be displayed, getView method will be called, where you'll perform all the necessary display customisation, including setting the background.
Instead of setting background property set Selector of list, as below:
android:listSelector="#drawable/list_selector_background"
I have a listview with image.When i select each item the image changes to clicked image.But when i select another item both images changes.I want only the selected item to change the image.it has to function like radio button with single choice mode.pls help.
public class ProvierActivity extends Activity {
private String text[] = { "BroadStripe-Cable (Seattle)", "BroadStripe-Digital (Seattle)",
"BroadStripe-Cable (Seattle)", "Comcast king county south)", "BroadStripe-Cable (Seattle)",
"Comcast king county south", "BroadStripe-Digital (Seattle)", "BroadStripe-Digital (Seattle)",
"BroadStripe-Cable (Seattle)", "Comcast king county south" };
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ListView list = (ListView) findViewById(R.id.listview_id);
list.setAdapter(new ArrayAdapter<String>(this, R.layout.list,
R.id.title, text));
list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View view,
int position, long arg3) {
// TODO Auto-generated method stub
if (view.findViewById(R.id.img).getVisibility() == ImageView.VISIBLE) {
ImageView icon = (ImageView) view.findViewById(R.id.img);
icon.setImageResource(R.drawable.checked);
}
}
});
}
}
The recommended solution here is to rely on built-in ListView's selection support specifying single-choice mode for it:
list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Then you should let your list item to implement to implement Checkable interface; that requires some customization of the root view of your layout. Assuming that it has LinearLayout as a root, an implementation may look like:
public class MyListItem extends LinearLayout implements Checkable {
public MyListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListItem(Context context) {
super(context);
}
private boolean checked = false;
private ImageView icon;
#Override
protected void onFinishInflate() {
super.onFinishInflate();
icon = (ImageView)findViewById(R.id.img); // optimisation - you don't need to search for image view every time you want to reference it
}
#Override
public boolean isChecked() {
return checked;
}
#Override
public void setChecked(boolean checked) {
this.checked = checked;
if (icon.getVisibility() == View.VISIBLE) {
icon.setImageResource((checked) ? R.drawable.checked : R.drawable.unchecked);
}
}
#Override
public void toggle() {
setChecked(!checked);
}
}
Your layout file should be updated to reference MyListItem class instead of LinearLayout too, of course.
This may be a somewhat difficult approach, but it has benefits like restoring checked item automatically when activity is recreated.
Another option is to use the list's choice mode again and override adapter's getView to know if the item is checked and update image accordingly:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ListView list = getListView();
list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
list.setAdapter(new ArrayAdapter<String>(this, R.layout.list_item,
R.id.title, text) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
ImageView icon = (ImageView) v.findViewById(R.id.img);
if (list.isItemChecked(position)) {
icon.setImageResource(R.drawable.checked);
} else {
icon.setImageResource(R.drawable.unchecked);
}
return v;
}
});
}
However this version has some performance issues - findViewById and setImageResource are relatively time-consuming operations so you should consider using some caching. I recommend to watch "The world of ListView" video to learn some very useful tips about this widget.
The latter approach also ties adapter to the listview which introduces unnecessary coupling.
Set the correct choice mode in your list view. setChoiceMode according what you needs.
Create a drawable selector to use the image that you want, according the state.
Set your selector as background.
android:background="#drawable/your_selector"
FYI:
http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList
http://android-developers.blogspot.mx/2008/12/touch-mode.html
http://www.charlesharley.com/2012/programming/custom-drawable-states-in-android/