First of all sorry for the language. I have a RecyclerView with items. I have a checkbox in each item. I have onCheckedChangeListener inside RecyclerAdapter.
When I check 4 checkboxes I need to disable the remaining. So how can I get an access for them? I can do it when on create Recycler by checking how many items are selected. But can't find how to get access for every checkbox in onCheckedChange method. Screenshot example
public class EditAtributesAdapter extends RecyclerView.Adapter<EditAtributesAdapter.EditAtributesViewHolder> {
private ArrayList<AtributeEditItem> mEditAtributeList;
DBHelper dbHelper;
public static class EditAtributesViewHolder extends RecyclerView.ViewHolder {
public ImageView mImageView;
public CheckBox mCheckBox;
public EditAtributesViewHolder(View itemView) {
super(itemView);
mImageView = itemView.findViewById(R.id.edit_atribute_icon);
mCheckBox = itemView.findViewById(R.id.enabledAtribute);
}
}
public EditAtributesAdapter(ArrayList<AtributeEditItem> editAtributeList){
mEditAtributeList = editAtributeList;
}
#Override
public EditAtributesViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.atribute_edit_item, parent, false);
final Context forClickCTX = parent.getContext();
final EditAtributesViewHolder eavh = new EditAtributesViewHolder(v);
eavh.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int pos = eavh.getAdapterPosition();
// System.out.println("Hello, you clicked: " + mEditAtributeList.get(pos).getName());
dbHelper = new DBHelper(forClickCTX);
try {
dbHelper.createDataBase();}
catch (IOException ioe) {
throw new Error("Не удалось создать базу данных");}
try {
dbHelper.openDataBase();}
catch (SQLException sqle) {
throw sqle;}
int countSelected = 0;
if (countSelected == 0){
Cursor cursor = null;
try {
SQLiteDatabase db = dbHelper.getReadableDatabase();
cursor = db.query(DBHelper.TABLE_TASKS, null, "id = " + mEditAtributeList.get(pos).getTask_id(), null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
String ids = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.TASK_ATRIBUTE_ID));
String[] parts = ids.split(",");
int[] ints = new int[parts.length];
for (int i = 0; i < parts.length; i++) {
ints[i] = Integer.parseInt(parts[i]);
}
countSelected = ints.length;
}
} finally {
if (cursor != null)
cursor.close();
}
if(countSelected>=5){
if(!eavh.mCheckBox.isChecked())
eavh.mCheckBox.setEnabled(false);
else
eavh.mCheckBox.setEnabled(true);
}
else
eavh.mCheckBox.setEnabled(true);
}
//here is saving new status to db ( a lot of code)
});
return eavh;
}
#Override
public void onBindViewHolder(EditAtributesViewHolder holder, int position) {
AtributeEditItem currentItem = mEditAtributeList.get(position);
holder.mImageView.setImageResource(currentItem.getImage());
holder.mCheckBox.setText(currentItem.getName());
holder.mCheckBox.setChecked(currentItem.isSelected());
int countSelected = 0;
for (int i = 0 ; i < mEditAtributeList.size() ; i++){
if(mEditAtributeList.get(i).isSelected())
countSelected++;
}
if(countSelected>=5){
if(!holder.mCheckBox.isChecked())
holder.mCheckBox.setEnabled(false);
else
holder.mCheckBox.setEnabled(true);
}
else
holder.mCheckBox.setEnabled(true);
}
#Override
public int getItemCount() {
return mEditAtributeList.size();
}
}
Your adapter and viewholder are actually doing too much, but for a small list you can probably get away with it. Calling the sqldatabse like this onclick can be a bit awkward, i am not sure, but i think you are blocking the main thread with this which means you may notice that onclick slightly freezes your app.
But the simplest way disable the items in your recyclerview is to update the items in your
private ArrayList<AtributeEditItem> mEditAtributeList
and call
notifyDataSetChanged()
on the adapter, as you can then derive the state based on the data
Related
I have a "favourite" button for each row of my recyclerview which the user clicks when the like the image (obviously). Each row is a cardview that I only want to "flip" when the user opens the fragment.
When the user clicks the button I update my database with "Y" or "N".
My problem is that my recyclerview refreshes even though the list hasn't changed. When it refreshes all my cards flip which I do not want. How can I stop the recyclerview from updating when the button is clicked?
Here is my adapter class
#Override
public void onBindViewHolder(#NotNull final ClothesViewHolder holder, final int position) {
String image;
if (flip) {
holder.flipView.flipTheView();
}
ClothingItem current = mClothingItems.get(position);
holder.itemNameView.setText(current.getItem());
holder.categoryNameView.setText(current.getCategory());
holder.seasonNameView.setText(current.getSeason());
Integer yesCount = current.getYesCount();
Integer noCount = current.getNoCount();
if (current.getFavourite().equalsIgnoreCase("N")) {
holder.animationView.setProgress(0);
}
else {
holder.animationView.setProgress(1);
}
holder.yesTextView.setText(String.valueOf(yesCount));
holder.noTextView.setText(String.valueOf(noCount));
image = current.getPhotoPath();
Glide.with(holder.cardView)
.load(image)
.into(holder.pictureView);
flip = false;
} catch (NullPointerException e) {
Log.e("Picture","onBindViweHolder: Null Point:" + e.getMessage());
}
holder.animationView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.onFavouriteClick(position);
}
});
public interface clickButtons {
void onFavouriteClick(int position);
}
Fragment Class
#Override
public void onFavouriteClick(int position) {
RecyclerView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(position);
LottieAnimationView animationView = holder.itemView.findViewById(R.id.favouriteAnimation);
ClothingItem item = springList.get(position);
final Long id = item.getId();
if (animationView.getProgress() > 0) {
animationView.setProgress(0);
mClothingViewModel.updateFavourite(id.intValue(), "N");
adapter.notifyItemChanged(position,"favourite");
} else if (animationView.getProgress() == 0) {
animationView.playAnimation();
mClothingViewModel.updateFavourite(id.intValue(),"Y");
}
}
I tried to use onBindViewHolder with payloads but I get the same result. I think I'm not calling this properly
adapter.notifyItemChanged(position,"favourite");
Adapter class
#Override
public void onBindViewHolder(final ClothesViewHolder holder ,final int position, final List<Object> payloads){
String image;
if(!payloads.isEmpty()) {
ClothingItem current = mClothingItems.get(position);
holder.itemNameView.setText(current.getItem());
holder.categoryNameView.setText(current.getCategory());
holder.seasonNameView.setText(current.getSeason());
Integer yesCount = current.getYesCount();
Integer noCount = current.getNoCount();
if(yesCount == null) {
yesCount = 0;
}
if (noCount == null) {
noCount = 0;
}
if (current.getFavourite() == null || current.getFavourite().equalsIgnoreCase("N")) {
holder.animationView.setProgress(0);
}
else {
holder.animationView.setProgress(1);
}
// Log.d("Counting","Yes count " + yesCount + " no count " + noCount);
holder.yesTextView.setText(String.valueOf(yesCount));
holder.noTextView.setText(String.valueOf(noCount));
image = current.getPhotoPath();
Glide.with(holder.cardView)
.load(image)
.into(holder.pictureView);
} else {
onBindViewHolder(holder,position);
}
}
Fragment
#Override
public void onFavouriteClick(int position) {
RecyclerView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(position);
LottieAnimationView animationView = holder.itemView.findViewById(R.id.favouriteAnimation);
ClothingItem item = springList.get(position);
final Long id = item.getId();
if (animationView.getProgress() > 0) {
animationView.setProgress(0);
mClothingViewModel.updateFavourite(id.intValue(), "N");
adapter.notifyItemChanged(position,"favourite");
} else if (animationView.getProgress() == 0) {
animationView.playAnimation();
mClothingViewModel.updateFavourite(id.intValue(),"Y");
adapter.notifyItemChanged(position,"favourite");
}
}
Recyclerview is updated or refreshed when notifiydataSetChanged is called.
try to remove notifiyDataSetChanged in the click event maybe
Currently, I am trying to create a mobile application in Android Studio which allows the user to enter an ISBN number and when the search button is clicked, it will then search a CSV in the raw folder and return results of that ISBN query. This CSV file includes all other information which is associated with that number separated by commas. Currently its returns all results of the CSV file ordered into edit text fields on the user interface. How can I change my code to only return the specific result which I want?
This is the readFile class:
public class readFile {
InputStream inputStream;
public readFile(InputStream inputStream){
this.inputStream = inputStream;
}
public List<String[]> read(){
List<String[]> resultList = new ArrayList<String[]>();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
String csvLine;
while ((csvLine = reader.readLine()) != null) {
String[] row = csvLine.split(",");
resultList.add(row);
}
}
catch (IOException ex) {
throw new RuntimeException("Error in reading CSV file: "+ex);
}
finally {
try {
inputStream.close();
}
catch (IOException e) {
throw new RuntimeException("Error while closing input stream: "+e);
}
}
return resultList;
}
}
The itemArrayAdapter class:
public class ItemArrayAdapter extends ArrayAdapter<String[]> {
private List<String[]> scoreList = new ArrayList<String[]>();
static class ItemViewHolder {
EditText title, publicationPlace,publicationDate,edition,author;
}
public ItemArrayAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
#Override
public void add(String[] object) {
scoreList.add(object);
super.add(object);
}
#Override
public int getCount() {
return this.scoreList.size();
}
#Override
public String[] getItem(int index) {
return this.scoreList.get(index);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ItemViewHolder viewHolder;
if (row == null) {
LayoutInflater inflater = (LayoutInflater) this.getContext().
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.item_layout, parent, false);
viewHolder = new ItemViewHolder();
viewHolder.title = (EditText) row.findViewById(R.id.title);
viewHolder.publicationDate = (EditText) row.findViewById(R.id.publicationDate);
viewHolder.publicationPlace = (EditText) row.findViewById(R.id.publicationPlace);
viewHolder.edition = (EditText) row.findViewById(R.id.edition);
viewHolder.author = (EditText) row.findViewById(R.id.authors);
row.setTag(viewHolder);
} else {
viewHolder = (ItemViewHolder)row.getTag();
}
String[] isbn = getItem(position);
viewHolder.title.setText(isbn[1]);
viewHolder.publicationDate.setText(isbn[2]);
viewHolder.publicationPlace.setText(isbn[3]);
viewHolder.edition.setText(isbn[4]);
viewHolder.author.setText(isbn[5]);
return row;
}
}
[...code...]
String[] row = csvLine.split(",");
// Add the check as shown here:
for ( int col = 0; col < row.length; col++ ) { // this could be avoided
if ( row[col].compareTo(ISBN_string) == 0 )
resultList.add(row);
}
// Maby if you know the column no. you can get away without the iteration
// resultList.add(row); <-- this is now obsolete
[...more code...]
I want to create something like this in RecyclerView:
But, I am getting this:
Is there a way to make it happen with RecyclerView?
try & check replacing your line
postLine.setLayoutManager(new GridLayoutManager(getActivity(),2));
with
postLine.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL));
init :
postLine.setAdapter(postsAdapter);
postLine.setLayoutManager(new GridLayoutManager(getActivity(),2));
The function that prepares the post to add them in recyclerView
private void preparePosts(JSONArray posts){
listOfLine1.clear(); //SparseArray
listOfLine2.clear(); //SparseArray
postLine.removeAllViews(); //RecyclerView
postList.clear(); //ListArray<Post> postList
int postline2h = 0;
int postline1h = 0;
try{
Post ps;
for(int i = 0; i<posts.length();i++){
ps = new Post(posts.getJSONObject(i));
if(postline1h>postline2h){
listOfLine2.put(listOfLine2.size(),ps);
postline2h += ps.getHeight();
}else{
postline1h += ps.getHeight();
listOfLine1.put(listOfLine1.size(),ps);
}
}
int i =0;
boolean firstnull,secondnull;
while (i!=listOfLine2.size()-1 || i!=listOfLine1.size()-1){
if(listOfLine1.get(i)!=null){
firstnull = false;
postList.add(listOfLine1.get(i));
listOfLine1.remove(i);
}else firstnull = true;
if(listOfLine2.get(i)!=null){
secondnull = false;
postList.add(listOfLine2.get(i));
listOfLine2.remove(i);
}else secondnull = true;
if(secondnull && firstnull) break;
i++;
}
postsAdapter.notifyDataSetChanged();
}catch (Exception e){
log(e);
}
}}
Adapter:
public class PostsAdapter extends RecyclerView.Adapter<PostsHolder> {
private ArrayList<Post> posts;
public PostsAdapter(ArrayList<Post> postslist){
posts = postslist;
}
#Override
public int getItemCount() {
return posts.size();
}
#Override
public PostsHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new PostsHolder(new LinearLayout(parent.getContext()));
}
#Override
public void onBindViewHolder(PostsHolder holder, int position) {
holder.setPost(posts.get(position));
}
#Override
public void onViewRecycled(PostsHolder holder) {
super.onViewRecycled(holder);
holder.getPost().die();
}
}
onBindViewHolder post.setPost is going to add the views to the Layout
In my LinearLayout, there's a variable number of CheckBoxes. In a question I had a month ago someone said it´s better to add checkboxes dynamicly instead of make them not visible.
Integer[] count = new Integer[]{1,2,3,4,5,6,7,8,9,10};
size = mFrageList.get(position).getAuswahlList().size();
for (int i = 0; i < size; i++) {
cBox = new CheckBox(this);
cBox.setText(mFrageList.get(position).getAuswahlList().get(i));
cBox.setId(count[i]);
cBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
antwortencode[position] += "" + buttonView.getId();
frageBeantworten.setText("Antwort :"+antwortencode[position]+" abgeben");
} else {
String id = Integer.toString(buttonView.getId());
antwortencode[position] = antwortencode[position].replaceAll(id,"");
if(!antwortencode[position].isEmpty() || antwortencode[position]!= "") {
frageBeantworten.setText("Antwort :" + antwortencode[position] + " abgeben");
} else {
frageBeantworten.setText("Keine Checkbox(en) gewählt");
}
}
}
});
antworten.addView(cBox);
Currently, I'm able to save a string with the checked checkboxes, if I un-check a checkbox, it deletes it's value out of the string.
If I update the activity, the string is saved, and the checkboxes get a new List from the mFrageList.get(position)getAuswahlList(); and fill a new string in the "antwortencode" List with their values.
If I go back to the last position, I have the string which was generated but the checkboxes aren't checked anymore. But they have the Strings from the old position. that means everything is saved except the state of the checkboxes. I cant set a cBox.setChecked(isChecked) or buttonView.setChecked(isChecked) or buttonView.setChecked(buttonView.isChecked()) or something which is nearly the same in syntax.
I don't know what I can do besides declaring 10 Checkboxes in a xml file to talk to them one by one and set the VISIBLE.false if the auswahlList.get(position).isEmpty().
IMPORTANT: My XML is a Scrollable Activity because the size of the content overextended the screen. Thats why i didn´t and can´t use a Listview. So i need a solution that uses a LinearLayout
The truth is, you should actually use a ListView. As long as you reuse a layout multiple times - do it.
There are 2 options:
ListView as root - add other contents of your layout as different types of view
ListView inside a scrollable layout - there are many lightweight implementations of ListView that allow it to wrap content, e.g. https://github.com/paolorotolo/ExpandableHeightListView
The other thing is how to maintain the state of Checkboxes - use model classes. It's extremely easy with a ListView as it forces you to use an Adapter which provides methods to iterate over all positions.
Example of an adapter:
public class CheckableItemAdapter extends BaseAdapter {
private List<Pair<Integer, Boolean>> items = new ArrayList<>();
public void setItems(List<Pair<Integer, Boolean>> items) {
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder holder;
if (convertView == null) {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_checkable, parent, false);
holder = new ViewHolder(view);
view.setTag(holder);
} else {
view = convertView;
holder = (ViewHolder) view.getTag();
}
Pair<Integer, Boolean> item = items.get(position);
holder.itemCheck.setChecked(item.second);
return view;
}
static class ViewHolder {
CheckBox itemCheck;
public ViewHolder(View itemView) {
itemCheck = (CheckBox) itemView.findViewById(R.id.check);
}
}
}
I´ve managed to solve my problem alone, and now i want to share it, even if it isn´t the best example of programming.
Integer[] count = new Integer[]{1,2,3,4,5,6,7,8,9,10}; //maximum of 10 Checkboxes
size = mFrageList.get(position).getAuswahlList().size();
for (int i = 0; i < size; i++) {
cBox = new CheckBox(this);
cBox.setText(mFrageList.get(position).getAuswahlList().get(i));
cBox.setId(count[i]);
try{ //this is where the magic happens
if(antwortencode[position] != ""){ //cause i won´t want null in my db i´ve set "" as standard string in my activity for the List<String>
String code = antwortencode[position];
char[] c = code.toCharArray();
for(int j=0;j<=c.length;j++){
int x = c[j] -'0'; // 'char 1' - 'char 0' = Integer 1 , lol
if(cBox.getId()== x){ //compare them
cBox.toggle(); //if it fits, toggle
}
}
}
} catch (Exception e){
e.printStackTrace();
} //and here it ends
cBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
antwortencode[position] += "" + buttonView.getId();
frageBeantworten.setText("Antwort :"+antwortencode[position]+" abgeben");
} else {
String id = Integer.toString(buttonView.getId());
antwortencode[position] = antwortencode[position].replaceAll(id,"");
if(!antwortencode[position].isEmpty() || antwortencode[position]!= "") {
frageBeantworten.setText("Antwort :" + antwortencode[position] + " abgeben");
} else {
frageBeantworten.setText("Keine Checkbox(en) gewählt");
}
}
}
});
antworten.addView(cBox);
Ty for the answers and for the correction of my question.
Nostramärus
I'm trying to build a custom RecyclerView in that RecyclerView layout of first row should be different from the rest of rows as shown in this figure
I've searched and found some resources and got a solution for custom adapter for RecyclerView as
public class PostDetailAdapter extends RecyclerView.Adapter<ViewHolder> {
private static final String TAG = PostDetailAdapter.class.getName();
private List<Solution> mSolutionList;
private Issue mIssue;
private Context mContext;
public static final int ISSUE = 0;
public PostDetailAdapter(Context mContext, Issue mIssue, List<Solution> mSolutionList){
this.mContext = mContext;
this.mIssue = mIssue;
this.mSolutionList = mSolutionList;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View v;
try {
if (viewType == ISSUE) {
v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.row_detail_issue, viewGroup, false);
return new PostDetailViewHolder(v);
} else {
v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.row_detail_solution, viewGroup, false);
return new AnswerDetailViewHolder(v);
}
}catch (Exception e){
Toast.makeText(mContext, "Error caused by " + e.getCause(), Toast.LENGTH_SHORT).show();
Log.e(TAG, "Error caused by " + e.getCause());
return null;
}
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
try {
if (viewHolder.getItemViewType() == ISSUE) {
PostDetailViewHolder postDetailViewHolder = (PostDetailViewHolder) viewHolder;
postDetailViewHolder.setTitleText(mIssue.getQ());
postDetailViewHolder.setDescText(mIssue.getD());
} else {
AnswerDetailViewHolder answerDetailViewHolder = (AnswerDetailViewHolder) viewHolder;
answerDetailViewHolder.setDescText(mSolutionList.get(position).getD());
}
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public int getItemViewType(int position) {
if (position == 0) {
return 0;
} else{
return 1;
}
}
#Override
public int getItemCount() {
return (null != mSolutionList) ? mSolutionList.size() : 0 / 0;
}
}
in MainActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecycler = (RecyclerView)findViewById(R.id.post_detail_recycler_view);
mRecycler.setHasFixedSize(true);
mSolutionList = populateSolutionList();
mAdapter = new PostDetailAdapter(MainActivity.this, new Issue("Issue title", "Issue Desc"), mSolutionList);
// Set up Layout Manager
mManager = new LinearLayoutManager(MainActivity.this);
mManager.setReverseLayout(false);
mManager.setStackFromEnd(false);
mStaggeredGridLayoutManager = new StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL);
mStaggeredGridLayoutManager.setReverseLayout(false);
mRecycler.setLayoutManager(mStaggeredGridLayoutManager);
mRecycler.setAdapter(mAdapter);
}
private List<Solution> populateSolutionList() {
List<Solution> solutions = new CopyOnWriteArrayList<>();
solutions.add(new Solution("Solution1"));
solutions.add(new Solution("Solution2"));
solutions.add(new Solution("Solution3"));
solutions.add(new Solution("Solution4"));
solutions.add(new Solution("Solution5"));
solutions.add(new Solution("Solution6"));
solutions.add(new Solution("Solution7"));
solutions.add(new Solution("Solution8"));
solutions.add(new Solution("Solution9"));
return Solutions;
}
at this point with pre-populated List<Soultion> it works well and as expected but when I want to remove populateSolutionList() and add Solution to through click as
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mCount +=1;
Solution solution = new Solution("Solution " + mCount);
mSolutionList.add(solution);
mAdapter.notifyDataSetChanged();
Log.e("MAIN_ACTIVITY", "Click return..." + mCount);
}
});
in adapter I've changed
#Override
public int getItemCount() {
try {
if (!mSolutionList.isEmpty()){
int i = (null != mSolutionList) ? mSolutionList.size() : 0 / 0;
return (i);
}else {
return 1;
}
}catch (Exception e){
return 0;
}
}
when I click first solution row is added but not shown in the RecyclerView all others are shown, I have figured out it is issue with adapter getItemCount() any one suggest me how can I solve this
You have to account for the first row in the list (Issue) and any solution you add will be added from the second row. Your item count should always be +1.
Therefore, getItemCount() should return mSolutionList.size() + 1