I am using a view pager with three tabs (three fragments). In one of these fragments I have a recycler view. The items in this recycler view get updated every 2 seconds from the web. When I first start up the app, the recycler view runs just fine. Even when navigating to different tabs or navigating out of my app with home button, it all works.
However when I close the app by using the backkey and then go into my app again, the recycler view is not updating anymore. It shows the status that it had when the app quit. I monitor the adapter via the console and it keeps on working with the correct data, only the recycler view doesn't show this. I tried all kinds of stuff to "reconnect" adapter and recycler view but it won't work. I am having this issue for days. Any idea for the cause of this problem? See the relevant code for troubleshooting. Thank you!
public class UserAreaFragment extends Fragment implements PopupMenu.OnMenuItemClickListener {
private RecyclerView mRecyclerview;
private UserAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutmanager;
private Handler mainHandler = new Handler();
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_user_area, container, false);
...
mAdapter = new UserAdapter(getActivity(), UserDataSingleton.getUserList());
mRecyclerview = root.findViewById(R.id.userListing);
mRecyclerview.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerview.setAdapter(mAdapter);
mAdapter.setOnItemClickListener(new UserAdapter.OnItemClickListener() {
#Override
public void onItemClick(int position) {
...
}
});
}
//Somewhere in my method that receives the data from an online DB:
UserDataSingleton.getUserList().clear();
UserDataSingleton.getUserList().addAll(serverResponse);
mainHandler.post(updateUi);
//And finally the updateUi method: this is essential just sorting and then notifydatasetchanged
Runnable updateUi = new Runnable() {
#Override
public void run() {
Collections.sort(UserDataSingleton.getUserList(), new Comparator<UserItem>() {
#Override
public int compare(UserItem lhs, UserItem rhs) {
// -1 - less than, 1 - greater than, 0 - equal, all inversed for descending
return Double.parseDouble(lhs.getmDistance()) > Double.parseDouble(rhs.getmDistance()) ? 1 : Double.parseDouble(lhs.getmDistance()) < Double.parseDouble(rhs.getmDistance()) ? -1 : 0;
}
});
mAdapter.notifyDataSetChanged();
}
};
//and this is my Adapter:
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHolder> {
private UserAdapter.OnItemClickListener mListener;
private Context mContext;
private List<UserItem> mUserlist;
public UserAdapter(Context context,List<UserItem> userList){
mUserlist=userList;
mContext = context;
}
public interface OnItemClickListener{
void onItemClick(int position);
}
public void setOnItemClickListener(UserAdapter.OnItemClickListener listener) {
this.mListener = listener;
}
public static class UserViewHolder extends RecyclerView.ViewHolder{
public TextView mUsername;
public TextView mDistance;
public ImageView userIcon;
public UserViewHolder(#NonNull View itemView, final UserAdapter.OnItemClickListener listener) {
super(itemView);
mUsername = itemView.findViewById(R.id.tvNearUsername);
mDistance = itemView.findViewById(R.id.tvDistance);
userIcon = itemView.findViewById(R.id.usericon);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null){
int position = getAdapterPosition();
if (position!= RecyclerView.NO_POSITION){
listener.onItemClick(position);
}
}
}
});
}
}
#NonNull
#Override
public UserAdapter.UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View v= inflater.inflate(R.layout.user_item, parent, false);
UserViewHolder uvh= new UserViewHolder(v,mListener);
return uvh;
}
#Override
public void onBindViewHolder(#NonNull UserAdapter.UserViewHolder holder, int position) {
UserItem currentitem = mUserlist.get(position);
holder.mUsername.setText(currentitem.getmNearUsername());
if (currentitem.isArea()){
holder.mDistance.setVisibility(View.INVISIBLE);
holder.userIcon.setImageResource(R.drawable.ic_placeicon);
}else{
holder.mDistance.setVisibility(View.VISIBLE);
}
int distToNextTen = ((Integer.parseInt(currentitem.getmDistance())+5)/10)*10+10;
holder.mDistance.setText("< "+distToNextTen+"m");
}
#Override
public int getItemCount() {
return mUserlist.size();
}
}
I tried to only display the lines that affect the issue for better readability. If you need to see more code just let me know. Thankful for this great community!
(I think problem might be in connection with the main Handler not pointing to the correct view or maybe an issue with adapter-recyclerview connection but I can't find a solution tried so many things already)
Ok I found the problem. It had something to do with my runnables and handlers not being declared within onCreate. Somehow that messed it up. I made a major reconstruction of my code to solve it so its hard to tell which line exactly was the problem but if you are facing a similar problem check that your runOnUi and handler declarations happen in the right places.
Related
In my app, I have an activity that uses fragment(s). In one such fragment, I am using a recyclerview.
I just want to add (one or more) onClick(s) whose action I can process in the fragment or in the activity as appropriate.
I searched a lot. I found many solutions, but none that seems to me to really respond to my case, which is however not so exceptional.
I found a lot of solutions with the onClick in OnBindviewHolder. However, I also read very often that this solution was not acceptable because it consumed unnecessary resources.
I found a solution (https://openclassrooms.com/en/courses/4568576-recover-and-display-distant-data/4893791-interact-with-the-recyclerview) using a itemClickSupport class that works, but I don't know how to fully exploit it.
So, I'm going around in circles, and I don't know which track I should go to, without wasting my time unnecessarily realizing at the end that I didn't go in the right direction.
Edit :
Another solution that seems perfect to me (https://openclassrooms.com/fr/courses/4568596-construisez-une-interface-utilisateur-flexible-et-adaptative/4789616-creez-votre-premiere-application-avec-des-fragments), automatically creates it in the fragment by a callback, BUT, I don't know how to get it in the parent activity.
What I really want is :
on smartphone :
an activity that open a fragment 1 with a recyclerview
another activity with another fragment (with another recyclerview) opened by a click on a button in the recyclerview from the fragment 1
on tablet (with another layout) :
an activity that open both fragments, and the button in the fragment/recyclerview 1 populate the fragment/recyclerview 2.
I realize that I have a lot of trouble explaining what I want, and that my problem to find the solution probably comes from there.
I think I finally found a solution that can be useful :
activity:
public class MainActivity extends FragmentActivity implements MainFragment.SampleOnClickedListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public void sampleOnClicked(int position) {
Log.e("TAG", " from activity / position:" + position);
}
}
fragment:
public class MainFragment extends Fragment implements SampleAdapter.SampleRecyclerViewHolder.SampleOnListener {
private SampleOnClickedListener mCallback;
public interface SampleOnClickedListener {
void sampleOnClicked(int position);
}
public MainFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
SampleAdapter sampleAdapter = new SampleAdapter(this);
RecyclerView recyclerView = view.findViewById(R.id.sampleRecyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
recyclerView.setAdapter(sampleAdapter);
return view;
}
#Override
public void sampleOnClick(int position) {
Log.e("TAG", " from fragment / position:" + position);
mCallback.sampleOnClicked(position);
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
this.createCallbackToParentActivity();
}
private void createCallbackToParentActivity() {
try {
mCallback = (SampleOnClickedListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(e.toString() + " must implement OnButtonClickedListener");
}
}
}
Adapter:
public class SampleAdapter extends RecyclerView.Adapter<SampleAdapter.SampleRecyclerViewHolder> {
private final SampleRecyclerViewHolder.SampleOnListener mListener;
public SampleAdapter(SampleRecyclerViewHolder.SampleOnListener listener) {
this.mListener = listener;
}
#NonNull
#Override
public SampleRecyclerViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
return new SampleRecyclerViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull SampleRecyclerViewHolder holder, int position) {
holder.itemView.setOnClickListener(v -> mListener.sampleOnClick(holder.getBindingAdapterPosition()));
}
#Override
public int getItemCount() {
// modify with real size
return 0;
}
public static class SampleRecyclerViewHolder extends RecyclerView.ViewHolder {
public SampleRecyclerViewHolder(#NonNull View itemView) {
super(itemView);
}
public interface SampleOnListener {
void sampleOnClick(int position);
}
}
}
If I'm not mistaken, the result of the click can be retrieved in the activity as well as in the fragment and that's what I needed.
Edit
It seems like my #Override for onMentorClick(Mentor mentor) is never being accessed.
I have a recycler view that shows a list of people.
I need to be able to click on one of the people in the list and have a new activity open.
in my adapter, I have the following code:
private OnMentorClickListener listener;
public interface OnMentorClickListener {
void onMentorClick(Mentor mentor);
}
public void setOnMentorClickListener(OnMentorClickListener listener){
this.listener = listener;
}
in my activity, I have the following code:
mentorAdapter.setOnMentorClickListener(new MentorAdapter.OnMentorClickListener(){
#Override
public void onMentorClick(Mentor mentor) {
Intent intent = new Intent(MentorActivity.this, MentorAddEditActivity.class);
intent.putExtra(MentorAddEditActivity.EXTRA_MENTOR_ID, mentor.getMentor_id());
intent.putExtra(MentorAddEditActivity.EXTRA_MENTOR_NAME, mentor.getMentor_name());
intent.putExtra(MentorAddEditActivity.EXTRA_MENTOR_PHONE, mentor.getMentor_phone());
intent.putExtra(MentorAddEditActivity.EXTRA_MENTOR_EMAIL, mentor.getMentor_email());
startActivityForResult(intent, EDIT_MENTOR_REQUEST);
}
});
I have implemented listeners for other portions of my app the exact same way and they all work.
I've scoured my code and this setup seems to be identical to other sections of code.
I get no errors, but when I'm on my "Mentors" page and have all the mentors listed out, nothing happens when I click on one.
No errors, no crashes, no nothing.
I've also tried to just implement the OnMentorClickListener strait into the Activity class like the following:
public class MentorActivity extends AppCompatActivity implements MentorAdapter.OnMentorClickListener
and overriding outside of OnCreate:
#Override
public void onMentorClick(Mentor mentor) {
Intent intent = new Intent(MentorActivity.this, MentorAddEditActivity.class);
intent.putExtra(MentorAddEditActivity.EXTRA_MENTOR_ID, mentor.getMentor_id());
intent.putExtra(MentorAddEditActivity.EXTRA_MENTOR_NAME, mentor.getMentor_name());
intent.putExtra(MentorAddEditActivity.EXTRA_MENTOR_PHONE, mentor.getMentor_phone());
intent.putExtra(MentorAddEditActivity.EXTRA_MENTOR_EMAIL, mentor.getMentor_email());
startActivityForResult(intent, EDIT_MENTOR_REQUEST);
}
This does not work either.
Any thoughts on my I can't actually click the mentor in the RecyclerView?
Entire MentorAdapter for clarification. This is as identical as possible to all of my other adapters with variable names changed for Mentor.
public class MentorAdapter extends RecyclerView.Adapter<MentorAdapter.MentorHolder> {
private static final String TAG = AppRepository.class.getSimpleName();
private OnMentorClickListener listener;
private List<Mentor> mentorList = new ArrayList<>();
class MentorHolder extends RecyclerView.ViewHolder{
private TextView textViewMentorName;
private TextView textViewMentorPhone;
private TextView textViewMentorEmail;
MentorHolder(#NonNull View itemView) {
super(itemView);
textViewMentorName = itemView.findViewById(R.id.textView_mentor_name);
textViewMentorPhone = itemView.findViewById(R.id.textView_mentor_phone);
textViewMentorEmail = itemView.findViewById(R.id.textView_mentor_email);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
if(listener!=null&&position!=RecyclerView.NO_POSITION){
listener.onMentorClick(mentorList.get(position));
}
}
});
}
}
#NonNull
#Override
public MentorHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.mentor_item, parent, false);
return new MentorHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull MentorHolder holder, int position) {
Mentor currentMentor = mentorList.get(position);
holder.textViewMentorName.setText(currentMentor.getMentor_name());
holder.textViewMentorEmail.setText(currentMentor.getMentor_email());
holder.textViewMentorPhone.setText(currentMentor.getMentor_phone());
}
#Override
public int getItemCount() {
return mentorList.size();
}
public void setMentorList(List<Mentor> mentors){
this.mentorList = mentors;
notifyDataSetChanged();
}
public Mentor getMentorAt(int position){return mentorList.get(position);}
public interface OnMentorClickListener {
void onMentorClick(Mentor mentor);
}
public void setOnMentorClickListener(OnMentorClickListener listener){
this.listener = listener;
}
}
If you are overriding onMentorClick your activity should implement OnMentorClickListener interface, then on your adapter you should set the activity as your listener with:
mentorAdapter.setOnMentorClickListener(this)
In your code, you're using a new click listener (not the activity)
mentorAdapter.setOnMentorClickListener(new MentorAdapter.OnMentorClickListener(){
#Override
public void onMentorClick(Mentor mentor) {
}
});
In addition, you should fire your onMentorClick on the user click in your adapter implementation, preferably in the onCreateViewHolder method:
public YourAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
ViewHolder holder = ViewHolder(
LayoutInflater.from(context).inflate(
R.layout.your_layout,
parent,
false
))
holder.itemView.setOnClickListener {
listener.onMentorClick(mentor)
}
return holder ;
}
Turns out that in my mentor_item.xml file used to create the layout for the Mentor RecyclerView, I had created an across the entire card on the top layer that I wasn't doing anything with.
Once I removed that ImageButton from the xml file, the clicks were being registered and everything worked as intended.
Thank you for everyone's time.
In some place you need a call to these Callback, something like this:
profileView.setOnClickListener(new ....
listener.onMentorListener(mentor);
)
Do you have this implementation in your Adapter?
I've noticed RecyclerView "recycle" the views in the adapter but i want stop RecyclerView adapter duplicate the checked action in another item of the RecyclerView.
I have 10 items drawed into a RecyclerView, each one of them have a RadioGroup with 2 RadioButton within, but when i fired the check in the first item, for example, the number ten item have a checked too.
I was reading this question but i could'nt got it work.
Using Radio button with recyclerview in android
How to avoid this?
My adapter:
...
public class PreguntaAdapter extends RecyclerView.Adapter<PreguntaAdapter.ViewHolder> {
private Context context;
private ArrayList<VistaEncuestaActivity.PreguntasSiNo> preguntas;
public ArrayList<Respuestas> respuestas = new ArrayList<Respuestas>();
public PreguntaAdapter(Context context, ArrayList<VistaEncuestaActivity.PreguntasSiNo> preguntas) {
this.context = context;
this.preguntas = preguntas;
}
public ArrayList<Respuestas> getCheckedItems() {
return respuestas;
}
#NonNull
#Override
public PreguntaAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
Context context = viewGroup.getContext();
LayoutInflater layoutInflater = LayoutInflater.from(context);
View v = layoutInflater.inflate(R.layout.item_pregunta_sino, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull final PreguntaAdapter.ViewHolder viewHolder, int i) {
final VistaEncuestaActivity.PreguntasSiNo currentPregunta = preguntas.get(i);
//viewHolder.pregunta.setText(currentEncuesta.getString_pregunta());
viewHolder.pregunta.setText(currentPregunta.getQuestion());
viewHolder.myLinearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
viewHolder.setIsRecyclable(false);
}
#Override
public int getItemCount() {
return preguntas.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public SharedPreferences prefs;
private LinearLayout myLinearLayout;
public TextView pregunta;
public RadioGroup radio_group;
public ViewHolder(#NonNull View itemView) {
super(itemView);
pregunta = (TextView) itemView.findViewById(R.id.pregunta);
radio_group = (RadioGroup) itemView.findViewById(R.id.radio_group_pregunta);
prefs = (SharedPreferences) context.getSharedPreferences("logged", Context.MODE_PRIVATE);
myLinearLayout = (LinearLayout) itemView.findViewById(R.id.myLinearLayout);
}
}
}
Thanks all of you for your responses. Unfortunally any response helped me to solve this issue, i used this function and i added the getItemViewType function to my adapter and it's working well now:
#Override
public int getItemViewType(int position) {
return position;
}
And going to the official google docs i could find the next information:
Return the view type of the item at position for the purposes of view recycling. The default implementation of this method returns 0, making the assumption of a single view type for the adapter. Unlike ListView adapters, types need not be contiguous. Consider using id resources to uniquely identify item view types. https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter
This make sense at the time RecyclerView "recycle" the views but when this is use it in the adapter this have a different behavior treating each view as unique.
Thank you to all of you.
RadioGroup.getCheckedRadioButtonId() is the way to go. In conjunction with a SparseArray<Integer>.
In onBindViewHolder():
On each RadioGroup, set a RadioGroup.OnCheckedChangeListener(). Add the checked state to the a SparseArray or Map<Integer,Integer> mapping the index of the item position to the updated value in RadioGroup.OnCheckedChangeListener() onCheckedChanged.
So your onBindViewHolder would look something like this:
private SparseArray<Integer> mMapping = new SparseArray<>();
public void onBindViewHolder(#NonNull final PreguntaAdapter.ViewHolder viewHolder,final int position) {
final VistaEncuestaActivity.PreguntasSiNo currentPregunta = preguntas.get(i);
//viewHolder.pregunta.setText(currentEncuesta.getString_pregunta());
viewHolder.pregunta.setText(currentPregunta.getQuestion());
viewHolder.myLinearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
viewHolder.radio_group.setOnCheckedChangeListener(new OnCheckedChangeListener(){
void onCheckedChanged(RadioGroup group,int checked) {
mMapping.put(position,checked);
}
}};
// Check to see if a previous checked value exists and restore.
if(mMapping.get(position) != null && mMapping.get(position) != -1){
radio_group.check(mMapping.get(position));
}
viewHolder.setIsRecyclable(false);
}
onCheckedChanged(RadioGroup group, int checkedId)
You should give each RadioButton a unique ID, like radioButton_1, radioButton_2 in your layout file.
How can I set two text blocks in the listView, of which the first is on the left, the other on the right? I am tried to create a new layout with two textViews. But I don't know how I can connect textViews with listView and how I can set texts on textViews. May anybody help me?
I would like to have a list like this
Assuming you have the list and your layout with those 2 textview is ready just use this adapter and set this adapter to recycler view of the activity. let me know if you face any issues
public class CountryCodeAdapter extends RecyclerView.Adapter<CountryCodeAdapter.ViewHolder> {
private CountryCodeActivity activity;
ArrayList<CountryCodeModel> list = new ArrayList<>();
int selected_pos = 0;
public CountryCodeAdapter(CountryCodeActivity activity, ArrayList<CountryCodeModel> list) {
this.activity = activity;
this.list = list;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_country_listing, parent, false);
return new ViewHolder(rootView);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
holder.tv_row_CountryCodeActivity_countrycode.setText(list.get(holder.getAdapterPosition()).getDial());
holder.tv_row_CountryCodeActivity_countryname.setText(list.get(holder.getAdapterPosition()).getName());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("country_code", list.get(holder.getAdapterPosition()).getDial());
activity.setResult(activity.RESULT_OK, intent);
activity.finish();
}
});
}
#Override
public int getItemCount() {
return list.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView tv_row_CountryCodeActivity_countryname,
tv_row_CountryCodeActivity_countrycode;
public ViewHolder(View itemView) {
super(itemView);
tv_row_CountryCodeActivity_countryname = itemView.findViewById(R.id.tv_row_CountryCodeActivity_countryname);
tv_row_CountryCodeActivity_countrycode = itemView.findViewById(R.id.tv_row_CountryCodeActivity_countrycode);
tv_row_CountryCodeActivity_countryname.setTypeface(AppClass.Lato_Regular);
tv_row_CountryCodeActivity_countrycode.setTypeface(AppClass.Lato_Regular);
}
}
}
1)You should use RecyclerView insteaad of listView, and for recyclerView you can achieve what you want with item decorator.
2)But if you have to use ListView(which is less possible case) you can do this by checking list item position and set the corresponding margin to the layout which is not recomended.
3)Also there is another way to achieve this, which is to use different layout resource xml files, but I would not use the last two variants. I would prefer the first.
This is example from Android for beginners book. I think that problem is with mNoteAdapter but I can't find where it exactly is. I tried to use adapter from other source and it was working(I mean click) so I could see at least log in logcat. How does mNoteAdapter affect on possibility of seeing users click by app? How can I find where problem is?
mNoteAdapter = new NoteAdapter();
ListView listNote = (ListView) findViewById(R.id.listView);
listNote.setAdapter(mNoteAdapter);
listNote.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> adapter, View view, int
whichItem, long id) {
Log.e("CLICK", "CliCK");
}
});
}
Here is a piece of NoteAdapter class
public class NoteAdapter extends BaseAdapter {
List<Note> noteList = new ArrayList<Note>();
#Override
public int getCount() {
}
#Override
public Note getItem(int whichItem) {
}
#Override
public long getItemId(int whichItem) {
}
#Override
public View getView(int whichItem, View view, ViewGroup viewGroup) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater)
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.listitem, viewGroup, false);
}
TextView txtTitle = (TextView) view.findViewById(R.id.txtTitle);
ImageView ivImportant = (ImageView) view.findViewById(R.id.imageViewImportant);
Note tempNote = noteList.get(whichItem);
if (!tempNote.isImportant()) {
ivImportant.setVisibility(View.GONE);
}
txtTitle.setText(tempNote.getTitle());
return view;
}
public void addNote(Note n) {
}
}
There are many ways to display items as list on Android..
--> ListView, RecyclerView etc.
Lists need a source right? Let's assume that your source is an array of 1000 elements. Your ListView has to have as many columns to display all of them right?
Adapter is responsible for taking the array, and assigning each element to every list placeholders
This row was added to the top of .xml file
android:descendantFocusability="blocksDescendants"