Androidプログラミング日記 (仮)  

新解釈 魔のCheckBox!!(補正編)

あなたは

人目のプログラマーだよ。

Androidプログラミング日記 (仮).

 

 

 

 

CheckBox

・・・モンダイハッセイ!

実は、前回のCheckBox解決編のソースを組み込んでデバッグ作業をしていたところ、よくわからない問題が発生していました。その問題を解決すべく onCheckedChangedメソッドを監視していたとこ、重大な欠陥というより勘違いをしていたことが判明しました。それを書いていきます。

 

/*
 * public void onCheckedChanged(CompoundButton,boolean)
 * このメソッドの説明として
 *
 * 「CheckBoxのチェックの有無(変化)があると呼び出される」
 *
 * と思っていて、たしかにこの通りなのです。しかしこの通り
 * すぎて見落としがあります。それは
 *
 * 「CheckBoxのタップを検知しているわけではない」
 *
 * ということなのです。
 * 多くの実験及び脳内会議によってこれがわかりました。
 *
 * どういう状況で問題があったかというと
 *
 * チェックが変な所に入るなら、元にしているチェックの有無
 * も入っているデータをもう一度すべて参照にして表示させれば
 * 良い。
 *
 * とする前回の考え方をした場合を前提にこのやり方をするな
 * らば、チェックが入った時点で、元データを書き換えることを
 * します。
 *
 * で、ここで重要なのは、元データを書き換えるタイミングなの
 * ですが、onCheckedChangedメソッド内で書き換えるとしますと
 * 、当然このあと元データ書き換え→再表示 です。
 * そしてここで問題発生、再表示された瞬間、もう一度
 * onCheckedChanged
 * が呼ばれてしまうのです。
 * 再表示とは、
 *
 * 「一度表示された部品を破棄してもう一度表示する」
 *
 * ようなものです。そして先に書いた
 *
 * 「CheckBoxのチェックの有無(変化)があると呼び出される」
 *
 * 「CheckBoxのタップを検知しているわけではない」
 *
 * を組み合わせると、再表示→onCheckedChanged呼び出し
 *
 * となるわけです。
 *
 * このメソッドに色々書くでしょうから、2度も呼び出されては
 * 都合が悪いのです。
 *
 * てことであまり今までの考え方や書き方を変えたくないですし、
 * 色々調べた結果、
 *
 * 「OnItemClickListener」
 *
 * を付け加えることにしました。これを
 * 「onCheckedChanged」とおなじくimplementsして下さい。
 *
 * これは、「部品どれかをタップしたら呼び出される」とか
 * 指定できますが、今回の場合はそのまま使用することで
 * CheckBoxをタップしたら、になります。
 *
 *	PS:たぶんですがonCheckedChangedに渡されている
 *		第一引数の.Pressed などでタップかどうかを
 *		判断できるとは思うのですが、その判断を調べる
 *		のがメンドーだったので確実にこちらの方法を取って
 *		います。・゜・(ノД`)・゜・
 *
 * しかもちょっと手を加えればonCheckedChangedと連携する
 * ことができます。ここでソースです。
 */

 

ファイル名「MainActivityjava」

package com.example.checkboxtest5;

import java.util.ArrayList;
import java.util.List;


import android.os.Bundle;
import android.app.Activity;
import android.graphics.PixelFormat;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView.OnItemClickListener;

public class MainActivity extends Activity  implements OnItemClickListener,OnScrollListener{
	private static final int
	MENU_ITEM0=0;

	float disp_w;
	float disp_h;

	Hensu hensu = new Hensu();

	static ListAdapter objlistadapter;
	ListView objlistview;
	static List<Object> objList = new ArrayList<Object>();

	int listview_position = 0;
	int listview_y = 0;

	int old_listview_position = 0;
	int old_listview_y = 0;

	//NEW!
	public int visibleitemcount;
	public int firstvisibleitem;

