ProgramingTip

RecyclerView에 EditText 콘텐츠 저장

bestdevel 2020. 11. 26. 19:42
반응형

RecyclerView에 EditText 콘텐츠 저장


목록 항목이 EditText있지만 몇 개의 항목이 있을지 모르겠습니다. 에 일부 텍스트를 입력 EditText한 다음 아래로 스크롤 할 때 문제가 있습니다. RecyclerView다시 위로 스크롤 한 후 첫 번째 텍스트가 없습니다 EditText.

사용자가 텍스트를 입력하거나 입력을 완료하는 동안 (을 사용하여 수행 할 생각합니다. TextWatcher) EditText파일에 저장 코드를 작성해야하는 부분과가 궁금한 사항 (. txt 파일을 외부 저장소에 저장)

onCreate활동 방법이나 클래스 또는 다른 곳에서 그렇게해야 어댑터?

다음은 몇 가지 코드입니다.

주요 활동 코드

 public class MainActivity extends Activity {

    RecyclerView mRecyclerView;
    MyAdapter mAdapter;
    String[] mDataSet= new String[20];
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // generating text for editText Views
        for (int i = 0; i<=19; i++){
        mDataSet[i]= "EditText n: "+i;

    }
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mAdapter = new MyAdapter(mDataSet);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.setHasFixedSize(true);
    }

내 어댑터 코드

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

private String[] mDataset;


public static class ViewHolder extends RecyclerView.ViewHolder {
    // each data item is just a string in this case
    public EditText mEditText;

    public ViewHolder(View v) {
        super(v);

        mEditText = (EditText) v.findViewById(R.id.list_item_edittext);
    }
}

public MyAdapter(String[] myDataset) {
    mDataset = myDataset;
}

@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                 int viewType) {

    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.list_item, parent, false);

    ViewHolder vh = new ViewHolder(v);
    return vh;
}

@Override
public void onBindViewHolder(ViewHolder holder,  final int position) {
    holder.mEditText.setText(mDataset[position]);

    //without this addtextChangedListener my code works fine ovbiusly
    // not saving the content of the edit Text when scrolled
    // If i add this code then when i scroll all textView that go of screen
    // and than come back in have messed up content
    holder.mEditText.addTextChangedListener(new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start,
                                  int before, int count) {
           //setting data to array, when changed
           // this is a semplified example in the actual app i save the text
           // in  a .txt in the external storage
           mDataset[position] = s.toString();
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start,
                                      int count, int after) {

        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    });

}

@Override
public int getItemCount() {
    return mDataset.length;
}

이 "addtextChangedListener"없이 내 코드는 스크롤 할 때 편집 텍스트의 내용을 저장하지 않고 작동합니다. 이 코드를 추가하면 모든 editText를 스크롤 할 때 화면에서 사라지고 다시 들어 오면 콘텐츠가 엉망이됩니다.


솔루션의 주요 문제는 onBindViewHolder에 TextWatcher를 할당하고 할당하는 것입니다.이 작업은 빠른 스크롤 중에 지연이 발생하는 비용이 많이 발생하는 mAdapter에서 업데이트 할 위치를 결정하는 데 방해가되는 것입니다.

onCreateViewHolder에서 모든 작업을 수행하는 것이 더 바람직한 작업 옵션입니다. 다음은 완벽하게 테스트 된 작업 솔루션입니다.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private String[] mDataset;

    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_edittext, parent, false);
        // pass MyCustomEditTextListener to viewholder in onCreateViewHolder
        // so that we don't have to do this expensive allocation in onBindViewHolder
        ViewHolder vh = new ViewHolder(v, new MyCustomEditTextListener());

        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        // update MyCustomEditTextListener every time we bind a new item
        // so that it knows what item in mDataset to update
        holder.myCustomEditTextListener.updatePosition(holder.getAdapterPosition());
        holder.mEditText.setText(mDataset[holder.getAdapterPosition()]);
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }


    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public EditText mEditText;
        public MyCustomEditTextListener myCustomEditTextListener;

        public ViewHolder(View v, MyCustomEditTextListener myCustomEditTextListener) {
            super(v);

            this.mEditText = (EditText) v.findViewById(R.id.editText);
            this.myCustomEditTextListener = myCustomEditTextListener;
            this.mEditText.addTextChangedListener(myCustomEditTextListener);
        }
    }

    // we make TextWatcher to be aware of the position it currently works with
    // this way, once a new item is attached in onBindViewHolder, it will
    // update current position MyCustomEditTextListener, reference to which is kept by ViewHolder
    private class MyCustomEditTextListener implements TextWatcher {
        private int position;

        public void updatePosition(int position) {
            this.position = position;
        }

        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
            // no op
        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
            mDataset[position] = charSequence.toString();
        }

        @Override
        public void afterTextChanged(Editable editable) {
            // no op
        }
    }
}

