I am trying to delete multiple items from recyclerView. The items in the recyclerView has an attribute isSelected. I am setting the attribute isSelected to true when user clicks on the recyclerView item. After selection user can click delete button present in the options menu to delete the selected items. The code has unexpected behaviour, like when delete button is pressed, some of the selected items are deleted while some are not. Also some of the items get automatically selected at random positions.
Model.java
public class Model {
private String text;
private boolean isSelected = false;
public Model(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
public boolean isSelected() {
return isSelected;
}
}
RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {
private List<Model> mModelList;
private Context mCtx;
public RecyclerViewAdapter(Context ctx, List<Model> modelList) {
this.mModelList = modelList;
this.mCtx = ctx;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mCtx);
View view = inflater.inflate(R.layout.item_row, parent, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
final Model model = mModelList.get(position);
holder.view.setBackgroundColor(model.isSelected() ? Color.CYAN : Color.WHITE);
holder.tvItems.setText(model.getText());
holder.tvItems.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
model.setSelected(!model.isSelected());
holder.tvItems.setBackgroundColor(model.isSelected() ? Color.CYAN : Color.WHITE);
notifyItemChanged(position);
}
});
}
#Override
public int getItemCount() {
return mModelList == null ? 0 : mModelList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private TextView tvItems;
private View view;
public MyViewHolder(View itemView) {
super(itemView);
view = itemView;
tvItems = itemView.findViewById(R.id.tvItems);
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private List<Model> mModelList;
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.rvListItems);
mAdapter = new RecyclerViewAdapter(this, getListData());
LinearLayoutManager manager = new LinearLayoutManager(MainActivity.this);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(manager);
mRecyclerView.setAdapter(mAdapter);
}
private List<Model> getListData() {
mModelList = new ArrayList<>();
for (int i = 1; i <= 25; i++) {
mModelList.add(new Model("TextView " + i));
}
return mModelList;
}
private void deleteSelectedItems() {
if (mModelList != null){
for (int i=0; i<mModelList.size(); i++){
if (mModelList.get(i).isSelected()){
Log.d("testingTAG", String.valueOf(i));
mModelList.remove(i);
mAdapter.notifyItemRemoved(i);
mAdapter.notifyItemRangeChanged(i, mModelList.size());
i--;
}
}
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.action_delete:
deleteSelectedItems();
break;
}
return super.onOptionsItemSelected(item);
}
}
Any kind of help will be greatly appreciated.
You should use holder.getAdapterPosition() inside your clicklistener .Change your onclick as given below and try.
public void onClick(View view) {
final Model model = mModelList.get(holder.getAdapterPosition());
model.setSelected(!model.isSelected());
holder.tvItems.setBackgroundColor(model.isSelected() ? Color.CYAN : Color.WHITE);
notifyItemChanged(holder.getAdapterPosition());
}
Answer from #pop selects items, change background color to CYAN if item is selected and inverse selected flag of Model class instance.
you should delete selected items from your mModelList using a iterator, you can't delete items from list inside for loop
Calling remove in foreach loop in Java
Related
I have a RecyclerView.Adapter which has some Arrays there.
ArrayList with Strings and ArrayList with Integer. Strings are like url and Integer is the photo.
When the app is open for first time the first item is selected.
I have another method for click which makes another item as selected and this works, but the problem is that the first item stays as selected and so for every image click makes as selected, I want only one item to be selected and take a color.
This is my code.
Adapter of RecyclerView
public class ListViewAdapter extends RecyclerView.Adapter<ListViewAdapter.ViewHolder>{
private int selectedItem;
private ArrayList<Integer> mImages = new ArrayList<>();
private ArrayList<String> mSearchUrl = new ArrayList<>();
private Context mContext;
public ListViewAdapter(ArrayList<Integer> images, ArrayList<String> SearchUrl, Context context) {
mImages = images;
mContext = context;
mSearchUrl = SearchUrl;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.s_engine_item, viewGroup, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder viewHolder, final int i) {
selectedItem = 0;
if (selectedItem == i) {
viewHolder.image.setBackgroundColor(Color.parseColor("#30000000"));
}
Glide.with(mContext).load(mImages.get(i))
.into(viewHolder.image);
viewHolder.searchUrl.setText(mSearchUrl.get(i));
viewHolder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
viewHolder.image.setBackgroundColor(Color.parseColor("#30000000"));
selectedItem = i;
}
});
}
#Override
public int getItemCount() {
return mImages.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView image;
TextView searchUrl;
public ViewHolder(#NonNull View itemView) {
super(itemView);
image = itemView.findViewById(R.id.ivEngine);
searchUrl = itemView.findViewById(R.id.ivEngineText);
}
}
}
And this is the MainActivity.class
public void intSearch() {
mImages.add(R.drawable.s_bing);
mSearchUrl.add("https://www.bing.com/search?q=");
mImages.add(R.drawable.s_google);
mSearchUrl.add("https://www.google.com/search?q=");
mImages.add(R.drawable.s_yahoo);
mSearchUrl.add("www.yahoo.com");
mImages.add(R.drawable.amazon_white256);
mSearchUrl.add("www.amazon.com");
mImages.add(R.drawable.amazon_white256);
mSearchUrl.add("www.amazon.com");
mImages.add(R.drawable.amazon_white256);
mSearchUrl.add("www.amazon.com");
initRecyclerView();
}
private void initRecyclerView() {
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
RecyclerView recyclerView = findViewById(R.id.lvEngines);
recyclerView.setLayoutManager(layoutManager);
ListViewAdapter adapter = new ListViewAdapter(mImages, mSearchUrl, this);
recyclerView.setAdapter(adapter);
}
Initialize your selected item globally
public class ListViewAdapter extends RecyclerView.Adapter<ListViewAdapter.ViewHolder>{
private int selectedItem = 0;
.....
Then inside your onBindViewHolder whenever you click a new Image notify your adapter for changes in the last selected item cell.
viewHolder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int previousSelectedItem = selectedItem;
selectedItem = i;
notifyItemChanged(previousSelectedItem);
viewHolder.image.setBackgroundColor(Color.parseColor("#30000000"));
}
});
Just remove this line from onBindViewHolder
selectedItem = 0;
and add an else to the background condition, like:
if (selectedItem == i) {
viewHolder.image.setBackgroundColor(Color.parseColor("#30000000"));
}else{
viewHolder.image.setBackgroundColor(“YOUR_DEFAULT_COLOR”);
}
and update the onClick:
viewHolder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
selectedItem = i;
notifyDataSetChanged();
}
});
This is basically a notes app,in which we dynamically add data to our app by taking in title and description from the user,the problem is that when we search about some note by its title then instead of giving the possible notes the data set in the adapter vanishes,the logic is written in my filter() function in the adapter class
MainActivity.java
public class MainActivity extends AppCompatActivity implements SearchView.OnQueryTextListener {
private ArrayList<Notes> list;
private NotesAdapter notesAdapter;//this is our notes adapter
private RecyclerView recyclerView;
private LinearLayoutManager linearLayoutManager;
#Override //
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
list=new ArrayList<>();//this is the list which we have to pass into adapter
recyclerView=findViewById(R.id.rv);
notesAdapter=new NotesAdapter(this,list);
View dialogView= LayoutInflater.from(this).inflate(R.layout.dialog_main,null,false);
final EditText title=dialogView.findViewById(R.id.t);
final EditText description=dialogView.findViewById(R.id.d);
final AlertDialog alertDialog=new AlertDialog.Builder(this)
.setTitle("Enter the details:")
.setCancelable(false)
.setView(dialogView)
.setPositiveButton("add", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
list.add(new Notes(title.getText().toString(),description.getText().toString(),false));
notesAdapter.notifyItemChanged(list.size());
}
})
.setNegativeButton("no", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
}).create();
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override // on pressing the fab we will get an alert dialog box where we can add title and description and with the option to add it or not
public void onClick(View view) {
alertDialog.show();
}
});
recyclerView.setAdapter(notesAdapter);
linearLayoutManager=new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
recyclerView.setLayoutManager(linearLayoutManager);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
final MenuItem searchItem = menu.findItem(R.id.search);
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
searchView.setOnQueryTextListener(this);
return true;
}``
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.search) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onQueryTextSubmit(String s) {
notesAdapter.filter(s);
return true;
}
#Override
public boolean onQueryTextChange(String s) {
notesAdapter.filter(s);
return true;
}
NotesAdapter.java
public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NotesHolder> {
private ArrayList<Notes> arrayList;
private ArrayList<Notes> arrayListCopy;
Context c;
NotesAdapter(Context context,ArrayList<Notes> list){
this.arrayList=list;
this.c=context;
this.arrayListCopy=new ArrayList<>(list);//this is where I store identical list which I get from Adapter
}
public class NotesHolder extends RecyclerView.ViewHolder {
TextView textView;
public NotesHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tv);
}
}
#Override
public NotesAdapter.NotesHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new NotesHolder(LayoutInflater.from(c).inflate(R.layout.item_row,parent,false));
}
#Override
public void onBindViewHolder(final NotesAdapter.NotesHolder holder, final int position) {
final Notes currentNote=arrayList.get(position);
holder.textView.setText(currentNote.getTitle());
holder.textView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
arrayList.remove(holder.getAdapterPosition());
notifyItemRemoved(holder.getAdapterPosition());
return true;
}
});
holder.textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent=new Intent(c,AnotherActivity.class);
intent.putExtra("NAME",currentNote.getDescription());
c.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return arrayList.size();
}
public void filter(String text){//This is my filter function
arrayList.clear();
if(TextUtils.isEmpty(text)){
arrayList.addAll(arrayListCopy);
}
else{
text=text.toLowerCase();
for(Notes note:arrayListCopy){
if(note.getTitle().toLowerCase().contains(text)){
arrayList.add(note);
}
}
}
notifyDataSetChanged();//the data set is still not updated and instead it vanishes
}
}
As soon as I search something the whole list gets vanished ,where am I missing out on?How should I modify my filter function in adapter class?
Change your getItemCount :
#Override
public int getItemCount() {
return arrayListCopy.size();
}
Try this:
NotesAdapter(Context context,ArrayList<Notes> list){
this.arrayList = new ArrayList<>(list.size());
this.c=context;
this.arrayListCopy=new ArrayList<>(list);//this is where I store identical list which I get from Adapter
}
#Override
public int getItemCount() {
return arrayList.size();
}
public void filter(String text){//This is my filter function
arrayList.clear();
if(text.trim().isEmpty() || text == null){
arrayList.addAll(arrayListCopy);
}
else{
text=text.toLowerCase();
for(Notes note:arrayListCopy){
if(note.getTitle().toLowerCase().contains(text)){
arrayList.add(note);
}
}
}
notifyDataSetChanged();//the data set is still not updated and instead it vanishes
}
Original Answer:
Here you are referencing the same variable (i.e. arrayList and arrayListCopy ) are pointing to the same variable. Instead, initialize arrayList like this inside your constructor:
this.arrayList = new ArrayList<>(arrayListCopy.size());
The problem is that arrayList and arrayListCopy are references to the same list.
NotesAdapter(Context context,ArrayList<Notes> list){
this.arrayList=list;
this.c=context;
this.arrayListCopy=list;
}
Changes done to one of them, will be reflected in both. For example arrayList.clear() will also empty the arrayListCopy list.
What you could do is something like this:
List<Notes> originalList;
NotesAdapter(Context context,ArrayList<Notes> list){
this.arrayList=list;
this.c=context;
this.originalList= new ArrayList<>(list); // create a new List that contains all the elements of `list`.
}
And to filter, do this:
public void filter(String text){
arrayList.clear();
if(text.isEmpty()){
arrayList.addAll(originalList);
} else{
text=text.toLowerCase();
for(Notes note : originalList){
if(note.getTitle().toLowerCase().contains(text)){
arrayList.add(note);
}
}
}
notifyDataSetChanged();
}
It seems that you're adding items to the list inside the MainActivity after you create the adapter which means that you'll have to use some other mechanism for adding new items so that originalList will contain all the items. Something like:
public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NotesHolder> {
public void addItem(Notes item){
originalList.add(item);
if(text.isEmpty() || item.getTitle().toLowerCase().contains(text.toLowerCase())){
arrayList.add(item);
}
}
}
Here, text is just a reference to the text variable passed to the filter method.
From MainActivity when a new item is created, do this:
adapter.addItem(new Notes(title.getText().toString(),description.getText().toString(),false));
In MainActivity I have ListView with custom single choice Checkboxes each item has one checkbox and they are set to List by Custom Adapter class. If user checked one then pop up dialog with choice confirmation and after accept all checkboxes should be disabled but for now only one from selected row is disabled. How can I disable all and block user possibility to change choice ?
Adapter
public class CustomAdapter extends BaseAdapter implements CompoundButton.OnCheckedChangeListener {
SparseBooleanArray mCheckStates;
private List<CustomClass> customList;
private Context context;
private LayoutInflater inflater;
private int selectedPosition = -1;
private Dialog dialog;
public CustomAdapter(Context _context, List<CustomClass> _customList) {
inflater = LayoutInflater.from(_context);
this.customList = _CustomList;
this.context = _context;
mCheckStates = new SparseBooleanArray(customList.size());
}
#Override
public int getCount() {
return customList.size();
}
#Override
public Object getItem(int i) {
return i;
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
CustomClass customClass = customList.get(i);
if (view == null)
view = inflater.inflate(R.layout.custom_list, null);
TextView name = (TextView) view.findViewById(R.id.tv_name);
CheckBox checkBox = (CheckBox) view.findViewById(R.id.checkbox);
checkBox.setTag(i);
checkBox.setChecked(mCheckStates.get(i, false));
checkBox.setOnCheckedChangeListener(this);
checkBox.setChecked(false);
if (i == selectedPosition) {
checkBox.setChecked(true);
} else {
checkBox.setChecked(false);
}
checkBox.setOnClickListener(onStateChangedListener(checkBox, i));
name.setText(customClass.getName());
return view;
}
private View.OnClickListener onStateChangedListener(final CheckBox checkBox, final int position) {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
if (checkBox.isChecked()) {
selectedPosition = position;
dialog = new Dialog(context);
dialog.setTitle("Accept");
dialog.setContentView(R.layout.dialog_accept);
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialogInterface) {
}
});
dialog.show();
Button btnAccept = (Button) dialog.findViewById(R.id.btnVote);
btnAccept.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
checkBox.setEnabled(false);
dialog.dismiss();
}
});
} else {
selectedPosition = -1;
}
notifyDataSetChanged();
}
};
}
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mCheckStates.put((Integer) buttonView.getTag(), isChecked);
notifyDataSetChanged();
}
CustomClass:
public class CustomClass{
String name;
public CustomClass(){}
public CustomClass(String name){
this.name = name;
}
public String getName(){return name;}
public void setName(String name){this.name = name;}
}
MainActivity have only set ListView and Adapter
Include all the check boxes inside a group so that only one check box can be selected .
ListView list = (ListView) findViewById("R.id.list");
for(int x = 0; x < list.getItems.length(); x++){ list.items.get(x).setEnabled(false);}
Not sure but maybe something like this. Worth a try?
In Checkbox change listener
private int selectedPosition = 0;
private View.OnClickListener onStateChangedListener(final CheckBox checkBox, final int position) {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
if (checkBox.isChecked()) {
customClass.setSelected(true);
customList.get(selectedPosition).setSelected(false);
selectedPosition = position;
}
notifyDataSetChanged();
}
};
in Getview()
if(customClass.getSelected()){
checkBox.setEnable(true);
}else{
checkBox.setEnable(false);
}
I am going through a tutorial which is using the Recycler View to display a list of weather for each day for a week.
There are two classes which I am confused in:
ForecastAdapter and MainActivity
Here is the code for the above two classes:
ForecastAdapter.java
public class ForecastAdapter extends RecyclerView.Adapter<ForecastAdapter.ForecastAdapterViewHolder> {
private String[] mWeatherData;
final private ForecastAdapterOnClickListener mClickHandler;
//Why do we need to create an interface here.
public interface ForecastAdapterOnClickListener {
void onClick(String weatherForDay);
}
public ForecastAdapter(ForecastAdapterOnClickListener forecastAdapterOnClickListener) {
mClickHandler = forecastAdapterOnClickListener;
}
public class ForecastAdapterViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public final TextView mWeatherTextView;
public ForecastAdapterViewHolder(View view) {
super(view);
mWeatherTextView = (TextView) view.findViewById(R.id.tv_weather_data);
view.setOnClickListener(this);
}
#Override
public void onClick(View v) {
int adapterPosition = getAdapterPosition();
String weatherForDay = mWeatherData[adapterPosition];
//Why are we calling onClick from mClickHandler here. Why can't we just display Toast here.
mClickHandler.onClick(weatherForDay);
/*Why can't we just display the Toast from here like this:
Toast.makeText(v.getContext(), weatherForDay, Toast.LENGTH_SHORT).show()
*/
}
}
#Override
public ForecastAdapterViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Context context = viewGroup.getContext();
int layoutIdForListItem = R.layout.forecast_list_item;
LayoutInflater inflater = LayoutInflater.from(context);
boolean shouldAttachToParentImmediately = false;
View view = inflater.inflate(layoutIdForListItem, viewGroup, shouldAttachToParentImmediately);
return new ForecastAdapterViewHolder(view);
}
#Override
public void onBindViewHolder(ForecastAdapterViewHolder forecastAdapterViewHolder, int position) {
String weatherForThisDay = mWeatherData[position];
forecastAdapterViewHolder.mWeatherTextView.setText(weatherForThisDay);
}
#Override
public int getItemCount() {
if (null == mWeatherData) return 0;
return mWeatherData.length;
}
public void setWeatherData(String[] weatherData) {
mWeatherData = weatherData;
notifyDataSetChanged();
}
}
MainActivity.java
//Why are implementing ForecastAdapterOnClickListener here?
public class MainActivity extends AppCompatActivity implements ForecastAdapter.ForecastAdapterOnClickListener{
private RecyclerView mRecyclerView;
private ForecastAdapter mForecastAdapter;
private TextView mErrorMessageDisplay;
private ProgressBar mLoadingIndicator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_forecast);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview_forecast);
mErrorMessageDisplay = (TextView) findViewById(R.id.tv_error_message_display);
LinearLayoutManager layoutManager
= new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);
mForecastAdapter = new ForecastAdapter(this);
mRecyclerView.setAdapter(mForecastAdapter);
mLoadingIndicator = (ProgressBar) findViewById(R.id.pb_loading_indicator);
loadWeatherData();
}
private void loadWeatherData() {
showWeatherDataView();
String location = SunshinePreferences.getPreferredWeatherLocation(this);
new FetchWeatherTask().execute(location);
}
#Override
public void onClick(String weatherForDay) {
Context context = this;
Toast.makeText(context, weatherForDay, Toast.LENGTH_SHORT)
.show();
}
private void showWeatherDataView() {
mErrorMessageDisplay.setVisibility(View.INVISIBLE);
mRecyclerView.setVisibility(View.VISIBLE);
}
private void showErrorMessage() {
mRecyclerView.setVisibility(View.INVISIBLE);
mErrorMessageDisplay.setVisibility(View.VISIBLE);
}
public class FetchWeatherTask extends AsyncTask<String, Void, String[]> {
#Override
protected void onPreExecute() {
super.onPreExecute();
mLoadingIndicator.setVisibility(View.VISIBLE);
}
#Override
protected String[] doInBackground(String... params) {
if (params.length == 0) {
return null;
}
String location = params[0];
URL weatherRequestUrl = NetworkUtils.buildUrl(location);
try {
String jsonWeatherResponse = NetworkUtils
.getResponseFromHttpUrl(weatherRequestUrl);
String[] simpleJsonWeatherData = OpenWeatherJsonUtils
.getSimpleWeatherStringsFromJson(MainActivity.this, jsonWeatherResponse);
return simpleJsonWeatherData;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(String[] weatherData) {
mLoadingIndicator.setVisibility(View.INVISIBLE);
if (weatherData != null) {
showWeatherDataView();
mForecastAdapter.setWeatherData(weatherData);
} else {
showErrorMessage();
}
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.forecast, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_refresh) {
mForecastAdapter.setWeatherData(null);
loadWeatherData();
return true;
}
return super.onOptionsItemSelected(item);
}
}
The adapter, view holder and recycler view is working as expected. We are now supposed to implement Click Handling on the rows of the recycler view. Whenever a particular row is clicked, we are supposed to display a toast.
As you can see, we are implementing OnClickListener in the ForecastAdapterViewHolder and in the onClick function we are calling the onClick of the interface "ForecastAdapterOnClickListener".
In the MainActivity.java, we are implementing this "ForecastAdapterOnClickListener" and then displaying the toast.
Why can't we just display the toast in the onClick that is defined for the "ForecastAdapterViewHolder" class. I have tried it and it works. What is the point of doing what is being done in the code?
Is there some advantage in setting the click listener like that?
Because you'll have to display information afterwards and isn't role of ViewHolder neither Adapter. Activity/fragment must do that.
It's to keep your code organized.
My Activity is called, so the User should select some Songs, he want to add to a Playlist. How do I make a RecyclerView selectable (the Background-Color should be an Accent Color) and how do I receive the List or Array of the selected Songs from my RecyclerView?
My SelectSongsActivity:
public class SelectSongsActivity extends AppCompatActivity {
private Song[] sSongs;
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private SelectSongRecyclerViewAdapter adapter;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getIntent().getExtras();
sSongs = (Song[]) bundle.get(Constants.IntentExtra.SONGS);
Initialize();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_selectsongs, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.mi_Add) {
Intent i = getIntent();
i.putExtra(Constants.IntentExtra.SONGS, adapter.getSelectedSongs());
setResult(RESULT_OK, i);
finish();
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
setResult(RESULT_CANCELED);
super.onBackPressed();
}
#Override
public boolean onSupportNavigateUp() {
setResult(RESULT_CANCELED);
return super.onSupportNavigateUp();
}
private void Initialize() {
setContentView(R.layout.activity_selectsongs);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.addItemDecoration(new RecyclerViewDivider(this));
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
adapter = new SelectSongRecyclerViewAdapter(sSongs);
recyclerView.setAdapter(adapter);
}
}
And my RecyclerViewAdapter:
public class SelectSongRecyclerViewAdapter extends RecyclerView.Adapter<SelectSongRecyclerViewAdapter.Holder> {
private Song[] sSongs;
private List<Song> selectedSongs;
public SelectSongRecyclerViewAdapter(Song[] songs) {
sSongs = songs;
selectedSongs = new ArrayList<>();
}
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_songview, parent, false);
Holder holder = new Holder(view);
return holder;
}
#Override
public void onBindViewHolder(Holder holder, int position) {
//holder.imvSong.setImageResource(R.drawable.standardartwork);
holder.txvSongTitle.setText(sSongs[position].getTitle());
holder.txvSongInfo.setText(sSongs[position].getArtists());
}
#Override
public int getItemCount() {
return sSongs != null ? sSongs.length : 0;
}
public Song[] getSelectedSongs() {
Song[] songs = new Song[selectedSongs.size()];
return selectedSongs.toArray(songs);
}
public class Holder extends RecyclerView.ViewHolder {
LinearLayout linearLayout;
ImageView imvSong;
TextView txvSongTitle;
TextView txvSongInfo;
public Holder(View layout) {
super(layout);
linearLayout = (LinearLayout) layout;
imvSong = (ImageView) layout.findViewById(R.id.imvSong);
txvSongTitle = (TextView) layout.findViewById(R.id.adap_txvSongtitle);
txvSongInfo = (TextView) layout.findViewById(R.id.txvSongInfo);
}
}
}
Thanks!
You mean when you click on the linearlayout?
What you can do is in the onbindviewholder-
holder.linearlayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
selectedSongs.add(sSongs[position]);
}
});
To display already selected songs in a different color-
if (selectedSongs.contains(sSongs[position])) {
holder.linearlayout.setBackgroundColor(R.color.color_accent);
} else {
//else as viewholders are reused
holder.linearlayout.setBackgroundColor(R.color.default_color);
}