fresco解析(2)之View
来源:互联网 发布:论坛网站排名数据库 编辑:程序博客网 时间:2024/06/05 17:40
如果让我来设计一个View的话,在Android中,属于自定义View的工作,常用的自定义View的方法都可以拿来用。但是,如果在View上必须draw一个Drawable,那自定义View的那一套就没有办法继续有用了。从Hierarchy来看,FB采用的好像是自定义Drawable的“捷径”——举个例子,画一个圆,既可以在正方形的画板上画一个圆,也可以在一个圆形的画板上,根据画板的“圆”这个特性,“描出”一个圆来。
如果对View这个“画板”自定义的话,那就可以定义一些属性,用来对Drawable进行设定。
所以说,光有Drawable还不行,它需要一个载体,也就是View。
1、“View”
还是通过继承关系,找到最顶端的类,看看作者背后的思考。
DraweeView就是用来展示Hierarchy的View。它继承自ImageView,但是不支持ImageView的setImageXxx, setScaleType等类似操作。FB选择让DraweeView继承它,主要是看中它类似padding的计算,后期可能会改成直接继承View。
public class DraweeView<DH extends DraweeHierarchy> extends ImageView {private final AspectRatioMeasure.Spec mMeasureSpec = new AspectRatioMeasure.Spec(); private float mAspectRatio = 0; private DraweeHolder<DH> mDraweeHolder; private boolean mInitialised = false;
DraweeView有一个很重要的函数如下,DraweeView必须设定DraweeController才可以正常显示Image。从这里就可以看出来,对ImageView设定图片来源,会定位到从mDraweeHolder来获取。
/** Sets the controller. */public void setController(@Nullable DraweeController draweeController) { mDraweeHolder.setController(draweeController); super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());}
比较重要的如 DH extends DraweeHierarchy和DraweeHolder
。DraweeHolder比较简单,它之所以叫Holder,是因为它持有其他类型,“像是一个仓库”,装有不同类型的成员。
public class DraweeHolder<DH extends DraweeHierarchy> implements VisibilityCallback {private boolean mIsControllerAttached = false; private boolean mIsHolderAttached = false; private boolean mIsVisible = true; private boolean mIsActivityStarted = true; private DH mHierarchy; private DraweeController mController = null; private final ActivityListener mActivityListener; private final DraweeEventTracker mEventTracker = new DraweeEventTracker();
看到这里,作者设计的思路也就出来了:继承自ImageView,并且有一个“Holder”,该“holder”持有MVC中的M,即Hierarchy,持有C,即DraweeController。对ImageView的将通过该Holder来处理。
2、GenericDraweeView
这个类简单,看它的构造函数。
public class GenericDraweeView extends DraweeView<GenericDraweeHierarchy> {public GenericDraweeView(Context context, GenericDraweeHierarchy hierarchy) {super(context);setHierarchy(hierarchy);}public GenericDraweeView(Context context) {super(context);inflateHierarchy(context, null);}public GenericDraweeView(Context context, AttributeSet attrs) {super(context, attrs);inflateHierarchy(context, attrs); // 从attrs里面提取属性}public GenericDraweeView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);inflateHierarchy(context, attrs);}@TargetApi(Build.VERSION_CODES.LOLLIPOP)public GenericDraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);inflateHierarchy(context, attrs);}
其中,从XML中提取属性的详细方法如下:
private void inflateHierarchy(Context context, @Nullable AttributeSet attrs) { Resources resources = context.getResources();// fading animation defaultsint fadeDuration = GenericDraweeHierarchyBuilder.DEFAULT_FADE_DURATION;// images & scale types defaultsint placeholderId = 0;ScalingUtils.ScaleType placeholderScaleType = GenericDraweeHierarchyBuilder.DEFAULT_SCALE_TYPE; int retryImageId = 0;ScalingUtils.ScaleType retryImageScaleType = GenericDraweeHierarchyBuilder.DEFAULT_SCALE_TYPE; int failureImageId = 0;ScalingUtils.ScaleType failureImageScaleType = GenericDraweeHierarchyBuilder.DEFAULT_SCALE_TYPE; int progressBarId = 0;ScalingUtils.ScaleType progressBarScaleType = GenericDraweeHierarchyBuilder.DEFAULT_SCALE_TYPE;ScalingUtils.ScaleType actualImageScaleType = GenericDraweeHierarchyBuilder.DEFAULT_ACTUAL_IMAGE_SCALE_TYPE; int backgroundId = 0; int overlayId = 0; int pressedStateOverlayId = 0;// rounding defaultsboolean roundAsCircle = false; int roundedCornerRadius = 0; boolean roundTopLeft = true; boolean roundTopRight = true; boolean roundBottomRight = true; boolean roundBottomLeft = true; int roundWithOverlayColor = 0; int roundingBorderWidth = 0; int roundingBorderColor = 0; int roundingBorderPadding = 0; int progressBarAutoRotateInterval = 0; if (attrs != null) { TypedArray gdhAttrs = context.obtainStyledAttributes( // 备注:从XML里提取属性 attrs,R.styleable.GenericDraweeView); try {final int indexCount = gdhAttrs.getIndexCount(); for (int i = 0; i < indexCount; i++) {final int idx = gdhAttrs.getIndex(i);// most popular ones firstif (idx == R.styleable.GenericDraweeView_actualImageScaleType) {// actual image scale typeactualImageScaleType = getScaleTypeFromXml( gdhAttrs,R.styleable.GenericDraweeView_actualImageScaleType,actualImageScaleType);} else if (idx == R.styleable.GenericDraweeView_placeholderImage) {// placeholder imageplaceholderId = gdhAttrs.getResourceId( R.styleable.GenericDraweeView_placeholderImage,placeholderId);} else if (idx == R.styleable.GenericDraweeView_pressedStateOverlayImage) {// pressedState overlaypressedStateOverlayId = gdhAttrs.getResourceId( R.styleable.GenericDraweeView_pressedStateOverlayImage,pressedStateOverlayId);} else if (idx == R.styleable.GenericDraweeView_progressBarImage) {// progress bar imageprogressBarId = gdhAttrs.getResourceId( R.styleable.GenericDraweeView_progressBarImage,progressBarId);// the remaining ones without any particular order} else if (idx == R.styleable.GenericDraweeView_fadeDuration) {// fade durationfadeDuration = gdhAttrs.getInt( R.styleable.GenericDraweeView_fadeDuration,fadeDuration);} else if (idx == R.styleable.GenericDraweeView_viewAspectRatio) {// aspect ratiosetAspectRatio(gdhAttrs.getFloat( R.styleable.GenericDraweeView_viewAspectRatio,getAspectRatio()));} else if (idx == R.styleable.GenericDraweeView_placeholderImageScaleType) {// placeholder image scale typeplaceholderScaleType = getScaleTypeFromXml( gdhAttrs,R.styleable.GenericDraweeView_placeholderImageScaleType,placeholderScaleType);} else if (idx == R.styleable.GenericDraweeView_retryImage) {// retry imageretryImageId = gdhAttrs.getResourceId( R.styleable.GenericDraweeView_retryImage,retryImageId);} else if (idx == R.styleable.GenericDraweeView_retryImageScaleType) {// retry image scale typeretryImageScaleType = getScaleTypeFromXml( gdhAttrs,R.styleable.GenericDraweeView_retryImageScaleType,retryImageScaleType);} else if (idx == R.styleable.GenericDraweeView_failureImage) {// failure imagefailureImageId = gdhAttrs.getResourceId( R.styleable.GenericDraweeView_failureImage,failureImageId);} else if (idx == R.styleable.GenericDraweeView_failureImageScaleType) {// failure image scale typefailureImageScaleType = getScaleTypeFromXml( gdhAttrs,R.styleable.GenericDraweeView_failureImageScaleType,failureImageScaleType);} else if (idx == R.styleable.GenericDraweeView_progressBarImageScaleType) {// progress bar image scale typeprogressBarScaleType = getScaleTypeFromXml( gdhAttrs,R.styleable.GenericDraweeView_progressBarImageScaleType,progressBarScaleType);} else if (idx == R.styleable.GenericDraweeView_progressBarAutoRotateInterval) {// progress bar auto rotate intervalprogressBarAutoRotateInterval = gdhAttrs.getInteger( R.styleable.GenericDraweeView_progressBarAutoRotateInterval,0);} else if (idx == R.styleable.GenericDraweeView_backgroundImage) {// backgroundbackgroundId = gdhAttrs.getResourceId( R.styleable.GenericDraweeView_backgroundImage,backgroundId);} else if (idx == R.styleable.GenericDraweeView_overlayImage) {// overlayoverlayId = gdhAttrs.getResourceId( R.styleable.GenericDraweeView_overlayImage,overlayId);} else if (idx == R.styleable.GenericDraweeView_roundAsCircle) {// rounding parametersroundAsCircle = gdhAttrs.getBoolean( R.styleable.GenericDraweeView_roundAsCircle,roundAsCircle);} else if (idx == R.styleable.GenericDraweeView_roundedCornerRadius) { roundedCornerRadius = gdhAttrs.getDimensionPixelSize( R.styleable.GenericDraweeView_roundedCornerRadius,roundedCornerRadius);} else if (idx == R.styleable.GenericDraweeView_roundTopLeft) { roundTopLeft = gdhAttrs.getBoolean( R.styleable.GenericDraweeView_roundTopLeft,roundTopLeft);} else if (idx == R.styleable.GenericDraweeView_roundTopRight) { roundTopRight = gdhAttrs.getBoolean( R.styleable.GenericDraweeView_roundTopRight,roundTopRight);} else if (idx == R.styleable.GenericDraweeView_roundBottomRight) { roundBottomRight = gdhAttrs.getBoolean( R.styleable.GenericDraweeView_roundBottomRight,roundBottomRight);} else if (idx == R.styleable.GenericDraweeView_roundBottomLeft) { roundBottomLeft = gdhAttrs.getBoolean( R.styleable.GenericDraweeView_roundBottomLeft,roundBottomLeft);} else if (idx == R.styleable.GenericDraweeView_roundWithOverlayColor) { roundWithOverlayColor = gdhAttrs.getColor( R.styleable.GenericDraweeView_roundWithOverlayColor,roundWithOverlayColor);} else if (idx == R.styleable.GenericDraweeView_roundingBorderWidth) { roundingBorderWidth = gdhAttrs.getDimensionPixelSize( R.styleable.GenericDraweeView_roundingBorderWidth,roundingBorderWidth);} else if (idx == R.styleable.GenericDraweeView_roundingBorderColor) { roundingBorderColor = gdhAttrs.getColor( R.styleable.GenericDraweeView_roundingBorderColor,roundingBorderColor);} else if (idx == R.styleable.GenericDraweeView_roundingBorderPadding) { roundingBorderPadding = gdhAttrs.getDimensionPixelSize( R.styleable.GenericDraweeView_roundingBorderPadding,roundingBorderPadding);} } } finally { gdhAttrs.recycle();} }/*备注:Builder...一下各个方法,主要对Builder的各个成员变量进行赋值从名字就可以看出来,该Builder是用来build出一个Hierarchy的。*/ GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(resources); // set fade durationbuilder.setFadeDuration(fadeDuration);// set images & scale typesif (placeholderId > 0) { builder.setPlaceholderImage(resources.getDrawable(placeholderId), placeholderScaleType);}if (retryImageId > 0) { builder.setRetryImage(resources.getDrawable(retryImageId), retryImageScaleType);}if (failureImageId > 0) { builder.setFailureImage(resources.getDrawable(failureImageId), failureImageScaleType);}if (progressBarId > 0) { Drawable progressBarDrawable = resources.getDrawable(progressBarId); if (progressBarAutoRotateInterval > 0) { progressBarDrawable =new AutoRotateDrawable(progressBarDrawable, progressBarAutoRotateInterval);} builder.setProgressBarImage(progressBarDrawable, progressBarScaleType);}if (backgroundId > 0) { builder.setBackground(resources.getDrawable(backgroundId));}if (overlayId > 0) { builder.setOverlay(resources.getDrawable(overlayId));}if (pressedStateOverlayId > 0) { builder.setPressedStateOverlay(getResources().getDrawable(pressedStateOverlayId));} builder.setActualImageScaleType(actualImageScaleType);// set rounding parametersif (roundAsCircle || roundedCornerRadius > 0) { RoundingParams roundingParams = new RoundingParams();roundingParams.setRoundAsCircle(roundAsCircle); if (roundedCornerRadius > 0) { roundingParams.setCornersRadii( roundTopLeft ? roundedCornerRadius : 0,roundTopRight ? roundedCornerRadius : 0,roundBottomRight ? roundedCornerRadius : 0,roundBottomLeft ? roundedCornerRadius : 0);}if (roundWithOverlayColor != 0) { roundingParams.setOverlayColor(roundWithOverlayColor);}if (roundingBorderColor != 0 && roundingBorderWidth > 0) { roundingParams.setBorder(roundingBorderColor, roundingBorderWidth);}if (roundingBorderPadding != 0) { roundingParams.setPadding(roundingBorderPadding);} builder.setRoundingParams(roundingParams);} setHierarchy(builder.build()); // build()出来所需要的“5”个Drawable,并赋值给 DraweeHolder}
上面build出来的Hierarchy会被赋值给Holder,这样Holder就持有Hierarchy的引用了。
/** Sets the hierarchy. */ public void setHierarchy(DH hierarchy) { mDraweeHolder.setHierarchy(hierarchy); super.setImageDrawable(mDraweeHolder.getTopLevelDrawable()); }
上面属性定义在attrs.xml中:
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="GenericDraweeView"><!-- Fade duration in milliseconds. --><attr name="fadeDuration" format="integer"/><!-- Images - Scale types must match values in ScaleType.fromString (ScalingUtils.java). For drawables that should not be scaled, such as those with the android:tileMode attribute set, use the value 'none'. --> <!-- Aspect ratio (width / height) of the view, not necessarily of the images. --><attr name="viewAspectRatio" format="float"/><!-- A drawable or color to be be used as a placeholder. --><attr name="placeholderImage" format="reference"/><!-- Scale type of the placeholder image. Ignored if placeholderImage is not specified. --><attr name="placeholderImageScaleType"> <enum name="none" value="-1" /> <enum name="fitXY" value="0" /> <enum name="fitStart" value="1" /> <enum name="fitCenter" value="2" /> <enum name="fitEnd" value="3" /> <enum name="center" value="4" /> <enum name="centerInside" value="5" /> <enum name="centerCrop" value="6" /> <enum name="focusCrop" value="7" /> </attr><!-- A drawable to be be used as a retry image. --><attr name="retryImage" format="reference"/><!-- Scale type of the retry image. Ignored if retryImage is not specified. --><attr name="retryImageScaleType"> <enum name="none" value="-1" /> <enum name="fitXY" value="0" /> <enum name="fitStart" value="1" /> <enum name="fitCenter" value="2" /> <enum name="fitEnd" value="3" /> <enum name="center" value="4" /> <enum name="centerInside" value="5" /> <enum name="centerCrop" value="6" /> <enum name="focusCrop" value="7" /> </attr><!-- A drawable to be be used as a failure image. --><attr name="failureImage" format="reference"/><!-- Scale type of the failure image. Ignored if failureImage is not specified. --><attr name="failureImageScaleType"> <enum name="none" value="-1" /> <enum name="fitXY" value="0" /> <enum name="fitStart" value="1" /> <enum name="fitCenter" value="2" /> <enum name="fitEnd" value="3" /> <enum name="center" value="4" /> <enum name="centerInside" value="5" /> <enum name="centerCrop" value="6" /> <enum name="focusCrop" value="7" /> </attr><!-- A drawable to be be used as a progress bar. --><attr name="progressBarImage" format="reference"/><!-- Scale type of the progress bar. Ignored if progressBarImage is not specified. --><attr name="progressBarImageScaleType"> <enum name="none" value="-1" /> <enum name="fitXY" value="0" /> <enum name="fitStart" value="1" /> <enum name="fitCenter" value="2" /> <enum name="fitEnd" value="3" /> <enum name="center" value="4" /> <enum name="centerInside" value="5" /> <enum name="centerCrop" value="6" /> <enum name="focusCrop" value="7" /> </attr><!-- Progress bar Auto Rotate interval in milliseconds --><attr name="progressBarAutoRotateInterval" format="integer"/><!-- Scale type of the actual image. --><attr name="actualImageScaleType"> <enum name="none" value="-1" /> <enum name="fitXY" value="0" /> <enum name="fitStart" value="1" /> <enum name="fitCenter" value="2" /> <enum name="fitEnd" value="3" /> <enum name="center" value="4" /> <enum name="centerInside" value="5" /> <enum name="centerCrop" value="6" /> <enum name="focusCrop" value="7" /> </attr><!-- A drawable or color to be used as a background. --><attr name="backgroundImage" format="reference"/><!-- A drawable or color to be used as an overlay. --><attr name="overlayImage" format="reference"/><!-- A drawable or color to be used as a pressed-state-overlay --><attr name="pressedStateOverlayImage" format="reference"/><!-- Rounding params - Declares attributes for rounding shape, mode and border. --> <!-- Round as circle. --><attr name="roundAsCircle" format="boolean"/><!-- Rounded corner radius. Ignored if roundAsCircle is used. --><attr name="roundedCornerRadius" format="dimension"/><!-- Round the top-left corner. Ignored if roundAsCircle is used. --><attr name="roundTopLeft" format="boolean"/><!-- Round the top-right corner. Ignored if roundAsCircle is used. --><attr name="roundTopRight" format="boolean"/><!-- Round the bottom-right corner. Ignored if roundAsCircle is used. --><attr name="roundBottomRight" format="boolean"/><!-- Round the bottom-left corner. Ignored if roundAsCircle is used. --><attr name="roundBottomLeft" format="boolean"/><!-- Round by overlying color. --><attr name="roundWithOverlayColor" format="color"/><!-- Rounding border width--><attr name="roundingBorderWidth" format="dimension"/><!-- Rounding border color --><attr name="roundingBorderColor" format="color"/><!-- Rounding border padding --><attr name="roundingBorderPadding" format="dimension" /> </declare-styleable></resources>
3、SimpleDraweeView
如注释所说,SimpleDraweeView接受一个 uri ,在内部会生成一个controller,并完成对controller的设定。如果说 Hierarchy是MVC中的M,SimpleDraweeView是MVC中的V,那 DraweeController就是MVC中的C。也就是说,DraweeController处于“中间”,负责协调两方的工作。
DraweeController的定义如下:既然它是连接两方SimpleDraweeView和Hierarchy,那它的功能无非就是“绑定”、“解绑”、“设定M(model)”、返回“model”、对V(View)的事件作出响应。
/** * Interface that represents a Drawee controller used by a DraweeView. * <p> The view forwards events to the controller. The controller controls * its hierarchy based on those events. */public interface DraweeController {/** Gets the hierarchy. */@Nullable DraweeHierarchy getHierarchy(); // 备注:View传递events给controller,controller根据events调整Hierarchy,反过来又让View改变。这是典型的MVC的“循环”。
/** Sets a new hierarchy. */void setHierarchy(@Nullable DraweeHierarchy hierarchy);/** * Called when the view containing the hierarchy is attached to a window * (either temporarily or permanently). */void onAttach();/** * Called when the view containing the hierarchy is detached from a window * (either temporarily or permanently). */void onDetach();/** * Called when the view containing the hierarchy receives a touch event. * @return true if the event was handled by the controller, false otherwise */boolean onTouchEvent(MotionEvent event);/** * For an animated image, returns an Animatable that lets clients control the animation. * @return animatable, or null if the image is not animated or not loaded yet */Animatable getAnimatable();}
回到SimpleDraweeView。SimpleDraweeControllerBuilder仍然是一个Builder模式,用来build出一个DraweeController。
/** * This view takes a uri as input and internally builds and sets a controller. * * <p>This class must be statically initialized in order to be used. If you are using the Fresco * image pipeline, use {@link com.facebook.drawee.backends.pipeline.Fresco#initialize} to do this. */public class SimpleDraweeView extends GenericDraweeView {private static Supplier<? extends SimpleDraweeControllerBuilder> sDraweeControllerBuilderSupplier;/** Initializes {@link SimpleDraweeView} with supplier of Drawee controller builders. */public static void initialize( Supplier<? extends SimpleDraweeControllerBuilder> draweeControllerBuilderSupplier) { sDraweeControllerBuilderSupplier = draweeControllerBuilderSupplier;}/** Shuts {@link SimpleDraweeView} down. */public static void shutDown() { sDraweeControllerBuilderSupplier = null;}private SimpleDraweeControllerBuilder mSimpleDraweeControllerBuilder;
SimpleDraweeView的构造函数,最终都需要调用init() : mSimpleDraweeControllerBuilder是从sDraweeControllerBuilderSupplier get到的,它的类型是Supplier<? extends SimpleDraweeControllerBuilder>
。看来,Supplier对前者又是一个“包装”,而这个“包装”可以生成build出一个Controller。Supplier从字面上理解,它是一个“提供者”,那先考虑一下,它提供的是什么?
private void init() {if (isInEditMode()) {return;} Preconditions.checkNotNull( sDraweeControllerBuilderSupplier,"SimpleDraweeView was not initialized!");mSimpleDraweeControllerBuilder = sDraweeControllerBuilderSupplier.get();}
现在来说说fresco的基本使用:
a、初始化Fresco.initialize(this);
b、使用
//创建SimpleDraweeView对象simpleDraweeView = (SimpleDraweeView) findViewById(R.id.main_sdv);//创建将要下载的图片的URIUri imageUri = Uri.parse(uri);//开始下载simpleDraweeView.setImageURI(imageUri);public void setImageURI(Uri uri, @Nullable Object callerContext) { DraweeController controller = mSimpleDraweeControllerBuilder// 备注:生成一个 controller .setCallerContext(callerContext) .setUri(uri) .setOldController(getController()) .build();setController(controller); // 设置controller}/** Sets the controller. */public void setController(@Nullable DraweeController draweeController) { mDraweeHolder.setController(draweeController); super.setImageDrawable(mDraweeHolder.getTopLevelDrawable()); // 从controller里面提取Drawable}
controller是由SimpleDraweeControllerBuilder生成的,而SimpleDraweeControllerBuilder是由sDraweeControllerBuilderSupplier get 出来的。
由此可见:sDraweeControllerBuilderSupplier ,即Supplier会“提供” DraweeController的Builder。可见,Fresco.initialize(this);里面会生成一个Supplier,并且生成一个SimpleDraweeControllerBuilder。
4、总结:
“View”体系也不复杂。其通过XML提取属性,构造一个Controller(MVC中的C,持有M和V,并连同M和V),根据这些属性构造一个Hierarchy,进而生成各个Drawable。并通过Supplier来提供来源更多的Drawable。fresco更精彩的地方在于Supplier和Controller。在这个过程中的Builder也好,还是Holder也好,都是辅助MVC的。Builder模式比较灵活,能够隐藏build出来对象的细节。Holder作为一个“容器”式的存在,持有对其他对象的引用,把多个对象集中在一起管理,比较简洁和方便。
- fresco解析(2)之View
- fresco解析之Hierarchy
- Fresco源码解析 - Hierarchy / View / Controller
- Fresco解析 (初始化)
- Fresco解析 (Controller)
- Fresco解析(DraweeView,DraweeHierarchy)
- Fresco解析
- Android自定义View解析之自定义View类型(三)
- 自定义View解析之自定义View实战(四)
- Fresco(2)——fresco的基本使用
- Fresco源码解析 - DraweeView
- android之View坐标解析
- Fresco源码解析 - 创建一个ImagePipeline(一)
- Fresco源码解析 - 创建一个ImagePipeline(一)
- Android自定义View解析之LayoutInflater类(三)
- Fresco之初识
- Fresco源码之DraweeHierachy
- Fresco源码之DraweeController
- 创建一个Cordova完整应用
- jQery.form.js中文API【整理】
- php中递归函数
- Android应用程序打包时,出现错误:"XXX" is not translated in "af" , "am" , "ar" ....
- vector作二维数组应用
- fresco解析(2)之View
- 最长回文串manacher算法模板
- Code Recyclebin Contact me QQ:149906878 Write operations are not allowed in read-only mode (FlushMo
- poj3009
- 延迟X秒之后执行某个方法
- 51nod 1089 最长回文字串V2(Manacher算法)
- 电源管理事件
- 小结选择器--CSS、JavaScript、JQuery
- 机器学习经典算法logistic回归