1

アクティビティに ListView があり、行ごとに、TextView と 2 つのボタンを使用してカスタム レイアウトを使用しています。これら 2 つのボタンのいずれかをクリックすると、特定のアクションが実行されます。ArrayAdapter の getView メソッドで、onClickListeners をこれら 2 つのボタンに設定しました。

public View getView(int position, View convertView, ViewGroup parent) {

    View v = convertView;
    if (v == null) {
        v = li.inflate(R.layout.process_row, null);
    }


    final Button processCheck = (Button) v.findViewById(R.id.processCheck);
    processCheck.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            if (process.isChecked() == false) {
                process.setChecked(true);
                processCheck.setBackgroundColor(Color.BLUE);
            }
            else {
                process.setChecked(false);
                processCheck.setBackgroundColor(Color.RED);
            }

        }
    });

    return v;
}

ArrayList は mz カスタム クラス Process のオブジェクトを保持し、コード内のプロセスはこのクラスのインスタンスです。したがって、ListView 行のいずれかでこのボタンをクリックすると、指定された Process インスタンスのブール変数が変更され、ボタンの色が変更されることが期待されます。それは起こりますが、この1行だけでなく、さらに3〜4行です。クリックすると、1 つではなく 5 つの変更されたボタンが表示されます。ここで何が間違っているか知っていますか?私は ListActivity のコーディングをよく知らないことに注意してください。ほとんどのコードは、実際にはサンプル ファイルから少しコピーして編集したものです。ありがとう!

編集:

問題はおそらくどこかのレイアウトだけです。ボタンをクリックすると、より多くの色が変わりますが、クリックしたプロセスだけがブール値が変更されます。

4

4 に答える 4

0

問題はここにあります

View v = convertView;
    if (v == null) {
        v = li.inflate(R.layout.process_row, null);
    }

これは、View vが表示されているアイテムに対してのみ作成され、再利用されるためです。したがって、ifv == null)を削除するだけで、コードは次のようになります。

public View getView(int position, View convertView, ViewGroup parent) {

    View v = convertView;

        v = li.inflate(R.layout.process_row, null);



    final Button processCheck = (Button) v.findViewById(R.id.processCheck);
    processCheck.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            if (process.isChecked() == false) {
                process.setChecked(true);
                processCheck.setBackgroundColor(Color.BLUE);
            }
            else {
                process.setChecked(false);
                processCheck.setBackgroundColor(Color.RED);
            }

        }
    });

    return v;
}
于 2012-05-12T21:06:26.027 に答える
0

getView は、リストがレンダリングされるたびに呼び出されます。したがって、色を設定するために gerview 条件に追加する必要があります: public View getView(int position, View convertView, ViewGroup parent) {

View v = convertView;

    v = li.inflate(R.layout.process_row, null);

//これらの行を以下に追加します

if (process.isChecked() == false) {

        processCheck.setBackgroundColor(Color.BLUE);
    }
    else {

        processCheck.setBackgroundColor(Color.RED);
    }

final Button processCheck = (Button) v.findViewById(R.id.processCheck);
processCheck.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        if (process.isChecked() == false) {
            process.setChecked(true);
            processCheck.setBackgroundColor(Color.BLUE);
        }
        else {
            process.setChecked(false);
            processCheck.setBackgroundColor(Color.RED);
        }

    }
});

return v;

}

于 2012-05-12T21:17:30.280 に答える
0

編集:投稿の最後にある「すぐに使える」例を見つけてください!

複数の行が影響を受けていることがわかるので、システムがリソースをリサイクルする方法に何か関係があると思います。おそらく への参照Buttonがあいまいです。

このプラクティスをどこで習得したかはわかりません (Android チュートリアル、またはこれらのチュートリアルで Android を学んだ以前の開発者)。ただし、ネストされたクラスを使用することをお勧めしますViewHolder

private static class ViewHolder {

    public Button processCheck;
}

これをアダプターまたはgetView()宣言されているクラスに配置し、次のように変更します。

