2

電話で利用可能なすべての連絡先を表示するだけのアプリを作成しました。アプリは正常に動作しています。連絡先の画像とdisplay_nameを取得できます。ただし、リストをスクロールすると、アプリは多少遅れます。このラグを取り除く方法を知りたいです。同時に、連絡先の画像がないときに画像を表示したい。連絡先画像カーソルがnullかどうかを示すブールフラグを作成しようとしました。nullの場合は、フラグ(image_found)をfalseにして、setImageResourceを使用して画像を設定しようとしましたが、機能しませんでした。それでは、画像がない場合にblobオブジェクトがnullになる可能性があると思いましたが、それも機能しました。これがmail_activityの私のコードです。

package legacy_systems.aggregatedcontactlist;

import java.util.ArrayList;
import java.util.List;

import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import android.app.ListActivity;
import android.content.Intent;
import android.database.Cursor;
import android.view.Menu;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends ListActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Cursor c = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, 
                                              null, 
                                              null, 
                                              null, 
                                              ContactsContract.Contacts.DISPLAY_NAME+" ASC");

        ArrayList<String> con_ids = new ArrayList<String>();
        if(c!=null)
        {
            for(c.moveToFirst();!c.isAfterLast();c.moveToNext()){
                con_ids.add(c.getString(c.getColumnIndex(ContactsContract.Contacts._ID)));
            }
        }



        ListView ls = getListView();

        MyAdapter ada = new MyAdapter(this, R.layout.listview, con_ids);

        ls.setAdapter(ada);
    }

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


}

そしてここにMyAdapterのコードがあります

package legacy_systems.aggregatedcontactlist;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;


public class MyAdapter extends ArrayAdapter<String>{
    Context cont;
    boolean image_found = true;
    ArrayList<String> ids;

    public MyAdapter(Context context, int resource,
            ArrayList<String> objects) {
        super(context, resource, objects);
        cont = context;
        ids = objects;
        // TODO Auto-generated constructor stub
    }

    public View getView(int position, View ConvertView, ViewGroup parent){
        //For Contact Photo
        Bitmap photo=null;
        //Inflator Work
        LayoutInflater infl = (LayoutInflater)cont.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View row = infl.inflate(R.layout.list_view, parent, false);

        //Retreiving photo and Setting Photo
        photo = BitmapFactory.decodeStream(retrievePhoto(ids.get(position)));
        ImageView iv = (ImageView)row.findViewById(R.id.listicon);
        iv.setImageBitmap(photo);

        if(!image_found)
        {
            iv.setImageResource(R.drawable.person);
        }

        //Retrieving ContactName
        TextView tv  =(TextView)row.findViewById(R.id.listtext);
        tv.setText(retrieveName(ids.get(position)));

        return row;
    }

    private InputStream retrievePhoto(String Id)
    {
        Uri ContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.valueOf(Id));
        Uri PhotoUri = Uri.withAppendedPath(ContactUri, Contacts.Photo.CONTENT_DIRECTORY);

        Cursor c = cont.getContentResolver().query(PhotoUri, new String[]{Contacts.Photo.PHOTO}, null,null,null);

        try
        {
            if(c.moveToFirst())
            {
                byte[] data = c.getBlob(0);
                if(data!=null)
                {
                    return new ByteArrayInputStream(data);
                }
                else
                {
                    image_found = false;
                }
            }
        }
        finally
        {
            c.close();
        }
        return null;
    }

    private String retrieveName(String Id)
    {
        Uri ContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.valueOf(Id));
        Cursor c = cont.getContentResolver()
                        .query(ContactUri, 
                                new String[]{ContactsContract.Contacts.DISPLAY_NAME}, 
                                null, 
                                null, 
                                null);
        if(c==null)
            return null;
        try
        {
            if(c.moveToFirst())
            {
                String name = c.getString(0);
                return name;
            }
        }
        finally
        {
            c.close();
        }
        return null;
    }

}

ここまで行って本当に嬉しいです。私の冒険を続けるのを手伝ってください。

[編集]提案されたものを実装しましたが、1つの連絡先画像を取得できなくなりました。何が問題なのかわかりません。MyAdapter.javaでのみ変更を加え、そのコードを投稿しています。

package legacy_systems.aggregatedcontactlist;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;


public class MyAdapter extends ArrayAdapter<String>{
    Context cont;
    boolean image_found = true;
    ArrayList<String> ids;

