英文:
How to make changes in a custom listview programmatically that should immediately reflect?
问题
我正在尝试将一个按钮放置在自定义的ListView中,该按钮应该重置该行中所有小部件的值(例如:取消所有的RadioButton选择)。我在 getView() 方法中使用了类似这样的代码:
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick() {
editor.putBoolean("rb1" + position, false).apply();
editor.putBoolean("rb2" + position, false).apply();
rb1.setChecked(false);
rb2.setChecked(false);
et.setText("");
notifyDataSetChanged();
}
});
但是更改不会立即反映出来。当我点击按钮时,单选按钮不会被取消选择。相反,有时会取消选择另一行中的单选按钮。但是当我向下滚动然后再回到那个位置时,我会看到更改。有人能告诉我如何解决这个问题吗?
注意:在 getView() 方法内部,我还指定了根据 SharedPreferences 值来选择或取消选择单选按钮。
英文:
I am trying to put a button in a custom listview that should reset all the values of the widgets in that row (eg: unchecking all the radiobuttons). I have used something like this inside getView()
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick() {
editor.putBoolean("rb1"+position,false).apply();
editor.putBoolean("rb2"+position,false).apply();
rb1.setChecked(false);
rb2.setChecked(false);
et.setText("");
notifyDataSetChanged();
}
}
But the changes are not reflected immediately. When I click on the button, the radio buttons are not unchecked. Rather, the radiobutton in another row gets unchecked sometimes. But when I scroll down and again come back to that place, I see the change. Can anyone say how to solve this problem?
Note: Inside the getView() method, I have also mentioned that the radio buttons should be checked or unchecked based on the SharedPreferences values.
EDIT
I am writing the full code.
ArrayAdapter class
import android.app.Activity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
public class MyListAdapter extends ArrayAdapter<String> {
private final Activity activity;
View rowView;
TextView tv;
RadioGroup rg;
RadioButton rb1, rb2;
EditText etQty;
ImageView imageView;
public MyListAdapter(Activity activity, String[] a) {
super(activity, R.layout.activity_row, a);
this.activity = activity;
}
public View getView(final int position, final View convertView, ViewGroup parent) {
LayoutInflater inflater = activity.getLayoutInflater();
if(convertView == null)
rowView = inflater.inflate(R.layout.activity_row, parent, false);
else
rowView = convertView;
tv = rowView.findViewById(R.id.textView);
rg = rowView.findViewById(R.id.radioGroup);
rb1 = rowView.findViewById(R.id.rbFB);
rb2 = rowView.findViewById(R.id.rbCB);
etQty = rowView.findViewById(R.id.etQty);
imageView = rowView.findViewById(R.id.imageView);
imageView.setClickable(true);
rb1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
notifyDataSetChanged();
Month.editor.putInt(Worker.name + Open.year + Month.month + (position+1) + "item",1).apply();
}
});
rb2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
notifyDataSetChanged();
Month.editor.putInt(Worker.name + Open.year + Month.month + (position+1) + "item",2).apply();
}
});
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Month.editor.putInt(Worker.name + Open.year + Month.month + (position+1) + "item",0).apply();
Month.editor.putInt(Worker.name + Open.year + Month.month + (position+1) + "dz",0).apply();
rb1.setChecked(false);
rb2.setChecked(false);
etQty.setText("");
notifyDataSetChanged();
}
});
etQty.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) {
if (!charSequence.toString().equals("")) {
Month.editor.putInt(Worker.name + Open.year + Month.month + (position + 1) + "dz", Integer.parseInt(charSequence.toString())).apply();
}
}
@Override
public void afterTextChanged(Editable editable) {
}
});
tv.setText((position+1)+"/"+Month.month+"/"+"20"+Open.year);
if (Month.settings.getInt(Worker.name + Open.year + Month.month + (position+1) + "item",0) == 1) {
rb1.setChecked(true);
notifyDataSetChanged();
}
else if (Month.settings.getInt(Worker.name + Open.year + Month.month + (position+1) + "item",0) == 2) {
rb2.setChecked(true);
notifyDataSetChanged();
}
if (Month.settings.getInt(Worker.name + Open.year + Month.month + (position+1) + "dz",0) != 0) {
etQty.setText(Integer.toString(Month.settings.getInt(Worker.name + Open.year + Month.month + (position + 1) + "dz", 0)));
notifyDataSetChanged();
}
return rowView;
}
@Override
public int getViewTypeCount() {
return getCount();
}
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public String getItem(int position) {
return getItem(position);
}
}
The MainActivity
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
public class MainActivity extends AppCompatActivity {
ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] a;
if (Month.month == 1 || Month.month == 3 || Month.month == 5 || Month.month == 7 || Month.month == 8 || Month.month == 10 || Month.month == 12)
a = new String[] {"a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"};
else if (Month.month == 4 || Month.month == 6 || Month.month == 9 || Month.month == 11)
a = new String[] {"a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"};
else {
if (Open.year % 4 == 0) {
a = new String[] {"a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"};
}
else {
a = new String[] {"a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"};
}
}
MyListAdapter adapter = new MyListAdapter(this,a);
listView = findViewById(R.id.listView);
listView.setAdapter(adapter);
}
}
And the Layout that is being inflated in the ListView
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintBottom_toBottomOf="@+id/etQty"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.348"
app:layout_constraintStart_toEndOf="@+id/etQty"
app:layout_constraintTop_toTopOf="@+id/etQty"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="@drawable/delete" />
<TextView
android:id="@+id/textView"
android:layout_width="110dp"
android:layout_height="40dp"
android:textSize="20sp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.053"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.46" />
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="130dp"
android:layout_height="40dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="@+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.074"
app:layout_constraintStart_toEndOf="@+id/textView"
app:layout_constraintTop_toTopOf="@+id/textView"
app:layout_constraintVertical_bias="0.0">
<RadioButton
android:id="@+id/rbFB"
android:layout_width="60dp"
android:layout_height="40dp"
android:text="FB"
android:textSize="20sp" />
<RadioButton
android:id="@+id/rbCB"
android:layout_width="60dp"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:text="CB"
android:textSize="20sp" />
</RadioGroup>
<EditText
android:id="@+id/etQty"
android:layout_width="50dp"
android:layout_height="40dp"
android:ems="10"
android:inputType="phone"
android:textSize="20sp"
android:textAlignment="center"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/radioGroup"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.118"
app:layout_constraintStart_toEndOf="@+id/radioGroup"
app:layout_constraintTop_toTopOf="@+id/radioGroup"
app:layout_constraintVertical_bias="0.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
The purpose of the ImageView is to uncheck all the radio buttons and set the text in the EditText to " " when clicked.
答案1
得分: 0
你的视图管理存在一般性问题。
每当列表需要显示下一个项目时,都会调用getView(...)
,这意味着此方法中使用的所有视图应仅在此方法内部访问。但你将它们存储在类成员中。这就是导致一个项目的视图影响另一个项目更改的原因。因为它是在加载item1时初始化的,但在其onClick(...)
回调中,类成员变量引用item2,因为在触发点击事件之前多次调用了getView()
。如果希望使用的视图都属于同一个布局,就必须在getView(...)
的范围内工作。
我注意到另一件肯定会导致错误的事情是你某些适配器方法的实现。具体来说:
@Override
public int getViewTypeCount() {
return getCount();
}
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public String getItem(int position) {
return getItem(position);
}
查阅一下它们的文档,然后相应地进行实现。
getItemViewType()
肯定不应该返回一个位置。
getViewTypeCount()
应该返回不同视图类型的数量,而不是getCount()
返回的项目数量。
getItem()
被重写了,并调用了自身。因此,getItem()
将陷入无限循环,直到线程由于堆栈溢出而崩溃。
所有这些实现都是错误的。我建议查阅ArrayAdapter
的文档,可能还有一些示例实现。然后正确地重新编写你的适配器,所有问题也将消失。
请注意,notifyDataSetChanged()
用于通知适配器有关数据模型更改的信息。因此,只有在实际更改数据时才调用它。更改单选按钮的选中状态不是操作数据!
你应该在数据模型中将标志设置为true/false,然后通知适配器进行更改。这将导致在getView(...)
被调用时,根据你的数据模型设置RadioButton
的启用/禁用状态。
英文:
You have a general problem with your View-Management.
getView(...)
is called whenever the list needs a view to display the next item. This means, that all Views used within this method should only be accessed within this method. But you store them in class members. This is which causes the behavior that the view of one item causes another item to change. Because it was initialized when item1 was loaded, but in its onClick(...)
callback the class member variables reference item2 because getView()
was called multiple times before the click event was triggered. You have to work inside the scope of getView(...)
if you want the Views that you work with to belong all to the same layout.
Another thing i noticed which surely causes errors is your implementation of some of the Adapters methods. Specifically
@Override
public int getViewTypeCount() {
return getCount();
}
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public String getItem(int position) {
return getItem(position);
}
Read up on their documentation, then implement them accordingly.
getItemViewType()
surely isn't supposed to return a position.
getViewTypeCount()
is supposed to return the count of different view types, not the count of items which getCount()
returns.
getItem()
is overriden and calls it self. So getItem()
would run into an infinite loop until the Thread would crash due to a stack overflow.
These are all implemented wrongly. I suggest looking up the documentation of ArrayAdapter and maybe also some exmaple implementations. Then redo your adapter properly and all your issues will vanish as well.
Note that notifyDataSet()
is supposed to inform the adapter about changes in the datamodel. So only call it if you actually changed data. Changing the checked-state of a RadioButton is not manipulating data!
You should rather set the flag in your datamodel to true/false, then notify the adapter about the change. This will cause getView(...)
to be called where you then set the RadioButton
enabled/disabled based on your datamodel.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论