나는 같은 문제가 있지만 다음 줄을 추가 할 내 편에서 문제를 해결합니다.

mRecyclerview.setItemViewCacheSize(mDataset.size());

바라건대는 당신의 문제를 해결하기를 바랍니다.


어댑터 데이터의 크기로 배열을 만듭니다.

예 : String[] texts = new String[dataSize];

어댑터 내부의 onBindViewHolder 메소드에서 TextChangedListener를 Textview에 추가하십시오.

예 :-

@Override
    public void onBindViewHolder(Viewholder holder, int position) {

//binding data from array 
   holder.yourEditText.setText(texts [position]);
   holder.yourEditText.addTextChangedListener(new TextWatcher() {

            @Override
            public void onTextChanged(CharSequence s, int start,
                    int before, int count) {
                //setting data to array, when changed
                texts [position] = s.toString();
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start,
                    int count, int after) {
                //blank
            }

            @Override
            public void afterTextChanged(Editable s) {
                //blank
            }
        });


}


안녕하세요 @mikwee는 onBindViewHolder ()에 추가하는 대신 아래 메소드에서 텍스트 변경 리스너를 추가하고 있는지 확인하십시오.

public ViewHolder(View v) {
        super(v);

  yourEditText.addTextChangedListener(new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start,
                int before, int count) {
            //setting data to array, when changed
            texts [position] = s.toString();
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start,
                int count, int after) {

        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    });


}

인터페이스를 만들고 현재 어댑터 위치를 전달하여 텍스트 변경 이벤트를 처리합니다.

    public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private String[] mDataset;

    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_edittext, parent, false);
        ViewHolder vh = new ViewHolder(v, new ViewHolder.ITextWatcher() {
            @Override
            public void beforeTextChanged(int position, CharSequence s, int start, int count, int after) {
                // do something
            }

            @Override
            public void onTextChanged(int position, CharSequence s, int start, int before, int count) {
                mDataset[position] = s.toString();
            }

            @Override
            public void afterTextChanged(int position, Editable s) {
                // do something
            }
        });

        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        holder.mEditText.setText(mDataset[position]);
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }


    public static class ViewHolder extends RecyclerView.ViewHolder {

        public EditText mEditText;
        private ITextWatcher mTextWatcher;

        public interface ITextWatcher {
            // you can add/remove methods as you please, maybe you dont need this much
            void beforeTextChanged(int position, CharSequence s, int start, int count, int after);

            void onTextChanged(int position, CharSequence s, int start, int before, int count);

            void afterTextChanged(int position, Editable s);
        }

        public ViewHolder(View v, ITextWatcher textWatcher) {
            super(v);

            this.mEditText = (EditText) v.findViewById(R.id.editText);

            this.mTextWatcher = textWatcher;

            this.mEditText.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    mTextWatcher.beforeTextChanged(getAdapterPosition(), s, start, count, after);
                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    mTextWatcher.onTextChanged(getAdapterPosition(), s, start, before, count);
                }

                @Override
                public void afterTextChanged(Editable s) {
                    mTextWatcher.afterTextChanged(getAdapterPosition(), s);
                }
            });
        }
    }

}

@dkarmazi 솔루션을 구현했지만 도움이되지 않았습니다. 그래서 저는 더 나아가 진정으로 작동하는 솔루션이 있습니다.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private String[] mDataset;

    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                               int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_edittext, parent, false);
        // pass MyCustomEditTextListener to viewholder in onCreateViewHolder
        // so that we don't have to do this expensive allocation in onBindViewHolder
        ViewHolder vh = new ViewHolder(v, new MyCustomEditTextListener());

        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        // update MyCustomEditTextListener every time we bind a new item
        // so that it knows what item in mDataset to update
        holder.myCustomEditTextListener.updatePosition(holder.getAdapterPosition());
        holder.mEditText.setText(mDataset[holder.getAdapterPosition()]);
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }

    @Override
    public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
        ((ViewHolder) holder).enableTextWatcher();
    }

    @Override
    public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) {
        ((ViewHolder) holder).disableTextWatcher();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public EditText mEditText;
        public MyCustomEditTextListener myCustomEditTextListener;

        public ViewHolder(View v, MyCustomEditTextListener myCustomEditTextListener) {
            super(v);

            this.mEditText = (EditText) v.findViewById(R.id.editText);
            this.myCustomEditTextListener = myCustomEditTextListener;
        }

        void enableTextWatcher() {
            mEditText.addTextChangedListener(myCustomEditTextListener);
        }

        void disableTextWatcher() {
            mEditText.removeTextChangedListener(myCustomEditTextListener);
        }
    }

    // we make TextWatcher to be aware of the position it currently works with
    // this way, once a new item is attached in onBindViewHolder, it will
    // update current position MyCustomEditTextListener, reference to which is kept by ViewHolder
    private class MyCustomEditTextListener implements TextWatcher {
        private int position;

        public void updatePosition(int position) {
            this.position = position;
        }

        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
            // no op
        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
            mDataset[position] = charSequence.toString();
        }

        @Override
        public void afterTextChanged(Editable editable) {
            // no op
        }
    }
}

