根据ListItem特定变量在ListItem上膨胀布局 [英] Inflate layout on ListItem based on ListItem specific variable

查看:70
本文介绍了根据ListItem特定变量在ListItem上膨胀布局的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用SimpleCursorAdapterListView来显示一些由Loader加载的数据. 在cursor内部,我有一个int范围从0到3的项目.

I'm using a SimpleCursorAdapter and a ListView to show some data loaded with a Loader. Inside the cursor I have items with a int that goes from 0 to 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.

I want the items with this int equals to 0-1 to have a layout (right aligned, one color) and item with 2-3 to have another layout (left aligned, another color). 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充气.

Is there a simple way to do it? Something like a switch where with 0-1 I inflate layout_1 and 2-3 I inflate layout_2.

我添加了我要填充的ListFragment的代码.用作开关的int是MyContentProvider.Data.E_TYPE. 我无法理解,但是也许有人可以清楚地解释我必须写的内容!

I've added the code of the ListFragment i'm trying to populate. The int to use as a switch is MyContentProvider.Data.E_TYPE. I can't get the hang of it, but maybe someone can explain clearly what i have to write!

   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);
}



   }

如果我做对了,我的最终EventsAdapter(扩展了SimpleCursorAdapter)应该是这样的:

if I got it right my final EventsAdapter (which extends SimpleCursorAdapter) should look like this:

    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;
}

    }

推荐答案

在使用ListViews(尤其是您所描述的复杂视图)时,正确处理视图回收很重要. BaseAdapter类是SimpleCursorAdapter的超类,您可以重写一些方法,以在使用最少资源的情况下实现所需的效果.我以前从未使用过SimpleCursorAdatper,因此在编写时要牢记普通的CursorAdapter,但是您可以将其与覆盖BaseAdapter的任何Adapter类一起使用.

It's important when working with ListViews, especially complicated ones like you describe, to handle view recycling properly. The BaseAdapter class, a superclass of SimpleCursorAdapter has a few methods that you can override to achieve the effect you want while using minimal resources. I've never used a SimpleCursorAdatper before, so this is written with a normal CursorAdapter in mind, but you can use this with any Adapter class that overwrites BaseAdapter.

ListView以非常特定的方式运行,以降低内存成本.当您滚动浏览ListView时,将移出屏幕的项目的视图放置在一个小的视图池中. convertView参数是从该池中获取的.之所以这样做,是因为将每个列表项View保留在内存中的伸缩性不好,并且可能很快导致OutOfMemory异常.在getView()方法中,您可以获取这些视图并为当前列表项配置它们.通常,您会看到这样的一行:

ListViews in Android behave in a very specific way to lower memory cost. As you scroll through a ListView, the views for items that move off the screen are placed in a small pool of views. The convertView parameter is taken from this pool. They do this because keeping each list item View in memory does not scale well and can quickly cause an OutOfMemory exception. The getView() method is where you take these views and configure them for the current list item. Usually you'll have a line that looks like this:

if(convertView == null)
    convertView = layoutInflator.inflate(R.layout.list_item, null);

在这种情况下,如果convertView不为null,则我们知道它以前被夸大了.我们不想对其重新充气,因为这是一项昂贵的操作,并且getView应该只在显示数据之前快速用数据填充视图.

In this case, if convertView is not null, we know it was previously inflated. We don't want to re-inflate it, because this is a costly action and getView should just be quickly populating the views with data before they are displayed.

现在,在您的情况下,convertView有两个潜在的通货膨胀.我们可以在基类中重写两个方法以确保convertView始终正确,而不是每次都重新填充视图(非常糟糕)或对每种视图使用具有唯一资源ID的某种技巧(更好,但不是理想选择)类型.这两个方法是getItemViewCount()getItemViewType(int position).

Now, in your case, there are two potential inflations for convertView. Rather than re-inflating the view every time (very bad) or using some kind of hack with unique resource ids for each view (better, but not ideal), we can override two methods in the base class to ensure convertView is always the correct type. These two methods are getItemViewCount() and getItemViewType(int position).

getItemViewCount()来确定它应该为列表维护多少个视图池.覆盖它很简单,在您的情况下,看起来像这样.

getItemViewCount() is used by the Adapter to determine how many pools of views it should maintain for the list. Overriding it is simple, and in your case would look something like this.

@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.)

getItemViewType(int position) is used by the Adapter BEFORE getView is called to decide which pool convertView should come from. In here, you want a switch or if/else statement that checks your underlying data source for which view type it is and return it. (Note, per the Android documentation, the return value here has to be between 0 and getViewTypeCount() -1, so in your case either 0 or 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,以便您在池中拥有正确的Views.

Now, finally, we'll modify getView to do the initial layoutInflation so that you have the correct Views in your pools.

@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中遇到的最复杂的事情之一,但是花点时间做正确的事将极大地改善应用程序的性能和用户体验.祝你好运!

ListViews and their Adapters are one of the most complicated things I've encountered in Android, but taking the time out to do it right will greatly improve the performance and user experience in your app. Good luck!

这篇关于根据ListItem特定变量在ListItem上膨胀布局的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