/* * CheckBoxをちゃんと機能させるために色々やっていきつきました * * 問題点として、 * ・チェック入れた場所と違う場所にチェックが入る * ことがある。 * ・チェックして画面外にでて戻ったらチェックが消えてる。 * * くらいでしょうか。 * 改善の考え方として、まずどういう風な仕組みでLisutViewが * 構成されているかを考えました。 * * 結局前回でわかっていましたが、画面に表示されているlayout * 部品は、使い回しにされている。 * ということが確実なものとなりました。 * * なので画面外に出たチェックしたものも消えるのも当然ですね(つд・) * そのチェックしたアイテムの状態を保存しておいて、次にその * アイテムが表示したときにその保存された状態を渡してあげればいいです。 * * てことで、アイテム分のBoolean状態を保存する変数を用意し、 * CheckBoxの変化があればそこに保存することにしました。 * * 変なとこにチェックが入るというのは、はっきりいってなぜ入るか * ワカリマセヌ * * しかしそれなら、全アイテムのBooleanを保存しているのですから、 * 常時更新させてあげればいいわけです。ソウデス、チカラワザです。 * * 今回ソースに詳しく書きました。新しいメソッドがでます。 * これに救われました。 * * OnScrollListener * * こいつはListViewの状態を教えてくれる便利なものです。 * これを使い、画面に表示させているlayout部品のアクセス権を常時設定し * 状態を保存しています。 * 「今、画面はこんなんですよ?」 * って感じですね。 * それでいざCheckBoxに変化があると、 * 監視して保存していたlayout部品のCheckBoxを、画面に表示させている分と * 保存している分を比較し、一致している同じく保存しているidで、チェック状態 * のみを保存している変数に設定します。 * * これで画面でCheckBoxの変化を捉えて、ListViewの全アイテムのから、完全一致 * する場所のBoolean値を変化させることができます。 * * となるとあとは前回と同じで、全アイテムのBoolean値を使い画面を更新させて * あげれば、チェックのものはチェックが入って、入ってないものは入らずで更新 * されます。 * * ぶっちゃけもっと簡単な方法があると思うのですが、これで解決できましたし、 * 画面のスクロールが重いということもありません。 * あるとしたら、ちょっとチェックの動作が遅いかな?くらいでした。 * * 新しいメソッドも学べたのでこれでヨシとしますヽ(*´∀`)ノ */
|
ファイル名「MainActivityjava」 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.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.CheckBox; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; 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; /////追加 /* * material_list_checkですが、すべてのアイテムの * Boolean値を設定保存させています。 */ public List<Boolean> material_list_check; CheckBox check; TextView text1; TextView text2; @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); } /////implements OnScrollListenerしたので自動追加 /* * タップした時に呼び出すメソッドがあるように、ListViewがスクロール * するときに呼び出されるメソッドがあります。 * メソッドが2つ自動生成され、 * * onScrollは引数から第1引数は、ListViewに設定されている行ごとのアイテム * 全部のことです。なのでアクセスする場合はview.getChildAt(position)な感じ * で一つ一つにアクセスすることになります。 * * 第二引数は表示されているアイテムの一番最初のポジションです。 * 例えば、ListViewに10個アイテムがセットされていて、表示が3個目から5個目 * まで表示されてたらその3個目が最初なので「3」がはいります。本当は2です * 一番最初が0個目からはじまるので。 * * 第三引数は今いくつ分画面に表示されているかです。 * たまに上半分が切れて表示されているアイテムとかも1つに入るので、そのスクロール * 停止位置により変動します。 * * 第四引数はListViewに表示させる予定の全アイテムの数です。 */ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // TODO 自動生成されたメソッド・スタブ objlistadapter.no_position.clear(); for(int i=0;i<visibleItemCount;i++){ /* * ここのfor分で、画面に表示されている分だけのlayoutの部品のアクセス権を設定して * それぞれの状態を保存しています。 * これは常時やっているのでスクロールするごとに保存していることになります。 */ check = (CheckBox) view.getChildAt(i).findViewById(objlistadapter.holder.h_checkbox.getId()); text1 = (TextView) view.getChildAt(i).findViewById(objlistadapter.holder.h_id.getId()); check.setChecked(material_list_check.get(firstVisibleItem+i)); //ここで保存 objlistadapter.no_position.add(new SetData(check, Integer.valueOf(text1.getText().toString()))); } } /////implements OnScrollListenerしたので自動追加 /* * こちらは上記とも似ているのですがよくわかりません。 * 色々試したんですが、スクロールの移動量というか大きさスピード * が大きくなると第2引数の値が変化する気がします。 */ public void onScrollStateChanged(AbsListView view, int scrollState) { // TODO 自動生成されたメソッド・スタブ //使用していません。 } /////追加 /* * CheckBoxに変化があるとここにきます。 * こちらで保存させている変数を引数で渡さ * れたIDの場所のBoolean値を変化させます。 * ここでは前回やったListViewの位置の保存 * 最後に画面更新 */ public void Row_Tap(int id,boolean check){ material_list_check.set(id,check); listview_position = objlistview.getFirstVisiblePosition(); listview_y = objlistview.getChildAt(0).getTop(); findView(); } /* * 今回はタップもチェックもできるのでその証拠にTost生成 */ 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.checkboxtest4; 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.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{ private Context context; private List<Object> list; public MainActivity mk; //public出来るようにコッチに移動 ViewHolder_Kouka holder; /////追加 public List<SetData> no_position; 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.setChecked(ob.isCheck()); holder.h_checkbox.setOnCheckedChangeListener(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 自動生成されたメソッド・スタブ makeTost("CheckBoxをタップ",0); for(int i=0;i<no_position.size();i++){ if(no_position.get(i).check ==buttonView ) { /* * 画面に表示されたアイテム分だけ * 保存しておいたCheckBoxと渡されたCheckBoxを比較し * 一致したらここを動作させます */ mk.Row_Tap(no_position.get(i).id,isChecked); break; } } } /* * エラーなど確認用などに便利。おまけ */ 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; } }
|
今回は画像はなしです。動きは前回と同じなので。 簡単にするためにidをレイアウトに追加しました。このidも見栄えがいやなら見えなくしてもいいですし、すごく小さくしてもいいかもしれません。しかし部品としては必要なので、必ず設定して下さい。 しかしながら変数があっち行ったりこっち行ったり、あっちに渡したりこっちに渡したりなソースになっています。 でもモットーの「動けばおk!」の状態で動作にストレスもないのでこれでいいかな・・・(つд・) |