You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android数据变更后GridView无法刷新的问题求助

问题描述

首次通过API获取数据后,MainActivity的GridView能正常展示并更新,但点击GridView项触发设备状态变更后,GridView无法刷新展示更新后的数据,尽管调用了首次加载时的相同回调方法。

APIManager.java

public class APIManager {

    DeviceCallback deviceCallback;
    private static final String TAG = "APIManager";
    private static final String API_URL = "https://base-url";
    private static final String AUTH_TOKEN = SplashScreen.getTOKEN();
    private static List<Device> deviceArray = null;

    public void setDeviceCallback(DeviceCallback deviceCallback) {
        this.deviceCallback = deviceCallback;
    }

    void toggleDeviceState(Context context, String deviceID) {
        String URL = "https://baseurl/" + deviceID;
        RequestQueue requestQueue = Volley.newRequestQueue(context);
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, URL, null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        try {
                            parseToggleStateResponse(response);
                        } catch (JSONException e) {
                            Log.e(TAG, "JSON parsing error: " + e.getMessage());
                        }
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e(TAG, "API error: " + error.getMessage());
                    }
                }) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> headers = new HashMap<>();
                headers.put("Authorization", "jwt " + AUTH_TOKEN);
                return headers;
            }
        };
        requestQueue.add(jsonObjectRequest);
    }

    void fetchInitialData(Context context) {
        RequestQueue requestQueue = Volley.newRequestQueue(context);
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, API_URL, null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        try {
                            parseResponseData(response);
                            JSONArray ownDevices = response.getJSONArray("owndevices");
                        } catch (JSONException e) {
                            Log.e(TAG, "JSON parsing error: " + e.getMessage());
                        }
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e(TAG, "API error: " + error.getMessage());
                    }
                }) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> headers = new HashMap<>();
                headers.put("Authorization", "Jwt " + AUTH_TOKEN);
                return headers;
            }
        };
        requestQueue.add(jsonObjectRequest);
    }

    ToggleResponse parseToggleStateResponse(JSONObject response) throws JSONException {
        Gson gson = new Gson();
        JSONObject device = response.getJSONObject("data");
        Device deviceData = gson.fromJson(device.toString(), Device.class);
        Log.w("API-MANAGER", "125 : Response = " + deviceData.getId());
        for (int i = 0; i < deviceArray.toArray().length; i++)
        {
            String deviceID = deviceArray.get(i).getId();
            if (deviceID == deviceData.getId()) {
                Log.w("API-MANAGER", "DEVICE FOUND : " + deviceArray.get(i).getGivenName());
                deviceArray.get(i).setState(deviceData.isState());
                deviceCallback.deviceCallbackFunction(deviceArray);
            }
        }
        ToggleResponse responseData = new ToggleResponse(deviceData, message);
        return responseData;
    }

    DeviceResponse parseResponseData(JSONObject response) throws JSONException {
        Gson gson = new Gson();
        JSONArray ownDevicesArray = response.getJSONArray("owndevices");
        Device ownDevices[] = gson.fromJson(String.valueOf(ownDevicesArray), Device[].class);
        DeviceResponse responseData = new DeviceResponse(ownDevices);
        List<Device> ownDevice = new ArrayList<>();
        List<Device> delDevice = new ArrayList<>();
        for (int i = 0; i < ownDevicesArray.length(); i++)
        {
            JSONObject deviceObject = ownDevicesArray.getJSONObject(i);
            Device device = new Device();
            Log.w("API-MANAGER", "153" + deviceObject.getString("_id"));
            device.set_id(deviceObject.getString("_id"));
            device.setName(deviceObject.getString("name"));
            device.setGivenName(deviceObject.getString("givenName"));
            device.setState(deviceObject.getBoolean("state"));
            device.setAdmin(deviceObject.getString("admin"));
            device.setDelegate(getStringListFromJSONArray(deviceObject.getJSONArray("delegate")));
            device.setSchedulerFlag(deviceObject.getBoolean("schedulerFlag"));
            device.setTopic(deviceObject.getString("topic"));
            device.setMultiState(deviceObject.optString("multiState"));
            ownDevice.add(device);
        }
        deviceArray = ownDevice;
        deviceCallback.deviceCallbackFunction(ownDevice);
        responseData.setOwndevices(ownDevice);
        return responseData;
    }

    private List<String> getStringListFromJSONArray(JSONArray jsonArray) throws JSONException {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < jsonArray.length(); i++) {
            list.add(jsonArray.getString(i));
        }
        return list;
    }

    public interface DeviceCallback {
        void deviceCallbackFunction(List<Device> deviceData);
    }
}

GridAdapter.java

public class GridAdapter extends BaseAdapter{

    Context context;
    private final List<Device> deviceData;
    LayoutInflater layoutInflater;
    APIManager apiManager = new APIManager();

    public GridAdapter(Context context, List<Device> deviceData) {
        this.context = context;
        this.deviceData = deviceData;
    }

