سؤال

كيفية تحديث جهاز أندرويد ListView بعد إضافة/حذف البيانات الديناميكية؟

هل كانت مفيدة؟

المحلول

يتصل notifyDataSetChanged() على حسابك Adapter كائن بمجرد تعديل البيانات في هذا المحول.

بعض التفاصيل الإضافية حول كيفية الاتصال notifyDataSetChanged() يمكن أن ينظر إليها فيديو جوجل I / O.

نصائح أخرى

كما يمكنك استخدام هذا:

myListView.invalidateViews();

يرجى تجاهل كل invalidate(), invalidateViews(), requestLayout(), ، ...إجابات على هذا السؤال.

الشيء الصحيح الذي يجب فعله (ولحسن الحظ أيضًا أنه تم وضع علامة على الإجابة الصحيحة) هو للإتصال notifyDataSetChanged() على المحول الخاص بك.

استكشاف الأخطاء وإصلاحها

إذا اتصل notifyDataSetChanged() لا تعمل جميع طرق التخطيط لن تساعد أيضًا.صدقني ال ListView تم تحديثه بشكل صحيح.إذا فشلت في العثور على الفرق، فأنت بحاجة إلى التحقق من مصدر البيانات الموجودة في المحول الخاص بك.

إذا كانت هذه مجرد مجموعة تحتفظ بها في الذاكرة، فتأكد من أنك قمت بالفعل بحذف العنصر (العناصر) أو إضافتها إلى المجموعة قبل الاتصال بـ notifyDataSetChanged().

إذا كنت تعمل مع قاعدة بيانات أو واجهة خلفية للخدمة، فسيتعين عليك استدعاء الطريقة لاسترداد المعلومات مرة أخرى (أو معالجة البيانات الموجودة في الذاكرة) قبل استدعاء notifyDataSetChanged().

الشيء هو هذا notifyDataSetChanged يعمل فقط إذا تغيرت مجموعة البيانات.لذلك هذا هو المكان الذي يجب أن تبحث فيه إذا لم تجد تغييرات قادمة.تصحيح إذا لزم الأمر.

ArrayAdapter مقابل BaseAdapter

لقد وجدت أن العمل مع محول يتيح لك إدارة المجموعة، مثل BaseAdapter يعمل بشكل أفضل.تقوم بعض المحولات مثل ArrayAdapter بالفعل بإدارة مجموعتها الخاصة مما يجعل من الصعب الوصول إلى المجموعة المناسبة للحصول على التحديثات.إنها في الحقيقة مجرد طبقة إضافية لا داعي لها من الصعوبة في معظم الحالات.

موضوع واجهة المستخدم

صحيح أنه يجب استدعاء هذا من مؤشر ترابط واجهة المستخدم.تحتوي الإجابات الأخرى على أمثلة حول كيفية تحقيق ذلك.ومع ذلك، فإن هذا مطلوب فقط إذا كنت تعمل على هذه المعلومات من خارج سلسلة واجهة المستخدم.هذا من خدمة أو مؤشر ترابط غير واجهة المستخدم.في الحالات البسيطة، ستقوم بتحديث بياناتك من خلال نقرة زر أو من خلال نشاط/جزء آخر.لذلك لا يزال داخل موضوع واجهة المستخدم.لا حاجة دائمًا إلى تشغيل برنامج runOnUiTrhead.

مشروع مثال سريع

يمكن العثور عليها في https://github.com/hanscappelle/so-2250770.git.ما عليك سوى استنساخ المشروع وفتحه في Android Studio (المدرج).يحتوي هذا المشروع على مبنى النشاط الرئيسي أ ListView مع كافة البيانات العشوائية.يمكن تحديث هذه القائمة باستخدام قائمة الإجراءات.

يكشف تطبيق المحول الذي قمت بإنشائه لهذا المثال ModelObject عن جمع البيانات

public class MyListAdapter extends BaseAdapter {

    /**
     * this is our own collection of data, can be anything we 
     * want it to be as long as we get the abstract methods 
     * implemented using this data and work on this data 
     * (see getter) you should be fine
     */
    private List<ModelObject> mData;

