2016-08-05 54 views
-1

我試圖動態地在ListView中添加項目。當我第一次這樣做(所以列表爲空)時,getView()工作得很好,convertView爲null,因此我可以使用ViewHolder來存儲對項目視圖的引用,並通過setTag將其綁定到視圖)。但是當我用數據添加第二個項目到列表並且調用notifyOnDataSetChanged()真的奇怪的行爲已經被注意到了。當我添加新項目到列表視圖時,getVIew的convertView == null爲已存在的項目

基本上,notifyOnDataSetChanged()使所有可見項目被重新繪製,並從第一個可見項目開始。問題是,當第一個項目(之前已經繪製過)正在重繪時,convertView == null,因此我需要爲它添加新的視圖並使用findViewById()來查找我之前已經找到並存儲在內部的所有引用ViewHolder。對於第二個項目,我收到第一個參考的convertView,因此我需要爲第二個項目找到它。我想要的是獲取之前創建的視圖(並且其ViewHolder中包含所有引用),併爲第二個項目創建新視圖,即在繪製剛剛添加的項目時使convertView == null。有沒有辦法做到這一點?

編輯1:添加新項目:

alarmClocksList.add(new AlarmClock(time, songTitle, weekDaysPanelVisible, weekDaysOn)); alarmsArrayAdapter.notifyDataSetChanged();

ArrayAdapter:

package com.example; 

import android.content.Context; 
import android.content.Intent; 
import android.support.v4.app.FragmentActivity; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.ArrayAdapter; 
import android.widget.CheckBox; 
import android.widget.CompoundButton; 
import android.widget.RelativeLayout; 
import android.widget.TextView; 

import java.util.ArrayList; 

public class AlarmsArrayAdapter extends ArrayAdapter { 

    ArrayList<AlarmClock> alarmClockArrayList; 
    private Context context; 

    static class ViewHolder { 
     TextView time; 
     TextView songTitle; 
     TextView nextRingTime; 
     CheckBox repeatCheckBox; 
     RelativeLayout repeatPanel; 
     WeekDayCheckBox monday; 
     WeekDayCheckBox tuesday; 
     WeekDayCheckBox wednesday; 
     WeekDayCheckBox thursday; 
     WeekDayCheckBox friday; 
     WeekDayCheckBox saturday; 
     WeekDayCheckBox sunday; 
    } 

    public AlarmsArrayAdapter(Context context, ArrayList<AlarmClock> alarmClocks) { 
     super(context, -1, alarmClocks); 
     alarmClockArrayList = alarmClocks; 
     this.context = context; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     final ViewHolder holder; 
     if (convertView == null) { 
      LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      convertView = inflater.inflate(R.layout.alarm_clock_item, parent, false); 

      holder = new ViewHolder(); 

      holder.time = (TextView) convertView.findViewById(R.id.alarmClockTime); 
      holder.songTitle = (TextView) convertView.findViewById(R.id.alarmClockSong); 
      holder.nextRingTime = (TextView) convertView.findViewById(R.id.nextRindDay); 
      holder.repeatCheckBox = (CheckBox) convertView.findViewById(R.id.repeatCheckBox); 

      holder.repeatPanel = (RelativeLayout) convertView.findViewById(R.id.repeatPanel); 

      holder.monday = (WeekDayCheckBox) convertView.findViewById(R.id.MO); 
      holder.tuesday = (WeekDayCheckBox) convertView.findViewById(R.id.TU); 
      holder.wednesday = (WeekDayCheckBox) convertView.findViewById(R.id.WE); 
      holder.thursday = (WeekDayCheckBox) convertView.findViewById(R.id.TH); 
      holder.friday = (WeekDayCheckBox) convertView.findViewById(R.id.FR); 
      holder.saturday = (WeekDayCheckBox) convertView.findViewById(R.id.SA); 
      holder.sunday = (WeekDayCheckBox) convertView.findViewById(R.id.SU); 

      holder.songTitle.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
       } 
      }); 

      holder.time.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
        Intent intent = new Intent(context, TimePickerActivity.class); 
        ((FragmentActivity) context).startActivityForResult(intent, MainActivity.TIME_PICK_REQUEST); 
       } 
      }); 

      holder.repeatCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 
       @Override 
       public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
        if (!isChecked) { 
         holder.repeatPanel.setVisibility(View.GONE); 
        } else { 
         holder.repeatPanel.setVisibility(View.VISIBLE); 
        } 
       } 
      }); 
      convertView.setTag(holder); 
     } else 
      holder = (ViewHolder) convertView.getTag(); 

     AlarmClock alarmClock = alarmClockArrayList.get(position); 
     holder.time.setText(alarmClock.time); 
     holder.songTitle.setText(alarmClock.songTitle); 
     holder.nextRingTime.setText("No data"); 
     return convertView; 
    } 
} 
+1

發佈一些代碼.... –

+1

「有什麼辦法可以做到嗎?」 - 不。你無法控制哪個'View',如果有的話,將會交給你的'getView()'方法。而且,它根本就不重要。如果需要的話,膨脹一個'View'。否則,只需更新給定的那個。 –

+0

@MikeM。所以每次我畫新的項目(即使我只是上下滾動列表),我不得不調用'findViewByID'? –

回答

0

好吧,我終於明白了!我需要做的是將ListView的高度和寬度設置爲match_parent。如果您使用wrap_content,它會強制ListView爲每個項目調用getView()至少2次,這是我的問題的主要原因。

1

你應該贊成的ListView的使用Recyclerview。使用Recyclerview可以更加精確地將數據陣列中的變化發佈到GUI,例如, RecyclerView.Adapter.notifyItemInsertedRecyclerView.Adapter.notifyItemChanged

+0

請記住,爲什麼'ListView'可以優先於'RecyclerView',例如與'CursorAdapter'一起使用它。 – Egor

1

你試圖實現的基本上是優化ListView的緩存解決方案,你不能也不應該這樣做。您的ViewHolder實現應該不知道緩存的工作方式ListView。確保您的代碼在任何情況下都能正常工作,並將緩存邏輯保留爲ListView

相關問題