Раздуйте макет в ListItem на основе определенной переменной ListItem
Я использую SimpleCursorAdapter
и ListView
чтобы показать некоторые данные, загруженные с загрузчиком. Внутри cursor
У меня есть предметы с int
это идет от 0 до 3.
Я хочу, чтобы элементы с этим int, равным 0-1, имели макет (выровненный по правому краю, один цвет) и элемент с 2-3, чтобы иметь другой макет (выровненный по левому краю, другой цвет).much like a chat app, where sent messages are on the right and received ones are on the left.
Есть ли простой способ сделать это? Что-то вроде переключателя, где 0-1 я надуваю layout_1 и 2-3 надуваю layout_2.
РЕДАКТИРОВАТЬ: я добавил код ListFragment, который я пытаюсь заполнить. Int для использования в качестве переключателя - MyContentProvider.Data.E_TYPE. Я не могу понять это, но, возможно, кто-то может объяснить, что я должен написать!
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.corsalini.survcontr.MyContentProvider.Data;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.content.CursorLoader;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
public class FragEvents extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor>{
@Override
public void onPause() {
allRead();
super.onPause();
}
private static final int EVENTS_LOADER = 0x02;
// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
// If non-null, this is the current filter the user has provided.
String mCurFilter;
@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText(this.getString(R.string.perform_event));
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Create an empty adapter we will use to display the loaded data.
mAdapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_2, null,
new String[] { MyContentProvider.Data.E_TEXT, MyContentProvider.Data.E_DATE,
MyContentProvider.Data.E_NUMBER, MyContentProvider.Data.E_TYPE },
new int[] { android.R.id.text1, android.R.id.text2 },
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getActivity().getSupportLoaderManager().initLoader(EVENTS_LOADER, null, this);
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_events, menu);
}
@Override public void onListItemClick(ListView l, View v, int position, long id) {
//TODO Insert desired behavior here.
Log.i("FragmentComplexList", "Item clicked: " + id);
}
// These are the Contacts rows that we will retrieve.
static final String[] SUMMARY_PROJECTION = new String[] {
MyContentProvider.Data.E_ID,
MyContentProvider.Data.E_DATE,
MyContentProvider.Data.E_NUMBER,
MyContentProvider.Data.E_TEXT,
MyContentProvider.Data.E_TYPE,
};
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(), MyContentProvider.Data.CONTENT_URI_EVENTS,
SUMMARY_PROJECTION, null, null,
Data.E_ID + " DESC");
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
public void deleteEvent(ContentResolver contentResolver,
long id){
String selection = Data.E_ID + "=";
String[] args = {String.valueOf(id)};
contentResolver.delete(Data.CONTENT_URI_EVENTS, selection, args);
}
public void allRead(){
ContentResolver contentResolver = getActivity().getContentResolver();
ContentValues contentValue = new ContentValues();
contentValue.put(Data.E_NUMBER, Data.RECEIVED_READ);
String selection= Data.E_TYPE+"=";
String[] args= {String.valueOf(Data.RECEIVED_UNREAD)};
contentResolver.update(Data.CONTENT_URI_EVENTS, contentValue, selection, args);
}
}
РЕДАКТИРОВАТЬ: если я правильно понял, мой последний EventAdapter (который расширяет SimpleCursorAdapter) должен выглядеть следующим образом:
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class EventsAdapter extends SimpleCursorAdapter {
private Context localContext;
public EventsAdapter(Context context, int layout, Cursor c, String[] from,
int[] to, int flags) {
super(context, layout, c, from, to, flags);
localContext = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Cursor c= getCursor();
c.moveToPosition(position);
if(convertView == null)
{
LayoutInflater layoutInflator = (LayoutInflater)localContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
switch (getItemViewType(position)){
case 0:
convertView = layoutInflator.inflate(R.layout.item_event_0, null);
break;
case 1:
convertView = layoutInflator.inflate(R.layout.item_event_1, null);
break;
case 2:
convertView = layoutInflator.inflate(R.layout.item_event_2, null);
break;
case 3:
convertView = layoutInflator.inflate(R.layout.item_event_3, null);
break;
}
}
switch (getItemViewType(position)){
case 0:
TextView date0=(TextView)convertView.findViewById(R.id.date0);
TextView text0=(TextView)convertView.findViewById(R.id.text0);
date0.setText(""+c.getString(c.getColumnIndex(Data.E_DATE)));
text0.setText(""+c.getString(c.getColumnIndex(Data.E_TEXT)));
break;
case 1:
TextView date1=(TextView)convertView.findViewById(R.id.date1);
TextView text1=(TextView)convertView.findViewById(R.id.text1);
date1.setText(""+c.getString(c.getColumnIndex(Data.E_DATE)));
text1.setText(""+c.getString(c.getColumnIndex(Data.E_TEXT)));
case 2:
TextView date2=(TextView)convertView.findViewById(R.id.date2);
TextView text2=(TextView)convertView.findViewById(R.id.text2);
date2.setText(""+c.getString(c.getColumnIndex(Data.E_DATE)));
text2.setText(""+c.getString(c.getColumnIndex(Data.E_TEXT)));
case 3:
TextView date3=(TextView)convertView.findViewById(R.id.date3);
TextView text3=(TextView)convertView.findViewById(R.id.text3);
date3.setText(""+c.getString(c.getColumnIndex(Data.E_DATE)));
text3.setText(""+c.getString(c.getColumnIndex(Data.E_TEXT)));
}
return convertView;
}
@Override
public int getItemViewType(int position) {
int type = 0;
int returnInt = 0;
Cursor c= getCursor();
c.moveToPosition(position);
type= c.getInt(c.getColumnIndex(Data.E_TYPE));
switch (type){
case Data.RECEIVED_READ:
returnInt=3;
case Data.RECEIVED_UNREAD:
returnInt= 2;
case Data.SENT_COMPLETED:
returnInt= 1;
case Data.SENT_PROGRESS:
returnInt= 0;
default:
returnInt=0;
}
return returnInt;
}
@Override
public int getViewTypeCount() {
return 4;
}
}
2 ответа
При работе с ListViews, особенно такими сложными, как вы описываете, очень важно правильно обрабатывать представления. BaseAdapter
класс, суперкласс SimpleCursorAdapter
имеет несколько методов, которые вы можете переопределить для достижения желаемого эффекта при использовании минимальных ресурсов. Я никогда ранее не использовал SimpleCursorAdatper, поэтому он написан с учетом обычного CursorAdapter, но вы можете использовать его с любым классом Adapter, который перезаписывает BaseAdapter.
ListViews в Android ведут себя очень специфическим образом, чтобы снизить стоимость памяти. При прокрутке ListView представления для элементов, которые перемещаются за пределы экрана, помещаются в небольшой пул представлений. convertView
параметр берется из этого пула. Они делают это потому, что хранение каждого элемента списка View в памяти плохо масштабируется и может быстро вызвать исключение OutOfMemory. getView()
Метод, где вы берете эти представления и настраиваете их для текущего элемента списка. Обычно у вас будет строка, которая выглядит следующим образом:
if(convertView == null)
convertView = layoutInflator.inflate(R.layout.list_item, null);
В этом случае, если convertView не равен нулю, мы знаем, что он ранее был завышен. Мы не хотим его раздувать, потому что это дорогостоящее действие, и getView должен просто быстро заполнить представления данными перед их отображением.
Теперь, в вашем случае, есть два возможных взвинчивания для convertView. Вместо того, чтобы заново раздувать представление каждый раз (очень плохо) или использовать какой-то взлом с уникальными идентификаторами ресурсов для каждого представления (лучше, но не идеально), мы можем переопределить два метода в базовом классе, чтобы убедиться, что convertView всегда правильный тип. Эти два метода getItemViewCount()
а также getItemViewType(int position)
,
getItemViewCount()
используется адаптером для определения количества пулов представлений, которые он должен поддерживать для списка. Переопределить это просто, и в вашем случае это будет выглядеть примерно так.
@Override
public int getViewTypeCount()
{
return 2; //Even though you have four cases, there are only 2 view types.
}
getItemViewType(int position)
используется адаптером ДО того, как вызывается getView, чтобы решить, из какого пула будет преобразовано convertView. Здесь вам нужен оператор switch или if/else, который проверяет базовый источник данных, для какого типа представления он находится, и возвращает его. (Обратите внимание, согласно документации Android, возвращаемое значение здесь должно быть между 0 и getViewTypeCount() -1, поэтому в вашем случае 0 или 1.)
@Override
public int getItemViewType(int position)
{
Item item = getItem(position) //Or however you're getting the data associated with a particular list position
switch(item.myInt)
{
//I simplified this a bit, basically, check your int, if it's the first type, return 0 for your first layout type, else return 1 for your second.
case(0):
case(1):
return 0;
case(2):
case(3):
return 1;
}
}
Теперь, наконец, мы изменим getView, чтобы сделать начальную layoutInflation, чтобы у вас были правильные представления в ваших пулах.
@Override
public View getView(int position, View convertView, ViewGroup viewParent)
{
//if convertView is not null, we got a view from the pool, just go on
if(convertView == null)
{
//This means we didn't have a view in the pool to match this view type. Inflate it and it will be placed in the proper pool when this list item is scrolled off the screen
if(getItemViewType(position) == 0)
convertView = layoutInflator.inflate(R.layout.list_item_type1, null);
else if(getItemViewType(position) == 1)
convertView = layoutInflator.inflate(R.layout.list_item_type2, null);
}
//Populate the view with whatever data you need here
//And finally....
return convertView;
}
ListViews и их адаптеры - одна из самых сложных вещей, с которыми я сталкивался в Android, но если потратить время на то, чтобы сделать это правильно, это значительно повысит производительность и удобство работы вашего приложения. Удачи!
Я думаю, что вам нужно только надуть правильный макет на вашем getView(int position, View convertView, ViewGroup parent)
метод:
MyItem item = getItem(position);
View vi = convertView;
if(vi == null){
switch(item.getStatus())
{
case 0:
vi = mInflater.inflate(R.layout.item1, null);
break;
case 1:
vi = mInflater.inflate(R.layout.item2, null);
break;
case 2:
vi = mInflater.inflate(R.layout.item3, null);
break;
}
//set viewholder ...
}else{
//get viewholder ...
}
// set values to views ...
Это то, что вам нужно?