ITKeyword,专注技术干货聚合推荐

注册 | 登录

解决Android: Can't access cursor data inside ContentProvider using CursorLoader

itPublisher 分享于

2021腾讯云限时秒杀,爆款1核2G云服务器298元/3年!(领取2860元代金券),
地址https://cloud.tencent.com/act/cps/redirect?redirect=1062

2021阿里云最低价产品入口+领取代金券(老用户3折起),
入口地址https://www.aliyun.com/minisite/goods

Currently i'm starting in Android development and i want to learn how to develop apps and apply good rules and recommendations. Somewhere i read that the right thing is to use CursorLoader to load lists to load data asynchronous in the view. Previously, thanks to a tutorial i created a simple app that shows a list of data in a SQLite database table, with a custom Adapter that i extended from SimpleCursorAdapter to show different icons basing on the value of a certain field of each row of the query (cursor). The code of my custom adapter is the next:

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

And the content of template_marcas.xml is the next:

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

So, in order to use CursorLoader and ContentProvider, i converted my normal class that had a method to query the table to get a cursor of that table, i extended from ContentProvider and registered in the manifest file, the class is the next:

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 and dbObj objets are used to manage connection, those object works. And finally, i have the Activity which implements LoaderCallbacs with the overriden methods and initializing the 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);
}}

But, when i run the application (4.2.2) i get a NullPointerException in the method getView of MarcaCursorAdapter in this line:

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

Because the cursor row values are null, and if i remove this code, the list loads the exact number of rows but with no values (the textviews nomMarca and rankMarca are empty) so i don't know what i'm doing wrong. Previously it worked without content provider and without cursorloader but i wanna develop good code so i changed my classes.

Regards.

UPDATE: By the way, i forgot to say, if in the Activity instead using MarcaCursorAdapter i just use SimpleCursorAdapter (losing the customization of my method getView) the list loads without problems and showing the data, so i think that my problem is in the way that i coded my MarcaCursorAdapter class, but i don't know what's the mistake.

Regards again.

android android-listview android-contentprovider simplecursoradapter android-cursorloader
|
  this question
edited Jul 3 '13 at 22:32 asked Jul 2 '13 at 16:15 Oscar Calderon 139 2 6 21

 | 

2 Answers
2

解决方法

Because the cursor row values are null,

The Cursor values aren't null(or at least this isn't the reason for the error) it's the cur reference that is null. When you first initialize the adapter you do it with a null Cursor(at this point cur is null). When your loader does its job and loads the data you call swapCursor() and the adapter will start building rows, unfortunately your cur reference will continue to be null as it doesn't get magically updated with the new Cursor reference.

By the way, i forgot to say, if in the Activity instead using MarcaCursorAdapter i just use SimpleCursorAdapter (losing the customization of my method getView) the list loads without problems and showing the data,

This is happening because of your implementation of the getView() method. First of all, for Cursor based adapter you'd want to override the two method responsible for building a row newView()(create the actual row view) and bindView()(bind the previously built row view with Cursor data). If all you want to do is change that image resource then you have two choices:

  • Use a ViewBinder on your adapter(recommended).
  • Extend SimpleCursorAdapter and override only it's bindView() method:

    //...
    @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);
        }
    }
    

|
  this answer
answered Jul 4 '13 at 4:47 Luksprog 71.9k 13 150 162

 | 

Thank you, it worked. Well, a couple of hours before reading your answer, i started reading about newView, bindView and getView, and i tried with newView and it worked, in the next way:

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

But, i am not too much clear about when i have to use newView and when i have to use bindView or which one is more optimal in my application. But i'm going to search about your piece of advice of using ViewBinder if is better in performance or development meanings.

Regards.


|
  this answer
answered Jul 5 '13 at 4:54 Oscar Calderon 139 2 6 21      Your change is small so a ViewBinder is more appropriate(and easier) to use. Those two methods are responsible together for building the row. If you're happy with the default implementation of one of them then you could get away with just implementing the other one. –  Luksprog Jul 5 '13 at 8:23      But, there's a difference in overriding newView or bindView? Regards. –  Oscar Calderon Jul 5 '13 at 13:51 1   Yes, they serve different purposes. One builds the row and one binds the data. You wouldn't want to mix that together. –  Luksprog Jul 5 '13 at 13:57

 | 


相关阅读排行


相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。