亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android開發筆記之:ListView刷新順序的問題詳解

2020-04-11 12:23:27
字體:
來源:轉載
供稿:網友
背景
一個典型的ListView,每個Item顯示一個TextView,代表一個Task,需要實現二個編輯方式:一個是用CheckBox來標識任務已經完成,另一個要實現的編輯是刪除任務。對于完成的CheckBox就直接放在布局中就可,但對于刪除不想使用ContextMenu來實現編輯,對于像iOS中那樣的列表,它的刪除都是通過對列表中每個項目的手勢來觸發。這個實現起來并不難,可以用一個ViewSwitcher,Checkbox和刪除按扭是放入其中,讓ViewSwitcher來控制顯示哪一個,正常情況下顯示Checkbox,隱藏刪除按扭,然后當點擊Item時就顯示刪除按扭,隱藏Checkbox,這樣也更符合操作習慣,可以一個一個條目的刪除。
實現起來的方式如下:
復制代碼 代碼如下:

public class ListOrderActivity extends Activity {
    private ListView mTaskList;
    private EditText mAddTaskEditor;
    private LayoutInflater mFactory;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_activity);

        mFactory = LayoutInflater.from(getApplication());
        mTaskList = (ListView) findViewById(R.id.task_list);
        final View headerView = mFactory.inflate(R.layout.header_view, null);
        mTaskList.addHeaderView(headerView);
        mAddTaskEditor = (EditText) headerView.findViewById(R.id.task_editor);
        mAddTaskEditor.setOnKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(View view, int keycode, KeyEvent event) {
         if (keycode == KeyEvent.KEYCODE_DPAD_CENTER || keycode == KeyEvent.KEYCODE_ENTER) {
             // finish editing
             final InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
             inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
             final String text = mAddTaskEditor.getText().toString();
             if (!TextUtils.isEmpty(text)) {
          final ContentValues values = new ContentValues(1);
          values.put(TaskColumns.TASK, text);
          values.put(TaskColumns.TYPE, Task.TYPE_TODAY);
          getContentResolver().insert(Task.CONTENT_URI, values);
             }
             mAddTaskEditor.setText("");
         }
         return false;
            }
        });
        final Cursor cursor = getContentResolver().query(Task.CONTENT_URI, Task.PROJECTION, TaskColumns.TYPE + " = " + Task.TYPE_TODAY, null, null);
        final TaskAdapter adapter = new TaskAdapter(getApplication(), cursor);
        mTaskList.setAdapter(adapter);
    }

    private class TaskAdapter extends CursorAdapter {
        private Cursor mCursor;

        public TaskAdapter(Context context, Cursor c) {
            super(context, c);
            mCursor = c;
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            if (view  == null) {
                view = mFactory.inflate(R.layout.today_task_item, null);
            }
            final ViewSwitcher switcher = (ViewSwitcher) view.findViewById(R.id.action_switcher);
//            if (switcher.getDisplayedChild() == 1) {
//         switcher.clearAnimation();
//         switcher.showPrevious();
//         switcher.clearAnimation();
//            }
            final CheckBox toggle = (CheckBox) view.findViewById(R.id.action_toggle_done);
            final short done = cursor.getShort(ProjectionIndex.DONE);
            final int id = cursor.getInt(ProjectionIndex.ID);
            toggle.setOnCheckedChangeListener(null);
            toggle.setChecked(done != 0);
            toggle.setOnCheckedChangeListener(new OnCheckedChangeListener() {
         @Override
         public void onCheckedChanged(CompoundButton view, boolean checked) {
             final Uri uri = ContentUris.withAppendedId(Task.CONTENT_URI, id);
             final ContentValues values = new ContentValues(1);
             values.put(TaskColumns.DONE, checked ? 1 : 0);
             getContentResolver().update(uri, values, null, null);
         }
            });
            view.setOnClickListener(new OnClickListener() {
         @Override
         public void onClick(View v) {
             switcher.showNext();
             if (switcher.getDisplayedChild() == 0) {
          switcher.getInAnimation().setAnimationListener(null);
          return;
             }
             final ImageView delete = (ImageView) v.findViewById(R.id.action_delete_task);
             delete.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View v) {
              switcher.getInAnimation().setAnimationListener(new AnimationListener() {
           @Override
           public void onAnimationEnd(Animation animation) {
               switcher.getInAnimation().setAnimationListener(null);
               final Uri uri = ContentUris.withAppendedId(Task.CONTENT_URI, id);
               getContentResolver().delete(uri, null, null);
           }

           @Override
           public void onAnimationRepeat(Animation animation) {
           }

           @Override
           public void onAnimationStart(Animation animation) {
           }
              });
              switcher.showPrevious();
          }
      });
         }
     });
            TextView task = (TextView) view.findViewById(R.id.task);
            final String taskContent = cursor.getString(ProjectionIndex.TASK);
            if (done != 0) {
         final Spannable style = new SpannableString(taskContent);
         style.setSpan(new StrikethroughSpan(), 0, taskContent.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
         style.setSpan(new StyleSpan(Typeface.ITALIC) , 0, taskContent.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
         task.setText(style);
         task.setTextAppearance(getApplication(), R.style.done_task_item_text);
            } else {
         task.setText(taskContent);
         task.setTextAppearance(getApplication(), R.style.task_item_text);
            }
        }
        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            View view = mFactory.inflate(R.layout.today_task_item, null);
            return view;
        }

        @Override
        public void onContentChanged() {
            mCursor.requery();
        }
    }
}

