显示列表视图后,尝试以编程方式设置 LISTVIEW 内 SWITCH 的状态

Trying to programmatically set the state of a SWITCH inside a LISTVIEW after the listview is displayed

所以我有一个警报列表,并将其绑定到列表视图 lstAlarms。在我的自定义 ListView 布局中,我还有一个开关,我想根据警报的状态以编程方式设置它。我想在刚刚显示 ListView 后立即执行此操作。

请看下面我的代码。 显示 ListView 的方法是 DisplayAlarmList()。 我试图用来设置开关状态的方法是 InitSwitches(),它在 DisplayAlarmList() 内部被调用。 在onCreate()方法中调用了DisplayAlarmList()

public void DisplayAlarmList()
{
    final String[] columns = {Database.getAlarmID(), Database.getAlarmTime(), Database.getAlarmName(), Database.getAlarmStatus(), Database.getAlarmRepeats()};
    Cursor c = Database.selectAlarm(db, Database.getTableName(), columns, null, null, null, null, null);

    int[] to = new int[]{
            R.id.alarmID,
            R.id.alarmTime,
            R.id.alarmName,
            R.id.alarmStatus,
            R.id.alarmRepeats,
    };

    SimpleCursorAdapter ca = new SimpleCursorAdapter(this,
            R.layout.alarm_info,
            c,
            columns,
            to,
            0);

    lstAlarm.setAdapter(ca);
    InitSwitches();

    lstAlarm.setOnItemClickListener(new OnItemClickListener()
    {

        @Override
        public void onItemClick(AdapterView<?> listView, View view, int position, long id)
        {
            Alarm alarm = new Alarm();
            Cursor selectedCursor = (Cursor) listView.getItemAtPosition(position);

            Switch s = (Switch) view.findViewById(R.id.alarmSwitch);

            String whereArgs = Integer.toString(selectedCursor.getInt(selectedCursor.getColumnIndexOrThrow(Database.getAlarmID())));


            Cursor data = Database.RawQuery(db, "SELECT * FROM " + Database.getTableName() + " WHERE " + Database.getAlarmID() + " = " + whereArgs);

            if (data.moveToFirst())
            {
                alarm.setAlarmID(data.getString(data.getColumnIndexOrThrow(Database.getAlarmID())));
                alarm.setAlarmName(data.getString(data.getColumnIndexOrThrow(Database.getAlarmName())));
                alarm.setAlarmTime(data.getString(data.getColumnIndexOrThrow(Database.getAlarmTime())));
                alarm.setAlarmSound(data.getString(data.getColumnIndexOrThrow(Database.getAlarmSound())));
                alarm.setAlarmRepeats(data.getString(data.getColumnIndexOrThrow(Database.getAlarmRepeats())));
                alarm.setAlarmStatus(data.getString(data.getColumnIndexOrThrow(Database.getAlarmStatus())));
            }

            Intent i = new Intent(view.getContext(), ScreenEdit.class);
            i.putExtra("editAlarm", new Gson().toJson(alarm));
            startActivityForResult(i, EDIT_ALARM);

        }
    });
}

public void InitSwitches()
{
    View v;
    Switch s;
    int index = 0;
    String query = "Select " + Database.getAlarmID() + ", " + Database.getAlarmStatus() + " FROM " + Database.getTableName();
    Cursor statuses = Database.RawQuery(db, query);

    while (statuses.moveToNext())
    {
        v = lstAlarm.getAdapter().getView(index++, null, null);
        s = (Switch) v.findViewById(R.id.alarmSwitch);

        if (statuses.getString(statuses.getColumnIndexOrThrow(Database.getAlarmStatus())).equals("ON")){
            s.toggle();
        }
        else{                

        }
    }
}

ScreenShot of app

从屏幕截图中可以看出,第二行的开关应该是打开的,但实际上没有。我也试过 InvilidateView() 但没有用。请帮忙

您的适配器是否使用 viewholder 模式?如果不是,离开屏幕的视图将被重新使用,并且可能具有滚动时新行不应该具有的旧切换状态。

问题是 ListView 是那些不断重绘的视图之一,这意味着这很糟糕,因为它会在重绘时失去状态

