1. 程式人生 > >自定義View實現的28種效果的載入中樣式

自定義View實現的28種效果的載入中樣式

GithHub地址:https://github.com/81813780/AVLoadingIndicatorView
該開源庫實現了28種載入中樣式,但是我們一般專案中為了統一風格,所有頁面的載入中效果都是一致的,所以我們一般也只會用到其中的一兩種效果,如果集成了該庫,但我們只用到了其中一種或者兩種效果,那麼就顯得有點太浪費了,因此我們可以通過分析原始碼實現,只新增我們需要的效果即可,因此也就有了本篇文章,手動整合也很簡單,此處作簡要記錄。

1,新增核心類:AVLoadingIndicatorView,繼承自View,程式碼如下:

public class AVLoadingIndicatorView
extends View { private static final String TAG="AVLoadingIndicatorView"; // 當xml中沒有指定樣式時,預設使用該樣式,xml中沒有指定顏色時,預設為白色(本類中110行設定的預設為白色) private static final BallPulseIndicator DEFAULT_INDICATOR=new BallPulseIndicator(); private static final int MIN_SHOW_TIME = 500; // ms private static final
int MIN_DELAY = 500; // ms private long mStartTime = -1; private boolean mPostedHide = false; private boolean mPostedShow = false; private boolean mDismissed = false; private final Runnable mDelayedHide = new Runnable() { @Override public void run() { mPostedHide =
false; mStartTime = -1; setVisibility(View.GONE); } }; private final Runnable mDelayedShow = new Runnable() { @Override public void run() { mPostedShow = false; if (!mDismissed) { mStartTime = System.currentTimeMillis(); setVisibility(View.VISIBLE); } } }; int mMinWidth; int mMaxWidth; int mMinHeight; int mMaxHeight; private Indicator mIndicator; private int mIndicatorColor; private boolean mShouldStartAnimationDrawable; public AVLoadingIndicatorView(Context context) { super(context); init(context, null,0,0); } public AVLoadingIndicatorView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs,0,R.style.AVLoadingIndicatorView); } public AVLoadingIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs,defStyleAttr,R.style.AVLoadingIndicatorView); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public AVLoadingIndicatorView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context,attrs,defStyleAttr,R.style.AVLoadingIndicatorView); } private void init(Context context,AttributeSet attrs,int defStyleAttr, int defStyleRes) { mMinWidth = 24; mMaxWidth = 48; mMinHeight = 24; mMaxHeight = 48; final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.AVLoadingIndicatorView); mMinWidth = a.getDimensionPixelSize(R.styleable.AVLoadingIndicatorView_minWidth, mMinWidth); mMaxWidth = a.getDimensionPixelSize(R.styleable.AVLoadingIndicatorView_maxWidth, mMaxWidth); mMinHeight = a.getDimensionPixelSize(R.styleable.AVLoadingIndicatorView_minHeight, mMinHeight); mMaxHeight = a.getDimensionPixelSize(R.styleable.AVLoadingIndicatorView_maxHeight, mMaxHeight); String indicatorName=a.getString(R.styleable.AVLoadingIndicatorView_indicatorName); mIndicatorColor=a.getColor(R.styleable.AVLoadingIndicatorView_indicatorColor, Color.WHITE); setIndicator(indicatorName); if (mIndicator==null){ setIndicator(DEFAULT_INDICATOR); } a.recycle(); } public Indicator getIndicator() { return mIndicator; } public void setIndicator(Indicator d) { if (mIndicator != d) { if (mIndicator != null) { mIndicator.setCallback(null); unscheduleDrawable(mIndicator); } mIndicator = d; //need to set indicator color again if you didn't specified when you update the indicator . setIndicatorColor(mIndicatorColor); if (d != null) { d.setCallback(this); } postInvalidate(); } } /** * setIndicatorColor(0xFF00FF00) * or * setIndicatorColor(Color.BLUE) * or * setIndicatorColor(Color.parseColor("#FF4081")) * or * setIndicatorColor(0xFF00FF00) * or * setIndicatorColor(getResources().getColor(android.R.color.black)) * @param color */ public void setIndicatorColor(int color){ this.mIndicatorColor=color; mIndicator.setColor(color); } /** * You should pay attention to pass this parameter with two way: * for example: * 1. Only class Name,like "SimpleIndicator".(This way would use default package name with * "com.wang.avi.indicators") * 2. Class name with full package,like "com.my.android.indicators.SimpleIndicator". * @param indicatorName the class must be extend Indicator . */ public void setIndicator(String indicatorName){ if (TextUtils.isEmpty(indicatorName)){ return; } StringBuilder drawableClassName=new StringBuilder(); if (!indicatorName.contains(".")){ String defaultPackageName=getClass().getPackage().getName(); drawableClassName.append(defaultPackageName) .append(".indicators") .append("."); } drawableClassName.append(indicatorName); try { Class<?> drawableClass = Class.forName(drawableClassName.toString()); Indicator indicator = (Indicator) drawableClass.newInstance(); setIndicator(indicator); } catch (ClassNotFoundException e) { Log.e(TAG,"Didn't find your class , check the name again !"); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } public void smoothToShow(){ startAnimation(AnimationUtils.loadAnimation(getContext(),android.R.anim.fade_in)); setVisibility(VISIBLE); } public void smoothToHide(){ startAnimation(AnimationUtils.loadAnimation(getContext(),android.R.anim.fade_out)); setVisibility(GONE); } public void hide() { mDismissed = true; removeCallbacks(mDelayedShow); long diff = System.currentTimeMillis() - mStartTime; if (diff >= MIN_SHOW_TIME || mStartTime == -1) { // The progress spinner has been shown long enough // OR was not shown yet. If it wasn't shown yet, // it will just never be shown. setVisibility(View.GONE); } else { // The progress spinner is shown, but not long enough, // so put a delayed message in to hide it when its been // shown long enough. if (!mPostedHide) { postDelayed(mDelayedHide, MIN_SHOW_TIME - diff); mPostedHide = true; } } } public void show() { // Reset the start time. mStartTime = -1; mDismissed = false; removeCallbacks(mDelayedHide); if (!mPostedShow) { postDelayed(mDelayedShow, MIN_DELAY); mPostedShow = true; } } @Override protected boolean verifyDrawable(Drawable who) { return who == mIndicator || super.verifyDrawable(who); } void startAnimation() { if (getVisibility() != VISIBLE) { return; } if (mIndicator instanceof Animatable) { mShouldStartAnimationDrawable = true; } postInvalidate(); } void stopAnimation() { if (mIndicator instanceof Animatable) { mIndicator.stop(); mShouldStartAnimationDrawable = false; } postInvalidate(); } @Override public void setVisibility(int v) { if (getVisibility() != v) { super.setVisibility(v); if (v == GONE || v == INVISIBLE) { stopAnimation(); } else { startAnimation(); } } } @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if (visibility == GONE || visibility == INVISIBLE) { stopAnimation(); } else { startAnimation(); } } @Override public void invalidateDrawable(Drawable dr) { if (verifyDrawable(dr)) { final Rect dirty = dr.getBounds(); final int scrollX = getScrollX() + getPaddingLeft(); final int scrollY = getScrollY() + getPaddingTop(); invalidate(dirty.left + scrollX, dirty.top + scrollY, dirty.right + scrollX, dirty.bottom + scrollY); } else { super.invalidateDrawable(dr); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { updateDrawableBounds(w, h); } private void updateDrawableBounds(int w, int h) { // onDraw will translate the canvas so we draw starting at 0,0. // Subtract out padding for the purposes of the calculations below. w -= getPaddingRight() + getPaddingLeft(); h -= getPaddingTop() + getPaddingBottom(); int right = w; int bottom = h; int top = 0; int left = 0; if (mIndicator != null) { // Maintain aspect ratio. Certain kinds of animated drawables // get very confused otherwise. final int intrinsicWidth = mIndicator.getIntrinsicWidth(); final int intrinsicHeight = mIndicator.getIntrinsicHeight(); final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight; final float boundAspect = (float) w / h; if (intrinsicAspect != boundAspect) { if (boundAspect > intrinsicAspect) { // New width is larger. Make it smaller to match height. final int width = (int) (h * intrinsicAspect); left = (w - width) / 2; right = left + width; } else { // New height is larger. Make it smaller to match width. final int height = (int) (w * (1 / intrinsicAspect)); top = (h - height) / 2; bottom = top + height; } } mIndicator.setBounds(left, top, right, bottom); } } @Override protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); drawTrack(canvas); } void drawTrack(Canvas canvas) { final Drawable d = mIndicator; if (d != null) { // Translate canvas so a indeterminate circular progress bar with padding // rotates properly in its animation final int saveCount = canvas.save(); canvas.translate(getPaddingLeft(), getPaddingTop()); d.draw(canvas); canvas.restoreToCount(saveCount); if (mShouldStartAnimationDrawable && d instanceof Animatable) { ((Animatable) d).start(); mShouldStartAnimationDrawable = false; } } } @Override protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int dw = 0; int dh = 0; final Drawable d = mIndicator; if (d != null) { dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth())); dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight())); } updateDrawableState(); dw += getPaddingLeft() + getPaddingRight(); dh += getPaddingTop() + getPaddingBottom(); final int measuredWidth = resolveSizeAndState(dw, widthMeasureSpec, 0); final int measuredHeight = resolveSizeAndState(dh, heightMeasureSpec, 0); setMeasuredDimension(measuredWidth, measuredHeight); } @Override protected void drawableStateChanged() { super.drawableStateChanged(); updateDrawableState(); } private void updateDrawableState() { final int[] state = getDrawableState(); if (mIndicator != null && mIndicator.isStateful()) { mIndicator.setState(state); } } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void drawableHotspotChanged(float x, float y) { super.drawableHotspotChanged(x, y); if (mIndicator != null) { mIndicator.setHotspot(x, y); } } @Override protected void onAttachedToWindow() {