Picasso分析05

前面将源码过了一遍,不难看出,分为以下几个模块:
a) 对于Picasso的创建,利用Builder可对Picasso的线程池,下载器等进行动态配置,即使用用户
自定义的是完全可以的。
b )RequestHandler和Action:前者是指明了图片的来源,后者是指明了使用者,并且后者提供了
complete和error抽象方法供用户使用
c)Picasso自己定义了一套下载器,线程池,缓存;还定义了状态器
d)Request请求包含了图片所需的各种参数
e)BitmapHunter下载图片的具体执行者,如利用下载器等,包含一个Request和响应集合,
之所以有响应集合因为key值一样,key值中包含了各种图片变换的参数如旋转,centerCrop等,
即大家需求一样,那么就一起等待吧
f)Dispatcher分发命令,调用BitmapHunter去执行,利用mainHander去通知Picasso
g) Picasso供用户使用,包括添加删除回调等等,一些细节如添加Imageview时,会将之前的Action删除等等,adaper。
最后,里面有很多取消,添加,删除,下载成功回调操作,这些操作不会冲突吗?比如,下载成功一个后,
回调时又执行了Action暂停的操作等?—因为在同一个线程操作的,所以没问题

流程——
Picasso.with(mContext)
.load(imageFile)
.tag(ZApp.getInstance()) // 没在Request,在RequestCreator
.placeholder(R.color.img_not) // 没在Request,在RequestCreator
.resize(mItemSize, mItemSize)
.centerCrop()
.into(image);
1)into(image)
2)Request request = createRequest(started)
3)createKey(request)
4)内存有则返回,否则从RequestCreator下获取holder设置
5)创建ImageViewAction,同时设置了tag参数。
6)提交ImageViewAction:如果之前有相同的ImageView的请求,且Action不同则
删除之前的即if (target != null && targetToAction.get(target) != action);cancelExistingRequest(target);
并添加当前的ImageViewAction,即targetToAction.put(target, action);
7)提交成功dispatcher.dispatchSubmit(action)
8)performSubmit(Action action, boolean dismissFailed),
如果BitmapHunter hunter = hunterMap.get(action.getKey());存在则说明当前已经在获取这个相同的图片了,
直接hunter.attach(action)添加到这个hunter中即可,等待获取成功回调
9)否则创建一个hunter即hunter = forRequest(action.getPicasso(), this, cache, stats, action);
并交给线程池去处理service.submit(hunter)
10)线程池调用,内存中有则直接取出;
否则通过RequestHandler去加载即requestHandler.load(data, networkPolicy),加载成功后,
如果需要needsTransformation()则BitmapHunter类进行加锁执行transform的转换。
11)成功时dispatcher.dispatchComplete(this),会存入缓存,之后回到主线程
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
12) 接着hunter.picasso.complete(hunter); 将hunter中所以的action回调action.complete(result, from);
即PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);

接下来,看一下Picasso的例子吧

1 主界面

首先看一下listview的adapter

final class PicassoSampleAdapter extends BaseAdapter {
  private static final int NOTIFICATION_ID = 666;
  enum Sample { // 构造函数有2个参数,并且包括一个launch方法
    GRID_VIEW("Image Grid View", SampleGridViewActivity.class),
    GALLERY("Load from Gallery", SampleGalleryActivity.class),
    CONTACTS("Contact Photos", SampleContactsActivity.class),
    LIST_DETAIL("List / Detail View", SampleListDetailActivity.class),
    SHOW_NOTIFICATION("Sample Notification", null) {
      @Override public void launch(Activity activity) {//覆盖了父类的launch方法
           // 具体代码见后分析
      }
    };
    private final Class<? extends Activity> activityClass;//类的成员1
    private final String name;//类的成员2
    Sample(String name, Class<? extends Activity> activityClass) {
      this.activityClass = activityClass;
      this.name = name;
    }
    public void launch(Activity activity) {// 类的方法
      activity.startActivity(new Intent(activity, activityClass));
      activity.finish();
    }
  } // enum Sample end
  private final LayoutInflater inflater;
  public PicassoSampleAdapter(Context context) {    inflater = LayoutInflater.from(context);  }
  @Override public int getCount() {    return Sample.values().length;  }
  @Override public Sample getItem(int position) {    return Sample.values()[position];  }
  @Override public long getItemId(int position) {    return position;  }
  @Override public View getView(int position, View convertView, ViewGroup parent) {
    TextView view = (TextView) convertView;
    if (view == null) 
       view = (TextView) inflater.inflate(R.layout.picasso_sample_activity_item, parent, false); 
    view.setText(getItem(position).name);
    return view;
  }
}