if (statuses.getString(statuses.getColumnIndexOrThrow(Database.getAlarmStatus())).equals("ON")){
    s.toggle();
}

首先创建AlarmItem...你应该根据自己的需要制作。这是我的

public class AlarmItem {

    private String alarmName;
    private String alarmDescption;
    private boolean state;
    private long id;

    public AlarmItem(String alarmName, String alarmDescption, long id, boolean state) {
        this.alarmName = alarmName;
        this.alarmDescption = alarmDescption;
        this.id = id;
        this.state = state;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getAlarmName() {
        return alarmName;
    }

    public void setAlarmName(String alarmName) {
        this.alarmName = alarmName;
    }

    public String getAlarmDescption() {
        return alarmDescption;
    }

    public void setAlarmDescption(String alarmDescption) {
        this.alarmDescption = alarmDescption;
    }

    public boolean getState() {
        return state;
    }

    public void setState(boolean state) {
        this.state = state;
    }
}

现在我们需要一个 CustomSwitch class 因为:LINK

import android.content.Context;
import android.util.AttributeSet;
import android.widget.Switch;

public class CustomSwitch extends Switch {


    private OnCheckedChangeListener mListener;

    public CustomSwitch(Context context) {
        super(context);
    }

    public CustomSwitch(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public CustomSwitch(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        // Do not call supper method
        mListener = listener;
    }

    @Override
    public void setChecked(boolean checked) {
        super.setChecked(checked);

        if (mListener != null) {
            mListener.onCheckedChanged(this, checked);
        }
    }

    public void setCheckedProgrammatically(boolean checked) {
        // You can call super method, it doesn't have a listener... he he :)
        super.setChecked(checked);
    }
}

创建布局文件并命名为alarm_list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:orientation="vertical">

    <com.example.alarm.list.CustomSwitch
        android:id="@+id/alarmSwitch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="10dp"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:layout_toLeftOf="@id/alarmSwitch"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tvAlarmName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:text="Alarm Name"
            android:textSize="18sp"/>

        <TextView
            android:id="@+id/tvAlarmDesc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:text="Alarm description"
            android:textSize="15sp"/>
    </LinearLayout>


</RelativeLayout>

现在主布局->activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <ListView
        android:id="@+id/lvAlarms"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

现在我们需要一个负责绘制项目及其处理的适配器。命名 class AlarmAdapter

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CompoundButton;
import android.widget.TextView;

import java.util.List;

public class AlarmAdapter extends BaseAdapter {

    private List<AlarmItem> listOfAlarms;
    private Context context;
    private OnAlarmCheckedChangeListener mCallback;
    // avoid constant allocation
    private View tmpView;
    private AlarmItemViewHolder mHolder;
    private AlarmItem tmpItem;
    public AlarmAdapter(List<AlarmItem> listOfAlarms, Context context, OnAlarmCheckedChangeListener callBack) {
        this.listOfAlarms = listOfAlarms;
        this.context = context;
        mCallback = callBack;
    }

    @Override
    public int getCount() {
        return listOfAlarms == null ? 0 : listOfAlarms.size();
    }

    @Override
    public AlarmItem getItem(int i) {
        return listOfAlarms == null ? null : listOfAlarms.get(i);
    }

    @Override
    public long getItemId(int i) {
        return 0;
    }

    @Override
    public View getView(final int i, View view, ViewGroup viewGroup) {

        tmpItem = listOfAlarms.get(i);

        if (view == null) {
            LayoutInflater inflater = LayoutInflater.from(context);
            tmpView = inflater.inflate(R.layout.alarm_list_item, null, false);
            mHolder = new AlarmItemViewHolder(tmpView);
            tmpView.setTag(mHolder);
        }
        else {
            tmpView = view;
            mHolder = (AlarmItemViewHolder) view.getTag();
        }

        mHolder.getAlarmNameTextView().setText(tmpItem.getAlarmName());
        mHolder.getAlarmDescriptionTextView().setText(tmpItem.getAlarmDescption());
        mHolder.getSwitch().setCheckedProgrammatically(tmpItem.getState());

        mHolder.getSwitch().setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                listOfAlarms.get(i).setState(b);
                mCallback.onAlarmStateChanged(listOfAlarms.get(i), i);
            }
        });


