【问题标题】:progressBar in ListView changes parent on scrollListView 中的进度条在滚动时更改父级
【发布时间】:2013-10-06 01:32:23
【问题描述】:

我有一个 ListView,其中包含使用特定布局创建的行。每行都有默认为 View.GONE 的 ProgressBar。单击此元素时,我需要使 ListView 项中的 ProgressBar 可见。问题是,当我单击元素时,ProgressBars 不仅在我单击的行中变得可见,而且在其他随机行中也可见。代码如下:

创建 ListView 并设置 OnItemClickListener:

private void buildTracklist(ArrayList<Audio> differences, ArrayList<Audio> vkAudioList) {
        ArrayList<Map<String, Object>> data = new ArrayList<Map<String, Object>>(vkAudioList.size());
        Map<String, Object> m;
        int i;
        for (i=0;i<vkAudioList.size();i++) {
            m = new HashMap<String, Object>();
            m.put(AUDIO_ARTIST, vkAudioList.get(i).artist);
            m.put(AUDIO_TITLE, vkAudioList.get(i).title);
            if (arrayListContainsAudio(differences, vkAudioList.get(i))) {
                m.put(AUDIO_DOWNLOADED, R.drawable.image_blank);
            } else {
                m.put(AUDIO_DOWNLOADED, R.drawable.image_downloaded);
            }
            data.add(m);
        }
        String[] from = {AUDIO_TITLE,AUDIO_ARTIST,AUDIO_DOWNLOADED};
        int[] to = {R.id.audioTitleTextView,R.id.audioArtistTextView,R.id.isDownloadedImageView};
        sAdapter = new SimpleAdapter(this, data, R.layout.tracklist_item, from, to);
        audioListView = (ListView) findViewById(R.id.audioListView);
        audioListView.setAdapter(sAdapter);

        audioListView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Log.i(TAG,"position="+position+" id="+id);

                view.findViewById(R.id.audioItemProgressBar).setVisibility(View.VISIBLE);
                //Here I'm passing progressBar further
            }
        });
    }

tracklistItem 布局:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/audioTitleTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:layout_marginTop="2dp"
            android:gravity="top|left"
            android:text="Title"
            android:textSize="24dp" />

        <TextView
            android:id="@+id/audioArtistTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/audioTitleTextView"
            android:layout_marginLeft="5dp"
            android:layout_marginTop="2dp"
            android:text="Artist"
            android:textSize="16sp" />

        <ProgressBar
            android:id="@+id/audioItemProgressBar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_below="@+id/audioArtistTextView"
            android:visibility="gone" />

    </RelativeLayout>

    <ImageView
        android:id="@+id/isDownloadedImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|right"
        android:src="@drawable/downloaded_backgroundv10"
        android:visibility="visible" />

</FrameLayout>

我尝试使用 getView() 的 getChildAt() 获取 listview 项目视图,但发生了同样的问题。感谢您的帮助,抱歉我的英语不好。

更新:当我滚动 ListView 时,我发现我使用的 progressBar 会更改父项,但无论 ListView 的哪个部分当前在屏幕上,我都需要它在一行中。

