首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android ListView 多 Item 數據變更時局部刷新並閃爍動畫:DiffUtil 思想與 BaseAdapter 實戰

     編輯:程式設計綜合問答

場景

金融行情列表,每 10 秒輪詢最新價格,需**僅刷新價格變動的 Item**,並對變動行執行**背景閃爍動畫**(紅漲綠跌)。

核心思路:DiffUtil 思想(手動實現)

  1. 保持舊數據列表 oldList 與新數據列表 newList。
  2. 遍歷對比關鍵字段(如 latestPri),記錄**變更的位置索引集合**。
  3. 調用 adapter.notifyItemChanged(position, payload) (RecyclerView) 或 ListView 自定義 updateView(position, newData) 僅刷新該行。
  4. 在 getView() / onBindViewHolder() 中根據 payload 或標誌位啟動動畫。

ListView 版完整實現

1. 數據模型

public class MarketList {
    private int id;
    private String name;
    private double latestPri; // 最新價
    private double markup;    // 漲跌幅
    // getter/setter/equals/hashCode 基於 id
}

2. Adapter 關鍵方法

public class MarketAdapter extends BaseAdapter {
    private List<MarketList> data = new ArrayList<>();
    private ListView listView;

    // 僅更新變更行
    public void updateChangedItems(List<MarketList> newData) {
        for (int i = 0; i < data.size() && i < newData.size(); i++) {
            MarketList old = data.get(i);
            MarketList nw = newData.get(i);
            if (Math.abs(old.getLatestPri() - nw.getLatestPri()) > 0.000001) {
                // 價格變了
                data.set(i, nw); // 更新數據源
                // ListView 無 notifyItemChanged,需自定義刷新單行
                updateSingleRow(listView, i, nw);
            }
        }
    }

    // 刷新指定位置的 View
    private void updateSingleRow(ListView lv, int position, MarketList item) {
        int visiblePos = position - lv.getFirstVisiblePosition();
        if (visiblePos >= 0 && visiblePos < lv.getChildCount()) {
            View view = lv.getChildAt(visiblePos);
            ViewHolder holder = (ViewHolder) view.getTag();
            bindViewHolder(holder, item, true); // true = 播放動畫
        }
        // 不可見行下次 getView 自然會綁定新數據
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_market, parent, false);
            holder = new ViewHolder(convertView);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        bindViewHolder(holder, data.get(position), false); // 初始綁定不動畫
        return view;
    }

    private void bindViewHolder(ViewHolder holder, MarketList item, boolean animate) {
        holder.tvName.setText(item.getName());
        holder.tvPrice.setText(String.valueOf(item.getLatestPri()));
        holder.tvChange.setText(String.valueOf(item.getMarkup()));

        // 顏色
        int color = item.getMarkup() >= 0 ? Color.RED : Color.GREEN;
        holder.tvPrice.setTextColor(color);
        holder.tvChange.setTextColor(color);

        // 閃爍動畫
        if (animate) {
            AlphaAnimation anim = new AlphaAnimation(0.3f, 1f);
            anim.setDuration(300);
            anim.setInterpolator(new LinearInterpolator());
            holder.itemView.startAnimation(anim);
        }
    }

    static class ViewHolder {
        TextView tvName, tvPrice, tvChange;
        View itemView;
        ViewHolder(View v) {
            tvName = v.findViewById(R.id.id_mr_name);
            tvPrice = v.findViewById(R.id.id_mr_new);
            tvChange = v.findViewById(R.id.id_mr_range);
            itemView = v;
        }
    }
}

3. Fragment 輪詢邏輯

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    @Override public void run() {
        new Handler(Looper.getMainLooper()).post(() ->
            presenter.getMarketList(categoryId) // 網絡請求
        );
    }
}, 0, 10_000);

// Presenter 回調
@Override
public void onMarketListReceived(List<MarketList> newList) {
    adapter.updateChangedItems(newList); // 僅刷新變更行
}

關鍵優化點

  • 數據源同步更新:data.set(i, newItem) 保證 getView() 見到最新數據。
  • 可見行精準刷新:getFirstVisiblePosition() + getChildAt() 直接操作 View,避免 notifyDataSetChanged() 全量重繪導致閃屏、滾動位置重置。
  • 不可見行延遲綁定:滾動進入時 getView() 自然讀取已更新的 data。
  • 動畫載體:AlphaAnimation、ObjectAnimator、ValueAnimator 均可,注意 startAnimation() 會重置動畫狀態。

遷移建議:RecyclerView + DiffUtil

新項目**強烈建議用 RecyclerView** + ListAdapter + DiffUtil.ItemCallback,框架自動計算差異、調用 notifyItemChanged(payload)、支持預取動畫,代碼量減半、性能更強。

掌握「數據源單一來源 + 差異計算 + 局部刷新 + 載荷動畫」模式,ListView/RecyclerView 皆通用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved