滚动时调用列表视图滞后

时间:2015-12-29 18:23:38

标签: android loader calllog

我正在研究调用日志应用程序的示例。在此应用程序中,我的片段在列表中显示Dialed Type调用。在每个列表项中,它显示来自联系人的照片,编号,名称和时间。它工作正常但滚动时滞后。

片段代码:

package com.example.vl.calllogs;


import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.CallLog;
import android.provider.ContactsContract;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.View;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.ResourceCursorAdapter;
import android.widget.TextView;

import org.w3c.dom.Text;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by vl on 12/29/2015.
 */
public class TabDialedFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {

    CursorAdapter mAdapter;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        setEmptyText("No Dialed Numbers");
        mAdapter = new MyCursorAdapter(getActivity(), R.layout.fragment_tab_dialed, null, 0);
        setListAdapter(mAdapter);
        getLoaderManager().initLoader(0, null, this);
    }

    private static final String[] PROJECTION = {
            CallLog.Calls._ID,
            CallLog.Calls.DATE,
            CallLog.Calls.CACHED_NAME,
            CallLog.Calls.CACHED_PHOTO_ID,
            CallLog.Calls.NUMBER,
            CallLog.Calls.DURATION
    };

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        Uri baseUri  = CallLog.Calls.CONTENT_URI;
        String selection = CallLog.Calls.TYPE + "= 2";
        return new CursorLoader(getActivity(), baseUri, PROJECTION,selection, null, CallLog.Calls.DATE + " DESC" );
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
       mAdapter.swapCursor(data);
    }

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

    class MyCursorAdapter extends ResourceCursorAdapter{

        MyCursorAdapter(Context context, int layout, Cursor cursor, int flags ){
            super(context, layout,cursor,flags);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            TextView name = (TextView) view.findViewById(R.id.name);
            String nameString = cursor.getString(cursor.getColumnIndex(CallLog.Calls.CACHED_NAME));
            if(nameString == null || "".equals(nameString.trim())){
                name.setText("Unknown");
            }else{
                name.setText(nameString);
            }

            TextView time = (TextView) view.findViewById(R.id.time);
            String timeS = cursor.getString(cursor.getColumnIndex(CallLog.Calls.DATE));
            Date callDayTime = new Date(Long.valueOf(timeS));
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm");
            String str = simpleDateFormat.format(callDayTime);

            String durationS = cursor.getString(cursor.getColumnIndex(CallLog.Calls.DURATION));
            time.setText(String.format(getActivity().getResources().getString(R.string.thirdLine), str, durationS));
            TextView number = (TextView) view.findViewById(R.id.number);
            String numberS = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
            number.setText(numberS);

            int contactID = getContactIDFromNumber(numberS);
            ImageView imageView = (ImageView) view.findViewById(R.id.imageView);
            imageView.setImageBitmap(getPhoto(contactID+""));
         }

        public int getContactIDFromNumber(String contactNumber)
        {
            contactNumber = Uri.encode(contactNumber);
            int phoneContactID = -1;
            Cursor contactLookupCursor = getActivity().getContentResolver().query(Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,contactNumber),new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME, ContactsContract.PhoneLookup._ID}, null, null, null);
            while(contactLookupCursor.moveToNext()){
                phoneContactID = contactLookupCursor.getInt(contactLookupCursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup._ID));
            }
            contactLookupCursor.close();

            return phoneContactID;
        }

        private Bitmap getPhoto(String id){
            Bitmap photo = null;
            try{
                InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(
                        getActivity().getContentResolver(),
                        ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, new Long(id).longValue()));
                if(inputStream != null)
                    photo= BitmapFactory.decodeStream(inputStream);
            }catch (Exception e){

            }
            return photo;
        }

    }

}

我觉得这可能是从联系人中获取照片的有效方式的问题。在这里我首先获取contact_id,然后使用联系人ID查询照片。这是正确的方法吗?

上次查询不是异步工作。要做异步我该怎么办?

1 个答案:

答案 0 :(得分:2)

您应该修复bindView上的三个主要性能问题。

  1. 最严重的问题
  2. private Bitmap getPhoto的调用是SQLite操作,后跟磁盘加载操作。这需要几毫秒才能完成,并且肯定会落后于用户界面。

    坏消息是后台线程图像加载和缓存是一个非常复杂的主题,很难正确编码。

    好消息是,如今我们拥有大量优秀的图书馆,可以帮助您。以下是使用Picasso library

    加载它的代码
    Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
    Picasso.with(getActivity()).load(uri).into(imageView);
    
    1. 非常糟糕的问题
    2. 调用public int getContactIDFromNumber也在进行SQLite查询,而且速度也很慢。

      不幸的是,我对你应该如何修复它没有任何重大建议。这是一个缓慢的操作,你应该在后台线程中做。它将是一个主要的重构,使它在适配器内部工作。

      我的建议是扩展CursorLoader并在完成加载游标(但仍然在后台线程上)后执行以执行所有这些查询并将其保留在某些HashMap

      1. 次要问题

        • 使用Holder Pattern来避免对findViewById(int)的所有调用。
        • 同时避免在bindView内创建新对象。例如,对于整个类,您应该只有1 private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm");并且始终使用相同的实例。也只有1 Date个适配器对象,只需调用callDayTime.setTime(Long.valueOf(timeS));
      2. 希望它有所帮助。