Android ListView Recycler 吓坏了,还是我失败了?
我创建了一个使用 row.xml 文件填充的自定义列表视图。每行包含两个 TextView 和一个 ImageView。我从 JSON 文件填充 TextView(但这工作正常,所以我认为您可以安全地忽略它),并且我基于 SharedPreferences 填充 ImageView...这就是问题所在。
基本上逻辑是这样的:有人单击列表视图中的一行,如果关联的活动成功完成,我通知 SharedPreferences,然后,一旦用户返回到列表,开关/案例读取 SharedPreferences 文件并更新 ImageView该特定行,显示绿色图标。如果用户使 Activity 失败,该行中的 ImageView 将显示一个红色图标。列表中第一个未完成的活动旁边会显示一个蓝色图标。
我通过在视图形成循环中为 SharedPreferences 运行 switch/case 命令来完成此操作。
所以...我的问题:我打开列表,一切正常。正如预期的那样,列表显示一个蓝色图标。当我向下滚动时,我看到下面有另一个蓝色图标......它不应该在那里。好吧,你在想“你搞砸了你的编码”。然而,当我向上滚动列表时,我看到蓝色图标现在已从预期的列表项“跳转”到下面的列表项。我再次向下滚动,看到蓝色图标成倍增加 - 现在有两个图标位于不同的两行中,而以前不存在。这样我就完成了一个任务。绿色图标出现在应有的位置。我上下滚动几次,突然在不同的列表行旁边出现了 3-4 个绿色图标。
我唯一的猜测是回收商吓坏了。我说得对吗?我可以做些什么来解决这个问题吗?我试着偷偷摸摸 - 我对不需要 imageview 的行使用了“setVisibility-GONE”...不过经过一些滚动后,它就在那里 - 它令人讨厌的绿色照亮了它不可能所在的行。这是我的源代码:
public class MainList extends ListActivity {
static SharedPreferences statusSettings;
String jsonString;
static JSONObject json;
static JSONArray arrayTest;
static int bob = 3;
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
try {
JSONObject e = arrayTest.getJSONObject(position);
Intent intent = new Intent();
intent.putExtra("clientName", e.getString("proj_name"));
intent.putExtra("clientAddress", e.getString("c_address"));
intent.putExtra("clientComments", e.getString("comments"));
intent.putExtra("clientOrder", e.getString("order"));
intent.setClass(MainList.this, ClientDetails.class);
MainList.this.startActivity(intent);
} catch (JSONException e) {
// TODO Auto-generated catch block
Log.e("JSON", "Problem creating object from array!");
e.printStackTrace();
}
}
private static class EfficientAdapter extends BaseAdapter {
private LayoutInflater mInflater;
public EfficientAdapter(Context context) {
// Cache the LayoutInflate to avoid asking for a new one each time.
mInflater = LayoutInflater.from(context);
}
public int getCount() {
return arrayTest.length();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.mainlist, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.icon = (ImageView) convertView.findViewById(R.id.ivMainIcon);
holder.textName = (TextView) convertView.findViewById(R.id.tvMainName);
holder.textAddress = (TextView) convertView.findViewById(R.id.tvMainAddress);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder.
JSONObject e;
try {
e = arrayTest.getJSONObject(position);
holder.textName.setText(e.getString("proj_name"));
holder.textAddress.setText(e.getString("c_address"));
switch (statusSettings.getInt(e.getString("order"), 0)){
case 1:
holder.icon.setVisibility(View.INVISIBLE);
break;
case 2:
if(bob == 3){
holder.icon.setImageResource(R.drawable.next_delivery);
bob = 5;
}
break;
case 3:
holder.icon.setImageResource(R.drawable.delivered_icon);
break;
case 4:
holder.icon.setImageResource(R.drawable.undelivered_icon);
break;
}
} catch (JSONException e1) {
// TODO Auto-generated catch block
Log.e("JSON", "Couldn't put one of JSON arrays into object");
e1.printStackTrace();
}
return convertView;
}
static class ViewHolder {
ImageView icon;
TextView textName;
TextView textAddress;
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
jsonString = getIntent().getExtras().getString("jsonString");
try {
json = new JSONObject(jsonString);
arrayTest = json.getJSONArray("client_list");
} catch (JSONException e) {
// TODO Auto-generated catch block
Log.e("JSON", "Couldn't create the JSON Array");
e.printStackTrace();
}
setListAdapter(new EfficientAdapter(this));
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
bob = 3;
statusSettings = getSharedPreferences("status", 0);
setListAdapter(new EfficientAdapter(this));
}
}
任何帮助将不胜感激。
/编辑:如果您需要它 - 这是 row.xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/LinearLayout02"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" >
<TextView
android:id="@+id/tvMainName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:text="Large Text"
android:textColor="#FFFFFF"
android:textSize="25dp" />
<ImageView
android:id="@+id/ivMainIcon"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_gravity="bottom"
android:layout_marginRight="20dp"
android:layout_marginBottom="10dp"
android:layout_weight="0"
android:scaleType="fitXY" />
</LinearLayout>
<TextView
android:id="@+id/tvMainAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="5dp"
android:text="TextView"
android:textSize="15dp" />
</LinearLayout>
更新的 Switch/Case (由 Brigham 提供)...不过仍然使用带有 bob 变量的半成品解决方案。至少现在一切都正常!:
switch (statusSettings.getInt(e.getString("order"), 0)){
case 1:
holder.icon.setVisibility(View.INVISIBLE);
break;
case 2:
if(bob.toString().equalsIgnoreCase("do it") || bob.toString().equalsIgnoreCase(e.getString("proj_name"))){
holder.icon.setVisibility(View.VISIBLE);
holder.icon.setImageResource(R.drawable.next_delivery);
bob = e.getString("proj_name");
}else{
holder.icon.setVisibility(View.INVISIBLE);
}
break;
case 3:
holder.icon.setVisibility(View.VISIBLE);
holder.icon.setImageResource(R.drawable.delivered_icon);
break;
case 4:
holder.icon.setVisibility(View.VISIBLE);
holder.icon.setImageResource(R.drawable.undelivered_icon);
break;
default:
holder.icon.setVisibility(View.INVISIBLE);
break;
}
I have created a custom listview that is populated using a row.xml file. Each row contains two TextViews and an ImageView. I populate the TextView from a JSON file (but this works fine, so I think you can safely ignore this), and I populate the ImageView based on SharedPreferences...which is where the problem lies.
Basically the logic is this: Someone clicks on a row in the listview, if the associated activity is completed successfully, I inform SharedPreferences and then, once the user returns to the list, a switch/case reads the SharedPreferences file and updates the ImageView in that specific row, to display a green icon. If the user fails the Activity, the ImageView in that row displays a red icon. A blue icon is displayed next to the first uncompleted activity in the list.
I do this by running a switch/case command for the SharedPreferences in the View formation loop.
So...my problem: I open the list, everything works fine. The list displays a single blue icon, as expected. When I scroll down, I see another blue icon further down...which shouldn't be there. OK, you're thinking "you F**ked up your coding". However - when I scroll back up the list, I see the blue icon has now 'jumped' from the expected list item, to the one below. I scroll back down again and I see the blue icon has multiplied - there are now two of them in two different rows, where none existed before. So I completed a task. A green icon appears where it should. I scroll up and down a few times and suddenly there are 3-4 green icons next to different list rows.
My only guess is that the recycler is freaking out. Am I right? Is there something I can do to fix this? I tried being sneaky - I used 'setVisibility-GONE' for rows that where imageview is not needed...after some scrolling though, there it is - it's annoying greenness lighting up the row it cannot possibly be in. Here's my source code:
public class MainList extends ListActivity {
static SharedPreferences statusSettings;
String jsonString;
static JSONObject json;
static JSONArray arrayTest;
static int bob = 3;
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
try {
JSONObject e = arrayTest.getJSONObject(position);
Intent intent = new Intent();
intent.putExtra("clientName", e.getString("proj_name"));
intent.putExtra("clientAddress", e.getString("c_address"));
intent.putExtra("clientComments", e.getString("comments"));
intent.putExtra("clientOrder", e.getString("order"));
intent.setClass(MainList.this, ClientDetails.class);
MainList.this.startActivity(intent);
} catch (JSONException e) {
// TODO Auto-generated catch block
Log.e("JSON", "Problem creating object from array!");
e.printStackTrace();
}
}
private static class EfficientAdapter extends BaseAdapter {
private LayoutInflater mInflater;
public EfficientAdapter(Context context) {
// Cache the LayoutInflate to avoid asking for a new one each time.
mInflater = LayoutInflater.from(context);
}
public int getCount() {
return arrayTest.length();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.mainlist, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.icon = (ImageView) convertView.findViewById(R.id.ivMainIcon);
holder.textName = (TextView) convertView.findViewById(R.id.tvMainName);
holder.textAddress = (TextView) convertView.findViewById(R.id.tvMainAddress);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder.
JSONObject e;
try {
e = arrayTest.getJSONObject(position);
holder.textName.setText(e.getString("proj_name"));
holder.textAddress.setText(e.getString("c_address"));
switch (statusSettings.getInt(e.getString("order"), 0)){
case 1:
holder.icon.setVisibility(View.INVISIBLE);
break;
case 2:
if(bob == 3){
holder.icon.setImageResource(R.drawable.next_delivery);
bob = 5;
}
break;
case 3:
holder.icon.setImageResource(R.drawable.delivered_icon);
break;
case 4:
holder.icon.setImageResource(R.drawable.undelivered_icon);
break;
}
} catch (JSONException e1) {
// TODO Auto-generated catch block
Log.e("JSON", "Couldn't put one of JSON arrays into object");
e1.printStackTrace();
}
return convertView;
}
static class ViewHolder {
ImageView icon;
TextView textName;
TextView textAddress;
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
jsonString = getIntent().getExtras().getString("jsonString");
try {
json = new JSONObject(jsonString);
arrayTest = json.getJSONArray("client_list");
} catch (JSONException e) {
// TODO Auto-generated catch block
Log.e("JSON", "Couldn't create the JSON Array");
e.printStackTrace();
}
setListAdapter(new EfficientAdapter(this));
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
bob = 3;
statusSettings = getSharedPreferences("status", 0);
setListAdapter(new EfficientAdapter(this));
}
}
Any help would be greatly appreciated.
/edit: in case you need it - here is the row.xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/LinearLayout02"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" >
<TextView
android:id="@+id/tvMainName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:text="Large Text"
android:textColor="#FFFFFF"
android:textSize="25dp" />
<ImageView
android:id="@+id/ivMainIcon"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_gravity="bottom"
android:layout_marginRight="20dp"
android:layout_marginBottom="10dp"
android:layout_weight="0"
android:scaleType="fitXY" />
</LinearLayout>
<TextView
android:id="@+id/tvMainAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="5dp"
android:text="TextView"
android:textSize="15dp" />
</LinearLayout>
Updated Switch/Case (courtesy of Brigham)...still using a half-assed solution with the bob variable though. At least everything now works as it should!:
switch (statusSettings.getInt(e.getString("order"), 0)){
case 1:
holder.icon.setVisibility(View.INVISIBLE);
break;
case 2:
if(bob.toString().equalsIgnoreCase("do it") || bob.toString().equalsIgnoreCase(e.getString("proj_name"))){
holder.icon.setVisibility(View.VISIBLE);
holder.icon.setImageResource(R.drawable.next_delivery);
bob = e.getString("proj_name");
}else{
holder.icon.setVisibility(View.INVISIBLE);
}
break;
case 3:
holder.icon.setVisibility(View.VISIBLE);
holder.icon.setImageResource(R.drawable.delivered_icon);
break;
case 4:
holder.icon.setVisibility(View.VISIBLE);
holder.icon.setImageResource(R.drawable.undelivered_icon);
break;
default:
holder.icon.setVisibility(View.INVISIBLE);
break;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在您的
情况 2
中,您仅在bob == 3
时设置图像。您应该添加一个 else 子句,并在bob != 3
时使图像不可见。您还应该确保在 switch 语句之前将图标设置为可见,因为大多数情况都要求它可见。您还应该考虑在 switch 语句中添加默认情况。
In your
case 2
, you only set the image ifbob == 3
. You should add an else clause and make the image invisible whenbob != 3
. You should also make sure to set the icon to visible before the switch statement since most of your cases require it to be visible.You should also consider adding a default case to your switch statement.
另一种选择是禁用您的回收器。您可能不想这样做,但您所要做的就是删除 ConvertView==null 及其 else 语句。这将阻止工件回收视图。
Another option would be to disable your recycler. You may not want to do that but all you have to do is remove the convertView==null and it's else statement. This will stop the artifacts from recycling views.