1

目前我开始从事 Android 开发,我想学习如何开发应用程序并应用好的规则和建议。在某处我读到正确的做法是使用 CursorLoader 加载列表以在视图中异步加载数据。以前,由于教程,我创建了一个简单的应用程序,该应用程序显示 SQLite 数据库表中的数据列表,我从 SimpleCursorAdapter 扩展了一个自定义适配器,以根据查询的每一行的某个字段的值显示不同的图标(光标)。我的自定义适配器的代码是下一个:

public class MarcaCursorAdapter extends SimpleCursorAdapter {

private Context ctx; 
private Cursor cur;

public MarcaCursorAdapter(Context context, int layout, Cursor c,
        String[] from, int[] to) {
    super(context, layout, c, from, to, 0);
    this.ctx = context;
    this.cur = c;
    // TODO Auto-generated constructor stub
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if(convertView == null)
        convertView = View.inflate(ctx, R.layout.template_marcas, null);
    View row = convertView;

    ImageView imgRk = (ImageView)convertView.findViewById(R.id.tipoRanking);
    //Dependiendo del ranking, cargaremos la imagen de mario o de wario
    if(cur.getInt(3) % 2 == 0)
        imgRk.setImageResource(R.drawable.btn1_pressed);
    else
        imgRk.setImageResource(R.drawable.btn1_focus);

    return row;
} }

而template_marcas.xml的内容如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="10dp" 
android:background="#002EB8"
android:paddingTop="20dp" >

<TextView
    android:id="@+id/nomMarca"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true"
    android:textColor="#FFFFFF" />

<TextView
    android:id="@+id/textView69"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignBottom="@+id/nomMarca"
    android:layout_toRightOf="@+id/nomMarca"
    android:text=" es la numero "
    android:textColor="#FFFFFF" />

<TextView
    android:id="@+id/rankMarca"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignBottom="@+id/textView69"
    android:layout_toRightOf="@+id/textView69"
    android:textColor="#FFFFFF" />

<ImageView
    android:id="@+id/tipoRanking"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_alignTop="@+id/textView69"
    android:layout_marginRight="56dp" /></RelativeLayout>

因此,为了使用 CursorLoader 和 ContentProvider,我转换了我的普通类,该类具有查询表的方法以获取该表的游标,我从 ContentProvider 扩展并在清单文件中注册,该类是下一个:

public class MarcaDAO extends ContentProvider {

private ConnHandler conn;
SQLiteDatabase dbObj;

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    // TODO Auto-generated method stub
    return 0;
}

@Override
public String getType(Uri uri) {
    // TODO Auto-generated method stub
    return null;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
    // TODO Auto-generated method stub
    return null;
}

@Override
public boolean onCreate() {
    conn = new ConnHandler(getContext());
    dbObj = conn.getDb();
    return true;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {
    Cursor cur = dbObj.query("MARCA", projection, null, null, null, null, null);
    return cur;
}

@Override
public int update(Uri uri, ContentValues values, String selection,
        String[] selectionArgs) {
    // TODO Auto-generated method stub
    return 0;
}}

conn 和 dbObj 对象用于管理连接,这些对象有效。最后,我有一个 Activity,它使用重写的方法实现 LoaderCallbacs 并初始化 CursorLoader:

public class MarcaActivity extends Activity implements

LoaderManager.LoaderCallbacks {

//MarcaDAO dao;
MarcaCursorAdapter crsAdap;

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

    final ListView lstMarcas = (ListView)findViewById(R.id.lstMarcas);
    //Sacamos los datos de las marcas para mostrarlos en el listview
    //dao = new MarcaDAO(this);
    //Cursor cur = dao.getList();
    getLoaderManager().initLoader(0, null, this);
    crsAdap = new MarcaCursorAdapter(this, R.layout.template_marcas, null , 
            new String[]{"hip_nombre", "hip_ranking"}, new int[]{R.id.nomMarca, R.id.rankMarca});
    lstMarcas.setAdapter(crsAdap);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.marca, menu);
    return true;
}

