I have a EditText in RecyclerView. When I edit the content of a EditText and scroll up/down, that same content is displayed in another line.
Also, If I return to the same position, the EditText is empty.
Here is the layout for every item of the RecyclerView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#F3F2F2"
android:orientation="vertical"
android:padding="5dp">
<TextView
android:id="#+id/txt_jantri_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/quicksand_bold"
android:text="1"
android:textColor="#color/theme_color"
android:textSize="10sp" />
<EditText
android:id="#+id/ed_jantri_number"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:background="#drawable/shape_jantri_edittext"
android:fontFamily="#font/quicksand_bold"
android:gravity="center"
android:inputType="number"
android:maxLines="1"
android:minLines="1"
android:padding="5dp"
android:textSize="12dp" />
</LinearLayout>
</LinearLayout>
Here is the Adapter class where I am setting text to the box number:
public class AdapterJantri extends RecyclerView.Adapter<AdapterJantri.Myholder> {
#NonNull
#Override
public Myholder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return new Myholder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.single_jantri_view, viewGroup, false));
}
#Override
public void onBindViewHolder(#NonNull Myholder myholder, int i) {
myholder.txtJantriNumber.setText(String.valueOf(i + 1));
}
#Override
public int getItemCount() {
return 99;
}
public class Myholder extends RecyclerView.ViewHolder {
EditText edJantriNumber;
TextView txtJantriNumber;
public Myholder(#NonNull View itemView) {
super(itemView);
edJantriNumber = itemView.findViewById(R.id.ed_jantri_number);
txtJantriNumber = itemView.findViewById(R.id.txt_jantri_number);
}
}
}
Here is the Activity
public class JantriActivity extends AppCompatActivity {
RecyclerView recyclerView;
SlideToActView slideToActView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jantri);
initiliaization();
recyclerView.setAdapter(new AdapterJantri());
}
private void initiliaization() {
recyclerView = findViewById(R.id.recycler_jantri);
recyclerView.setLayoutManager(new GridLayoutManager(this, 4));
slideToActView = findViewById(R.id.swipe_save_transaction);
}
}
A ViewAdapter re-uses a View. So, when scrolling, instead of creating new views, an Adapter will re-use a view that is no longer visible.
So, that is the main reason you have the onBindViewHolder. With that method, Android gives you chance to update the view content before it becomes visible. If you won't update all contents of the (the TextView and the EditText), you observe issues like that one that you mentioned.
Note this code:
#Override
public void onBindViewHolder(#NonNull Myholder myholder, int i) {
myholder.txtJantriNumber.setText(String.valueOf(i + 1));
}
As you can see, you are only updating the TextView. So, that TextView (re-used or not) will always display proper text. However, you are not updating the content of the EditText. This way, if the View was just created, the EditText is empty. If the view is being re-used, you will see the old value (from a different line).
So, in order to keep update the EditText properly, you must store all texts in a separated list. For example:
public class AdapterJantri extends RecyclerView.Adapter<AdapterJantri.Myholder> {
private String[] mTextList;
public AdapterJantri() {
mTextList = new String[getItemCount()];
}
#Override
public void onBindViewHolder(#NonNull Myholder myholder, int i) {
myholder.txtJantriNumber.setText(String.valueOf(i + 1));
// Here, you update the EditText content
myholder.edJantriNumber.setText(mTextList[i]);
}
#Override
public void onViewRecycled(#NonNull final Myholder holder) {
// Here, you save the text before the view gets recycled
int index = holder.getAdapterPosition();
if (index >= 0) {
mTextList[index] = holder.edJantriNumber.getText().toString();
}
}
}
EDIT
If you have a TextWatcher and you need to disable it temporarily, I suggest to:
First
Don't create a TextWatcher inside the bindViewHolder. You don't need to create a new one everytime. Just add it and remove it. You can use your own MyHolder as a TextWatcher
public class Myholder extends RecyclerView.ViewHolder implements TextWatcher {
EditText edJantriNumber;
TextView txtJantriNumber;
public Myholder(#NonNull View itemView) {
super(itemView);
edJantriNumber = itemView.findViewById(R.id.ed_jantri_number);
txtJantriNumber = itemView.findViewById(R.id.txt_jantri_number);
}
#Override
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
// Add your code here
}
#Override
public void onTextChanged(final CharSequence s, final int start, final int before,
final int count) {
// Add your code here
}
#Override
public void afterTextChanged(final Editable s) {
// Add your code here
}
public void disableTextChangeListner() {
// This will remove the listener. So, it will stop to listen to new events
edJantriNumber.removeTextChangedListener(this);
}
public void addTextChangeListner() {
// This will add the listener. So, it will start to listen to new events
edJantriNumber.addTextChangedListener(this);
}
}
Then, during bindViewHolder, disable the listener, set the new text, and add the listener again as foolows:
#Override
public void onBindViewHolder(#NonNull Myholder myholder, int i) {
myholder.disableTextChangeListner();
myholder.txtJantriNumber.setText(String.valueOf(i + 1));
myholder.edJantriNumber.setText(mTextList[i]);
myholder.addTextChangeListner();
}
Related
Hi everyone i have created project for survey in area. i have loaded the question in the recycler view where edittext,radiobutton,checkbox,spinner are generated dynamically(there no separate layout files for each) i have used new edittext and used textwatcher to keep the answers dynamically updated in the list so that it can return to mainactivity i.e from adapterclass to activity class. my problem is that the edittext, radiobuttons,etc are getting duplicated but not the question in the textview on scroll up or down or keyboard down after the if there operator performed like enter input inside edittext or change value in spinner then scroll up
note:- the textview is static component the dynamic input are add to linearlayout inside the row layout file that is textview and edittext are both childcomponents of linearlayout based on if case the input type is added as 2nd child of linear layout
Activity.class
mlist=new ArrayList<>();
mlist.clear();
mlist=db.populatequestion();
Log.d("size", String.valueOf(mlist.size()));
Log.d("Question",mlist.get(0).getQuestionType());
mRecyclerView=findViewById(R.id.rvForm);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
formsadapter =new formsadapter(mlist,this,mRecyclerView);
mRecyclerView.setAdapter(formsadapter);
formsadapter.class
public class formsadapter extends RecyclerView.Adapter<formsadapter.ViewHolder> {
private List<FormQData> mlists;
private List<formanswer> formanswers;
private Context mContext1;
private RecyclerView mRecyclerV1;
public class ViewHolder extends RecyclerView.ViewHolder{
public LinearLayout linearLayout;
public TextView Question;
public View layout;
public ViewHolder(View v) {
super(v);
layout=v;
Question=v.findViewById(R.id.tvQuestion);
linearLayout=v.findViewById(R.id.llform);
formanswers = new ArrayList<>();
}
}
public formsadapter(List<FormQData> myDataset, Context context, RecyclerView recyclerView) {
mlists = myDataset;
mContext1 = context;
mRecyclerV1 = recyclerView;
}
public formsadapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType){
LayoutInflater inflater = LayoutInflater.from(
parent.getContext());
View v =inflater.inflate(R.layout.row_form, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
public void onBindViewHolder(formsadapter.ViewHolder holder, final int position){
final FormQData fb=mlists.get(position);
formanswer fa=new formanswer();
holder.Question.setText(fb.getQuestionContent());
LinearLayout.LayoutParams mainparams1 = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT,1.0f);
mainparams1.gravity= Gravity.RIGHT;
if(fb.getQuestionType().equals("textbox")){
EditText et=new EditText(mContext1);
et.setBackgroundResource(R.drawable.boxwithcircularcoloredborders);
et.setTextColor(mContext1.getResources().getColor(R.color.colorPrimary));
et.setPadding(15,15,15,15);
et.addTextChangedListener(new TextWatcher() {
#Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
#Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
#Override
public void afterTextChanged(Editable editable) {
fa.setQuestionid(fb.getQuestionid());
fa.setAnswer(et.getText().toString());
if(formanswers.contains(fa)){
formanswers.set(formanswers.indexOf(fa),fa);
}
else
formanswers.add(fa);
}
});
holder.linearLayout.addView(et,1);
}
else if(fb.getQuestionType().equals("radiobox")){
RadioGroup rg=new RadioGroup(mContext1);
rg.setOrientation(RadioGroup.HORIZONTAL);
String[] arrOfStr = fb.getQuestionOptions().split(":");
RadioButton[] radiobutton = new RadioButton[arrOfStr.length];
for(int i=0;i<arrOfStr.length;i++){
radiobutton[i]=new RadioButton(mContext1);
radiobutton[i].setText(arrOfStr[i]);
radiobutton[i].setLayoutParams(mainparams1);
radiobutton[i].setBackgroundResource(R.drawable.boxwithcircularcoloredborders);
radiobutton[i].setTextColor(mContext1.getResources().getColor(R.color.colorPrimary));
radiobutton[i].setTypeface(Typeface.DEFAULT_BOLD);
radiobutton[i].setPadding(15,15,15,15);
int finalI = i;
radiobutton[i].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
fa.setQuestionid(fb.getQuestionid());
fa.setAnswer(radiobutton[finalI].getText().toString());
if(formanswers.contains(fa)){
formanswers.set(formanswers.indexOf(fa),fa);
}
else
formanswers.add(fa);
}
});
rg.addView(radiobutton[i],i);
}
holder.linearLayout.addView(rg,1);
}
else if(fb.getQuestionType().equals("checkbox")){
List<String> answer=new ArrayList<>();
RadioGroup rg=new RadioGroup(mContext1);
rg.setOrientation(RadioGroup.HORIZONTAL);
String[] arrOfStr = fb.getQuestionOptions().split(":");
CheckBox[] checkBoxes = new CheckBox[arrOfStr.length];
for(int i=0;i<arrOfStr.length;i++){
checkBoxes[i]=new CheckBox(mContext1);
checkBoxes[i].setText(arrOfStr[i]);
checkBoxes[i].setLayoutParams(mainparams1);
checkBoxes[i].setBackgroundResource(R.drawable.boxwithcircularcoloredborders);
checkBoxes[i].setTextColor(mContext1.getResources().getColor(R.color.colorPrimary));
checkBoxes[i].setTypeface(Typeface.DEFAULT_BOLD);
checkBoxes[i].setPadding(15,15,15,15);
int finalI = i;
checkBoxes[i].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
fa.setQuestionid(fb.getQuestionid());
if(checkBoxes[finalI].isChecked()){
String temp=checkBoxes[finalI].getText().toString();
if(answer.contains(temp)){
answer.set(answer.indexOf(temp),temp);
}else {
answer.add(checkBoxes[finalI].getText().toString());
}
}else{
answer.remove(checkBoxes[finalI].getText().toString());
}
fa.setAnswer(answer.toString());
if(formanswers.contains(fa)){
formanswers.set(formanswers.indexOf(fa),fa);
}
else
formanswers.add(fa);
}
});
rg.addView(checkBoxes[i],i);
}
holder.linearLayout.addView(rg,1);
}
else if(fb.getQuestionType().equals("dropdown")){
Spinner spinner=new Spinner(mContext1);
fa.setQuestionid(fb.getQuestionid());
List<String> arrOfStr=new ArrayList<>(Arrays.asList(fb.getQuestionOptions().split(":")));
arrOfStr.add(0,"Select A Option");
ArrayAdapter<String> spinnerAdapter=new ArrayAdapter<>(mContext1,R.layout.row_dropdownsinglecelltv,R.id.tvsinglecell2,arrOfStr);
spinnerAdapter.setDropDownViewResource(R.layout.row_dropdownsinglecelltv);
spinner.setPopupBackgroundDrawable(mContext1.getResources().getDrawable(R.drawable.boxcircularcorner));
spinner.setAdapter(spinnerAdapter);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
String spinnerValue=String.valueOf(adapterView.getSelectedItem());
if (!spinnerValue.equals("Select A Option")){
//Log.d("value","upload");
fa.setAnswer(spinnerValue);
if(formanswers.contains(fa))
formanswers.set(formanswers.indexOf(fa),fa);
else
formanswers.add(fa);
}else{
if(formanswers.contains(fa))
formanswers.remove(fa);
}
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
holder.linearLayout.addView(spinner,1);
}
else {
TextView et=new TextView(mContext1);
et.setText("invalid question type");
holder.linearLayout.addView(et,1);
}
}
public int getItemCount(){
return mlists.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
public List<formanswer> formanswers(){
return formanswers;
}
}
row_form.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:padding="10dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="#+id/llform"
android:background="#drawable/boxwithcoloredborder"
android:layout_width="match_parent"
android:padding="15dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/tvQuestion"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/colorPrimary"
android:textStyle="bold"
android:text="TextView" />
</LinearLayout>
</LinearLayout>
here is the sample video
https://imgur.com/a/hi3Vb5a
i have found the solution it seems the problem is entirely different. The problem is that the recycler view is not getting recycled properly its has viewCachesize which needs to be increased to list size
you can check if your recyclerview is getting recycled or not using
#Override
public void onViewRecycled(#NonNull formsadapter.ViewHolder holder) {
Log.d("recyclerview","recyclerview is recycled");
}
if the log is being appeared then you solve this issue adding
holder.setIsRecyclable(false);
in the onbindviewholder or adding
mRecyclerView.setItemViewCacheSize(mlist.size()+1);
in the main activity itself
Evidently just use the listview itself instead of recyclerview then these lines dont need to be added
links:-
RecyclerView not recycling views if the view count is small
How can I detect recycling from within a recyclerView adapter, so I can perform an action before it gets recycled?
I have a ViewPager2 contains ScrollView.
What the problem is, when I try to get ScrollView of current page in onPageSelected(), it doesn't work.
Here I'd like to set previous scrollY to the ScrollView when user back to see selected page. (because scrollY is reset for some reason before that)
My code is below.
ViewPagerAdapter.java (edited)
public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewHolder> {
private LayoutInflater mInflater;
private List<String> mText;
private ViewPager2 pager2;
MainActivity main;
public ViewPagerAdapter(Context context, List<String> data, ViewPager2 pager2, MainActivity main){
this.mInflater = LayoutInflater.from(context);
this.mText = data;
this.pager2 = pager2;
this.main = main;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View view = mInflater.inflate(R.layout.tab_scroll_item, parent, false);
return new ViewPagerAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, int position){
holder.scrollView.setTag("scv_tab" + position);
holder.scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if(scrollY != 0) {
main.setScrollY(scrollY, getPosition());
}
System.out.println("onScrollChanged : " + scrollY);
}
});
holder.textView.setEnabled(false);
holder.textView.setEnabled(true);
holder.textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, applyTextSize());
holder.textView.setText(mText.get(position));
holder.textView.setTag("tv_tab" + position);
}
#Override
public int getItemCount(){
return mText.size();
}
protected int getPosition(){
return pager2.getCurrentItem();
}
public class ViewHolder extends RecyclerView.ViewHolder{
ScrollView scrollView;
TextView textView;
public ViewHolder(View itemView){
super(itemView);
scrollView = itemView.findViewById(R.id.tab_scroll);
textView = itemView.findViewById(R.id.tab_textview);
}
}
[REVICED]onBindViewHolder
#Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, final int position){
holder.scrollView.setTag("scv_tab" + position);
holder.scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if(scrollY != 0) {
main.storeScrollY(scrollY, position);
}
System.out.println("onScrollChanged : " + scrollY);
}
});
int y = main.retrieveScrollY(position);
holder.scrollView.setScrollY(y);
holder.textView.setEnabled(false);
holder.textView.setEnabled(true);
holder.textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, applyTextSize());
holder.textView.setText(mText.get(position));
holder.textView.setTag("tv_tab" + position);
}
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
...
mPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
if(!searchView.isIconified()){
searchView.setIconified(true);
}
if(highlightModel.getHighlitedOrNot(position)){
searchText.deleteTextHighlight(position);
highlightModel.setHighlitedOrNot(position, false);
}
int positionY[] = getScrollFromViewModel();
ScrollView sv = findScrollView(); // HERE IS THE PROBLEM
if(sv != null) {
sv.setScrollY(positionY[position]);
}else{
System.out.println("sv is null"); // ALWAYS SHOWS NULL
}
}
});
...
searchView = (SearchView) findViewById(R.id.searchview);
searchView.setOnSearchClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
fab.setVisibility(View.VISIBLE);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int actualResult = searchText.scrollToHighlightedWord(findTextView()
, findScrollView() // here findScrollView() works perfectly.
, searchResultIndex);
if(actualResult == (searchResultIndex + 1)) {
++searchResultIndex;
}else if(actualResult == searchResultIndex){
showToastAtMain("last word");
}else{
forOnClose();
}
}
});
}
});
private ScrollView findScrollView(){
ScrollView sv = mPager2.findViewWithTag("scv_tab" + mPager2.getCurrentItem());
return sv;
}
tab_scroll_item.xml (edited)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/tab1_layout">
<ScrollView
android:id="#+id/tab_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tab_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:lineSpacingExtra="7dp"
android:textIsSelectable="true"
android:paddingStart="9dp"
android:paddingTop="9dp"
android:paddingEnd="9dp"/>
</ScrollView>
</LinearLayout>
Any advice is truly appreciated. Thank you for reading this long question.
onPageSelected will be called way earlier than selected page drawing mechanism, before any onCreateViewHolder and onBindViewHolder. I would advise you to "incject" somehow your data into your "page" (is it View or whole Fragment? that makes big difference) and in your case set this scroll position inside onBindViewHolder. This will make rendering of RecyclerView draw even first frame in correct scrolled position. Your way, even when you will wait some time for RecyclerView drawing, will make that onBindViewHolder will draw first frame on 0 scroll and your method will scroll a bit in next frames - this will be visible like some blink or fast-autoscroll (I think this behavior isn't intended)
edit - add proper method for setting scroll for your Adapter even before super call
#Override
public void onPageSelected(int position) {
int positionY[] = getScrollFromViewModel();
adapter.setScrollForPosition(positionY[position]);
super.onPageSelected(position);
...
for adapter
private final HashMap<Integer, Integer> scrollYHistory = new HashMap<>();
public void setScrollForPosition(int position, int scrollY){
scrollYHistory.put(position, scrollY);
}
use stored scrollY value in onBindViewHolder and clean entry in your HashMap
#Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, final int position){
Integer scrollY = scrollYHistory.remove(position);
if (scrollY == null) scrollYa = 0; // may be null if not stored yet!
holder.scrollView.setScrollY(scrollY);
...
also remove unnecessary reference to MainActivity main in adapter's constructor and variable inside, it already gets Context reference and doesn't (shouldn't) need to know which Activity is creating it
edit - expanding comment:
instead of calling scrollView.setScrollY straight inside onBindViewHolder you should set up your TextView at first, then wait for rendering it and then scrolling to proper position
#Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, final int position){
...
holder.textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, applyTextSize());
holder.textView.setText(mText.get(position));
...
holder.textView.post(new Runnable(){
// this will be called after nearest drawing
#Override
public void run() {
Integer scrollY = scrollYHistory.remove(position);
if (scrollY == null) scrollYa = 0; // may be null if not stored yet!
holder.scrollView.setScrollY(scrollY);
}
});
}
Thanks to snachmsm, I got to know that onPageSelected will be called way earlier than ViewPager2 drawing. And for some reason I found my solution using mPager2.postDelayed().
After adding these, I got NO null ScrollView here.
mPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
...
//Here I added onPageScrollStateChanged() and postDelayed() in it.
#Override
public void onPageScrollStateChanged(int state){
if(state == ViewPager2.SCROLL_STATE_SETTLING){
mPager2.postDelayed(new Runnable() {
#Override
public void run() {
int position = mPager2.getCurrentItem();
int y = scrollModel.getPreviousScrollY(position);
ScrollView sv = findScrollView();
sv.scrollTo(0, y);
}
}, 50);
}
}
});
}
The first list of elements correctly show the marquee effect, however the elements that I add later after receiving a notification from Firebase do not present the marquee effect in a TextView.
MyActivity.java
public abstract class MyActivity extends AppCompatActivity {
...
protected void onCreate(Bundle savedInstanceState) {
mRecycler = findViewById(R.id.choices_recycler);
mRecycler.setLayoutManager(new LinearLayoutManager(this));
mRecycler.setAdapter(new ChoiceAdapter(this, getChoices()));
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int insertIndex = 0;
choices.add(insertIndex, new Choice('foo1','foo2'));
adapter.notifyItemInserted(insertIndex);//New elements don't marquee automatically
//adapter.notifydatasetchanged();//This works! New marquee elements start moving, but OLD elements stop with this...
}
};
}
The new elements are inserted correctly with the design they should, however the only thing that does not work in the new elements is the movement of the marquee. Here is part of the .XML:
<TextView
android:id="#+id/item_date"
android:layout_width="0dp"
android:layout_height="19dp"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:ellipsize="marquee"
android:freezesText="true"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/item_separator"
app:layout_constraintTop_toTopOf="parent"
tools:text="Here the marquee text" />
<!-- I've also tried: focusableintouchmode=true and focusable=true -->
I'm also using TextView.setSelected() in my ViewHolder, but not working on new elements.
Any help will be more than appreciated!
UPDATE#1
I just tried one thing, and that is that if I minimize the App to the background and reopen it and bring it to the foreground, the marquees do work.
Also if I pull down the top notification bar and then go back to my App, the marquees work.
UPDATE#2 I have tried this and unfortunately it has not worked for me either, although it looked good: https://gist.github.com/LeonardoCardoso/1116723900c815a48496
UPDATE#3 MyAdapter:
public class ChoiceAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private final Activity activity;
private List<Choice> choices;
public ChoiceAdapter(Activity activity, List<Choice> choices) {
this.activity = activity;
this.choices = choices;
}
#Override
public int getItemViewType(int position) {
return position;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_item_choice, parent, false);
return new ViewHolder1(view);
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
Log.d("onBindViewHolder", "onBindViewHolder executed");
Choice choice = choices.get(position);
ViewHolder1 viewHolder1 = (ViewHolder1)holder;
viewHolder1.bind(choice);
}
#Override
public int getItemCount() {
return choices.size();
}
public class ViewHolder1 extends RecyclerView.ViewHolder {
private final TextView titleText;
private final TextView dateText;
public ViewHolder1(View itemView) {
super(itemView);
titleText = itemView.findViewById(R.id.item_title);
dateText = itemView.findViewById(R.id.item_date);
dateText.setSelected(true);
}
public void bind(final Choice choice) {
titleText.setText(choice.title);
dateText.setText(choice.date);
}
}
}
UPDATE#4 Live example - The first four items are inserted when starting the App, and your dial works correctly.
The items that appear are added when receiving calls to Firebase Clod Messaging:
https://imgur.com/84b5pfJ
1) 4 elements are loaded in the RecyclerView when starting the App, and their marquee have the correct animation
2) Later we are adding NEW elements, but they do not present the animation in the TextView marquee
3) If I pull down the notification bar or send the App to the background and go back to it, all the marquee works mysteriously
I have implemented a RecyclerView and customer Adapter many times, but for some reason I cannot get this one to display any data. I am feeding in data from JSON using retrofit and calling notifyDataSetChanged() once this has been loaded, yet it still remains blank. I have stripped this back to just one text view to try and simplify but still not getting anything. Can anyone see where I am going wrong here?
When I debug, I am getting the List to contain data so I am definitely parsing the data correctly, I just cant get it display in the recycler view. I have even checked the list.size() in the loadTrailerList method and it has data.
My Activity onCreate method:
trailerAdapter = new TrailerAdapter(this);
trailerRecyclerView = findViewById(R.id.trailer_recycler_view);
trailerRecyclerView.setLayoutManager(new LinearLayoutManager(this));
trailerRecyclerView.setAdapter(trailerAdapter);
Retrofit onResponse method:
if (response.body() != null) {
trailers = response.body().getTrailers();
}
trailerAdapter.loadTrailerList(response.body().getTrailers());
My custom adapter:
public class TrailerAdapter extends RecyclerView.Adapter<TrailerAdapter.TrailerViewHolder> {
private final List<Trailer> trailerList = new ArrayList<>();
private final TrailerClickListener listener;
public TrailerAdapter(TrailerClickListener listener) {
this.listener = listener;
}
#NonNull
#Override
public TrailerViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.trailer_list_item, viewGroup, false);
return new TrailerViewHolder(itemView, this);
}
#Override
public void onBindViewHolder(#NonNull TrailerViewHolder trailerViewHolder, int i) {
trailerViewHolder.trailerTitle.setText(trailerList.get(i).getName());
}
#Override
public int getItemCount() {
return trailerList.size();
}
public void loadTrailerList(List<Trailer> trailers) {
this.trailerList.clear();
if (trailers != null) {
trailers.addAll(trailers);
}
notifyDataSetChanged();
}
class TrailerViewHolder extends RecyclerView.ViewHolder {
final TrailerAdapter trailerAdapter;
private final TextView trailerTitle;
private TrailerViewHolder(#NonNull View itemView, TrailerAdapter trailerAdapter) {
super(itemView);
this.trailerAdapter = trailerAdapter;
trailerTitle = itemView.findViewById(R.id.text_view_trailer_title);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.onTrailerClicked(trailerList.get(getAdapterPosition()));
}
});
}
}
}
My List Item XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/text_view_trailer_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Play Trailer" />
</LinearLayout>
the recycler view in my activity XML:
<android.support.v7.widget.RecyclerView
android:id="#+id/trailer_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary"
app:layout_constraintTop_toBottomOf="#+id/trailer_divider">
</android.support.v7.widget.RecyclerView>
I am grateful for anyone that can point me in the right direction
It is because you are sending a list to adapter but, you are not initializing your list which is used in the adapter.
try this.
public void loadTrailerList(List<Trailer> trailers) {
this.trailerList.clear();
if (trailers != null) {
trailerList = trailers;
}
notifyDataSetChanged();
}
Doh! I just realised what I was doing wrong:
In my loadTrailerList() method in my adapter, I was calling:
trailers.addAll(trailers);
instead of:
trailerList.addAll(trailers);
to load the list of items into the actual ArrayList! whoops!
I have a Fragment which carries a Button and a RecyclerView, set up by an RecyclerView Adapter. In the RecyclerView are several Items, one of it is a EditText. Now I want that when the Button is clicked(which is NOT in the RecyclerView object), that I get the values of the EditTexts.
I already tried to get the recyclerView.getItemAtPosition() but there is no function like that, also tried the same for the adapter. So I would need something like
ArrayList s.add(recyclerView.getItemAtPosition(position).getEditText().getText().toString());
This is my Adapter:
public class RVSetAdapter extends RecyclerView.Adapter<RVSetAdapter.ViewHolder> {
private Exercise exercise;
public static class ViewHolder extends RecyclerView.ViewHolder {
public EditText et_weight;
public TextView tv_sets,tv_indication;
public ViewHolder(#NonNull final View itemView) {
super(itemView);
tv_sets = itemView.findViewById(R.id.tv_sets);
tv_indication = itemView.findViewById(R.id.tv_indication);
et_weight = itemView.findViewById(R.id.et_weight);
}
}
public RVSetAdapter(Exercise exercise) {
this.exercise = exercise;
}
#NonNull
#Override
public RVSetAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.rv_set,viewGroup,false);
RVSetAdapter.ViewHolder vh_set = new RVSetAdapter.ViewHolder(view);
return vh_set;
}
#Override
public void onBindViewHolder(#NonNull final RVSetAdapter.ViewHolder viewHolder,final int i) {
if(exercise.getKind() == 80) {
viewHolder.tv_sets.setText("");
viewHolder.tv_indication.setText("sec.");
}else if(exercise.getKind() == 90) {
viewHolder.tv_sets.setText("");
viewHolder.tv_indication.setText("min.");
}else {
viewHolder.tv_sets.setText(Integer.toString(i + 1) + ".");
}
viewHolder.et_weight.setText(Integer.toString(exercise.getWeights().get(i)));
}
#Override
public int getItemCount() {
return exercise.getWeights().size();
}
}
this is my Fragment:
final View view = layoutInflater.inflate(R.layout.fragment_exercise, container,false);
ImageView iv_exercise = view.findViewById(R.id.iv_exercise);
ImageView iv_musclekind = view.findViewById(R.id.iv_musclekind);
ImageView iv_save = view.findViewById(R.id.iv_save);
TextView tv_exercisename = view.findViewById(R.id.tv_exercisename);
TextView tv_exercisedescription = view.findViewById(R.id.tv_exercisedescription);
iv_exercise.setImageResource(exercises.get(position).getImage());
iv_musclekind.setImageResource(exercises.get(position).getMusclekindImage());
tv_exercisename.setText(exercises.get(position).getName());
tv_exercisedescription.setText(exercises.get(position).getDescription());
iv_save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//here I want to get the Values of the EditTexts and put them into an Array
}
});
recyclerView = view.findViewById(R.id.rv_sets);
recyclerView.setHasFixedSize(true); //maybe change this
layoutManager = new LinearLayoutManager(view.getContext());
adapter = new RVSetAdapter(exercises.get(position));
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
container.addView(view);
return view;
I don't have any ideas to go on so I would appreciate your help. If there is any uncertainty with my description of the problem please don't hesitate to ask.
Greetings Alexander
With RecyclerView, you have to understand that your EditTexts will be recycled. For example, if you have a list of 200 items, and it shows 2 items at one time, you will only ever have 2 EditText. They will reuse the higher EditText for the lower elements.
For example, here is a list that contains EditText showing only 2 at a time, and as the user scrolls, it will recycle and reuse them.
EditText A
Edittext B
EditText A (recycled)
EditText B (recycled)
....
This means you cannot just loop over all the elements later and get the values, as they don't store their values.
So, what you want to do, is when the user modifies an EditText, you want to store that value right away. You can do this by adding a TextWatcher to your EditText.
Note - I did assume you store your weights as String values, so I just took the value from the EditText and stored it into your Exercise Object. You may want to convert it before that.
public class RVSetAdapter extends RecyclerView.Adapter<RVSetAdapter.ViewHolder> {
private Exercise exercise;
// ...
#Override
public void onBindViewHolder(#NonNull final RVSetAdapter.ViewHolder viewHolder,final int i) {
// ...
viewHolder.et_weight.setText(Integer.toString(exercise.getWeights().get(i)));
viewHolder.et_weight..addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
// This will be the text from the EditText
String text = s.toString();
// Store the value back into your exercise Object.
exercise.getWeights().get(i).setWeight(text);
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
}
// ...
// Add a method for easy access to your weights.
public ArrayList<String> getWeights() {
return exercise.getWeights();
}
}
And now, within your Fragment, you can easily get the values out of your RVSetAdapter.
public View onCreateView() {
final View view = layoutInflater.inflate(R.layout.fragment_exercise, container,false);
// ...
iv_save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Use the method we added to your adapter to return the weights.
ArrayList<String> weights = adapter.getWeights();
}
});
// ...
return view;
}
I think you should use ArrayList in Adapter class to keep your items (or just Strings of EditText components). Add String to ArrayList in your onBindViewHolder() after you set text for editext. Then make a function which will get item from your ArrayList like:
public String getItem(int position){
arrayList.get(position);
}
and call it from your onClick() function in Fragment.
I think you can create static button and you can then access that button in your adapter then implement the functionality on the onclick of your button.
static Button btn;
Then implement like this in your adapter...
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
for(int i=0;i<arraylist.size();i++)
{
arr[i]= holder.edit_Text.getText().toString();
}
}
});
and put this onclick in your onbindviewholder method.