In my app the user will select his hobbies from a list.
For now I have a recycler view that show the name and image of the hobby and a check box.
I would like to have only 2 checkboxes enabled(and this I can do it) and when the user click for the first time on one checkbox it will show up 1 instead of the 'check' and when he select the second one there will be 2.
Also when 1 is unchecked 2 will become 1
Example code:
ArrayList checkedList = new ArrayList();
int first = -1;
int second = -1;
-
#Override
public void onBindViewHolder(#NonNull CustomItemHolder itemHolder, int i) {
final int position = i;
...
if (checkedList.size() > 2){
itemHolder.checkBox.setClickable(false);
}
itemHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked){
checkedList.add(position);
if (first == -1){
first = position;
}
else if (second == -1){
second = position;
}
}
else{
checkedList.remove(position);
if (first == position){
if (second == -1){
first = -1;
}
else{
first = second;
second = -1;
}
}
else if (second == position){
second = -1;
}
}
}
});
}
My recommendation would be to assign each "Hobby" a unique ID
public class Hobby {
private final int id;
// ... other fields, getters
}
Then inside your Presentation object (or even just in your adapter for now to get it working) store a list of checked things:
List<Integer> checked;
When you bind, you can check this list for a few things:
If the position's id is the first item in the list (checked.indexOf(item) == 0) then you have your #1
If the position's id is the last item in the list (checked.indexOf(item) == 1) then you have your #2
If there are two items in the list, and the position isn't one of them (checked.size() == 2 && checked.indexOf(item) == -1) then you know to disable it's checkbox
When you click on an item's checkbox and it's now selected, you add its id to the end of the checked list. When you uncheck it, remove it from the list. By using a list like this, you always guarentee your #1 and #2 are in the right order, and automatically get the repositioning (#2 becoming #1) correct.
Remember to notifyDataSetChanged() whenever you click, after you update your list of checked ids. You can optimize this as well with more specific calls, but it's a little more complicated.
Related
I have created a RecyclerView with alternating row color like this:
Whenever I delete an row from the list say for example I delete row whose product name is cookies my list gets updated like this:
as you can see the updated list no longer supports alternating row color. The simple solution would be to change the background color of next View (row) after deleting the current View. For that I first need a reference of next View but as a beginner in android I don't know how to get it.
Adapter:
public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder>{
private List<Model> originalList;
Adapter(List<Model> list){ originalList = list; }
#Override
public void onBindViewHolder(final Adapter.ViewHolder holder, final int position) {
Model list = originalList.get(position);
if (position % 2 == 1)
holder.itemView.setBackgroundColor(Color.parseColor("#e9e9e9"));
final View holder.nextItemView = ? // how to get reference to next View here
holder.product.setText(list.getName());
holder.price.setText(String.valueOf(list.getPrice()));
holder.quantity.setText(String.valueOf(list.getQuantity()));
holder.options.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showPopupMenu(holder.options, nextItemView, position);
}
});
}
private void showPopupMenu(View options, final View view, final int position){
final PopupMenu popup = new PopupMenu(options.getContext(), options);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.options_menu, popup.getMenu());
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
String menuItem = item.toString();
if (menuItem.equals("Delete")){
originalList.remove(position);
notifyItemRemoved(position);
int count = originalList.size();
if (count != 0){
int color = Color.TRANSPARENT;
Drawable background = view.getBackground();
if (background instanceof ColorDrawable)
color = ((ColorDrawable) background).getColor();
if (color == Color.parseColor(#e9e9e9))
color = Color.TRANSPARENT;
view.setBackground(color);
}
}
return true;
}
});
popup.show();
}
}
options is a ImageButton on the click of which I show a popup menu with item labelled as Delete on click of which the row gets deleted from the list.
Quick fix you can call notifyDataSetChanged() instead of notifyItemRemoved(position)
and add an else part
if (position % 2 == 1)
holder.itemView.setBackgroundColor(Color.parseColor("#e9e9e9")); // gray
else
holder.itemView.setBackgroundColor(Color.parseColor("#ffffff")); // white
but this solution will do heavy operations if your list contains a lot of items
the solution which I recommend is using ListAdapter with DiffUtil which will trigger the operation for the only modified items , you can find a sample for it here link1 ,link2, link3
when an item removed. background of all items after that need to be updated.
Method 1
so after you remove item with position of p from list of itemList and notify it you have to do this.
if (itemList.size - p > 0)
notifyItemRangeChanged(p, itemList.size - p)
Method 2
at this point it should work but it can be more optimized. you need just change background but now it will call onBindViewHolder for each items after p.
you can use payload to just change background and do nothing more.
add a const value:
const val PAYLOAD_BACKGROUND = 10
change first method code into this. it says that only update background.
if (itemList.size - p > 0)
notifyItemRangeChanged(p, itemList.size - p, PAYLOAD_BACKGROUND)
override this method
onBindViewHolder(holder, position, payloads)
after you implemented this method the previous onBindViewHolder method will not call anymore. and you have to do all of that inside this method.
onBindViewHolder(holder, position, payloads) {
if (payloads.contains(PAYLOAD_BACKGROUND) || payloads.isEmpty()) {
// set background color
}
if (payloads.isEmpty()) {
// do anything else that you were doing inside onBindViewHolder
}
}
if payloads is not empty it means this is a partial update. but if payloads is empty means this is a complete update like the old onBindViewHolder
Good morning,
I have a ListView in a first activity (main_activity), when I click on a selected item, I have a new activity (details_activity) that opens with more informations and details, and the user had to check it, then the (details_activity) is closed and back to the main_activity with the same list.
I want to disable the click on listView's rows that are already checked by the user.
I tried to get the position from main_activity to details_activity to use it in SQL to get the informations, it's working.
Then I sent it back, in the main_activity I used this code:
mPrr = getSharedPreferences("reponse",Context.MODE_PRIVATE);
int IDs = mPrr.getInt("ID",-1);
String rep =mPrr.getString("reponse","");
if(IDs != -1) {
lst.getChildAt(IDs).setFocusable(false);
}
Is there any help please?
Create a ArrayList in adapter than whenever perform click on that row check that row position if position not in list than add position to the array list and perform click else nothing to do.
private ArrayList<String> positionClicked = new ArrayList<>();
public void onBindViewHolder(final MyViewHolder holder, final int position) {
if (!positionClicked.contains(String.valueOf(position))) {
positionClicked.add(String.valueOf(position));
//perform Click
}else {
//
}
}
Write setClickable(false) instead of setFocusable(false);
Create custom ArrayAdapter, find the position of item you want to disable click and #Overide isEnabled method
#Override
public boolean isEnabled(int position) {
if(position == positionYouWannaDisableClick)
return false;
return true;
}
Yes,I know there is position parametar in AdapterView.OnItemClickListener callback, but this parametar return position of filtered(shown) list.
For example I have _countriesEvisitor arraylist and I would like to get selected country object by looking into position like this:
public AdapterView.OnItemClickListener setOnCitizenshipAutocompleteItemClickListener(){
return (adapterView, view, position, id) -> {
CountryEvisitor citizenship=new CountryEvisitor();
citizenship=_countriesEvisitor.get(position);
_visit.setCitizenship(citizenship);
};
}
_countriesEvisitor has 250 items, and when I start typing for example Hrvatska I get only one item, and it has position 0 (because it is first and only one in shown list) although it is 247th in the _countriesEvisitor list from which list autocomplete adapter is made of.
How to get selected country?
Just use like below code.
public AdapterView.OnItemClickListener setOnCitizenshipAutocompleteItemClickListener(){
return (adapterView, view, position, id) -> {
CountryEvisitor citizenship= (CountryEvisitor ) view.getAdapter().getItem(position);
citizenship=_citizenship.getcitizenship();
_visit.setCitizenship(citizenship);
};}
Keep a second list with the original items, and search through the list matching your search result and get the index of that item
...
_countriesEvisitor.get(position);
int result = -1;
for(int x = 0; x < originalList.size; x++) {
if (/* some check here */) {
result = x;
break;
}
}
if (result != -1) {
// check if result is not -1 and use the index
}
I am using default project for Android TV. Following is the code for creating cards in my BrowseFragment:
private void loadRows() {
List<Movie> list = MovieList.setupMovies();
ListRowPresenter mListRowPresenter = new ListRowPresenter();
mRowsAdapter = new ArrayObjectAdapter(mListRowPresenter);
mListRowPresenter.setRowViewSelected(/*HOW TO GET VIEWHOLDER HERE?*/, false);
CardPresenter cardPresenter = new CardPresenter();
int i;
for (i = 0; i < NUM_ROWS; i++) {
if (i != 0) {
Collections.shuffle(list);
}
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
for (int j = 0; j < NUM_COLS; j++) {
listRowAdapter.add(list.get(j % 5));
}
HeaderItem header = new HeaderItem(i, MovieList.MOVIE_CATEGORY[i]);
mRowsAdapter.add(new ListRow(header, listRowAdapter));
}
setAdapter(mRowsAdapter);
}
I am doing this as I don't want to make first card of row get selected when I launch app. It should only get selected after user press down button on Dpad. If I can't do it this way, what should I do to get mentioned behavior?
You can setRowViewSelected by subclassing ListRowPresenter and overriding initializeRowViewHolder(RowPresenter.ViewHolder holder)
#Override
protected void initializeRowViewHolder(RowPresenter.ViewHolder holder) {
super.initializeRowViewHolder(holder);
setRowViewSelected(holder, false);
}
But I don't think you can unselect all items in BrowseFragment using this approach.
Try setting your ItemViewSelectedListener after your data is loaded instead of setting in onActivityCreated to have all items unselected on initial launch.
Possible reason why top left item of row will always get selected by default and you cannot have all unselected items on initial launch:
BrowseFragment's onItemSelected method (line 1372-1382) on initial launch calls mMainFragmentRowsAdapter.getSelectedPosition()
#Override
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
int position = mMainFragmentRowsAdapter.getSelectedPosition(); //<--
if (DEBUG) Log.v(TAG, "row selected position " + position);
onRowSelected(position);
if (mExternalOnItemViewSelectedListener != null) { //<--
mExternalOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
rowViewHolder, row);
}
}
where getSelectedPosition() always returns 0
(line 483-485)
public int getSelectedPosition() {
return 0;
}
It also calls mExternalOnItemViewSelectedListener.onItemSelected where mExternalOnItemViewSelectedListener is the ItemViewSelectedListener that you set in MainFragment of your app.
So on initial launch, 0th item in 0th row gets selected as a default selected item but if you delay setting mExternalOnItemViewSelectedListener this call will not reach your item selected listener the first time.
you can use this callback method .
void onRowViewSelected (RowPresenter.ViewHolder vh,
boolean selected)
Called when the given row view changes selection state. A subclass may override this to respond to selected state changes of a Row. A subclass may make visual changes to Row view but must not create animation on the Row view.
mListRowPresenter.setRowViewSelected(vh, false);
why you are deselecting initially ? i didn't get your Question Clearly can you please explain what you want to do Exactly ??
How to get the checked item id (custom id, not the position or name of the selected item -in my case order id i need to retrieve) in a custom list view with multiple selections in android.
I have Order name and Order id from json and its populated in custom list view ,In the custom list view i have text view and check box but how to get the Orderid's of the selected/checked Orders.
I have a button when i click the button i need to retrieve the id not the name or position , in my case i need order id to be retrieved
You just need to call ListView.getCheckedItemIds(). It will return a long[] with all checked ids.
There is also ListView.getCheckedItemPositions() which will give you all checked positions.
Make sure you set ListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE) in onCreate() or whereever you set up your views (or in layout xml).
To get the checked values you just need to do this:
SparseBooleanArray checked = mListView.getCheckedItemPositions();
for (int i = 0; i < checked.size(); i++) {
if (checked.valueAt(i)) {
int pos = checked.keyAt(i);
Object o = mListView.getAdapter().getItem(pos);
// do something with your item. print it, cast it, add it to a list, whatever..
}
Jerry, set your order object as tag to the view which has selection event [CheckBox, TextView, Row view], when user select item you can get selected order object from tag and you can get any member of that object(order). for ex.
Order Object
Order {
int id;
String name;
boolean isSelected;
//add getters and setters
}
void getview(...) {
View v = //inflate view
CheckBox cb = (CheckBox) v.findViewById(..);
cb.setTag(yourlist.get(position));
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
((Order) buttonView.getTag()).setSelected(isChecked);
}
});
}
I Solved and got the OrderId by using below code , this worked for me and i could retrieve the custom ORDERID which i passed to the list
int isSelectedOrderNumber=0;
mOpenOrdersSelected = new ArrayList<OpenOrders>();
StringBuffer sb = new StringBuffer();
Iterator<OpenOrders> it = mOpenOrders.iterator();
while(it.hasNext())
{
OpenOrders objOpenOrders = it.next();
//Do something with objOpenOrders
if (objOpenOrders.isSelected()) {
isSelectedOrderNumber++;
mOpenOrdersSelected.add(new OpenOrders(objOpenOrders.getOrderID(),objOpenOrders.getOrderName()));
sb.append(objOpenOrders.getOrderID());
sb.append(",");
}
}
//Below Condition Will Check the selected Items With parameter passed "mMAX_ORDERS_TOBEPICKED"
if(isSelectedOrderNumber<1){
ShowErrorDialog("Please Select atleast One order");
return;
}
if(isSelectedOrderNumber>mMAX_ORDERS_TOBEPICKED){
ShowErrorDialog(" Select Maximum of "+mMAX_ORDERS_TOBEPICKED+ " Orders only to process");
return;
}
Log.d(MainActivity.class.getSimpleName(), "cheked Order Items: " +sb);
Toast.makeText(getApplicationContext(), "cheked Order Items id:" +sb, Toast.LENGTH_LONG).show();