    /**
     * our ctor for this adapter, we'll accept all the things 
     * we need here
     *
     * @param mData
     */
    public MyListAdapter(final Context context, final List<ModelObject> mData) {
        this.mData = mData;
        this.mContext = context;
    }

    public List<ModelObject> getData() {
        return mData;
    }

    // implement all abstract methods here
}

الكود من النشاط الرئيسي

public class MainActivity extends Activity {

    private MyListAdapter mAdapter;

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

        ListView list = (ListView) findViewById(R.id.list);

        // create some dummy data here
        List<ModelObject> objects = getRandomData();
        // and put it into an adapter for the list
        mAdapter = new MyListAdapter(this, objects);
        list.setAdapter(mAdapter);

        // mAdapter is available in the helper methods below and the 
        // data will be updated based on action menu interactions

        // you could also keep the reference to the android ListView 
        // object instead and use the {@link ListView#getAdapter()} 
        // method instead. However you would have to cast that adapter 
        // to your own instance every time
    }

    /**
     * helper to show what happens when all data is new
     */
    private void reloadAllData(){
        // get new modified random data
        List<ModelObject> objects = getRandomData();
        // update data in our adapter
        mAdapter.getData().clear();
        mAdapter.getData().addAll(objects);
        // fire the event
        mAdapter.notifyDataSetChanged();
    }

    /**
     * helper to show how only changing properties of data 
     * elements also works
     */
    private void scrambleChecked(){
        Random random = new Random();
        // update data in our adapter, iterate all objects and 
        // resetting the checked option
        for( ModelObject mo : mAdapter.getData()) {
            mo.setChecked(random.nextBoolean());
        }
        // fire the event
        mAdapter.notifyDataSetChanged();
    }
}

معلومات اكثر

تم العثور على مشاركة لطيفة أخرى حول قوة listViews هنا: http://www.vogella.com/articles/AndroidListView/article.html

استدعاء Runnable كلما أردت:

runOnUiThread(run);

OnCreate(), ، قمت بتعيين موضوع Runnable الخاص بك:

run = new Runnable() {
    public void run() {
        //reload content
        arraylist.clear();
        arraylist.addAll(db.readAll());
        adapter.notifyDataSetChanged();
        listview.invalidateViews();
        listview.refreshDrawableState();
    }
};

حصلت على بعض المشاكل مع تحديث ديناميكي من ListView.

استدعاء notifydataStsChanged () على المحول الخاص بك.

يمكن عرض بعض التفاصيل الإضافية حول كيفية الاتصال NotifyDataSetchanged () في فيديو Google I / O.

NotifyDataSttchanged () لم يعمل بشكل صحيح في حالتي [اتصلت NotifyDataStchanged من فئة أخرى]. فقط في حالة تحرير ListView في نشاط التشغيل (موضوع). ذلك الفيديو بفضل كريستوفر أعطى التلميح النهائي.

في صفي الثاني اعتدت

Runnable run = new Runnable(){
     public void run(){
         contactsActivity.update();
     }
};
contactsActivity.runOnUiThread(run);

ليقوم بتسجيل التحديث () من نشاطي. يتضمن هذا التحديث.

myAdapter.notifyDataSetChanged();

لإخبار المحول بتجديد الرأي. عملت على ما يرام بقدر ما أستطيع أن أقول.

إذا كنت تستخدم SimpleCursorAdapter. حاول الاتصال مطلوبة () على كائن المؤشر.

إذا لم تكن راضيا عن مرطبات ListView، فيمكنك أن تنظر إلى هذا المقتطف، فهذا هو لتحميل ListView من DB، في الواقع ما عليك فعله هو ببساطة إعادة تحميل ListView، بعد إجراء أي عملية Crud ليست أفضل طريقة ل الرمز، ولكن سيتم تحديث ListView كما يحلو لك ..

إنه يعمل من أجلي .... إذا وجدت حل أفضل، يرجى مشاركة ...