復制代碼 代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="#f0f0f0"
    android:paddingBottom="5dip"
    android:paddingLeft="12dip"
    android:paddingRight="12dip"
    android:paddingTop="5dip" >
    <ListView
        android:id="@+id/task_list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:divider="@color/divider"
        android:dividerHeight="0.6dip" />
</LinearLayout>

復制代碼 代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_gravity="center"
  android:gravity="center">
    <ViewSwitcher android:id="@+id/action_switcher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:inAnimation="@anim/action_switcher_in"
        android:outAnimation="@anim/action_switcher_out">
     <CheckBox android:id="@+id/action_toggle_done"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:gravity="center"
         android:layout_gravity="center" />
     <ImageView android:id="@+id/action_delete_task"
         android:src="@drawable/ic_delete"
         android:layout_width="48dip"
         android:layout_height="48dip"
         android:contentDescription="@string/delete_description"
         android:gravity="center"
         android:layout_gravity="center"
         android:scaleType="center" />
    </ViewSwitcher>
 <TextView android:id="@+id/task"
           style="@style/task_item_text" />
</LinearLayout>



問題
但這有一個問題,就是如果其中某個條目是處于刪除狀態,這時再添加一個新任務,或者點擊另外條目的Checkbox時,條目的狀態會錯亂,本來處于正常狀態的條目會處于刪除狀態!
原因分析
最開始以為是數據問題,因為事件的處理都是匿名的類,可能會指向不正確的外部數據,通過打印調試發現所有數據都是對的。最后通過在bindView方法中加LOG信息發現了原因:每次ListView刷新bindView的順序并不相同,原來處在第3的子View,刷新后可能被放在第1位置。ViewSwitcher的顯示狀態是它自己維護的,也就是說沒有在View的外部保存其應該顯示的狀態,所以當數據發生變化(Checkbox會引發數據變化)刷新列表時,原來處于刪除狀態的子View(可能在第4位置)現在可能變成了第2位置,造成了第二個處于刪除狀態,而第四個處于正常狀態。
解決方案
這個問題沒有完美解決方法,只能做一個Workaround的方法:那就是每次刷新bindView時把刪除狀態清掉,都換成默認狀態,這樣至少不會出現狀態混亂的狀況。但是,還是會看到刪除會閃一下。
要想完全解決這個問題就是避免在有其他方式導致數據變化時使用這種設計,這種設計僅適用于:整個列表僅有刪除,沒有其他方式能導致列表會刷新時,這時每當刪除時,直接把子View從ListView中移除,就不會出現混亂了。