	public List<Boolean> material_list_check;


@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	requestWindowFeature(Window.FEATURE_NO_TITLE);
	getWindow().setFormat(PixelFormat.TRANSLUCENT);
		Window window = getWindow();
		window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		WindowManager manager = window.getWindowManager();
		Display disp = manager.getDefaultDisplay();
    disp_w = disp.getWidth();
    disp_h = disp.getHeight();

    init();
}

public void init(){

	material_list_check = new ArrayList<Boolean>();

	for(int i=0;i<hensu.Name.length;i++)
	{
		objList.add(new Object(hensu.check[i],i,hensu.Name[i]));
		material_list_check.add(false);
	}


	findView();
}

public void findView(){
	setContentView(R.layout.listview);

	objlistview = (ListView)findViewById(R.id.listView1);

	objlistview.setOnItemClickListener(this);
	objlistadapter = new ListAdapter(this,objList);
	objlistview.setAdapter(objlistadapter);
	objlistview.setSelectionFromTop(listview_position, listview_y);
	objlistview.setOnScrollListener(this);
}

public void onScroll(AbsListView view, int firstVisibleItem,
		int visibleItemCount, int totalItemCount) {
	// TODO 自動生成されたメソッド・スタブ

	//NEW!
	visibleitemcount = visibleItemCount;
	firstvisibleitem = firstVisibleItem;

	//NEW!
	/*
	 * 前回と違うのが、ただたんにCheckBoxの画面更新しているだけ
	 */
	for(int i=0;i<visibleItemCount;i++){
		objlistadapter.check = (CheckBox) view.getChildAt(i).findViewById(objlistadapter.holder.h_checkbox.getId());
		objlistadapter.check.setChecked(material_list_check.get(firstVisibleItem+i));
	}

}

public void onScrollStateChanged(AbsListView view, int scrollState) {
	// TODO 自動生成されたメソッド・スタブ
	//使用していません。
}

public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id) {
	makeTost("ListViewをタップ",0);
}

  //オプションメニューを選択した時の処理
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()){
case MENU_ITEM0:
	finish();
return true;
}
return true;
}
/*
 * エラーなど確認用などに便利。おまけ
 */
public void makeTost(String str,int id){
	int tost = Toast.LENGTH_LONG;
	if(id==1) tost = Toast.LENGTH_SHORT;
	Toast toast = Toast.makeText(this.getApplicationContext(),
			str, tost);
		toast.show();
	}
}
						

ファイル名「ListAdapterjava」

package com.example.checkboxtest5;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.CompoundButton.OnCheckedChangeListener;


class ListAdapter extends BaseAdapter implements OnCheckedChangeListener,OnClickListener{
	private Context context;
	public List<Object> list;
	public MainActivity mk;
	ViewHolder_Kouka holder;
	public List<SetData> no_position;
	public CompoundButton buttonview;

	//NEW!
	CheckBox check;
	TextView text1;

	public ListAdapter(Context context,List obj) {
		super();
		this.context = context;
		list = obj;
		mk = (MainActivity)context;
		no_position = new ArrayList<SetData>();
	}

	public int getCount() {return list.size();}
	public Object getItem(int position) {return list.get(position);}
	public long getItemId(int position) {return position;}

	public View getView(int position, View convertView, ViewGroup parent) {
		//ViewHolder_Kouka holder;public出来るように移動

		Object ob = (Object) getItem(position);

		if (convertView == null) {
			LayoutInflater inflater = LayoutInflater.from(context);
			convertView = inflater.inflate(R.layout.listview_row, null);

			holder = new ViewHolder_Kouka();
			holder.h_checkbox = (CheckBox) convertView.findViewById(R.id.checkBox1);
			holder.h_name = (TextView) convertView.findViewById(R.id.textView1);

			holder.h_id = (TextView) convertView.findViewById(R.id.textView2);

			convertView.setTag(holder);
		} else {
			holder = (ViewHolder_Kouka) convertView.getTag();
		}

		holder.h_checkbox.setOnCheckedChangeListener(this);

		//NEW!
		//これを忘れずに
		holder.h_checkbox.setOnClickListener(this);

		holder.h_name.setText(ob.getName());
		holder.h_id.setText(Integer.toString(ob.getId()));

		return convertView;
	}

	/*
	 * CheckBoxに変化があると呼ばれるメソッド
	 */
	public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
		// TODO 自動生成されたメソッド・スタブ
		//すべてonClickに移動
		//NEW!
		/*
		 * この変数によりonClickと連携できるようになります
		 */
		buttonview = buttonView;
	}

	//NEW!
	/*
	 * CheckBoxをタップされると呼ばれるメソッド
	 */
	public void onClick(View v) {

		//makeTost("CheckBoxをタップ",0);
		// TODO 自動生成されたメソッド・スタブ

		//NEW! こっちに移動してきました
		/*
		 * 画面に表示されている部品分だけループさせます
		 * 今回のプログラムでは、画面表示分だけループさ
		 * せていますが、今回のような場合はループしなく
		 * てもよいかもしれません。
		 * が、確実に画面上の部品をとらえたいのでこの方法
		 * をとっています。
		 */
		no_position.clear();
		for(int i=0;i<mk.visibleitemcount;i++){

			/*
			 * ここのfor分で、画面に表示されている分だけのlayoutの部品のアクセス権を設定して
			 * それぞれの状態を保存しています。
			 */
			check = (CheckBox) mk.objlistview.getChildAt(i).findViewById(R.id.checkBox1);
			text1 = (TextView)  mk.objlistview.getChildAt(i).findViewById(R.id.textView2);

			//ここで画面部品保存
			no_position.add(new SetData(check, Integer.valueOf(text1.getText().toString())));

			/*
			 * 画面CheckBoxに変化があった部品ならば
			 */
			if(check ==buttonview){
				/*
				 * このアダプターに渡されている全アイテムから
				 * チェックに変化があったアイテムをIDで探す
				 */
				for(int j=0;j<list.size();j++){
					/*
					 * 全アイテムとタップした部品のIDが一致したなら
					 */
					if(list.get(j).getId() == Integer.parseInt(text1.getText().toString())){
						/*
						 * ここに渡されているlistを直接書き換え
						 * 画面表示用の変数も書き換え
						 */
						list.get(j).setCheck(buttonview.isChecked());
						mk.material_list_check.set(j,buttonview.isChecked());
						break;
					}
				}
			}

		}

		//画面ListViewポジション記憶
		mk.listview_position = mk.objlistview.getFirstVisiblePosition();
		mk.listview_y = mk.objlistview.getChildAt(0).getTop();
		//画面再表示
		mk.findView();

	}

    /*
     * エラーなど確認用などに便利。おまけ
     */
    public void makeTost(String str,int id){
    	int tost = Toast.LENGTH_LONG;
    	if(id==1) tost = Toast.LENGTH_SHORT;
    	Toast toast = Toast.makeText(mk.getApplicationContext(),
    			str, tost);
   		toast.show();
   	}


}

class ViewHolder_Kouka {
	CheckBox h_checkbox;
	TextView h_id;
	TextView h_name;
}

/*
 * 画面に表示されているアイテムのデータを入れるクラス
 * CheckBox部品そのものとアイテムのIDがはいっています。
 */
class SetData{
	CheckBox check;
	int id;
	SetData(CheckBox check,int id){
		this.check = check;
		this.id = id;
	}
}


						

 

 

 

今回も画像はなしです。動きは前回と同じなので。

てことで前回の問題点、変数があっちいったりこっちいったりがなくなりスッキリしました。

もうあれです。画面に表示させているオブジェクトはすべて把握したほうがいいってことと思います。なにかしらの変数に入れておいたほうがいいですね。

 

 

Androidプログラミング日記 (仮) | サイトマップ | 個人情報保護方針 | 応援メールテヘペロ | ©2012 Japan  相互リンク大募集中です