가장 큰 문제는 TextWatcher가 항목 재활용 중에 계속된다는 것입니다.

재활용하기 전에 시도했지만 "beforeRecycle" 그래서 저는 onViewDetachedFromWindow방법을 사용 하여 잘 작동했습니다.

EditText custom Editable에 추가 한 후 한 가지 문제가 있습니다. 목록을 다시 제동합니다. 이유를 모르겠습니다. 아마도이 문제에 대한 해결책을 찾을 수있을 것입니다.


다음과 같이 RecyclerView 어댑터에서 onViewRecycled 메서드를 재정의합니다.

@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
    mDataSet[holder.getAdapterPosition()] = holder.mEditText.getText().toString();
}

나에 따르면 이것은 @dkarmazi의 대답을 더 최적화합니다.

public class UploadPhotoAdapter extends RecyclerView.Adapter<UploadPhotoAdapter.MyViewHolder> {
        ArrayList<Feed> feeds;
        Activity activity;
        public UploadPhotoAdapter(Activity activity, ArrayList<Feed> feeds) {
            this.feeds = feeds;
            this.activity = activity;
        }

        @Override
        public UploadPhotoAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.upload_feeds_recycler, parent, false);
            return new UploadPhotoAdapter.MyViewHolder(itemView);
        }

        @Override
        public void onBindViewHolder(final UploadPhotoAdapter.MyViewHolder holder, int position) {
            Feed feed = feeds.get(holder.getAdapterPosition());
            holder.captionEditText.setText(feed.getDescription());
            holder.captionEditText.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    feeds.get(holder.getAdapterPosition()).setDescription(s.toString());
                }
                @Override
                public void afterTextChanged(Editable s) {}
            });
        }
        @Override
        public int getItemCount() {
            return feeds.size();
        }

        public class MyViewHolder extends RecyclerView.ViewHolder {
            EditText captionEditText;
            public MyViewHolder(View view) {
                super(view);
                captionEditText = (EditText) view.findViewById(R.id.captionEditText);
            }
        }

    }

RecyclerView 개체에 익숙하지 않지만 ListView에서 동일한 문제가 발생했습니다. 이러한 경우에는 일반적으로 내 뷰에 삽입 된 값을 나타내는 임시 클래스를 만들고 (EditTexts, Checkboxes, RadioButtons ...에서 작동 함)이를 통해 업데이트 된 데이터를 가져옵니다. 그런 다음 컨테이너 객체로 구성된 사용자 지정 ArrayAdapter를 만들고, 모든 getView () 콜백에서 edittext에 넣을 값을 검색하고 이러한 객체를 최신 상태로 유지하기 위해 textwatcher를 구현합니다. 다시 말하지만 RecyclerView가 어떻게 작동하는지 정확히 기억하지 못하지만 어댑터가 포함 된 경우 시도해 볼 수있는 좋은 해킹이 될 수 있습니다.


나를 위해 위의 솔루션이 작동하지 않았습니다. 그들 중 일부는 리스너가 호출되지 않았고 리스너가 onBindViewHolder 메소드에서 호출되었을 때 리스너를 스크롤 할 때도 이벤트가 호출되는 것처럼 보입니다. '텍스트'가 변경되어 키 리스너를 시도했는데 제대로 작동했습니다. 스크롤하는 동안 아무 키도 누르지 않았습니다.

holder.ticketNo.setOnKeyListener(new View.OnKeyListener() {
                @Override
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    results.get(position).TicketNo = holder.ticketNo.getText().toString();
                    return false;
                }
            });

나를 위해 일한 코드.


문제를 해결하려면 아래 방법을 재정의하십시오.

@Override
public int getItemViewType(final int position) {
    return position;
}

참고 URL : https://stackoverflow.com/questions/31844373/saving-edittext-content-in-recyclerview

반응형