ListViewの使用中に発生した問題


今週ListViewの練習中に問題が発生し、データベースから検索したデータがLIstViewにバインドされ、あるitemを長押しして削除操作を行い、itemをクリックするたびに取得したidが間違っていたため、半日調整してやっと原因を見つけた.肝心なのは自分がカスタマイズしたBaseAdapterを理解していないことだ.
まずListViewがBaseAdapterを使用する最適化テクニックについてお話しします.
一般的にシステムが提供するAdapterがプロジェクトのニーズを満たすことができない場合、自分でAdpaterをカスタマイズし、getViewなどの方法を実現してitemごとにスタイルを1つずつ与える必要があります.getViewメソッドは、Itemオブジェクトごとに画面に描画されると、「ねえ、boy、私はあなたの前に現れますが、私がどんな顔をしてほしいのか分かりません.getViewメソッドでスタイルを選んでください」というメッセージを送ってくれます.そこでitemの新しいスタイルレイアウトを描きます.しかしitemというやつが私たちに手伝ってもらうたびに、レイアウトオブジェクトを再描画し、明らかにシステムの性能に影響を与えるので、最適化してレイアウトをキャッシュする必要があります.ここで公式に推奨されているのはViewHolderオブジェクトを採用することです.キャッシュから直接取り出しitemに捨てる.
具体的なコードは以下の通りです.
public class NoteListAdapter extends BaseAdapter {
	private List<Note> noteList = null;
	private LayoutInflater layoutInflater;
	private static final String TAG = NoteListAdapter.class.getSimpleName();
	
	public NoteListAdapter(Context context, List<Note> list) {
		this.noteList = list;
		layoutInflater = LayoutInflater.from(context);
	}
	
	public void setNoteList(List<Note> noteList) {
		this.noteList = noteList;
	}
    
	//      List Item   
	@Override
	public int getCount() {
		return noteList.size();
	}

	//       Item  
	@Override
	public Object getItem(int position) {
		return noteList.get(position);
	}

	//  Item Id
	@Override
	public long getItemId(int position) {
		return position;
	}

	@SuppressLint("SimpleDateFormat")
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		Log.i(TAG, "getView   ");
		ViewHolder holder;
		if (convertView == null) {
			convertView = layoutInflater.inflate(
					R.layout.note_list, null);
			holder = new ViewHolder();
			TextView tv = (TextView) convertView
					.findViewById(R.id.tv_info_title);
			holder.setInfotitle(tv);
			TextView tvtime = (TextView) convertView
					.findViewById(R.id.tv_info_time);
			holder.setInfotime(tvtime);
			convertView.setTag(holder);
		}else {
			holder = (ViewHolder) convertView.getTag();
		}
		if(!TextUtils.isEmpty(noteList.get(position).getTitle().toString().trim())){
		    long id = noteList.get(position).getId();
		    String title = noteList.get(position).getTitle();
		    Date date = noteList.get(position).getCreateDate();
		    Log.i(TAG, " Adapter    Id"+id);
		    holder.getInfotitle().setText(title);
		    SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
		    holder.getInfotime().setText(format.format(date));
        }
		return convertView;
	}


	private class ViewHolder {
		private TextView infotitle;
		private TextView infotime;

		public TextView getInfotitle() {
			return infotitle;
		}

		public void setInfotitle(TextView infotitle) {
			this.infotitle = infotitle;
		}

		public TextView getInfotime() {
			return infotime;
		}

		public void setInfotime(TextView infotime) {
			this.infotime = infotime;
		}

	}

  }

ここにある場所から気づかなかった
 long id = noteList.get(position).getId();

このコードidはpostitionの位置に基づいて得られる.
そこでitemをクリックすると取得したidはパラメータのid変数ではなくパラメータのposition変数で取得されるはずです
開始時のエラーコードを先に示します.
	//  
	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		Intent intent = new Intent(this,NoteAddActivity.class);
		//       id   
		intent.putExtra("id", id);
		intent.putExtra("edit", true);
		startActivity(intent);
	}
	

正しいコードは次のとおりです.
	
	//  
	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		Intent intent = new Intent(this,NoteAddActivity.class);
		id = noteList.get(position).getId();
		intent.putExtra("id", id);
		intent.putExtra("edit", true);
		startActivity(intent);
	}

また、コンテキストメニューでitemのidを取得する場合、MenuItemオブジェクトパラメータが1つしかない場合は、AdapterViewを利用する.AdapterContextMenuInfoオブジェクト取得id
コードクリップは次のとおりです.
@Override
	public boolean onContextItemSelected(MenuItem item) {
		//         
	    AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo(); 
		//   Item ID
		final Long MID = noteList.get(info.position).getId();
		switch (item.getItemId()) {
		//  
		case ITEM1:
			service = DbService.getInstance(this);
			AlertDialog.Builder builder = new AlertDialog.Builder(ManagerActivity.this);
			builder.setTitle("  ").setIcon(android.R.drawable.ic_dialog_alert).setMessage("     ?")
			.setNegativeButton(" ", new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					dialog.dismiss();
				}
			}).setPositiveButton(" ", new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					Runnable deleteNote = new Runnable() {
						@Override
						public void run() {
							//     handler
							service.deleteNote(MID);
							Toast.makeText(ManagerActivity.this, "    ", Toast.LENGTH_SHORT)
							.show();
							Log.i(TAG, "   ID"+MID);
						    noteList = service.loadAllNote();
						    ma.setNoteList(noteList);
							ma.notifyDataSetChanged();
						}
					};
					myHandler.post(deleteNote);
				}
			}).create().show();
			return true;
最後に注意した点は、adpterのlistデータが変化した場合、新しいlistオブジェクトsetをadpterオブジェクトに渡す必要があり、後にnotifyDataSetChanged()メソッドを加えて再描画することを忘れないでください.