    public MyAdapter(Context context, int resource,
            ArrayList<String> objects) {
        super(context, resource, objects);
        cont = context;
        ids = objects;
        // TODO Auto-generated constructor stub
    }

    public View getView(int position, View ConvertView, ViewGroup parent){

        View row = ConvertView;
        //For Contact Photo
        Bitmap photo=null;
        //Inflator Work
        if(row==null)
        {
            LayoutInflater infl = (LayoutInflater)cont.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            row = infl.inflate(R.layout.list_view, parent, false);
        }

        //Retreiving photo and Setting Photo
        ImageView iv = (ImageView)row.findViewById(R.id.listicon);


        synchronized (iv) {
            Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.valueOf(ids.get(position)));
            PhotoLoader loader = new PhotoLoader(iv, contactUri);
            loader.execute();

        }



        //Retrieving ContactName
        TextView tv  =(TextView)row.findViewById(R.id.listtext);
        tv.setText(retrieveName(ids.get(position)));

        return row;
    }



    private String retrieveName(String Id)
    {
        Uri ContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.valueOf(Id));
        Cursor c = cont.getContentResolver()
                        .query(ContactUri, 
                                new String[]{ContactsContract.Contacts.DISPLAY_NAME}, 
                                null, 
                                null, 
                                null);
        if(c==null)
            return null;
        try
        {
            if(c.moveToFirst())
            {
                String name = c.getString(0);
                return name;
            }
        }
        finally
        {
            c.close();
        }
        return null;
    }

    class PhotoLoader extends AsyncTask<Void, Void, Bitmap>
    {
        final WeakReference<ImageView> mView;
        final Uri mUri;

        public PhotoLoader(ImageView view, Uri uri) {

            if(view == null)
            {
                throw new IllegalArgumentException("View Cannot be null");
            }
            if(uri == null)
            {
                throw new IllegalArgumentException("Uri cant be null");
            }
            mView = new WeakReference<ImageView>(view);
            mUri = uri;
        }

        protected Bitmap doInBackground(Void...args){
            Bitmap bitmap;
            InputStream in = ContactsContract.Contacts.openContactPhotoInputStream(cont.getContentResolver(), mUri);
            bitmap = BitmapFactory.decodeStream(in);

            if(bitmap == null)
            {   Resources mResources = cont.getResources();
                bitmap = BitmapFactory.decodeResource(mResources, R.drawable.person);
            }
            return bitmap;
        }
    }

}

キャッシュを実装していません。この問題が解決したら、キャッシュとホルダーを実装しようとします。それまでは、問題を解決してください、

【再編集】今回は。画像はすべてめちゃくちゃです。

package legacy_systems.aggregatedcontactlist;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;


public class MyAdapter extends ArrayAdapter<String>{
    Context cont;
    boolean image_found = true;
    ArrayList<String> ids;
    ImageView iv;

    public MyAdapter(Context context, int resource,
            ArrayList<String> objects) {
        super(context, resource, objects);
        cont = context;
        ids = objects;
        // TODO Auto-generated constructor stub
    }

    public View getView(int position, View ConvertView, ViewGroup parent){

        View row = ConvertView;
        //For Contact Photo

        //Inflator Work

        if(row==null)
        {
            LayoutInflater infl = (LayoutInflater)cont.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            row = infl.inflate(R.layout.list_view, parent, false);
        }


        //Retreiving photo and Setting Photo
        iv = (ImageView)row.findViewById(R.id.listicon);


        synchronized (this) {
            Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.valueOf(ids.get(position)));
            PhotoLoader loader = new PhotoLoader(iv, contactUri);
            loader.execute();

        }



        //Retrieving ContactName
        TextView tv  =(TextView)row.findViewById(R.id.listtext);
        tv.setText(retrieveName(ids.get(position)));

