我试图在 RecyclerView 的单个元素上实现将整数转换为十进制,反之亦然的简单功能(它对我更高级的问题的简化),在每个用户输入上都存在。问题是,当我在 cons*
内部调用 public String formatDuration(Duration duration) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("h:mm.SSS");
return LocalTime.ofNanoOfDay(duration.toNanos()).format(formatter);
}
时,它最终会陷入 RecyclerView.Adapter.notifyItemChanged()
方法的无限循环中。有没有办法实现这种行为?我想更新我在 TextWatcher.afterTextChanged()
中的原始项目。
代码如下:
TextWatcher
TextWatcher.afterTextChanged()
更新:
我在 public class TestElementAdapter extends RecyclerView.Adapter<TestElementAdapter.TestElementViewHolder> {
private final Context context;
private final RecyclerView recyclerView;
private List<TestElement> testElements;
public TestElementAdapter(Context context, List<TestElement> testElements, RecyclerView recyclerView) {
this.context = context;
this.testElements = testElements;
this.recyclerView = recyclerView;
testElements.add(new TestElement(1, 1.0F));
}
private class MyTextWatcher<T> implements TextWatcher {
int position = 0;
BiConsumer<TestElement, T> consumer;
Function<String, T> mappingFunction;
public MyTextWatcher(BiConsumer<TestElement, T> consumer, Function<String, T> mappingFunction) {
this.consumer = consumer;
this.mappingFunction = mappingFunction;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (s == null || s.length() == 0) {
return;
}
TestElement testElement = testElements.get(position);
consumer.accept(testElement, mappingFunction.apply(s.toString()));
TestElementAdapter.this.recyclerView.post(() -> notifyItemChanged(position));
System.out.println("afterTextChanged");
}
public void updatePosition(int adapterPosition) {
this.position = adapterPosition;
}
}
@NonNull
@Override
public TestElementViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_element, parent, false);
TestElementViewHolder holder = new TestElementViewHolder(view);
holder.initTextWatchers();
return holder;
}
@Override
public void onBindViewHolder(@NonNull TestElementViewHolder holder, int position) {
TestElement testElement = testElements.get(position);
holder.integer.getText().clear();
holder.decimal.getText().clear();
holder.integer.append(testElement.getInteger() + "");
holder.decimal.append(testElement.getDecimal() + "");
holder.watchers.forEach(watcher -> watcher.updatePosition(holder.getAdapterPosition()));
}
@Override
public int getItemCount() {
return testElements.size();
}
public static class TestElement {
private Integer integer;
private Float decimal;
public TestElement(Integer integer, Float decimal) {
this.integer = integer;
this.decimal = decimal;
}
public Integer getInteger() { return integer; }
public Float getDecimal() { return decimal; }
public void updateByInteger(Integer integer) {
this.integer = integer;
this.decimal = integer * 1.0F;
}
public void updateByDecimal(Float decimal) {
this.decimal = decimal;
this.integer = decimal.intValue();
}
}
public class TestElementViewHolder extends RecyclerView.ViewHolder {
EditText integer;
EditText decimal;
List<MyTextWatcher<?>> watchers = new ArrayList<>();
private MyTextWatcher<Integer> integerWatcher;
private MyTextWatcher<Float> decimalWatcher;
public TestElementViewHolder(@NonNull View view) {
super(view);
integer = view.findViewById(R.id.integerNumber);
decimal = view.findViewById(R.id.decimalNumber);
}
public void initTextWatchers() {
MyTextWatcher<Integer> integerWatcher = new MyTextWatcher<>(this, TestElement::updateByInteger, Integer::parseInt);
integer.addTextChangedListener(integerWatcher);
this.integerWatcher = integerWatcher;
MyTextWatcher<Float> decimalWatcher = new MyTextWatcher<>(this, TestElement::updateByDecimal, Float::parseFloat);
decimal.addTextChangedListener(decimalWatcher);
this.decimalWatcher = decimalWatcher;
watchers.add(this.integerWatcher);
watchers.add(this.decimalWatcher);
}
}
}
中添加了禁用和启用 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:theme="@style/ThemeOverlay.AppCompat.Light"
android:orientation="vertical"
android:gravity="top">
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" android:id="@+id/linearLayout2">
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="number"
android:padding="8dp"
android:id="@+id/integerNumber"
android:gravity="center"
android:layout_weight="1"
/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:padding="8dp"
android:id="@+id/decimalNumber"
android:gravity="center"
android:layout_weight="1"
/>
</LinearLayout>
</LinearLayout>
的方法。我在 TextWatchers
之前禁用它们,然后在 ViewHolder
中启用它们。
notifyItemChanged
它对我有用。
我也将通话更改为 onBindViewHolder
来自
public void disableWatchers() {
integer.removeTextChangedListener(integerWatcher);
decimal.removeTextChangedListener(decimalWatcher);
}
public void enableWatchers() {
integer.addTextChangedListener(integerWatcher);
decimal.addTextChangedListener(decimalWatcher);
}
到
notifyItemChanged
。
从 UI 线程调用此方法使 TestElementAdapter.this.recyclerView.post(() -> notifyItemChanged(position))
响应不够快。它也可以正常工作,但仅适用于所有元素都可以适合我的 TestElementAdapter.this.notifyItemChanged(position)
的数量,如果有更多元素我收到异常:EditText
。有什么办法可以让 RecyclerView
中的 java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling...
离开并避免这个错误?
更新的适配器代码:
TestElementAdapter.this.notifyItemChanged(position)
答案 0 :(得分:1)
listernes 重复的原因是您调用 notifyItemChanged()
添加一个新的监听器,同时在调用 notifyItemChanged()
后保持当前项的当前监听器处于活动状态。
要解决此问题,您需要在使用 notifyItemChanged()
调用 editText.removeTextChangedListener()
之前禁用侦听器,然后使用 editText.addTextChangedListener()
重新启用它们。
然后你得到了
<块引用>IllegalStateException: Cannot call this method while
RecyclerView is computing a layout or scrolling
这是一个 common issue,您可以通过发布 recyclerView 更改主线程循环使用来解决该问题。
但是请确保您在此帖子中添加了侦听器以在 recyclerView 更改后进行同步,这是更改:
@Override
public void afterTextChanged(Editable s) {
if (s == null || s.length() == 0) {
return;
}
TestElement testElement = testElements.get(position);
consumer.accept(testElement, mappingFunction.apply(s.toString()));
// remove the listeners
viewHolder.disableWatchers();
recyclerView.post(() -> {
TestElementAdapter.this.notifyItemChanged(position)
// add the listeners
viewHolder.enableWatchers();
});
System.out.println("afterTextChanged");
}
这是整个班级的变化
public class TestElementAdapter extends RecyclerView.Adapter<TestElementAdapter.TestElementViewHolder> {
private final Context context;
private final RecyclerView recyclerView;
private List<TestElement> testElements;
public TestElementAdapter(Context context, List<TestElement> testElements, RecyclerView recyclerView) {
this.context = context;
this.testElements = testElements;
this.recyclerView = recyclerView;
IntStream.range(0, 20).forEach(v -> testElements.add(new TestElement(1, 1.0F)));
}
private class MyTextWatcher<T> implements TextWatcher {
int position = 0;
TestElementViewHolder viewHolder;
BiConsumer<TestElement, T> consumer;
Function<String, T> mappingFunction;
public MyTextWatcher(TestElementViewHolder viewHolder, BiConsumer<TestElement, T> consumer, Function<String, T> mappingFunction) {
this.viewHolder = viewHolder;
this.consumer = consumer;
this.mappingFunction = mappingFunction;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (s == null || s.length() == 0) {
return;
}
TestElement testElement = testElements.get(position);
consumer.accept(testElement, mappingFunction.apply(s.toString()));
viewHolder.disableWatchers();
recyclerView.post(() -> {
TestElementAdapter.this.notifyItemChanged(position)
viewHolder.enableWatchers();
});
System.out.println("afterTextChanged");
}
public void updatePosition(int adapterPosition) {
this.position = adapterPosition;
}
}
@NonNull
@Override
public TestElementViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_element, parent, false);
TestElementViewHolder holder = new TestElementViewHolder(view);
holder.initTextWatchers();
holder.disableWatchers();
return holder;
}
@Override
public void onBindViewHolder(@NonNull TestElementViewHolder holder, int position) {
TestElement testElement = testElements.get(position);
holder.integer.getText().clear();
holder.decimal.getText().clear();
holder.integer.append(testElement.getInteger() + "");
holder.decimal.append(testElement.getDecimal() + "");
holder.watchers.forEach(watcher -> watcher.updatePosition(holder.getAdapterPosition()));
holder.enableWatchers();
}
@Override
public int getItemCount() {
return testElements.size();
}
public static class TestElement {
private Integer integer;
private Float decimal;
public TestElement(Integer integer, Float decimal) {
this.integer = integer;
this.decimal = decimal;
}
public Integer getInteger() { return integer; }
public Float getDecimal() { return decimal; }
public void updateByInteger(Integer integer) {
this.integer = integer;
this.decimal = integer * 1.0F;
}
public void updateByDecimal(Float decimal) {
this.decimal = decimal;
this.integer = decimal.intValue();
}
}
public class TestElementViewHolder extends RecyclerView.ViewHolder {
EditText integer;
EditText decimal;
List<MyTextWatcher<?>> watchers = new ArrayList<>();
private MyTextWatcher<Integer> integerWatcher;
private MyTextWatcher<Float> decimalWatcher;
public TestElementViewHolder(@NonNull View view) {
super(view);
integer = view.findViewById(R.id.integerNumber);
decimal = view.findViewById(R.id.decimalNumber);
}
public void initTextWatchers() {
MyTextWatcher<Integer> integerWatcher = new MyTextWatcher<>(this, TestElement::updateByInteger, Integer::parseInt);
integer.addTextChangedListener(integerWatcher);
this.integerWatcher = integerWatcher;
MyTextWatcher<Float> decimalWatcher = new MyTextWatcher<>(this, TestElement::updateByDecimal, Float::parseFloat);
decimal.addTextChangedListener(decimalWatcher);
this.decimalWatcher = decimalWatcher;
watchers.add(this.integerWatcher);
watchers.add(this.decimalWatcher);
}
public void disableWatchers() {
integer.removeTextChangedListener(integerWatcher);
decimal.removeTextChangedListener(decimalWatcher);
}
public void enableWatchers() {
integer.addTextChangedListener(integerWatcher);
decimal.addTextChangedListener(decimalWatcher);
}
}
}