....... ...... هل عملياتك Crud الخاصة بك .. ...... ..... dbadapter.open ()؛ DBADAPTER.INSERT_INTO_SINGLELIST ()؛ // جلب هذا db_results وإضافته إلى قائمة كمحتوياته .... LS2.SetAdapter (ArrayAdapter جديد (Dyntabsample.this، Android.r.layout.simple_list_item_1، dbadapter.db_listview)؛ dbadapter.Close ()؛

الحلول التي اقترحها الأشخاص في هذا المنصب يعمل أو لا تعتمد بشكل أساسي على إصدار Android من جهازك. على سبيل المثال لاستخدام طريقة Addall، عليك وضع Android: minsdkversion = "10" في جهاز Android الخاص بك.

لحل هذه الأسئلة لجميع الأجهزة التي قمت بإنشائها في طريقتي الخاصة في محولي واستخدامها داخل وراثة طريقة الإضافة وإزالة من الزراعة من الزراعة التي تحديث بياناتك دون مشاكل.

الرمز الخاص بي: استخدام Raceresult من فئة البيانات الخاصة بي، يمكنك استخدام نموذج البيانات الخاص بك.

يؤدي انهيارها

public class ResultGpRowAdapter extends ArrayAdapter<RaceResult> {

    Context context;
    int resource;
    List<RaceResult> data=null;

        public ResultGpRowAdapter(Context context, int resource, List<RaceResult> objects)           {
        super(context, resource, objects);

        this.context = context;
        this.resource = resource;
        this.data = objects;
    }

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

        ........
        }

        //my own method to populate data           
        public void myAddAll(List<RaceResult> items) {

        for (RaceResult item:items){
            super.add(item);
        }
    }

residesg.java.

public class ResultsGp extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {

    ...........
    ...........
    ListView list = (ListView)findViewById(R.id.resultsGpList); 

    ResultGpRowAdapter adapter = new ResultGpRowAdapter(this,  R.layout.activity_result_gp_row, new ArrayList<RaceResult>()); //Empty data

   list.setAdapter(adapter);

   .... 
   ....
   ....
   //LOAD a ArrayList<RaceResult> with data

   ArrayList<RaceResult> data = new ArrayList<RaceResult>();
   data.add(new RaceResult(....));
   data.add(new RaceResult(....));
   .......

   adapter.myAddAll(data); //Your list will be udpdated!!!

إذا كنت ترغب في الحفاظ على موضع التمرير الخاص بك عند التحديث، ويمكنك القيام بذلك:

if (mEventListView.getAdapter() == null) {
    EventLogAdapter eventLogAdapter = new EventLogAdapter(mContext, events);
    mEventListView.setAdapter(eventLogAdapter);
} else {
    ((EventLogAdapter)mEventListView.getAdapter()).refill(events);
}

public void refill(List<EventLog> events) {
    mEvents.clear();
    mEvents.addAll(events);
    notifyDataSetChanged();
}

للحصول على معلومات التفاصيل، يرجى الاطلاع Android ListView: الحفاظ على موقف التمرير الخاص بك عند التحديث.

مجرد استخدام myArrayList.remove(position); داخل المستمع:

  myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override public void onItemClick(AdapterView<?> parent, android.view.View view, int position, long id) {
           myArrayList.remove(position);
           myArrayAdapter.notifyDataSetChanged();
        }
    });

تحتاج إلى استخدام كائن واحد في تلك القائمة بيانات الخروس التي تقوم بتضخمها ListView. وبعد إذا تم تغيير المرجع بعد ذلك notifyDataSetChanged() لا يعمل. عندفر أنت حذف العناصر من عرض القائمة حذفها أيضا من القائمة التي تستخدمها سواء كانت قائمة صفيف <> أو أي شيء آخر ثم اتصلnotifyDataSetChanged() على كائن فئة محولك.

لذلك هنا نرى كيف تمكنت من ذلك في محول بلدي انظر أدناه

