2016-08-05 19 views
-1

私はListViewに項目を動的に追加しようとしています。私が初めてそれを行うと(リストが空であるため)、getView()はかなりうまく動作します。convertViewはnullであるため、ViewHolderを使用してアイテムのビューへの参照を格納し、setTagを通してビューにバインドします)。しかし、2番目のアイテムをリストに追加すると、notifyOnDataSetChanged()と呼ぶと本当に奇妙な動作が気づいています。getVIewは、listViewに新しい項目を追加すると、既に存在していた項目のconvertView == nullを持っています

基本的にnotifyOnDataSetChanged()は、すべての表示項目を再描画し、最初の表示項目から開始します。問題は、既に描画されていた最初のアイテムが再描画されているときにconvertView == nullが発生するため、新しいビューを展開してfindViewById()を使用して、 ViewHolder。 2番目のアイテムについては、最初のものへの参照を持つconvertViewを受け取るので、2番目のアイテムのためにそれを見つける必要があります。私が欲しいのは、最初に作成したビュー(ViewHolder内のすべての参照)を最初のアイテム用に取得し、2番目のアイテム用の新しいビューを作成することです。それを行う方法はありますか?

EDIT 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は、問題の主な原因であった各項目に対して少なくとも2回getView()を呼び出すように強制します。

1

あなたはリストビューの賛成でRecyclerviewを使用する必要があります。 Recyclerviewを使用すると、データ配列からの変更をより正確にGUIに公開することができます。 RecyclerView.Adapter.notifyItemInsertedまたはRecyclerView.Adapter.notifyItemChangedです。

+0

'ListView'が' RecyclerView'よりも好まれる理由があることに留意してください。 'CursorAdapter'でそれを使用します。 – Egor

1

あなたが達成しようとしているのは、基本的にListViewのキャッシングソリューションを最適化することです。キャッシングソリューションは不可能でもなくてもかまいません。 ViewHolderの実装では、キャッシュの動作方法には無関係である必要があります。どのような場合でもコードが正しく動作することを確認し、キャッシングロジックをListViewのままにしておきます。

関連する問題