        return row;
    }



    private String retrieveName(String Id)
    {
        Uri ContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.valueOf(Id));
        Cursor c = cont.getContentResolver()
                        .query(ContactUri, 
                                new String[]{ContactsContract.Contacts.DISPLAY_NAME}, 
                                null, 
                                null, 
                                null);
        if(c==null)
            return null;
        try
        {
            if(c.moveToFirst())
            {
                String name = c.getString(0);
                return name;
            }
        }
        finally
        {
            c.close();
        }
        return null;
    }

    class PhotoLoader extends AsyncTask<Void, Void, Bitmap>
    {
        final WeakReference<ImageView> mView;
        final Uri mUri;

        public PhotoLoader(ImageView view, Uri uri) {

            if(view == null)
            {
                throw new IllegalArgumentException("View Cannot be null");
            }
            if(uri == null)
            {
                throw new IllegalArgumentException("Uri cant be null");
            }
            mView = new WeakReference<ImageView>(view);
            mUri = uri;
        }

        protected Bitmap doInBackground(Void...args){
            Bitmap bitmap;
            InputStream in = ContactsContract.Contacts.openContactPhotoInputStream(cont.getContentResolver(), mUri);
            bitmap = BitmapFactory.decodeStream(in);


            return bitmap;
        }

        protected void onPostExecute(Bitmap bitmap)
        {
            if(bitmap == null)
            {   Resources mResources = cont.getResources();
                bitmap = BitmapFactory.decodeResource(mResources, R.drawable.person);
                iv.setImageBitmap(bitmap);
                return;
            }
            iv.setImageBitmap(bitmap);
        }
    }

}
4

3 に答える 3

2

Gabe Sechanが毎回ビューが膨らんでいると言ったこと以外に、いくつか変更する必要があります。コンテンツプロバイダーを使用して写真を取得する代わりにopenContachPhotoInputStream、Contactsクラスのメソッドを使用します。これはここにあります。また、AsyncTaskを使用して、ビットマップのオープンとロードをバックグラウンドスレッドに移動する必要があります。そして最後に、ここLruCacheで説明されているように、効率を上げるために、すでに開いているビットマップをキャッシュするためにを使用してみてください。私は最近似たようなことをしなければならなかったので、あなたに手を差し伸べるためにコードの断片を共有しています。これを使用して、背景に画像をロードします。

class PhotoLoader extends AsyncTask<Void, Void, Bitmap> {

    final WeakReference<ImageView> mView;
    final Uri mUri;

    PhotoLoader(ImageView view, Uri uri) {
        if (view == null) {
            throw new IllegalArgumentException("View cannot be null!");
        }

        if (uri == null) {
            throw new IllegalArgumentException("Uri cannot be null!");
        }

        mView = new WeakReference<ImageView>(view);
        mUri = uri;
    }

    @Override
    protected Bitmap doInBackground(Void... args) {
                    // use the uri passed to the constructor
        InputStream is = Contacts.openContactPhotoInputStream(mResolver,
                mUri);
        Bitmap bm = BitmapFactory.decodeStream(is);

                    // load custom resource image if contact has no photo
        if (bm == null) {
            bm = BitmapFactory.decodeResource(mResources,
                    R.drawable.ic_contact);
        }

        return bm;
    }

    @Override
    protected void onPostExecute(Bitmap result) {
                    // use a transition drawable for nice fade effect
        Drawable[] layers = new Drawable[2];
        layers[0] = mResources.getDrawable(android.R.color.transparent);

        layers[1] = new BitmapDrawable(mResources, result);
        TransitionDrawable t = new TransitionDrawable(layers);
        t.setCrossFadeEnabled(true);

        ImageView view = mView.get();

        if(view != null) {
            view.setImageDrawable(t);
            t.startTransition(mShortAnimation);
        }
        synchronized (mPhotoCache) {
                            // store bitmap in LruCache for later use...
            mPhotoCache.put(mUri.toString(), result);
        }
    }
}

私はアダプターで次のように使用します。

synchronized (mPhotoCache) {
    Bitmap b = mPhotoCache.get(key);

    if (b != null) {
    imageView.setImageBitmap(b);
    } else {
    imageView.setImageResource(android.R.color.transparent);
    PhotoLoader loader = new PhotoLoader(imageView, uri);
    loader.execute();
    }
}

編集:明確にするために、私はキャッシュキーとして文字列形式の連絡先URIを使用しています。キャッシュをチェックする前のアダプタのどこかに、次の行があります。 String key = uri.toString();

于 2013-02-21T22:08:52.657 に答える
2

私はついにすべての問題に取り組みました。アプリケーションはカスタムアダプタを使用しないのが最善だと思いました。そのため、すでに提供されているアダプタを使用することをお勧めします。私の場合、SimpleCursorAdapterを使用することにしました。また、連絡先画像が利用できない場合に画像を表示するという質問に取り組むには、レイアウトファイル内の連絡先画像の場所のソースとして事前定義された画像を定義する必要があります。連絡先に画像がない場合、ビットマップにはnullが含まれるため、連絡先の画像が存在しないときに画像を変更しないのがコツです。