public class CountryCodeListAdapter extends BaseAdapter implements OnItemClickListener{

private Context context;
private ArrayList<CountryDataObject> dObj;
private ViewHolder holder;
private Typeface itemFont;
private int selectedPosition=-1;
private ArrayList<CountryDataObject> completeList;

public CountryCodeListAdapter(Context context, ArrayList<CountryDataObject> dObj) {
    this.context = context;
    this.dObj=dObj;
    completeList=new  ArrayList<CountryDataObject>();
    completeList.addAll(dObj);
    itemFont=Typeface.createFromAsset(context.getAssets(), "CaviarDreams.ttf");
}

@Override
public int getCount() {
    return dObj.size();
}

@Override
public Object getItem(int position) {
    return dObj.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
    if(view==null){
        holder = new ViewHolder();
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.states_inflator_layout, null);
        holder.textView = ((TextView)view.findViewById(R.id.stateNameInflator));
        holder.checkImg=(ImageView)view.findViewById(R.id.checkBoxState);
        view.setTag(holder);
    }else{
        holder = (ViewHolder) view.getTag();
    }
    holder.textView.setText(dObj.get(position).getCountryName());
    holder.textView.setTypeface(itemFont);

    if(position==selectedPosition)
     {
         holder.checkImg.setImageResource(R.drawable.check);
     }
     else
     {
         holder.checkImg.setImageResource(R.drawable.uncheck);
     }
    return view;
}
private class ViewHolder{
    private TextView textView;
    private ImageView checkImg;
}

public void getFilter(String name) {
    dObj.clear();
    if(!name.equals("")){
    for (CountryDataObject item : completeList) {
        if(item.getCountryName().toLowerCase().startsWith(name.toLowerCase(),0)){
            dObj.add(item);
        }
    }
    }
    else {
        dObj.addAll(completeList);
    }
    selectedPosition=-1;
    notifyDataSetChanged();
    notifyDataSetInvalidated(); 
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
        long id) {
    Registration reg=(Registration)context;
    selectedPosition=position;
    reg.setSelectedCountryCode("+"+dObj.get(position).getCountryCode());
    notifyDataSetChanged();
}
}

بعد حذف البيانات من عرض القائمة، يجب عليك الاتصال refreshDrawableState()وبعد هنا هو المثال:

final DatabaseHelper db = new DatabaseHelper (ActivityName.this);

db.open();

db.deleteContact(arg3);

mListView.refreshDrawableState();

db.close();

و deleteContact طريقة في DatabaseHelper سوف تكون الطبقة تبدو إلى حد ما

public boolean deleteContact(long rowId) {

   return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0;

}

لم أتمكن من الحصول على NotifyDataStStchanged () للعمل () للعمل على تحديث بلدي Smpleadapter، لذلك حاولت بدلا من ذلك أولا إزالة جميع الآراء التي تم إرفاقها بالتخطيط الأصل باستخدام RemoveAlViews ()، ثم قم بإضافة ListView، وعملت ذلك، مما يتيح لي تحديث واجهة المستخدم:

LinearLayout results = (LinearLayout)findViewById(R.id.results);
ListView lv = new ListView(this);
ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
SimpleAdapter adapter = new SimpleAdapter( this, list, R.layout.directory_row, 
                new String[] { "name", "dept" }, new int[] { R.id.name, R.id.dept } );

for (...) { 
    HashMap<String, String> map = new HashMap<String, String>();
    map.put("name", name);
    map.put("dept", dept);
    list.add(map);
}

lv.setAdapter(adapter);
results.removeAllViews();     
results.addView(lv);

بالنسبة لي بعد تغيير المعلومات في قاعدة بيانات SQL، لا شيء يمكن تحديث طريقة عرض القائمة (لتكون عرض قائمة قابلة للتوسيع محددة)، لذلك إذا لم يساعد NotifyDataSetchanged ()، فيمكنك محاولة مسح قائمتك أولا وإضافتها مرة أخرى بعد هذه الاتصال NotifyDataStchanged (). علي سبيل المثال

private List<List<SomeNewArray>> arrayList;
List<SomeNewArray> array1= getArrayList(...);
List<SomeNewArray> array2= getArrayList(...);
arrayList.clear();
arrayList.add(array1);
arrayList.add(array2);
notifyDataSetChanged();

