I have a problem with RecyclerView.
Every time I try to manage a Rec View for insert or delete or update some element I have to fight with Recycler View and the evil notifyDataSetChange.
I tried with runOnUiThread and everytime this solution is useless.
I tried to call notifyDataSetChange in the Adapter or in the Activity that instance the Rec View.
I really need to know how to make this work everytime to be a little better developer.
I really hope to solve this problem definitely.
This is my new try
public class MainActivity extends AppCompatActivity {
private Button nextTurn;
private Button submit;
private EditText name;
private EditText number;
private CounterElementAdapter mAdapter;
private RecyclerView recV;
private List<CounterElement> list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
nextTurn = (Button) findViewById(R.id.button2);
submit = (Button) findViewById(R.id.button);
name = (EditText) findViewById(R.id.editText);
number = (EditText) findViewById(R.id.editText2);
recV = (RecyclerView) findViewById(R.id.recV);
list = new ArrayList<CounterElement>();
mAdapter = new CounterElementAdapter(list);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recV.setLayoutManager(mLayoutManager);
recV.setItemAnimator(new DefaultItemAnimator());
recV.setAdapter(mAdapter);
submit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(name.getText().toString().length() > 0 && number.getText().toString().length() > 0) {
mAdapter.addElement(new CounterElement(name.getText().toString(), Integer.parseInt(number.getText().toString())));
}
}
});
nextTurn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mAdapter.nextTurn();
}
});
}
}
public class CounterElementAdapter extends RecyclerView.Adapter<CounterElementAdapter.MyViewHolder> {
private List<CounterElement> elementList;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView name, counter;
public MyViewHolder(View view) {
super(view);
name = view.findViewById(R.id.name);
counter = view.findViewById(R.id.counter);
}
}
public CounterElementAdapter(List<CounterElement> elementList) {
this.elementList = elementList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.element, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
CounterElement ce = elementList.get(position);
holder.name.setText(ce.getName());
holder.counter.setText(ce.getDuration());
}
#Override
public int getItemCount() {
return elementList.size();
}
public void addElement(CounterElement ce){
elementList.add(ce);
notifyDataSetChanged();
}
public void nextTurn(){
for(CounterElement ce : elementList){
if(ce.getStatus() >= 1)
ce.setStatus(ce.getStatus() - 1);
else {
elementList.remove(elementList.indexOf(ce));
notifyDataSetChanged();
}
}
}
}
Thanks in advance
Your code looks fine. I would recommend you to take a look at this article
Also try to debug your code and check if your elementList updates correctly.
Looked at your code for a while, didn't seem to find anything wrong, Are you using an android emulator for testing or a physical device? Sometimes emulators tend to be buggy.
Try initializing your array list with a few dummy values at first and then try to add or remove and see if that helps
Related
I am designing online quiz App. I have designed PlayQuiz.java file as below:
public class PlayQuiz extends AppCompatActivity {
private RecyclerView recyclerView;
DataBaseHelper database;
private List<DmQuiz> quizList;
private QuizAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_quiz);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
recyclerView=(RecyclerView)findViewById(R.id.recycler_view_quiz_display);
database= new DataBaseHelper(PlayQuiz.this);
quizList= database.fillObjQuesList();
adapter=new QuizAdapter(quizList,getApplicationContext());
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
});
}
}
Now, this is my QuizAdapter.java file
public class QuizAdapter extends RecyclerView.Adapter<QuizAdapter.CustomViewHolder>{
private List<DmQuiz> questionList;
private Context context;
public QuizAdapter(List<DmQuiz> questionList, Context context) {
this.questionList = questionList;
this.context = context;
}
#NonNull
#Override
public CustomViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView= LayoutInflater.from(parent.getContext()).inflate(R.layout.quiz_display_format,parent,false);
return new CustomViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull final CustomViewHolder holder, final int position) {
DmQuiz questionsList=questionList.get(position);
holder.tvquestion.getLayoutParams().width= LinearLayout.LayoutParams.WRAP_CONTENT;
holder.tvquestion.setText(questionsList.getQuestion());
holder.optA.setText(questionsList.getOpta());
holder.optB.setText(questionsList.getOptb());
holder.optC.setText(questionsList.getOptc());
holder.optD.setText(questionsList.getOptd());
holder.optA.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.optA.setBackgroundColor(context.getResources().getColor(R.color.colorButton));
holder.optA.setBackgroundResource(R.drawable.button_border); holder.optB.setBackgroundResource(R.drawable.button_border_unselected); holder.optC.setBackgroundResource(R.drawable.button_border_unselected); holder.optD.setBackgroundResource(R.drawable.button_border_unselected);
}
});
holder.optB.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.optA.setBackgroundResource(R.drawable.button_border_unselected);
holder.optB.setBackgroundResource(R.drawable.button_border);
holder.optC.setBackgroundResource(R.drawable.button_border_unselected);
holder.optD.setBackgroundResource(R.drawable.button_border_unselected);
}
});
holder.optC.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.optA.setBackgroundResource(R.drawable.button_border_unselected);
holder.optB.setBackgroundResource(R.drawable.button_border_unselected);
holder.optC.setBackgroundResource(R.drawable.button_border);
holder.optD.setBackgroundResource(R.drawable.button_border_unselected);
}
});
holder.optD.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.optA.setBackgroundResource(R.drawable.button_border_unselected);
holder.optB.setBackgroundResource(R.drawable.button_border_unselected);
holder.optC.setBackgroundResource(R.drawable.button_border_unselected);
holder.optD.setBackgroundResource(R.drawable.button_border);
}
});
holder.tvClear.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.optA.setBackgroundResource(R.drawable.button_border_unselected);
holder.optB.setBackgroundResource(R.drawable.button_border_unselected);
holder.optC.setBackgroundResource(R.drawable.button_border_unselected);
holder.optD.setBackgroundResource(R.drawable.button_border_unselected);
}
});
}
#Override
public int getItemCount() {
return questionList.size();
}
public class CustomViewHolder extends RecyclerView.ViewHolder{
TextView tvquestion, tvClear;
Button optA,optB,optC,optD;
public CustomViewHolder(View itemView) {
super(itemView);
tvquestion=(TextView)itemView.findViewById(R.id.tvQuestion);
optA=(Button)itemView.findViewById(R.id.button1);
optB=(Button)itemView.findViewById(R.id.button2);
optC=(Button)itemView.findViewById(R.id.button3);
optD=(Button)itemView.findViewById(R.id.button4);
tvClear=(TextView)itemView.findViewById(R.id.tvClear);
}
}
public QuizAdapter(List<DmQuiz> questionList)
{
this.questionList=questionList;
}
public void setSearchOperation(List<DmQuiz> newList){
questionList= new ArrayList<>();
questionList.addAll(newList);
notifyDataSetChanged();
}
}
Data in the Recyclerview is being displayed from SQLite database.
Now I want to handle/store the clicks on multiple buttons and send it to the remote MySql server. I am confused , how to store response for different questions ? Should I use ArrayList or something else, please help......
It would be better if you created a question with generic list of options, so quiz model would be like:
public class DmQuiz {
int id;
String question;
String answer;
int selectedOptionId;
List<Options> options;
}
and option model would be like:
public class Option {
int id;
String option;
boolean isSelected;
}
After that you can create another recycler view inside each question item and populate the options, and each time the user click on one of the option, you can delegate those clicks back to the activity to handle the question with the new answer.
In your DmQuiz model add a questionId and selectedOption fields which helps you to identify the proper answer to the question, then in onBindViewHolder() set the radio button against the selected option and make others unchecked. Also, add listeners to radio buttons which sets the selectedOption to questionId. Whenever you want to upload the quiz to the server, you can make a separate list by traversing the list as per the remote server requirements.
Refer DmQuiz model below..
class DmQuiz {
//unique id per question
int id;
String question;
String answer;
String opta;
String optb;
String optc;
String optd;
}
In this, you can use id as a unique question id and answer as a selection i.e. "a"/"b"/"c"/"d" etc. , so after you just need to traverse through this List<DmQuiz> and create a HashMap<String, String> (or equavalent to maintain a (key=value) pairs ) just like ("id" => "answer").
I am new to android ,Here i trying to bind ListView by using RecyclerView and Cardview .In my case I didn't get any errors but I could not get the ListView items.
This is my adapter class
RecyclerviewAdapter_list.java
public class RecyclerviewAdapter_list extends RecyclerView.Adapter<RecyclerviewAdapter_list.MyViewHolder> {
private Context mContext;
private List<myAlarms> myAlarmsdata;
public RecyclerviewAdapter_list(Context mContext, List<myAlarms>myAlarms){
this.mContext = mContext;
this.myAlarmsdata =myAlarms;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
view = inflater.inflate(R.layout.cardview_alarm, parent, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
myAlarms myAlarms =myAlarmsdata.get(position);
holder.mTime.setText(myAlarmsdata.get(position).getALARM_TIME());
holder.mTitle.setText(myAlarmsdata.get(position).getALARM_TITLE());
}
#Override
public int getItemCount() {
return myAlarmsdata.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView mTime,mTitle,mAmPm;
SwitchButton mbtn;
public MyViewHolder(View itemView) {
super(itemView);
mTime = (TextView) itemView.findViewById(R.id.mTime);
// mAmPm = (TextView) itemView.findViewById(R.id.mAmPm);
mTitle = (TextView) itemView.findViewById(R.id.mTitle);
// mbtn = (SwitchButton) itemView.findViewById(R.id.mTime);
}
}
}
AlarmActivity.java
public class AlarmActivity extends Activity {
private ImageView mAddIcon;
List<myAlarms>myAlarms;
RecyclerView recyclerView;
Database_Helper database_helper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alarm);
Utils.darkenStatusBar(this, R.color.StatusbarColor);
database_helper = new Database_Helper(this);
mAddIcon = (ImageView) findViewById(R.id.mAddIcon);
myAlarms = new ArrayList<>();
recyclerView=(RecyclerView)findViewById(R.id.mRecyclerView_id);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
RecyclerviewAdapter_list recyclerviewAdapter_list =new RecyclerviewAdapter_list(this,myAlarms);
recyclerView.setAdapter(recyclerviewAdapter_list);
mAddIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
List<myAlarms> myAlarms = database_helper.getMyAlarms();
Intent intent = new Intent(AlarmActivity.this, CreateAlarm.class);
startActivity(intent);
overridePendingTransition(R.anim.slide_up, R.anim.slide_out);
}
});
}
#Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
}
Can anyone tell me where I need to do the changes and where I did the mistakes .
Thanks.
You put this code in your project
//when you want horizontal
layoutManager.setOrientation(context,LinearLayoutManager.HORIZONTAL,false);
//when you want vertical
layoutManager.setOrientation(context,LinearLayoutManager.VERTICAL,false);
I add the same problem before, a solution is to remove your CardView and just add this line to a Layout:
android:background="#android:drawable/dialog_holo_light_frame"
This drawable has exactly the same design than a CardView. Besides, you don't need any implementation to use it so you can remove your CardView implementation from Gradle.
You can also check this : How to use RecyclerView and CardView
I am making a mobile application using React Native and included list components didn't have high enough performance for it so I started using Android's RecyclerView as the list component. There is a problem though with it. The RecyclerView doesn't update its contents views until I scroll or change RecyclerView's size. What could cause this problem and how I can fix it? I have tried notifyDatasetChanged, notifyItemChanged, forceLayout, invalidate, postInvalidate and many different variations with each.
Try this one this.setIsRecyclable(true);
It will referesh your views
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private ArrayList<String> mSingleItemLists = new ArrayList<>();
private SingleListItemAdapter mSingleListItemAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view_single_item);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(linearLayoutManager);
setDummyData();
}
private void setDummyData() {
for (int i = 0; i <= 30; i++)
mSingleItemLists.add("item" + i);
}
#Override
protected void onResume() {
super.onResume();
mSingleListItemAdapter = new SingleListItemAdapter(mSingleItemLists);
mRecyclerView.setAdapter(mSingleListItemAdapter);
}
class SingleListItemAdapter extends RecyclerView.Adapter<SingleListItemAdapter.SingleListItemHolder> {
private ArrayList<String> mSingleItemLists;
private SingleListItemAdapter(ArrayList<String> singleItemLists) {
mSingleItemLists = singleItemLists;
//You can do notifydatasetchange if u r having any saved value
}
#Override
public SingleListItemAdapter.SingleListItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View inflatedView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_recyclerview, parent, false);
return new SingleListItemHolder(inflatedView);
}
#Override
public void onBindViewHolder(SingleListItemAdapter.SingleListItemHolder holder, int position) {
holder.mItemDate.setText(mSingleItemLists.get(position));
}
#Override
public int getItemCount() {
return mSingleItemLists.size();
}
class SingleListItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView mItemDate;
SingleListItemHolder(View v) {
super(v);
mItemDate = (TextView) v.findViewById(R.id.textview_recycler_list_item);
v.setOnClickListener(this);
this.setIsRecyclable(true); // This will help u
}
#Override
public void onClick(View v) {
//do your stuff
notifyDataSetChanged();
}
}
}
}
I'm doing a RecyclerView example. The thing is that when I add the first item on the list it shows the item correctly on the recyclerview but the next items I add aren't showing on the recycle view.
Please help me.
Adapter code is:
public class PersonAdapter extends RecyclerView.Adapter<PersonAdapter.PersonViewHolder> {
private ArrayList<Person> mData;
LayoutInflater inflater;
public PersonAdapter() {
}
public PersonAdapter(Context context, ArrayList<Person> data) {
inflater = LayoutInflater.from(context);
update(data);
}
public void update(ArrayList<Person> data) {
mData = data;
notifyDataSetChanged();
}
#Override
public PersonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.custom_row, parent, false);
PersonViewHolder holder = new PersonViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(PersonViewHolder holder, int position) {
Person person = mData.get(position);
holder.ivImagen.setImageResource(R.drawable.ic_action_android);
holder.tvNombre.setText(person.getName());
holder.tvApellido.setText(person.getLast_name());
}
#Override
public int getItemCount() {
return mData.size();
}
public static class PersonViewHolder extends RecyclerView.ViewHolder {
ImageView ivImagen;
TextView tvNombre;
TextView tvApellido;
public PersonViewHolder(View itemView) {
super(itemView);
ivImagen = (ImageView) itemView.findViewById(R.id.cr_iv_imagen);
tvNombre = (TextView) itemView.findViewById(R.id.cr_tv_name);
tvApellido = (TextView) itemView.findViewById(R.id.cr_tv_last_name);
}
}}
Main Activity Code is:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
ArrayList<Person> persons = new ArrayList<>();
EditText et_name;
EditText et_last_name;
RecyclerView rv_names;
PersonAdapter adapter;
Button btAgregar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_name = (EditText) findViewById(R.id.tv_name);
et_last_name = (EditText) findViewById(R.id.tv_last_name);
btAgregar = (Button) findViewById(R.id.bt_agregar);
rv_names = (RecyclerView) findViewById(R.id.rv_nombres);
LinearLayoutManager manager = new LinearLayoutManager(this);
rv_names.setLayoutManager(manager);
adapter = new PersonAdapter(this, persons);
rv_names.setAdapter(adapter);
btAgregar.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Person person = new Person(et_name.getText().toString(), et_last_name.getText().toString());
persons.add(person);
Toast.makeText(this, "Items: " + String.valueOf(persons.size()), Toast.LENGTH_SHORT).show();
} }
Try this code for onClick function
#Override
public void onClick(View v) {
Person person = new Person(et_name.getText().toString(), et_last_name.getText().toString());
persons.add(person);
adapter.update(persons);
Toast.makeText(this, "Items: " + String.valueOf(persons.size()), Toast.LENGTH_SHORT).show();
}
UPDATE:
In Adapter code, try the following changes..
public void update(ArrayList<Person> data) {
mData.clear();
mData.addAll(data)
notifyDataSetChanged();
}
In the onClick you are adding a person, but you are not passing the updated list to the adapter by calling the adapter's update method. Might this be the reason?
i just added called adapter.notifyDataSetChanged();
I just started using RecyclerViews but i cant completely understand how to add or remove items from it. Below i will attach my adapter code it is a test code and everything in the layout works fine. I feel like im also writing too much unnecessary code so any tips or criticism is appreciated.
public class PlatesAdapter extends
RecyclerView.Adapter<PlatesAdapter.ViewHolder> {
//Declaring a List<> of Plates
private List<Plates> mPlates;
int amountOfPlates;
public static class ViewHolder extends RecyclerView.ViewHolder {
//Declaring Buttons and textViews
public TextView plateWeightTextView, amountOfPlatesTextView;
public Button addButton, subButton, addLayoutButton;
public ViewHolder(View itemView) {
super(itemView);
//initializing Buttons and TextViews
plateWeightTextView = (TextView) itemView.findViewById(R.id.plate_weight_value_textView);
amountOfPlatesTextView = (TextView) itemView.findViewById(R.id.amount_of_plates_textView);
addButton = (Button) itemView.findViewById(R.id.add_button);
subButton = (Button) itemView.findViewById(R.id.subtract_button);
addLayoutButton = (Button) itemView.findViewById(R.id.button);
}
}
//Constructor
public PlatesAdapter(List<Plates> plates) {
mPlates = plates;
}
#Override
public PlatesAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View PlatesView = inflater.inflate(R.layout.plate_item_layout, parent, false);
ViewHolder viewHolder = new ViewHolder(PlatesView);
return viewHolder;
}
#Override
public void onBindViewHolder(PlatesAdapter.ViewHolder holder, int position) {
final TextView textView2 = holder.amountOfPlatesTextView;
//BUTTONS add 1 or subtract 1 from amountOfPlates;
Button button = holder.addButton;
Button button2 = holder.subButton;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
amountOfPlates++;
textView2.setText(Integer.toString(amountOfPlates));
}
});
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
amountOfPlates--;
textView2.setText(Integer.toString(amountOfPlates));
}
});
}
#Override
public int getItemCount() {
return mPlates.size();
}
Here is my Model Layer which i feel is completely wrong but im not 100% sure if it is.
public class Plates {
private int mPlateWeight;
private int mAmountOfPlates;
public Plates() {
//mPlateWeight = plateWeight;
//mAmountOfPlates = amountOfPlates;
}
public int getmPlateWeight() {
return mPlateWeight;
}
public int getmAmountOfPlates() {
return mAmountOfPlates;
}
public static List<Plates> createPlateList() {
List<Plates> plates = new ArrayList<>();
plates.add(new Plates());
return plates;
}
}
This is where im comfused. Its were do i call the addPlates or addItem method and what do i pass to it? Below is my main activity. I Just dont know where to add these addItems or addPlates methods is it to the Model Layer or the Adapter?
public class MainActivity extends AppCompatActivity {
private RecyclerView.LayoutManager mLayoutManager;
Button mButton;
private List<Plates> mData = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Button to add layout to recyclerView
mButton = (Button) findViewById(R.id.button);
//Adapter LayoutManager
mLayoutManager = new LinearLayoutManager(this);
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.weights_recycler_view);
PlatesAdapter adapter = new PlatesAdapter(mData);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(mLayoutManager);
}
});
}
}
What I usually do with RecyclerViews is to add a method to set the data.
In your example :
public void setPlates(List<Plates> plates) {
mPlates = plates;
notifyDataSetChanged();
}`
You can also add a getter if you want to verify if the data have changed or not.
You can add a method in your adapter to add a Plates in the arrayList and to notify the change.
Something like:
public void addPlates(Plates plate) {
if (mPlates == null) mPlates = new ArrayList();
mPlates.add(plate);
//notifyDataSetChanged();
notifyItemInserted(mPlates.size()-1)
}`
First of all the createPlateList method is not needed.
You should add a method in your adapter that looks like this:
public void addItem(Plates plate)
{
mPlates.add(plate);
}
Since your adapter works with this list, all you need to do to add or remove items, is to add/remove the items from your list. After all you need to call notifyDataSetChanged() in your adapter so it knows data was changed in your list.