ListView 在以编程方式调整视图后滚动期间更改视图

ListView changing views during scrolling after adjusting them programmatically

昨晚我花了整整一晚上的时间来寻找解决我的问题的方法,但我没有找到,所以它不是重复的。

我有一个 listView 和简单的 ArrayAdapter。我希望每次单击项目时,它的 textView 字段都会变为粗体,而如果它存在,则之前的变为正常。这是一个非常简单的问题,但我遇到了很多麻烦。

我的设备一次可以显示 13 个项目。所以,如果我不滚动一切正常。但是如果我尝试滚动,我发现一些下一个项目也变得粗体并且点击事件工作不正常。在调试中,我看到 listView 只有前 13 个元素。我了解适配器会异步加载其他适配器,但在这些情况下如何解决我的问题?

这是我的代码:

ListView listLibrary;
int posPrev;
ArrayAdapter<String> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_about_info);

    listLibrary = findViewById(R.id.listSongs);
    listLibrary.setOnItemClickListener(this);

    fillList();
    posPrev = -1;
}

private void fillList() {
    String[] arrList = {"New York", "San Francisco", "Los Angeles", "Dallas", "Houston", "Tucson", "Phoenix", "Denver", "Las Vegas", "Kansas City", "Chicago", "Detroit", "Atlanta", "Miami", "Philadelphia", "Baltimore", "Washington", "Boston", "Pittsburg", "Columbus", "Cincinnati", "Indianapolis", "St. Louis", "Seattle", "Portland", "San Jose", "San Diego", "San Antonio"};
    ArrayList<String> cities = new ArrayList<String>();
    for (String s : arrList)
        cities.add(s);

    if (listLibrary != null) {
        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, cities);
        listLibrary.setAdapter(adapter);
    }
}

@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {

    if (posPrev != -1) {
        ((TextView)listLibrary.getChildAt(posPrev)).setTypeface(null, Typeface.NORMAL);
    }

    ((TextView)listLibrary.getChildAt(i)).setTypeface(null, Typeface.BOLD);
    posPrev = i;
}

编辑 因此,正如您所建议的,我添加了自定义适配器。如果 tvCaption.setTypeface(null, Typeface.NORMAL) 被删除,我会在列表的下一个随机获得粗体项目。其实我明白这个问题我不知道如何解决。

 ArrayList<String> cities;
Context context;
int resource;
TextView tvCaption;
int posClicked;

public CustomArrayAdapter(@NonNull Context context, int resource, @NonNull List<String> objects) {
    super(context, resource, objects);

    this.context = context;
    this.resource = resource;
    cities = (ArrayList) objects;

    posClicked = -1;
}

@NonNull
@Override
public View getView(int position, @Nullable View view, @NonNull ViewGroup parent) {
    if (view == null) {
        LayoutInflater layoutInflater = LayoutInflater.from(context);
        view = layoutInflater.inflate(resource, null);
    }

    tvCaption = view.findViewById(R.id.tvCaption);
    tvCaption.setText(cities.get(position));

    if (posClicked != -1) {
        if (posClicked == position)
            tvCaption.setTypeface(null, Typeface.BOLD);
    } else
        tvCaption.setTypeface(null, Typeface.NORMAL);

    LinearLayout llCity = view.findViewById(R.id.llCity);
    llCity.setTag(position);
    llCity.setOnClickListener(this);

    return view;
}

@Override
public void onClick(View view) {
    posClicked = (int)view.getTag();
    notifyDataSetChanged();
}

最后编辑:有工作代码!

所有编辑后我得到了这个代码:

Activity:

ListView listLibrary;
CustomArrayAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_about_info);

    listLibrary = findViewById(R.id.listSongs);
    listLibrary.setOnItemClickListener(this);

    fillList();
}

private void fillList() {
    String[] arrList = {"New York", "San Francisco", "Los Angeles", "Dallas", "Houston", "Tucson", "Phoenix", "Denver", "Las Vegas", "Kansas City", "Chicago", "Detroit", "Atlanta", "Miami", "Philadelphia", "Baltimore", "Washington", "Boston", "Pittsburg", "Columbus", "Cincinnati", "Indianapolis", "St. Louis", "Seattle", "Portland", "San Jose", "San Diego", "San Antonio"};
    ArrayList<String> cities = new ArrayList<String>();
    for (String s : arrList)
        cities.add(s);

    if (listLibrary != null) {
        adapter = new CustomArrayAdapter(this, R.layout.list_custom_item, cities);
        listLibrary.setAdapter(adapter);
    }
}

@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {

    adapter.setCurClickedPos(i);
    adapter.notifyDataSetChanged();

}

和自定义适配器:

ArrayList<String> cities;
Context context;
int resource;
int curClickedPos;

public CustomArrayAdapter(@NonNull Context context, int resource, @NonNull List<String> objects) {
    super(context, resource, objects);

    this.context = context;
    this.resource = resource;
    cities = (ArrayList) objects;

    curClickedPos = -1;
}

@NonNull
@Override
public View getView(int position, @Nullable View view, @NonNull ViewGroup parent) {
    if (view == null) {
        LayoutInflater layoutInflater = LayoutInflater.from(context);
        view = layoutInflater.inflate(resource, null);
    }

    TextView tvCaption = view.findViewById(R.id.tvCaption);
    tvCaption.setText(cities.get(position));

    if (curClickedPos != -1) {
        if (curClickedPos == position)
            tvCaption.setTypeface(null, Typeface.BOLD);
        else
            tvCaption.setTypeface(null, Typeface.NORMAL);
    } else
        tvCaption.setTypeface(null, Typeface.NORMAL);

    LinearLayout llCity = view.findViewById(R.id.llCity);
    llCity.setTag(position);

    return view;
}

public void setCurClickedPos(int curClickedPos) {
    this.curClickedPos = curClickedPos;
}

如果我是对的,您希望 touched/clicked 项目视图中有粗体文本。

假设posPrev代表触摸项的索引,

  • 首先你应该调用 notifyDataSetChanged()(在点击事件之后)
  • 然后在自定义适配器的getView方法下添加这段代码:

    public View getView(int position, View convertView, ViewGroup parent)  {
    
        yourTextView.setTypeface(null, Typeface.NORMAL);
    
        if(posPrev == position){
            yourTextView.setTypeface(null, Typeface.BOLD);
        }
    
    }
    

如果你的 getView() 函数,你正在重用你的视图。因此,在创建视图时,您必须检查该项目是否被点击。当您检查 posClicked 是否不等于 -1 时,在该 if 块中您需要添加一个 else 语句,如下所示:

public View getView(int position, @Nullable View view, @NonNull ViewGroup parent) {
    // your logic here
    if (posClicked != -1) {
        if (posClicked == position)
            tvCaption.setTypeface(null, Typeface.BOLD);
        else
            tvCaption.setTypeface(null, Typeface.NORMAL);
    } else
        tvCaption.setTypeface(null, Typeface.NORMAL);
    }
}