@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
    return new CursorLoader(this, Uri.parse("content://com.example.crudapp.marcaData"),
            new String[]{"_id", "hip_nombre", "hip_codigo", "hip_ranking"}, null, null, null);
}

@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
    crsAdap.swapCursor(arg1);
}

@Override
public void onLoaderReset(Loader<Cursor> arg0) {
    crsAdap.swapCursor(null);
}}

但是,当我运行应用程序(4.2.2)时,我在这一行的 MarcaCursorAdapter 的 getView 方法中得到了 NullPointerException:

if(cur.getInt(3) % 2 == 0)

因为光标行值为空,如果我删除此代码,列表会加载确切的行数但没有值(文本视图 nomMarca 和 rankMarca 为空)所以我不知道我做错了什么。以前它在没有内容提供者和没有光标加载器的情况下工作,但我想开发好的代码,所以我改变了我的课程。

问候。

更新:顺便说一句,我忘了说,如果在 Activity 中而不是使用 MarcaCursorAdapter,我只使用 SimpleCursorAdapter(失去对我的方法 getView 的自定义)列表加载没有问题并显示数据,所以我认为我的问题出在我编码我的 MarcaCursorAdapter 类的方式,但我不知道错误是什么。

再次问候。

4

2 回答 2

2

因为游标行值为空,

这些Cursor值不是空的(或者至少这不是错误的原因)它cur是空的引用。当您第一次初始化适配器时,您会使用 null Cursor(此时cur为 null)。当您的加载器完成其工作并加载您调用的数据swapCursor()并且适配器将开始构建行时,不幸的是您的cur引用将继续为空,因为它不会使用新Cursor引用神奇地更新。

顺便说一句,我忘了说,如果在 Activity 中而不是使用 MarcaCursorAdapter,我只使用 SimpleCursorAdapter(失去对我的方法 getView 的自定义)列表加载没有问题并显示数据,

发生这种情况是因为您实施了该getView()方法。首先,对于基于游标的适配器,您需要覆盖负责构建行newView()(创建实际行视图)和bindView()(将先前构建的行视图与游标数据绑定)的两种方法。如果您只想更改该图像资源,那么您有两种选择:

  • ViewBinder在您的适配器上使用 a (推荐)。
  • 仅扩展SimpleCursorAdapter和覆盖它的bindView()方法:

    //...
    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        super.bindView(view, context, cursor); // let the default implementation populate our views
        // change the image
        ImageView imgRk = (ImageView)view.findViewById(R.id.tipoRanking);
        //Dependiendo del ranking, cargaremos la imagen de mario o de wario
        if(cursor.getInt(3) % 2 == 0) {
           imgRk.setImageResource(R.drawable.btn1_pressed);
        } else {
           imgRk.setImageResource(R.drawable.btn1_focus);
        }
    }
    
于 2013-07-04T04:47:40.940 回答
0

谢谢,它奏效了。好吧,在阅读您的答案前几个小时,我开始阅读有关 newView、bindView 和 getView 的内容,并且我尝试使用 newView 并且它以以下方式起作用:

@Override 
public View newView(Context context, Cursor cursor, ViewGroup parent) {
    View view = View.inflate(ctx, R.layout.template_marcas, null);
    ImageView imgRk = (ImageView)view.findViewById(R.id.tipoRanking);
    //Dependiendo del ranking, cargaremos la imagen de mario o de wario
    if(getCursor().getInt(3) % 2 == 0)
        imgRk.setImageResource(R.drawable.btn1_pressed);
    else
        imgRk.setImageResource(R.drawable.btn1_focus);

    return view;
}

但是,我不太清楚何时必须使用 newView 以及何时必须使用 bindView 或者在我的应用程序中哪个更优化。但是,如果在性能或开发意义方面更好,我将搜索您对使用 ViewBinder 的建议。

问候。

于 2013-07-05T04:54:47.750 回答