【问题讨论】:

    标签: android listview android-listview listviewitem


    【解决方案1】:

    ListView 和所有适配器视图一样,使用视图回收。

    问题是,当我单击元素时,ProgressBars 不仅在我单击的行中变得可见,而且在其他随机行中也可见。

    我敢打赌,当您滚动视图时会发生这种情况。它的工作原理如下:显示进度条,然后滚动列表。带有进度条的项目滚动到屏幕的可见区域之外。 这是视图被回收的时刻。系统将其从列表视图中分离并再次附加以显示列表的另一行。这就是您再次看到进度条的原因。

    怎么办?

    您应该在自定义适配器的 getView() 方法中打开进度条的可见性(可以基于BaseAdapter)。是否开启取决于一些标志。

    阅读this 以了解有关适配器视图中视图回收的更多信息。


    我需要更改进度条的可见性,它是我的进度 单击元素,而不是在我创建列表视图时。

    只需在您的适配器中定义一个方法,该方法将更改标志的值。然后你可以调用 notifyDataSetChanged。

    处理适配器视图时要记住的重要一点是,您更改的是底层数据,而不是视图,因为您的列表中可能有数千个项目,但只有少数视图可以处理可见行。

    【讨论】:

      【解决方案2】:

      您的代码看起来不错,但我想您应该使用 baseAdapter 而不是 SimpleAdapter。如果您要在每一行上显示或隐藏视图,建议您使用 baseAdapter。

      这是 baseAdapter 的示例。首先,创建一个名为 MyBaseAdapter 的新类。

      public class MyBaseAdapter  extends BaseAdapter{
      
          private Activity mContext;
          private ArrayList<Map<String, Object>> mList;
      
          MyBaseAdapter(Activity context, ArrayList<Map<String, Object>> list){
              this.mContext = context;    
              mList = list;
          }
      
          @Override
          public int getCount() {
              return mList.size();        
          }
      
          @Override
          public Map<String, Object> getItem(int position) {      
              return mList.get(position);
          }
      
          @Override
          public long getItemId(int position) {       
              return position;
          }
      
      
          private static class ViewHolder {
              public final TextView audioTitleTextView, autioArtistTextView;
              public final ImageView isDownloadedImageView;
              public final FrameLayout fl;
      
      
              public ViewHolder(TextView audioArtist, TextView audioTitle,ImageView isDownloaded, FrameLayout fl){
                  this.autioArtistTextView = audioArtist;
                  this.audioTitleTextView = audioTitle;
                  this.isDownloadedImageView = isDownloaded;
                  this.fl = fl;
              }
          }
      
          @Override
          public View getView(int position, View convertView, ViewGroup parent){
      
      
              TextView audioTitleTextView,autioArtistTextView;
              FrameLayout fl;
      
              ImageView isDownloadedImageView;
              /*
               * If convertView is not null, tried reuse it
               * else create LayoutInflater to pack up the row view
               */
              if(convertView == null){            
                  convertView = LayoutInflater.from(mContext).inflate(R.layout.tracklistItem , parent,false);
                  audioTitleTextView = (TextView)convertView.findViewById(R.id.audioTitleTextView);
                  autioArtistTextView = (TextView)convertView.findViewById(R.id.audioArtistTextView);     
                  isDownloadedImageView = (ImageView)convertView.findViewById(R.id.isDownloadedImageView);
                  fl = (FrameLayout)convertView.findViewById(R.id.parentFrameLayout);
                  convertView.setTag(new ViewHolder(autioArtistTextView,audioTitleTextView,isDownloadedImageView));
      
      
              } else{
                  ViewHolder holder = (ViewHolder)convertView.getTag();
                  audioTitleTextView = holder.audioTitleTextView;
                  autioArtistTextView = holder.autioArtistTextView;
                  isDownloadedImageView = holder.isDownloadedImageView;
                  fl = holder.fl;
              }
      
      
              @SuppressWarnings("unchecked")
              Map<String, Object> rowItem = getItem(position);
      
      
              audioTitleTextView.setText(rowItem.get(AUDIO_TITLE));   
              autioArtistTextView.setText(rowItem.get(AUDIO_ARTIST));
      
              // Change setImageResource to your own image resources
              isDownloadedImageView.setImageResource(rowItem.get(AUDIO_DOWNLOADED));
      
              fl.setOnClickListener( new View.OnClickListener(){
                   @Override
                   public void onClick(View view){
                        // hide your progress bar here
                   }
              });
      
              return convertView;
          }   
      

      然后,将您的 SimpleAdapter 更改为以下

      MyBaseAdapter sAdapter = new MyBaseAdapter(this, data);
      audioListView = (ListView) findViewById(R.id.audioListView);
      audioListView.setAdapter(sAdapter);
      

      编辑:将 FrameLayout 添加到 viewHolder 并设置 FrameLayout OnClickListener

      【讨论】:

      • 感谢您的帮助,但即使您的代码也会出现同样的问题。也许创建 LinearLayout 而不是 ListView 并逐个添加项目会起作用吗?
      • 还是同样的问题,我无法将这个进度条传递给 onClick 和程序的其他部分,如 asynctasks
      【解决方案3】:

      要了解为什么会发生此问题,您必须首先了解 ListView 的工作原理。互联网上有很多关于它的信息,所以我将在这里简要介绍。如果您的适配器中有 50 个项目,那么这并不意味着 ListView 将为您创建 50 行。相反,它会创建 5 可能是 10 可能更多,具体取决于您的屏幕大小,但不是 50!然后当你滚动ListView 时,就会发生这种情况。滚动出屏幕的行将被重复使用。 getView() 将被调用,您必须再次设置每个属性,否则旧的将被重新使用。在您的情况下,该行的可见性值被重新使用。

      解决方法如下:

      将可见性数据存储在您的数据ArrayList 中并覆盖适配器的getView() 方法,然后像这样处理这两种情况:

      if(data.isProressVisible){
         audioItemProgressBarView.setVisibility(View.VISIBLE);
      }else{
         audioItemProgressBarView.setVisibility(View.INVISIBLE);
      }
      

      【讨论】:

      • 我需要更改进度条的可见性,当我单击元素时它是进度,而不是在我创建列表视图时。
      猜你喜欢
      • 1970-01-01
      • 2011-05-22
      • 2010-12-26
      • 2018-11-21
      • 2011-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多