主界面简析

import android.widget.ToggleButton;//android:textOff="@string/more",android:textOn="@string/hide"
activityList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
      @Override
      public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
        adapter.getItem(position).launch(PicassoSampleActivity.this);//启动配置的Activity
      }
});
 @Override
protected void onDestroy() {
    super.onDestroy();
    Picasso.with(this).cancelTag(this);//取消加载图片
  }

2 SampleGridViewActivity

从网络获取图片
GridView的滑动监听

public class SampleScrollListener implements AbsListView.OnScrollListener {
  private final Context context;
  public SampleScrollListener(Context context) {
    this.context = context;
  }
  @Override
  public void onScrollStateChanged(AbsListView view, int scrollState) {
    final Picasso picasso = Picasso.with(context);
    if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL) {
      picasso.resumeTag(context);// 将Pause队列中的Action重新submit提交执行
    } else {
      picasso.pauseTag(context); //将Action从hunter中暂时去除,加入Pause队列中
    }
  }
  @Override
  public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
                       int totalItemCount) {
  }
}

adapter

final class SquaredImageView extends ImageView {
  @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
  }
}
final class SampleGridViewAdapter extends BaseAdapter {
  private final Context context;
  private final List<String> urls = new ArrayList<String>();
  public SampleGridViewAdapter(Context context) {
    this.context = context;
    // Ensure we get a different ordering of images on each run.
    Collections.addAll(urls, Data.URLS);
    //Moves every element of the list to a random new position in the list.
    Collections.shuffle(urls);    

    // Triple up the list. 3倍
    ArrayList<String> copy = new ArrayList<String>(urls);
    urls.addAll(copy);
    urls.addAll(copy);
  }
  @Override public View getView(int position, View convertView, ViewGroup parent) {
    SquaredImageView view = (SquaredImageView) convertView;
    if (view == null) {
      view = new SquaredImageView(context);
      view.setScaleType(CENTER_CROP);
    }
    // Get the image URL for the current position.
    String url = getItem(position);
    // Trigger the download of the URL asynchronously into the image view.
    Picasso.with(context) //Picasso
        .load(url) // RequestCreator 
        .placeholder(R.drawable.placeholder) //RequestCreator 
        .error(R.drawable.error) //RequestCreator 
        .fit() //RequestCreator 
        .tag(context) //RequestCreator 
        .into(view);
    return view;
    /**流程简述:配置了fit(),defered说明需要延时设定view的宽高,比如第一次new的时候,宽高不确定,
    利用picasso.defer(target, new DeferredRequestCreator(this, target, callback));添加;
    之后DeferredRequestCreator检测到onPreDraw时宽高已确定了,调用
        creator.unfit().resize(width, height).into(target, callback);注意unfit();
    先看内存中是否有,没有的话就提交一个Action给Dispatch,Dispatch会判断是否已经存在对应key的Hunter了,
    有的话就将此Action添加到此Hunter中,等待之前的Run获取图片后回调;
    否则就新建一个Hunter,新建过程中会检测出适合的RequestHandler,新建之后service.submit(hunter);
    获取成功后,将保留Action的列表中删除这个Action
    */
  }
}

3 SampleGalleryActivity

从文件中选择图片

  <ViewAnimator
      android:id="@+id/animator"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:background="#ffc0c0c0"
      android:animateFirstView="false"
      >
    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
    <ProgressBar
        android:id="@+id/progress"        />
  </ViewAnimator>