        return tmpView;
    }

    public void clear() {
        listOfAlarms.clear();
        notifyDataSetChanged();
    }

    public void refill(List<AlarmItem> listOfAlarms) {
        this.listOfAlarms = listOfAlarms;
        notifyDataSetChanged();
    }

    public void toggleAllSwitches() {
        for (AlarmItem item : listOfAlarms) {
            item.setState(!item.getState());
        }
        notifyDataSetChanged();
    }

    public interface OnAlarmCheckedChangeListener {
        public void onAlarmStateChanged(AlarmItem item, int postionInList);
    }

    private class AlarmItemViewHolder {

        View base;
        CustomSwitch mSwitch;
        TextView mAlarmName;
        TextView mAlarmDescription;

        public AlarmItemViewHolder(View base) {
            this.base = base;
        }

        public CustomSwitch getSwitch() {
            if (mSwitch == null) {
                mSwitch = (CustomSwitch) base.findViewById(R.id.alarmSwitch);
            }
            return mSwitch;
        }

        public TextView getAlarmNameTextView() {
            if (mAlarmName == null) {
                mAlarmName = (TextView) base.findViewById(R.id.tvAlarmName);
            }
            return mAlarmName;
        }

        public TextView getAlarmDescriptionTextView() {
            if (mAlarmDescription == null) {
                mAlarmDescription = (TextView) base.findViewById(R.id.tvAlarmDesc);
            }
            return mAlarmDescription;
        }
    }
}

现在终于 MainActivity

import android.app.ActionBar;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;

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


public class MainActivity extends ActionBarActivity implements AdapterView.OnItemClickListener, AlarmAdapter.OnAlarmCheckedChangeListener {

    private ListView listView;
    private AlarmAdapter adapter;
    private Toast toast;
    private Handler handler;
    private Runnable handlerRunnable;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        // Make a nice actionBar title
        ActionBar ab = getActionBar();
        if (ab != null) {
            ab.setTitle("Alarm List");
        }

        listView = (ListView) findViewById(R.id.lvAlarms);

        // Simulating alarms from database. You need to convert your items to these
        List<AlarmItem> alarmsFromDb = new ArrayList<>();
        alarmsFromDb.add(new AlarmItem("Alarm 1", "Lalalala", 1, true));
        alarmsFromDb.add(new AlarmItem("Alarm 2", "something", 2, false));
        alarmsFromDb.add(new AlarmItem("Alarm 3", "gfdgdf", 3, true));
        alarmsFromDb.add(new AlarmItem("Alarm 4", "sda", 4, true));
        alarmsFromDb.add(new AlarmItem("Alarm 5", "yxcxyc", 5, false));
        alarmsFromDb.add(new AlarmItem("Alarm 6", "dsfsd", 6, false));

        adapter = new AlarmAdapter(alarmsFromDb, this, this);

        listView.setAdapter(adapter);
        listView.setOnItemClickListener(this);

        // Toggle all switches after 5s... this is what you need, right?
        handlerRunnable = new Runnable() {
            @Override
            public void run() {
                adapter.toggleAllSwitches();
                showToast("All switches toggeled :)");
            }
        };

        handler = new Handler(Looper.getMainLooper());
        handler.postDelayed(handlerRunnable, 5 * 1000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (handler != null) {
            handler.removeCallbacks(handlerRunnable);
            handler = null;
            handlerRunnable = null;
        }
    }

    private void showToast(String str) {
        if (toast != null) {
            toast.cancel();
        }
        toast = Toast.makeText(this, str, Toast.LENGTH_SHORT);
        toast.show();
    }

    @Override
    public void onAlarmStateChanged(AlarmItem item, int postionInList) {
        String onOff = item.getState() ? "ON" : "OFF";
        showToast("Alarm " + item.getAlarmName() + " is: " + onOff);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        AlarmItem item = adapter.getItem(position);
        showToast("Alarm " + item.getAlarmName() + " clicked");
    }
}

这里是完整的android工作室项目,如果您有任何问题:LINK

现在您需要做的就是将数据库中的结果转换为该列表或修改 AlarmItem 和 UI。此解决方案将起作用,并且您在适配器中已经有了一些有用的方法。

编码愉快!