آمل أن يكون من المنطقي بالنسبة لك.

أثناء استخدام SimpleCursorAdapter، يمكن استدعاء Changecursor (Newcursor) على المحول.

كنت نفس الشيء عندما، في شظية، أردت أن أملك ListView (في نص واحد) مع عنوان MAC للأجهزة BLE الممسوحة ضوئيا خلال بعض الوقت.

ما فعلته كان هذا:

public class Fragment01 extends android.support.v4.app.Fragment implements ...
{
    private ListView                listView;
    private ArrayAdapter<String>    arrayAdapter_string;

...

@Override
public void onActivityCreated(Bundle savedInstanceState)
{
    ...
    this.listView= (ListView) super.getActivity().findViewById(R.id.fragment01_listView);
    ...
    this.arrayAdapter_string= new ArrayAdapter<String>(super.getActivity(), R.layout.dispositivo_ble_item, R.id.fragment01_item_textView_titulo);
    this.listView.setAdapter(this.arrayAdapter_string);
}


@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
{
    ...
    super.getActivity().runOnUiThread(new RefreshListView(device));
}


private class RefreshListView implements Runnable
{
    private BluetoothDevice bluetoothDevice;

    public RefreshListView(BluetoothDevice bluetoothDevice)
    {
        this.bluetoothDevice= bluetoothDevice;
    }

    @Override
    public void run()
    {
        Fragment01.this.arrayAdapter_string.add(new String(bluetoothDevice.toString()));
        Fragment01.this.arrayAdapter_string.notifyDataSetChanged();
    }
}

ثم بدأت ListView في ملء ديناميكيا مع عنوان MAC للأجهزة الموجودة.

أعتقد أنه يعتمد على ما تقصده بالتحديث. هل تقصد أن يتم تحديث شاشة واجهة المستخدم الرسومية، أو هل تقصد أن يتم تحديث وجهات النظر الطفل بحيث يمكنك استدعاء GetChildat (int) بشكل طبيعي والحصول على العرض المقابل لما هو في المحول.

إذا كنت ترغب في عرض واجهة المستخدم الرسومية منتعشة، ثم اتصل بالرقم NotifyDataSetchanged () على المحول. سيتم تحديث واجهة المستخدم الرسومية عند إعادة رسمه التالي.

إذا كنت ترغب في أن تكون قادرا على استدعاء GetChildat (int) والحصول على طريقة عرض تعكس ما هو ما هو موجود في المحول، ثم اتصل ب Layoutchildren (). سيؤدي ذلك إلى إعادة بنائه الطفل من بيانات المحول.

كان لدي قائمة صفيف التي أردت عرضها في قائمة ListView. تتضمن Arraylist عناصر من MySQL. لقد تجاوزت طريقة onrefresh وفي تلك الطريقة التي استخدمتها TableLayout.removeAllviews ()؛ ثم كرر العملية للحصول على البيانات مرة أخرى من قاعدة البيانات. ولكن قبل أن تأكد من مسح قائمة صفيفك أو أي خروج بيانات البيانات أو البيانات الجديدة، سيتم إلحاق البيانات القديمة ..

إذا كنت ترغب في تحديث ListView ListView من خدمة، فقم بإجراء محول ثابت في نشاطك الرئيسي والقيام بذلك:

@Override
public void onDestroy() {
    if (MainActivity.isInFront == true) {
        if (MainActivity.adapter != null) {
            MainActivity.adapter.notifyDataSetChanged();
        }

        MainActivity.listView.setAdapter(MainActivity.adapter);
    }
}    

إذا كنت تسير بخطوط دليل Android وأنت تستخدم ContentProviders للحصول على البيانات من Database وأنت تعرضها في ListView باستخدام CursorLoader و CursorAdapters ، ثم تنعكس جميع التغييرات في البيانات ذات الصلة تلقائيا في ListView.

لك getContext().getContentResolver().notifyChange(uri, null); على المؤشر في ContentProvider سيكون كافيا لتعكس التغييرات. لا تحتاج إلى العمل الإضافي حولها.

ولكن عندما لا تستخدم هذه جميعها، فأنت بحاجة إلى إخبار المحول عندما تتغير DataSet. تحتاج أيضا إلى إعادة ملء / إعادة تحميل مجموعة البيانات الخاصة بك (Say List) ثم تحتاج إلى الاتصال notifyDataSetChanged() على المحول.

notifyDataSetChanged()لن تعمل إذا لم تكن هناك تغييرات في Datset. هنا هو التعليق فوق الطريقة في المستندات

/**
 * Notifies the attached observers that the underlying data has been changed
 * and any View reflecting the data set should refresh itself.
 */

كنت قادرا فقط على الحصول على NotifyDataSetchanged فقط عن طريق الحصول على بيانات محول جديدة، ثم إعادة تعيين المحول لعرض القائمة، ثم إجراء المكالمة مثل ذلك:

    expandableAdapter = baseFragmentParent.setupEXLVAdapter();
    baseFragmentParent.setAdapter(expandableAdapter);
    expandableAdapter.notifyDataSetChanged(); 

على خيار آخر هو onWindowFocusChanged الطريقة، ولكن بالتأكيد حساسيتها وتحتاج إلى بعض الترميز الإضافي لمن يهتم

 override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)

       // some controls needed
        programList = usersDBHelper.readProgram(model.title!!)
        notesAdapter = DailyAdapter(this, programList)
        notesAdapter.notifyDataSetChanged()
        listview_act_daily.adapter = notesAdapter
    }

فكر في أنك قد مرت قائمة على المحول الخاص بك.
يستخدم:

list.getAdapter().notifyDataSetChanged()

لتحديث قائمتك.

إذا تحدثت عن سيناريو هنا، فلن يعمل بغير الإجابات المذكورة أعلاه لأنني أجريت نشاطا أظهر قائمة قيم DB مع زر حذف وعند الضغط على زر حذف، أردت حذف هذا العنصر من القائمة.

كان الشيء الرائع، لم أستخدم عرض Recycler ولكن عرض قائمة بسيطة وعرض القائمة هذه تهيئة في فئة المحول. لذلك، استدعاء notifyDataSetChanged() لن تفعل أي شيء داخل فئة المحول وحتى في فئة النشاط حيث يتم تهيئة كائن المحول لأن حذف الأسلوب كان في فئة المحول.

لذلك، كان الحل لإزالة الكائن من المحول في فئة المحول getView الطريقة (لحذف هذا الكائن المحدد فقط ولكن إذا كنت ترغب في حذف الكل، اتصل clear()).

لك للحصول على بعض الفكرة، ما كان شكل رمز بلدي،

public class WordAdapter extends ArrayAdapter<Word> {
  Context context;

  public WordAdapter(Activity context, ArrayList<Word> words) {}
    //.......

    @NonNull
    @Override
    public View getView(final int position, View convertView, ViewGroup group) {
      //.......
     ImageButton deleteBt = listItemView.findViewById(R.id.word_delete_bt);
     deleteBt.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             if (vocabDb.deleteWord(currentWord.id)) {
                //.....
             } else{
                //.....
             }
             remove(getItem(position)); // <---- here is the trick ---<
             //clear() // if you want to clear everything
         }
    });
 //....

ملاحظة: هنا remove() و getItem() ترثت الطرق من فئة المحول.

  • remove() - لإزالة العنصر المحدد الذي تم النقر عليه
  • getItem(position) - هو الحصول على العنصر (هنا، هذا كائن كلامي الذي أضفته إلى القائمة) من الموضع النقر.

هذه هي الطريقة التي أقوم بها المحول إلى ListView في فئة النشاط،

    ArrayList<Word> wordList = new ArrayList();
    WordAdapter adapter = new WordAdapter(this, wordList);

    ListView list_view = (ListView) findViewById(R.id.activity_view_words);
    list_view.setAdapter(adapter);

الأسهل هو مجرد جعل عددا جديدا وإسقاط واحد القديم:

myListView.setAdapter(new MyListAdapter(...));
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top