同時也說明為什么我們每次bindView時要重新給子View添數據,而不是僅當創建子View添數據。因為每次刷新bindView時順序并不一定是先前的順序,所以一定要重新添數據。而數據通常是與View分享開來,或是在數據庫中,或是其他形式,會以特定的順序存在,它不會因為View的刷新而改變,所以為了不使用戶感覺狀態錯亂,就必須要重新按照數據的順序來給View填充數據。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日韩免费区域视频在线观看| 一本久久综合亚洲鲁鲁| 欧美丝袜一区二区| 久操成人在线视频| 日韩中文字幕欧美| 国产精品∨欧美精品v日韩精品| 国产精品久久久久久久app| 日韩av在线直播| 欧美精品日韩www.p站| 琪琪第一精品导航| 亚洲国产精品999| 日韩av毛片网| 中文字幕国产精品久久| 久久久久久久999精品视频| 久久久精品视频成人| 成人网在线免费看| 91精品国产91久久久久久最新| 日本高清不卡的在线| 亚洲女同精品视频| 欧美激情视频网址| 欧美激情国产日韩精品一区18| 国产精品久久久精品| 亚洲另类图片色| 日本精品中文字幕| 国语自产在线不卡| 欧美猛男性生活免费| 茄子视频成人在线| 国产精品久久久久久久av大片| 91精品久久久久| 成人妇女免费播放久久久| 国产美女久久精品香蕉69| 日韩色av导航| 欧美色图在线视频| 久久久久久999| 久久天天躁日日躁| 精品视频在线播放| 欧美一级免费视频| 欧美野外wwwxxx| 国产精品69精品一区二区三区| 日韩精品中文在线观看| 亚洲精品国产欧美| 国产精品入口尤物| www.欧美免费| 最近2019中文字幕第三页视频| 色综合视频一区中文字幕| 91成人免费观看网站| 一区二区三欧美| 日本精品久久久久影院| 国产综合福利在线| 亚洲a在线观看| 日韩av中文在线| 国产日本欧美视频| 久久久久久久色| 青草青草久热精品视频在线网站| 久久久久成人精品| 日本精品久久久久久久| 成人免费淫片视频软件| 欧美成人一二三| 日韩激情第一页| 欧美日韩国产中文精品字幕自在自线| 欧美性受xxxx黑人猛交| 成人97在线观看视频| 国产日韩欧美在线播放| 日韩专区在线播放| 成人深夜直播免费观看| 国产精品视频一| 亚洲综合精品伊人久久| 亚洲天堂男人天堂女人天堂| 日本久久久久久久| 国产精品美乳一区二区免费| 国产一区二区三区视频在线观看| 日韩成人小视频| 欧美老少做受xxxx高潮| 国产美女精彩久久| 欧美成在线视频| 欧美黄网免费在线观看| 91九色国产视频| 亚洲国产精彩中文乱码av| 在线免费看av不卡| 国产精品户外野外| 国产精品久久久久久久app| 亚洲一区二区三区毛片| 国产精品成人aaaaa网站| 91国偷自产一区二区三区的观看方式| 91九色国产视频| 另类图片亚洲另类| 欧美性极品少妇精品网站| 亚洲一区制服诱惑| 精品亚洲男同gayvideo网站| 日韩欧美在线视频日韩欧美在线视频| 国产精品久久久久久久av大片| 91在线色戒在线| 国产精品视频自拍| 国产91精品久久久久久| 亚洲区中文字幕| 亚洲国产精品视频在线观看| 久久99久久久久久久噜噜| 精品色蜜蜜精品视频在线观看| 最近中文字幕日韩精品| 精品久久久久久中文字幕一区奶水| 亚洲精品天天看| 亚洲国产成人在线视频| 欧美激情精品久久久久久大尺度| 亚洲欧美另类中文字幕| 国产精品久久久999| 日韩亚洲第一页| 国产午夜精品一区理论片飘花| 蜜臀久久99精品久久久久久宅男| 日本一欧美一欧美一亚洲视频| 日韩欧美综合在线视频| 欧美国产高跟鞋裸体秀xxxhd| 国产精品日韩欧美综合| 国产精品久久久久免费a∨大胸| 在线电影中文日韩| 一区二区中文字幕| 日韩黄色av网站| www.欧美三级电影.com| 性亚洲最疯狂xxxx高清| 亚洲国产一区二区三区四区| 久久91精品国产| 成人看片人aa| 欧美性xxxx18| 国产91成人在在线播放| 日韩有码在线播放| 国产在线视频一区| 亚洲深夜福利在线| 欧美野外猛男的大粗鳮| 亚洲欧美日韩精品久久奇米色影视| 大伊人狠狠躁夜夜躁av一区| 亚洲午夜精品久久久久久久久久久久| 亚洲欧美国内爽妇网| 久久久久久久久久久免费精品| 91福利视频在线观看| 国产精品69精品一区二区三区| 97久久精品人人澡人人爽缅北| 精品福利在线视频| 成人高清视频观看www| 国语自产精品视频在线看| 日韩高清人体午夜| 日本精品中文字幕| 欧美性色19p| 亚洲精品国产精品国产自| 成人精品久久av网站| 国产日韩在线看| 永久免费看mv网站入口亚洲| 日韩专区在线播放| 国产香蕉一区二区三区在线视频| 久久久999国产| 久久久久久网站| 久久黄色av网站| 欧美老女人性生活| 欧美精品免费在线观看| 国产香蕉一区二区三区在线视频| 国产美女主播一区| 日韩精品亚洲精品| 国产成人精品最新| 国产视频福利一区| 中文字幕亚洲字幕| 38少妇精品导航| 久久久精品一区二区| 国产精品亚洲аv天堂网| 亚洲精品视频网上网址在线观看| 国内精品久久久久伊人av| 午夜剧场成人观在线视频免费观看|