public class SampleGalleryActivity extends PicassoSampleActivity {
  private static final int GALLERY_REQUEST = 9391;
  private static final String KEY_IMAGE = "com.example.picasso:image";
  private ImageView imageView;
  private ViewAnimator animator;//控制子view的显示与隐藏,一次显示一个view
  private String image;

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.sample_gallery_activity);
    animator = (ViewAnimator) findViewById(R.id.animator);
    imageView = (ImageView) findViewById(R.id.image);
    findViewById(R.id.go).setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View view) {// "android.intent.action.PICK", content://
        Intent gallery = new Intent(ACTION_PICK, EXTERNAL_CONTENT_URI);
        startActivityForResult(gallery, GALLERY_REQUEST);
      }
    });
    if (savedInstanceState != null) {
      image = savedInstanceState.getString(KEY_IMAGE);
      if (image != null) {
        loadImage();
      }
    }
  }

  @Override protected void onPause() {
    super.onPause();
    if (isFinishing()) {//Check to see whether this activity is in the process of finishing
      // Always cancel the request here, this is safe to call even if the image has been loaded.
      // This ensures that the anonymous callback we have does not prevent the activity from
      // being garbage collected. It also prevents our callback from getting invoked even after 
      // the activity has finished. !!!!
      Picasso.with(this).cancelRequest(imageView);
    }
  }
  @Override protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString(KEY_IMAGE, image);
  }
  @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == GALLERY_REQUEST && resultCode == RESULT_OK && data != null) {
      image = data.getData().toString();
      loadImage();
    } else {
      super.onActivityResult(requestCode, resultCode, data);
    }
  }

  private void loadImage() {
    // Index 1 is the progress bar. Show it while we're loading the image.
    animator.setDisplayedChild(1);
    Picasso.with(this).load(image).into(imageView, new EmptyCallback() {
      @Override public void onSuccess() {
        // Index 0 is the image view.
        animator.setDisplayedChild(0);
      }
    });
  }
}

4 SampleContactsActivity

Contacts 联系人

class SampleContactsAdapter extends CursorAdapter {
  private final LayoutInflater inflater;
  public SampleContactsAdapter(Context context) {
    super(context, null, 0);
    inflater = LayoutInflater.from(context);
  }
  @Override public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
    View itemLayout = inflater.inflate(R.layout.sample_contacts_activity_item, viewGroup, false);
    ViewHolder holder = new ViewHolder();
    holder.text1 = (TextView) itemLayout.findViewById(android.R.id.text1);
    holder.icon = (QuickContactBadge) itemLayout.findViewById(android.R.id.icon);
    itemLayout.setTag(holder);
    return itemLayout;
  }

  @Override public void bindView(View view, Context context, Cursor cursor) {
    Uri contactUri = Contacts.getLookupUri(cursor.getLong(ContactsQuery.ID),
        cursor.getString(ContactsQuery.LOOKUP_KEY));
    // content://com.android.contacts/contacts/lookup/3176r1-5A6158E16D57/1
    ViewHolder holder = (ViewHolder) view.getTag();
    holder.text1.setText(cursor.getString(ContactsQuery.DISPLAY_NAME));
    holder.icon.assignContactUri(contactUri);//点击后弹出联系人详情
    Picasso.with(context)
        .load(contactUri)
        .placeholder(R.drawable.contact_picture_placeholder)
        .tag(context)
        .into(holder.icon);
  }
  @Override public int getCount() {
    return getCursor() == null ? 0 : super.getCount();
  }
  private static class ViewHolder {
    TextView text1;
    QuickContactBadge icon;
  }
}

