ListView
동적 데이터를 추가 / 삭제 한 후 Android를 새로 고치는 방법은 무엇입니까?
ListView
동적 데이터를 추가 / 삭제 한 후 Android를 새로 고치는 방법은 무엇입니까?
답변:
해당 어댑터의 데이터를 수정 한 후 객체를 호출 notifyDataSetChanged()
하십시오 Adapter
.
전화 방법 / 통화 방법에 대한 추가 정보 notifyDataSetChanged()
는 이 Google I / O 비디오 에서 볼 수 있습니다 .
또한 이것을 사용할 수 있습니다 :
myListView.invalidateViews();
invalidateViews()
소스 코드 세트에 mDataChanged = true;
있으므로 다음과 비교하여 더 나은 성능을 notifyDataSetChanged()
invalidate()
, invalidateViews()
, requestLayout()
이 질문에 대한 ... 답변을.올바른 일을 할 (그리고 다행히도도 정답으로 표시)하는 것입니다 전화 notifyDataSetChanged()
어댑터에 .
호출 notifyDataSetChanged()
이 작동하지 않으면 모든 레이아웃 방법이 도움이되지 않습니다. 나를 ListView
제대로 업데이트 했다고 믿습니다 . 차이점을 찾지 못하면 어댑터의 데이터 위치를 확인해야합니다.
이 컬렉션이 메모리에있는 경우을 호출하기 전에 실제로 항목을 삭제하거나 컬렉션에 추가했는지 확인하십시오 notifyDataSetChanged()
.
데이터베이스 또는 서비스 백엔드로 작업하는 경우을 호출하기 전에 메소드를 호출하여 정보를 다시 검색하거나 메모리 데이터를 조작해야합니다 notifyDataSetChanged()
.
문제는 notifyDataSetChanged
데이터 세트가 변경된 경우에만 작동합니다. 따라서 변경 사항이없는 경우 살펴볼 곳입니다. 필요한 경우 디버그하십시오.
BaseAdapter와 같이 컬렉션을 관리 할 수있는 어댑터를 사용하면 더 잘 작동한다는 것을 알았습니다. ArrayAdapter와 같은 일부 어댑터는 이미 자체 컬렉션을 관리하므로 업데이트를위한 적절한 컬렉션에 도달하기가 더 어렵습니다. 그것은 실제로 대부분의 경우 불필요하게 여분의 어려움 층입니다.
UI 스레드에서 호출해야합니다. 다른 답변에는이를 달성하는 방법에 대한 예가 있습니다. 그러나이 정보는 UI 스레드 외부에서 작업하는 경우에만 필요합니다. 그것은 서비스 또는 비 UI 스레드에서 온 것입니다. 간단한 경우에는 버튼 클릭이나 다른 활동 / 조각으로 데이터를 업데이트합니다. 여전히 UI 스레드 내에 있습니다. 항상 runOnUiTrhead를 팝업 할 필요가 없습니다.
https://github.com/hanscappelle/so-2250770.git 에서 찾을 수 있습니다 . Android Studio (gradle)에서 프로젝트를 복제하고 엽니 다. 이 프로젝트에는 ListView
모든 임의의 데이터 가 포함 된 MainAcitivity 빌딩이 있습니다. 이 메뉴는 작업 메뉴를 사용하여 새로 고칠 수 있습니다.
이 예제 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
}
MainActivity의 코드
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
adapter.clear()
하고 adapter.addAll(Array<T>)
전화하기 전에notifyDataSetChanged()
원할 때마다 실행 가능 전화 :
runOnUiThread(run);
OnCreate()
실행 가능한 스레드를 설정합니다.
run = new Runnable() {
public void run() {
//reload content
arraylist.clear();
arraylist.addAll(db.readAll());
adapter.notifyDataSetChanged();
listview.invalidateViews();
listview.refreshDrawableState();
}
};
runOnUiThread()
함수 를 통해 코드를 호출 할 필요가 없습니다 .
run = new Runnable()
내 함수에 public void refreshUIThread()
메소드를 모델링 하는 메소드를 제공했다 run()
.
내 목록보기의 동적 새로 고침에 문제가 있습니다.
어댑터에서 notifyDataSetChanged ()를 호출하십시오.
notifyDataSetChanged ()를 호출하는 방법 / 언제에 대한 일부 추가 정보는이 Google I / O 비디오에서 볼 수 있습니다.
내 경우에는 notifyDataSetChanged ()가 제대로 작동하지 않았습니다 [다른 클래스에서 notifyDataSetChanged를 호출했습니다]. 이 경우 실행중인 활동 (스레드)에서 ListView를 편집했습니다. Christopher 덕분에 그 비디오 는 마지막 힌트를 주었다.
내 두 번째 수업에서 나는
Runnable run = new Runnable(){
public void run(){
contactsActivity.update();
}
};
contactsActivity.runOnUiThread(run);
내 활동에서 update ()에 액세스합니다. 이 업데이트에는 다음이 포함됩니다
myAdapter.notifyDataSetChanged();
보기를 새로 고치도록 어댑터에 지시하십시오. 내가 말할 수있는 한 잘 작동했습니다.
SimpleCursorAdapter 를 사용 하는 경우 Cursor 객체에서 requery () 를 호출 하십시오.
여전히 ListView Refreshment에 만족하지 않으면이 스 니펫을 볼 수 있습니다.이 코드는 DB에서 listView를로드하기위한 것입니다. 실제로 CRUD 작업을 수행 한 후 ListView를 다시로드하는 것이 가장 좋습니다. 코드,하지만 원하는대로 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 장치에 android : minSdkVersion = "10"을 넣어야합니다.
모든 장치에 대해이 질문을 해결하기 위해 어댑터에서 자체 메소드를 작성하고 add 및 remove 메소드 내부에서 ArrayAdapter에서 상속하여 문제없이 데이터를 업데이트합니다.
내 코드 : 내 자신의 데이터 클래스 RaceResult를 사용하면 자신의 데이터 모델을 사용합니다.
ResultGpRowAdapter.java
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);
}
}
ResultsGp.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()
작동하지 않습니다. 목록보기에서 요소를 삭제할 때마다 사용중인 목록에서 ArrayList <> 또는 다른 항목인지 여부를 지정하고 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;
}
SimpleAdapter를 업데이트하는 데 notifyDataSetChanged ()를 사용할 수 없으므로 대신 removeAllViews ()를 사용하여 부모 레이아웃에 첨부 된 모든보기를 제거한 다음 ListView를 추가하고 작동하여 업데이트했습니다. UI :
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 ()가 도움이되지 않으면 먼저 목록을 지우고 notifyDataSetChanged () 호출 후 다시 추가 할 수 있습니다. 예를 들어
private List<List<SomeNewArray>> arrayList;
List<SomeNewArray> array1= getArrayList(...);
List<SomeNewArray> array2= getArrayList(...);
arrayList.clear();
arrayList.add(array1);
arrayList.add(array2);
notifyDataSetChanged();
그것이 당신에게 의미가 있기를 바랍니다.
조각에서 한 번에 스캔 한 BLE 장치의 mac 주소로 ListView (단일 TextView로)를 채우고 싶을 때도 마찬가지였습니다.
내가 한 일은 다음과 같습니다.
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 주소로 동적으로 채워지기 시작했습니다.
나는 그것이 새로 고침의 의미에 달려 있다고 생각합니다. GUI 표시를 화면 갱신해야합니까, 아니면 프로그램 적으로 getChildAt (int)를 호출하여 어댑터의 내용에 해당하는보기를 얻을 수 있도록 하위보기를 화면 갱신해야 함을 의미합니까?
GUI 표시를 새로 고치려면 어댑터에서 notifyDataSetChanged ()를 호출하십시오. 다음에 다시 그릴 때 GUI가 새로 고쳐집니다.
getChildAt (int)를 호출하고 어댑터에있는 내용을 반영하는보기를 얻으려면 layoutChildren ()을 호출하십시오. 그러면 어댑터 데이터에서 하위보기가 재구성됩니다.
안드로이드 가이드 라인을 사용하고 ContentProviders
있고 데이터를 가져 오기 Database
위해를 ListView
사용하고 CursorLoader
있고를 사용하여 표시하는 경우 CursorAdapters
관련 데이터에 대한 모든 변경 사항이 자동으로 ListView에 반영됩니다.
의 getContext().getContentResolver().notifyChange(uri, null);
커서 ContentProvider
는 변경 사항을 반영하기에 충분합니다. 추가 작업이 필요 없습니다.
그러나이 모든 것을 사용하지 않으면 데이터 세트가 변경 될 때 어댑터에 알려야합니다. 또한 데이터 세트 (예 : 목록)를 다시 채우거나 다시로드 한 다음 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
}
여기에서 내 시나리오에 대해 이야기하면 삭제 버튼과 함께 db 값 목록을 표시하는 활동이 있고 삭제 버튼을 누르면 목록에서 해당 항목을 삭제하고 싶기 때문에 위의 답변이 작동하지 않습니다.
멋진 점은 리사이클 뷰를 사용하지 않고 간단한 목록보기와 해당 목록보기를 어댑터 클래스에서 초기화했다는 것입니다. 따라서를 호출하면 notifyDataSetChanged()
어댑터 클래스 내부에서 delete 메소드가 어댑터 클래스에 있었기 때문에 어댑터 클래스가 초기화 된 활동 클래스에서도 아무 것도 수행하지 않습니다.
따라서 해결책은 어댑터 클래스 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()
메소드는 Adapter 클래스에서 상속됩니다.
remove()
-클릭 한 특정 항목을 제거getItem(position)
-클릭 한 위치에서 항목 (여기서 목록에 추가 한 Word 개체)을 가져 오는 것입니다.이것은 활동 클래스에서 어댑터를 목록보기로 설정하는 방법입니다.
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);
가장 쉬운 방법은 새로운 Adaper를 만들고 기존 Adaper를 삭제하는 것입니다.
myListView.setAdapter(new MyListAdapter(...));