    @Override
    public int getCount() {
        return deviceData.toArray().length;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

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

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        Holder holder = new Holder();
        View rowView;
        rowView = layoutInflater.inflate(R.layout.grid_item_layout, null);
        holder.GivenName = (TextView) rowView.findViewById(R.id.GivenName);
        holder.image= (ImageView) rowView.findViewById(R.id.image);
        holder.image.setImageResource(deviceData.get(position).isState()? R.drawable.switch_on : R.drawable.switch_off);
        holder.GivenName.setText(deviceData.get(position).getGivenName());
        rowView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                apiManager.toggleDeviceState(context.getApplicationContext(), deviceData.get(position).getId());
                Toast.makeText(context, "You Clicked "+deviceData.get(position).getId(), Toast.LENGTH_LONG).show();
                notifyDataSetChanged();
            }
        });
        return rowView;
    }

    public class Holder
    {
        TextView Name;
        TextView GivenName;
        TextView State;
        TextView MultiState;
        TextView ConnectFlag;
        ImageView image;
    }
}

MainActivity.java

import io.socket.client.Socket;

public class MainActivity extends AppCompatActivity implements APIManager.DeviceCallback {

    public static GridView gridView;
    private Boolean hasConnection = false;
    String token = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
        token = sharedPreferences.getString("token", null);
        APIManager apiManager = new APIManager();
        SocketClass socketClass = new SocketClass();
        apiManager.setDeviceCallback(this);
        apiManager.fetchData(getApplicationContext());
        getSupportActionBar().setTitle("Home");
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setDisplayShowHomeEnabled(true);
         gridView = (GridView) findViewById(R.id.gridview);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        Log.w("API-MANAGER", "Item : " + item.toString());
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void deviceCallbackFunction(List<Device> deviceData) {
        GridAdapter gridAdapter = new GridAdapter(this, deviceData);
        Log.w("API-MANAGER", "49" + deviceData.get(0).getGivenName());
        gridView.setAdapter(gridAdapter);
    }
}
问题原因与修复方案

1. GridAdapter使用未绑定回调的新APIManager实例

GridAdapter中apiManager = new APIManager()是全新实例,未设置DeviceCallback,导致调用toggleDeviceState后,API响应无法通知到MainActivity。

修复:

  • 修改GridAdapter构造方法,传入已绑定回调的APIManager实例:
private APIManager apiManager;

public GridAdapter(Context context, List<Device> deviceData, APIManager apiManager) {
    this.context = context;
    this.deviceData = deviceData;
    this.apiManager = apiManager;
}
  • MainActivity创建Adapter时传入实例:
gridAdapter = new GridAdapter(this, deviceData, apiManager);

2. 字符串比较误用==

parseToggleStateResponse中用deviceID == deviceData.getId()比较字符串,==判断的是对象引用而非内容,会导致设备匹配失败,无法更新状态。

修复:
改用equals()比较内容:

if (deviceID.equals(deviceData.getId())) {
    // 设备状态更新逻辑
}

3. MainActivity每次回调都新建Adapter

原代码每次回调都创建新GridAdapter,既浪费资源,又可能导致数据源不一致。应复用Adapter实例,更新数据源后触发刷新。

修复:

  • MainActivity中保存Adapter实例:
private GridAdapter gridAdapter;
  • 回调中复用实例:
@Override
public void deviceCallbackFunction(List<Device> deviceData) {
    if (gridAdapter == null) {
        gridAdapter = new GridAdapter(this, deviceData, apiManager);
        gridView.setAdapter(gridAdapter);
    } else {
        gridAdapter.updateData(deviceData);
    }
}
  • GridAdapter添加数据更新方法:
public void updateData(List<Device> newDeviceData) {
    this.deviceData.clear();
    this.deviceData.addAll(newDeviceData);
    notifyDataSetChanged();
}

4. MainActivity调用不存在的方法

APIManager中只有fetchInitialData方法,原代码写的apiManager.fetchData是笔误,会导致编译错误。

修复:
改为apiManager.fetchInitialData(getApplicationContext())

5. GridAdapter未复用convertView(性能优化)

原代码每次getView都重新inflate布局,影响滚动性能,建议修复:

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    Holder holder;
    if (convertView == null) {
        layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = layoutInflater.inflate(R.layout.grid_item_layout, parent, false);
        holder = new Holder();
        holder.GivenName = convertView.findViewById(R.id.GivenName);
        holder.image = convertView.findViewById(R.id.image);
        convertView.setTag(holder);
    } else {
        holder = (Holder) convertView.getTag();
    }
    // 设置数据
    holder.image.setImageResource(deviceData.get(position).isState()? R.drawable.switch_on : R.drawable.switch_off);
    holder.GivenName.setText(deviceData.get(position).getGivenName());
    convertView.setOnClickListener(v -> {
        apiManager.toggleDeviceState(context.getApplicationContext(), deviceData.get(position).getId());
        Toast.makeText(context, "You Clicked "+deviceData.get(position).getId(), Toast.LENGTH_LONG).show();
    });
    return convertView;
}

内容的提问来源于stack exchange,提问作者Amorphous

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 08:32:03