手把手教大家撸一个知乎的自定义loadingview

buxiemg 7年前
   <p> </p>    <p>第一,希望能帮到各位,与大家分享下知识点;</p>    <p>第二,希望能从中梳理一些知识点,这也对我后期开发以及能力的提升有一些帮助。</p>    <p>废话不多说,马上开始正文:</p>    <p>大家都知道,在android开发中,或多或少都会使用到loading状态的view,因为网络请求延时或者彰显app的logo,都会设计对应的loading,这次我就以知乎之前的loadingview为例,帮大家讲解下,这个效果怎么实现:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/557519f6fc31b4ec19a10e659dabc2c2.gif"></p>    <p style="text-align:center">效果图</p>    <p><strong>1.配置一个dialog的样式,我的loadingview是继承自DialogFragment的布局</strong></p>    <pre>  <code class="language-java"><- 设置透明背景,居中,而且可悬浮->      <style name="cart_dialog"      parent="@android:style/Theme.Holo.Light.Dialog">      <item name="android:windowFrame">@null</item>      <item name="android:windowIsFloating">true</item>      <item name="android:windowIsTranslucent">true</item>      <item name="android:windowNoTitle">true</item>      <item name="android:background">@android:color/transparent</item>      <item name="android:windowBackground">@android:color/transparent</item>      <item name="android:backgroundDimEnabled">true</item>      <item name="android:backgroundDimAmount">0.6</item>      </style></code></pre>    <p><strong>2.自定义样式需要填充的layout 比如 取名:catloading_main.xml</strong></p>    <pre>  <code class="language-java"><RelativeLayout      xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:tools="http://schemas.android.com/tools"     android:layout_width="170dp"     android:layout_height="200dp"     android:background="@drawable/background"     tools:context=".MainActivity">      <ImageView          android:layout_centerInParent="true"          android:src="@drawable/mouse"          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:id="@+id/mouse"/>      <RelativeLayout          android:layout_centerInParent="true"          android:layout_width="wrap_content"          android:layout_height="wrap_content">      <ImageView          android:layout_centerInParent="true"          android:src="@drawable/cat"          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:id="@+id/cat"/>      <ImageView          android:layout_marginLeft="3.5dp"          android:layout_marginTop="12.5dp"          android:src="@drawable/eyes"          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:id="@+id/eye_left"/>      <com.roger.catloadinglibrary.EyelidView          android:layout_marginLeft="3.5dp"          android:layout_marginTop="12.5dp"          android:src="@drawable/eyes"          android:layout_width="24dp"          android:layout_height="15dp"          android:id="@+id/eyelid_left"/>      <ImageView          android:layout_marginLeft="28dp"          android:layout_marginTop="12.5dp"          android:src="@drawable/eyes"          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:id="@+id/eye_right"/>      <com.roger.catloadinglibrary.EyelidView          android:layout_marginLeft="28dp"          android:layout_marginTop="12.5dp"          android:src="@drawable/eyes"          android:layout_width="24dp"          android:layout_height="15dp"          android:id="@+id/eyelid_right"/>       </RelativeLayout>       <com.roger.catloadinglibrary.GraduallyTextView       android:id="@+id/graduallyTextView"       android:layout_marginBottom="10dp"        android:textSize="15sp"       android:layout_alignParentBottom="true"       android:layout_centerHorizontal="true"       android:textColor="#ffffff"       android:text="L O A D I N G ..."       android:layout_width="110dp"       android:layout_height="wrap_content"/>      </RelativeLayout>       样式如下:</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/710551913b9c57558f2f927f18fe29e3.png"></p>    <p style="text-align:center">猫眼图</p>    <p><strong>3.自定义样式背景的shape</strong></p>    <pre>  <code class="language-java"><shape xmlns:android="http://schemas.android.com/apk/res/android">      <solid android:color="#5d4773"/>      <corners android:radius="30px"/>      <padding android:left="0dp" android:top="0dp"            android:right="0dp"android:bottom="0dp" />      </shape></code></pre>    <p><strong>4.自定义GraduallyTextView,主要是用于展示loading...文字的自定义view</strong></p>    <pre>  <code class="language-java">public class GraduallyTextView extends EditText {  private CharSequence text;//自定义的文本  private int startY = 0;  private float progress;//读取进度条  private boolean isLoading;//判断是否正在读取  private Paint mPaint;  private int textLength;//设置文本长度  private boolean isStop = true;  private float scaleX;//设置缩放比例  private int duration = 2000;  private float sigleDuration;    private ValueAnimator valueAnimator;//设置属性平移、缩放动画      public GraduallyTextView(Context context) {      super(context);      init();  }      public GraduallyTextView(Context context, AttributeSet attrs) {      super(context, attrs);      init();  }      public GraduallyTextView(Context context, AttributeSet attrs, int defStyleAttr) {      super(context, attrs, defStyleAttr);      init();  }      public void init() {      mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);      mPaint.setStyle(Paint.Style.FILL);      setBackground(null);      setCursorVisible(false);      setFocusable(false);      setEnabled(false);      setFocusableInTouchMode(false);      //设置平移动画      valueAnimator = ValueAnimator.ofFloat(0, 100).setDuration(duration);      valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());      valueAnimator.setRepeatCount(Animation.INFINITE);      valueAnimator.setRepeatMode(ValueAnimator.RESTART);      valueAnimator.addUpdateListener(              new ValueAnimator.AnimatorUpdateListener() {                  @Override                  public void onAnimationUpdate(ValueAnimator animation) {                 //设置监听,当动画更新的时候触发重绘                      progress = (Float) animation.getAnimatedValue();                      GraduallyTextView.this.invalidate();                  }              });  }    //设置开始读取的方法  public void startLoading() {      if (!isStop) {          return;      }      textLength = getText().length();      if (TextUtils.isEmpty(getText().toString())) {          return;      }      isLoading = true;      isStop = false;      text = getText();        scaleX = getTextScaleX() * 10;      startY = 88;      mPaint.setColor(getCurrentTextColor());      mPaint.setTextSize(getTextSize());      setMinWidth(getWidth());      setText("");      setHint("");      valueAnimator.start();      sigleDuration = 100f / textLength;  }    //停止loading的方法  public void stopLoading() {      isLoading = false;      valueAnimator.end();      valueAnimator.cancel();      isStop = true;      setText(text);  }    //设置时长  public void setDuration(int duration) {      this.duration = duration;  }      @Override  protected void onVisibilityChanged(View changedView, int visibility) {      super.onVisibilityChanged(changedView, visibility);      if (!isLoading) {          return;      }      if (visibility == View.VISIBLE) {          valueAnimator.resume();      }      else {          valueAnimator.pause();      }  }    //重写ondraw方法,如果还在loading,而且进度还小于1,则让它和透明度联动  @Override protected void onDraw(Canvas canvas) {      super.onDraw(canvas);      if (!isStop) {          mPaint.setAlpha(255);          if (progress / sigleDuration >= 1) {              canvas.drawText(String.valueOf(text), 0,                      (int) (progress / sigleDuration), scaleX, startY,                      mPaint);          }          mPaint.setAlpha(                  (int) (255 * ((progress % sigleDuration) / sigleDuration)));          int lastOne = (int) (progress / sigleDuration);          if (lastOne < textLength) {              canvas.drawText(String.valueOf(text.charAt(lastOne)), 0, 1,                      scaleX + getPaint().measureText(                              text.subSequence(0, lastOne).toString()),                      startY, mPaint);              }           }        }      }</code></pre>    <p><strong>5.自定义眼睛旋转的EyelidView,</strong></p>    <pre>  <code class="language-java">public class EyelidView extends View {      private float progress;      private boolean isLoading;      private Paint mPaint;      private boolean isStop = true;      private int duration = 1000;      private ValueAnimator valueAnimator;      private boolean isFromFull;      public EyelidView(Context context) {          super(context);          init();      }      public EyelidView(Context context, AttributeSet attrs) {          super(context, attrs);          init();      }      public EyelidView(Context context, AttributeSet attrs, int defStyleAttr) {          super(context, attrs, defStyleAttr);          init();      }      public void init() {          mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);          mPaint.setColor(Color.BLACK);          mPaint.setStyle(Paint.Style.FILL);          setBackground(null);          setFocusable(false);          setEnabled(false);          setFocusableInTouchMode(false);          valueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(duration);          valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());          valueAnimator.setRepeatCount(Animation.INFINITE);          valueAnimator.setRepeatMode(ValueAnimator.REVERSE);          valueAnimator.addUpdateListener(                new ValueAnimator.AnimatorUpdateListener() {                    @Override                    public void onAnimationUpdate(ValueAnimator animation) {                        progress = (float) animation.getAnimatedValue();                        invalidate();                    }                });        }    //可以设置画笔颜色      public void setColor(int color) {          mPaint.setColor(color);      }    //定义开始读取方法      public void startLoading() {            if (!isStop) {              return;          }        isLoading = true;        isStop = false;        valueAnimator.start();      }        public void resetAnimator(){          valueAnimator.start();      }    //停止读取方法,与生命周期绑定      public void stopLoading() {          isLoading = false;          valueAnimator.end();          valueAnimator.cancel();          isStop = true;      }        public void setDuration(int duration) {          this.duration = duration;    }      //重写当eyeview可见状态改变时候的动画      @Override        protected void onVisibilityChanged(View changedView, int visibility) {            super.onVisibilityChanged(changedView, visibility);              if (!isLoading) {                  return;            }              if (visibility == View.VISIBLE) {                valueAnimator.resume();            }              else {            valueAnimator.pause();              }        }       public void setFromFull(boolean fromFull) {          isFromFull = fromFull;      }        @Override     protected void onDraw(Canvas canvas) {        super.onDraw(canvas);            if (!isStop) {              float bottom = 0.0f;              if (!isFromFull) {                bottom = progress * getHeight();              }              else {                  bottom = (1.0f - progress) * getHeight();            }            bottom = bottom >= (getHeight() / 2) ? (getHeight() / 2) : bottom;            canvas.drawRect(0, 0, getWidth(), bottom, mPaint);          }      }      private boolean whenStop() {          return (isLoading == false && progress <= 0.001f);          }      }</code></pre>    <p><strong>6.编写,loadingview的核心类CatLoadingView</strong></p>    <pre>  <code class="language-java">public class CatLoadingView extends DialogFragment {       public CatLoadingView() {    }  Animation  operatingAnim, eye_left_Anim, eye_right_Anim;  Dialog mDialog;  View mouse, eye_left, eye_right;  EyelidView eyelid_left, eyelid_right;  GraduallyTextView mGraduallyTextView;    @Override public Dialog onCreateDialog(Bundle savedInstanceState) {      if (mDialog == null) {            //以1步骤的样式创建一个dialog并填充2步骤的布局          mDialog = new Dialog(getActivity(), R.style.cart_dialog);          mDialog.setContentView(R.layout.catloading_main);          mDialog.setCanceledOnTouchOutside(true);          mDialog.getWindow().setGravity(Gravity.CENTER);        //设置旋转动画,以圆心为准点进行旋转,时长2s          operatingAnim = new RotateAnimation(360f, 0f,                  Animation.RELATIVE_TO_SELF, 0.5f,                  Animation.RELATIVE_TO_SELF, 0.5f);          operatingAnim.setRepeatCount(Animation.INFINITE);          operatingAnim.setDuration(2000);        //创建5中的左侧眼睛旋转动画          eye_left_Anim = new RotateAnimation(360f, 0f,                  Animation.RELATIVE_TO_SELF, 0.5f,                  Animation.RELATIVE_TO_SELF, 0.5f);          eye_left_Anim.setRepeatCount(Animation.INFINITE);          eye_left_Anim.setDuration(2000);           //创建5中的右侧眼睛旋转动画          eye_right_Anim = new RotateAnimation(360f, 0f,                  Animation.RELATIVE_TO_SELF, 0.5f,                  Animation.RELATIVE_TO_SELF, 0.5f);          eye_right_Anim.setRepeatCount(Animation.INFINITE);          eye_right_Anim.setDuration(2000);          //设置水平插值器          LinearInterpolator lin = new LinearInterpolator();          operatingAnim.setInterpolator(lin);          eye_left_Anim.setInterpolator(lin);          eye_right_Anim.setInterpolator(lin);            View view = mDialog.getWindow().getDecorView();            mouse = view.findViewById(R.id.mouse);            eye_left = view.findViewById(R.id.eye_left);            eye_right = view.findViewById(R.id.eye_right);            eyelid_left = (EyelidView) view.findViewById(R.id.eyelid_left);            eyelid_left.setColor(Color.parseColor("#d0ced1"));            eyelid_left.setFromFull(true);            eyelid_right = (EyelidView) view.findViewById(R.id.eyelid_right);            eyelid_right.setColor(Color.parseColor("#d0ced1"));            eyelid_right.setFromFull(true);            mGraduallyTextView = (GraduallyTextView) view.findViewById(                  R.id.graduallyTextView);            operatingAnim.setAnimationListener(                  new Animation.AnimationListener() {                      @Override                      public void onAnimationStart(Animation animation) {                      }                          @Override                      public void onAnimationEnd(Animation animation) {                      }                      //加多一个重复动画,当文本走动时,让左侧和右侧眼睛也同时转动                      @Override                      public void onAnimationRepeat(Animation animation) {                          eyelid_left.resetAnimator();                          eyelid_right.resetAnimator();                      }                  });                }               return mDialog;            }         //重写onresume方法,绑定activity的生命周期       @Override public void onResume() {          super.onResume();          mouse.setAnimation(operatingAnim);          eye_left.setAnimation(eye_left_Anim);          eye_right.setAnimation(eye_right_Anim);          eyelid_left.startLoading();          eyelid_right.startLoading();          mGraduallyTextView.startLoading();           }        //重写onPause方法,绑定activity的生命周期       @Override public void onPause() {         super.onPause();         operatingAnim.reset();          eye_left_Anim.reset();          eye_right_Anim.reset();          mouse.clearAnimation();          eye_left.clearAnimation();          eye_right.clearAnimation();            eyelid_left.stopLoading();          eyelid_right.stopLoading();          mGraduallyTextView.stopLoading();      }       //设置关闭loadingview的方法         @Override public void onDismiss(DialogInterface dialog) {           super.onDismiss(dialog);           mDialog = null;           System.gc();           }      }</code></pre>    <p><strong>7.最后一步,你只需要在引用到loadingview的地方,添加</strong></p>    <pre>  <code class="language-java">CatLoadingView mView = new CatLoadingView();  mView.show(getSupportFragmentManager(), "");</code></pre>    <p>OK,这样就可以实现,有个性的loadlingview效果了,大伙,不自己自定义一个么。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/f8a9cfb729f9</p>    <p> </p>