public class SampleContactsActivity extends PicassoSampleActivity
    implements LoaderManager.LoaderCallbacks<Cursor> {
  private static final boolean IS_HONEYCOMB =
      Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
  private SampleContactsAdapter adapter;
  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.sample_contacts_activity);
    adapter = new SampleContactsAdapter(this);
    ListView lv = (ListView) findViewById(android.R.id.list);
    lv.setAdapter(adapter);
    lv.setOnScrollListener(new SampleScrollListener(this));
    getSupportLoaderManager().initLoader(ContactsQuery.QUERY_ID, null, this);
  }

  @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    if (id == ContactsQuery.QUERY_ID) {
      return new CursorLoader(this, //
          ContactsQuery.CONTENT_URI, //
          ContactsQuery.PROJECTION, //
          ContactsQuery.SELECTION, //
          null, //
          ContactsQuery.SORT_ORDER);
    }
    return null;
  }

  @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    adapter.swapCursor(data);//Swap in a new Cursor, returning the old Cursor.
  }
  @Override public void onLoaderReset(Loader<Cursor> loader) {
    adapter.swapCursor(null);
  }

  interface ContactsQuery {
    int QUERY_ID = 1;
    Uri CONTENT_URI = Contacts.CONTENT_URI;
    String SELECTION = (IS_HONEYCOMB ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME)
        + "<>''"
        + " AND "
        + Contacts.IN_VISIBLE_GROUP
        + "=1";
    String SORT_ORDER = IS_HONEYCOMB ? Contacts.SORT_KEY_PRIMARY : Contacts.DISPLAY_NAME;
    String[] PROJECTION = {
        Contacts._ID, //
        Contacts.LOOKUP_KEY, //
        IS_HONEYCOMB ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME, //
        IS_HONEYCOMB ? Contacts.PHOTO_THUMBNAIL_URI : Contacts._ID, //
        SORT_ORDER
    };
    int ID = 0;
    int LOOKUP_KEY = 1;
    int DISPLAY_NAME = 2;
  }
}

5 SampleListDetailActivity

final class SampleListDetailAdapter extends BaseAdapter {
  public SampleListDetailAdapter(Context context) {
    this.context = context;
    Collections.addAll(urls, Data.URLS);
  }
  @Override public View getView(int position, View view, ViewGroup parent) {
    ViewHolder holder;   
    // Get the image URL for the current position.
    String url = getItem(position);
    holder.text.setText(url);
    // Trigger the download of the URL asynchronously into the image view.
    Picasso.with(context)
        .load(url)
        .placeholder(R.drawable.placeholder)
        .error(R.drawable.error)
        .resizeDimen(R.dimen.list_detail_image_size, R.dimen.list_detail_image_size)//设置大小
        .centerInside()
        .tag(context)
        .into(holder.image);
    return view;
  }
}

public class SampleListDetailActivity extends PicassoSampleActivity {
  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState == null) {
      getSupportFragmentManager().beginTransaction()
          .add(R.id.sample_content, ListFragment.newInstance())
          .commit();
    }
  }
  void showDetails(String url) {
    getSupportFragmentManager().beginTransaction()
        .replace(R.id.sample_content, DetailFragment.newInstance(url))
        .addToBackStack(null) //保留,返回键可恢复
        .commit();
  }

  public static class ListFragment extends Fragment {
    public static ListFragment newInstance() {
      return new ListFragment();
    }
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      final SampleListDetailActivity activity = (SampleListDetailActivity) getActivity();
      final SampleListDetailAdapter adapter = new SampleListDetailAdapter(activity);
      ListView listView = (ListView) LayoutInflater.from(activity)
          .inflate(R.layout.sample_list_detail_list, container, false);
      listView.setAdapter(adapter);
      listView.setOnScrollListener(new SampleScrollListener(activity));
      listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
          String url = adapter.getItem(position);
          activity.showDetails(url);
        }
      });
      return listView;
    }
  }

  public static class DetailFragment extends Fragment {
    private static final String KEY_URL = "picasso:url";
    public static DetailFragment newInstance(String url) {
      Bundle arguments = new Bundle();
      arguments.putString(KEY_URL, url);
      DetailFragment fragment = new DetailFragment();
      fragment.setArguments(arguments);
      return fragment;
    }
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      Activity activity = getActivity();
      View view = LayoutInflater.from(activity)
          .inflate(R.layout.sample_list_detail_detail, container, false);
      TextView urlView = (TextView) view.findViewById(R.id.url);
      ImageView imageView = (ImageView) view.findViewById(R.id.photo);
      Bundle arguments = getArguments();
      String url = arguments.getString(KEY_URL);
      urlView.setText(url);
      Picasso.with(activity)
          .load(url)
          .fit() // 参数defered延期
          .tag(activity)
          .into(imageView);
      return view;
    }
  }
}