public View getView(int position, View convertView, ViewGroup parent) {

    View v = convertView;
    $ViewHolder viewHolder; //wait for the magic!
    if (v == null) {
        v = li.inflate(R.layout.process_row, null);
        $viewHolder = new ViewHolder();
        $viewHolder.processCheck = (Button) v.findViewById(R.id.processCheck);
        $v.setTag(viewHolder);
        //ok we somewhat stuffed an object with the Button into our View... so what?
    }

    $viewHolder = (ViewHolder) v.getTag();
    //see explanation below

    $viewHolder.processCheck.setOnClickListener(new View.OnClickListener() {
    //set the onClickListener for this and only this button.
        @Override
        public void onClick(View v) {
            if (process.isChecked() == false) {
                process.setChecked(true);
                processCheck.setBackgroundColor(Color.BLUE);
            }
            else {
                process.setChecked(false);
                processCheck.setBackgroundColor(Color.RED);
            }

        }
    });

    return v;
}

(私は $ で変更をマークしました。Eclipse を使用する場合、この方法で簡単に見つけることができると思います。それ以外の場合は、検索 + 置換 ^^)

したがって、getTag()は呼び出されたビューに関連付けられたオブジェクトを返します。Object は非常に汎用的であるため、作成した ViewHolder オブジェクトに呼び出すことができます。したがって、ボタンを参照してオブジェクトに保存できます。
これで、ListView を取得するたびに、オブジェクトを取得してOnclickListener新しいものと新しいものを設定できます。このようにして、呼び出されるリスナーは 1 つだけになります。

v.setTag();変更を「保存」するために再度呼び出す必要がないことに注意してください。

なんらかの理由で同じリスト内の異なるレイアウトを膨らませるなどのクレイジーなことをすると、落とし穴になる可能性があります。ViewHandlers次のように、それらに異なるものを割り当てたくなるかもしれません。

if (v == null) {
    if( someCriteria) {
        v = li.inflate(R.layout.process_row, null);
        viewHolder = new ViewHolder(); 
    }
        v = li.inflate(R.layout.process_another_row, null);
        viewHolder = new AnotherViewHolder(); 
}

次に、getView()いずれかの ViewHolder をチェックせずに呼び出すと、実行時にエラーが発生する可能性があります。

于 2012-05-12T21:25:01.010 に答える
0

これは私がそれを解決した方法です:

私のアダプターでは、クラスを作成しました:

private static class ViewHolder {
    public Button       processCheck;
    public Process      process;
    public TextView     name;
}

ボタンのカスタムリスナーを作成したよりも:

private class CustomListener implements View.OnClickListener {

    public ViewHolder   viewHolder;

    public CustomListener(ViewHolder holder) {
        this.viewHolder = holder;
    }

    @Override
    public void onClick( View v ) {
        if (viewHolder.process.isChecked()) {
            viewHolder.name.setText("not checked");
            viewHolder.process.setChecked(false);               
        }
        else {
            viewHolder.name.setText("checked");
            viewHolder.process.setChecked(true);                
        }           
    }
}

そして私の getView 関数:

public View getView( int position, View convertView, ViewGroup parent ) {
    ViewHolder viewHolder;

    if ( convertView == null ) {
        convertView = mInflater.inflate( R.layout.process_row, null );

        viewHolder = new ViewHolder();
        viewHolder.processCheck = (Button) convertView.findViewById(R.id.processCheck);
        viewHolder.name = (TextView) convertView.findViewById(R.id.processName);            
        convertView.setTag( viewHolder );
    }


    viewHolder = (ViewHolder) convertView.getTag();
    viewHolder.process = list.get(position);

    if (viewHolder.process.isChecked()) {
        viewHolder.name.setText("checked");         
    } 
    else {
        viewHolder.name.setText("not checked");         
    }       

    CustomListener listener = new CustomListener( viewHolder );
    viewHolder.processCheck.setOnClickListener(listener);

    return convertView;
}
于 2012-05-13T12:28:57.703 に答える