SimpleCursorAdapterを単純に拡張したMyAdapterのコードを次に示します。

package com.example.contact;

import java.io.InputStream;
import java.util.ArrayList;



import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.support.v4.util.LruCache;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.View;
import android.widget.ImageView;

public class MyAdapter extends SimpleCursorAdapter{

    private Cursor cursor;  
    private static int con_id_index;
    private static Drawable person;
    private Context context;
    private static LruCache<Long, Bitmap> cache;
    long con_id;
    private ArrayList<Long> id_list = new ArrayList<Long>();

    public MyAdapter(Context context, int layout, Cursor c, String[] from,
            int[] to, int flags) {
        super(context, layout, c, from, to, flags);
        this.cursor = c;        
        con_id_index = cursor.getColumnIndex(ContactsContract.Contacts._ID);
        person = context.getResources().getDrawable(R.drawable.person);
        this.context = context;

        final int maxMem = (int)Runtime.getRuntime().maxMemory()/1024;
        final int cacheSize = maxMem/8;

        cache = new LruCache<Long, Bitmap>(cacheSize);
        id_list.clear();
    }   


    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        Object tag;
        // TODO Auto-generated method stub
        super.bindView(view, context, cursor);

        con_id = cursor.getLong(con_id_index);
        if(!id_list.contains(Long.valueOf(con_id)))
        {
            id_list.add(con_id);
        }
        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, con_id);

        ImageView con_image = (ImageView) view.findViewById(R.id.contact_iamge);
        tag = con_id;
        con_image.setImageDrawable(person);
        con_image.setTag(tag);
        if(getBitmapFromCache(con_id)!=null)
        {
            con_image.setImageBitmap(getBitmapFromCache(con_id));
        }
        else
        {
            ImageLoader loader = new ImageLoader(con_image, contactUri, con_id);
            loader.execute();
        }

    }

    class ImageLoader extends AsyncTask<Void, Void, Bitmap>
    {
        private ImageView mView;
        private Uri mUri;       
        private Object tag;
        private long position;

        public ImageLoader(ImageView view, Uri uri, long position) {

            if(view == null)
            {
                throw new IllegalArgumentException("View Cannot be null");
            }
            if(uri == null)
            {
                throw new IllegalArgumentException("Uri cant be null");
            }
            mView = view;
            tag = mView.getTag();
            this.position = position;
            mUri = uri;         
        }

        protected Bitmap doInBackground(Void...args){
            Bitmap bitmap;

            //Load image from the Content Provider
            InputStream in = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), mUri);
            bitmap = BitmapFactory.decodeStream(in);

            return bitmap;
        }

        protected void onPostExecute(Bitmap bitmap)
        {
            //If is in somewhere else, do not temper
            if(!mView.getTag().equals(tag))
                return;
            //If no image was there and do not put it to cache
            if(bitmap!= null)
            {   

                mView.setImageBitmap(bitmap);
                addBitmapToCache(position, bitmap);
                return;
            }
            //Otherwise, welcome to cache           
            return;
        }
    }
    /**Add image to cache*/
    private void addBitmapToCache(Long key, Bitmap bitmap)
    {
        if(getBitmapFromCache(key)==null)
        {
            cache.put(key, bitmap);
        }
    }
    /**Retrive image from cache*/
    private Bitmap getBitmapFromCache(Long key)
    {
        return cache.get(key);
    }

    public ArrayList<Long> getIDList()
    {
        return id_list;
    }

}

ロードされたカーソルには画像が含まれておらず、コンテンツプロバイダーでは一度に複数のテーブルをクエリすることはできません。したがって、バックグラウンドスレッドで画像を取得するのが最適です。そこでASyncTaskを使用しました。

MyAdpterはgetView()をオーバーライドしませんが、代わりにbindView()をオーバーライドします。これにより、拡張されているビューに追加情報を追加/バインドできます。アダプタはメモリキャッシュを利用し、連絡先IDをキーとして使用して画像を取得してキャッシュに保存します。

それが他の誰かを助けることを願っています。

于 2013-03-29T19:39:19.453 に答える
1

getViewは、呼び出しごとに新しい行を膨らませています。渡されたビューがnullの場合にのみそれを行う必要があります。そうでない場合は、渡されたビューをオーバーライドする必要があります。これにより、多くのラグが高速化されます。

于 2013-02-21T21:36:33.673 に答える