6 NOTIFICATION

SHOW_NOTIFICATION("Sample Notification", null) {//开头那个
      @Override public void launch(Activity activity) {
        RemoteViews remoteViews =
            new RemoteViews(activity.getPackageName(), R.layout.notification_view);
        Intent intent = new Intent(activity, SampleGridViewActivity.class);
        NotificationCompat.Builder builder =
            new NotificationCompat.Builder(activity).setSmallIcon(R.drawable.icon)
                .setContentIntent(PendingIntent.getActivity(activity, -1, intent, 0))
                .setContent(remoteViews);
        Notification notification = builder.getNotification();
        // Bug in NotificationCompat that does not set the content. !!!
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
          notification.contentView = remoteViews;
        }
        NotificationManager notificationManager =
            (NotificationManager) activity.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(NOTIFICATION_ID, notification);
        // Now load an image for this notification.
        Picasso.with(activity) //
            .load(Data.URLS[new Random().nextInt(Data.URLS.length)]) //
            .resizeDimen(R.dimen.notification_icon_width_height,
                R.dimen.notification_icon_width_height) //
            .into(remoteViews, R.id.photo, NOTIFICATION_ID, notification);
      }
    };
    // 获取图片成功后
    remoteViews.setImageViewBitmap(viewId, result);
    NotificationManager manager = getService(picasso.context, NOTIFICATION_SERVICE);
    manager.notify(notificationTag, notificationId, notification);//notificationTag=null

7 其它

自定义的图片转换

public class GrayscaleTransformation implements Transformation {
  private final Picasso picasso;
  public GrayscaleTransformation(Picasso picasso) {
    this.picasso = picasso;
  }
  @Override public Bitmap transform(Bitmap source) {
    Bitmap result = createBitmap(source.getWidth(), source.getHeight(), source.getConfig());
    Bitmap noise;
    try {
      noise = picasso.load(R.drawable.noise).get();
    } catch (IOException e) {
      throw new RuntimeException("Failed to apply transformation! Missing resource.");
    }
    BitmapShader shader = new BitmapShader(noise, REPEAT, REPEAT);
    ColorMatrix colorMatrix = new ColorMatrix();
    colorMatrix.setSaturation(0);
    ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
    Paint paint = new Paint(ANTI_ALIAS_FLAG);
    paint.setColorFilter(filter);

    Canvas canvas = new Canvas(result);
    canvas.drawBitmap(source, 0, 0, paint);
    paint.setColorFilter(null);
    paint.setShader(shader);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
    canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);

    source.recycle();
    noise.recycle();
    return result;
  }
  @Override public String key() {    return "grayscaleTransformation()";  }
}
Manifinest.xml
    <receiver android:name="SampleWidgetProvider">
      <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
      </intent-filter>
      <meta-data android:name="android.appwidget.provider"
          android:resource="@xml/sample_widget_info"/>
    </receiver>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="@dimen/widget_min_width"
    android:minHeight="@dimen/widget_min_height"
    android:updatePeriodMillis="86400000"
    android:initialLayout="@layout/sample_widget"---ImageView
    android:widgetCategory="home_screen"
    />
// extends BroadcastReceiver
public class SampleWidgetProvider extends AppWidgetProvider {
  @Override
  public void onUpdate(final Context context, AppWidgetManager appWidgetManager,
      int[] appWidgetIds) {
    RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.sample_widget);
    // Load image for all appWidgetIds.
    Picasso picasso = Picasso.with(context);
    picasso.load(Data.URLS[new Random().nextInt(Data.URLS.length)]) //
        .placeholder(R.drawable.placeholder) //
        .error(R.drawable.error) //
        .transform(new GrayscaleTransformation(picasso)) //
        .into(updateViews, R.id.image, appWidgetIds);
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值